diff --git a/.circleci/config.yml b/.circleci/config.yml deleted file mode 100644 index e2bce283..00000000 --- a/.circleci/config.yml +++ /dev/null @@ -1,146 +0,0 @@ -version: 2.1 - -executors: - node: - docker: - - image: circleci/node:10 - firefox: - docker: - - image: circleci/node:10 - - image: selenium/standalone-firefox:3.141.59-20200525 - chrome: - docker: - - image: circleci/node:10 - - image: selenium/standalone-chrome:3.141.59-20200525 - -commands: - checkout_repo: - steps: - - checkout - - run: - name: Checkout submodule - command: | - git submodule sync - git submodule update --init --recursive - - skip_if_only_changed: - parameters: - filter: - type: string - steps: - - run: - name: Skip tests if only "<< parameters.filter >>" are changed - command: git diff --name-only << pipeline.git.base_revision >>... | grep -qvE '<< parameters.filter >>' || circleci step halt - - install_dependencies: - steps: - - restore_cache: - keys: - - yarn-deps-v5-{{ checksum "yarn.lock" }} - - yarn-deps-v5- - - run: - name: Install dependencies - command: yarn --immutable - - save_cache: - key: yarn-deps-v5-{{ checksum "yarn.lock" }} - paths: - - .yarn - - .pnp.js - - screenshot: - parameters: - flags: - type: string - default: "" - steps: - - run: - name: Verify screenshots and generate diffs and new screenshots - command: yarn node dockers/screenshotter/screenshotter.js -b $CIRCLE_JOB --verify --diff --new << parameters.flags >> - -jobs: - test: - executor: node - steps: - - run: - name: Check whether the build is running on the main repository - command: | - if [[ $CIRCLE_PULL_REQUEST && $CIRCLE_PROJECT_USERNAME != "KaTeX" ]]; then - echo "Please disable CircleCI on your forked repository!" - exit 1 - fi - - - checkout_repo - - skip_if_only_changed: - filter: '^docs/|^LICENSE|\.md$' - - install_dependencies - - - run: - name: Lint code - command: yarn test:lint - - skip_if_only_changed: - filter: '^static/|^website/' - - run: - name: Run Flow and Jest tests - command: | - yarn test:flow - yarn test:jest --coverage --runInBand - - run: - name: Upload code coverage reports to Codecov - command: yarn codecov - - screenshotter: - parameters: - browser: - type: executor - browserstack: - type: string - default: "" - executor: << parameters.browser >> - steps: - - checkout_repo - - skip_if_only_changed: - filter: '^docs/|^static/|^website/|^LICENSE|\.md$' - - install_dependencies - - - when: - condition: << parameters.browserstack >> - steps: - - screenshot: - flags: --browserstack --selenium-capabilities '<< parameters.browserstack >>' - - unless: - condition: << parameters.browserstack >> - steps: - - screenshot: - flags: --selenium-ip localhost - - - store_artifacts: - path: test/screenshotter/new - destination: new - - store_artifacts: - path: test/screenshotter/diff - destination: diff - -workflows: - test: - jobs: - - test - - screenshotter: - name: firefox - browser: firefox - - screenshotter: - name: chrome - browser: chrome - - screenshotter: - name: safari - browser: node - browserstack: | - { - "browserName": "Safari", - "browser_version": "13.1", - "os": "OS X", - "os_version": "Catalina" - } - filters: - branches: - # Forked pull requests don't have access to Browserstack credentials - ignore: /pull\/[0-9]+/ diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml new file mode 100644 index 00000000..c30c61cb --- /dev/null +++ b/.github/workflows/ci.yml @@ -0,0 +1,179 @@ +name: CI + +on: + push: + branches: [ master ] + pull_request: + branches: [ master ] + pull_request_target: + branches: [ master ] + types: [ labeled ] # 'test screenshots' label on PRs from fork + +jobs: + test: + runs-on: ubuntu-latest + if: | + github.event_name != 'pull_request_target' && + !contains(toJSON(github.event.commits.*.message), '[skip ci]') && + !contains(toJSON(github.event.commits.*.message), '[ci skip]') + + steps: + - uses: actions/checkout@v2 + with: + submodules: recursive + + - name: Use Node.js 12.x + uses: actions/setup-node@v1 + with: + node-version: '12' + + - name: Cache dependencies + uses: actions/cache@v2 + with: + path: | + .yarn/cache + .pnp.js + key: yarn-deps-v1-${{ hashFiles('yarn.lock') }} + restore-keys: | + yarn-deps-v1- + + - name: Install dependencies + run: yarn --immutable + + - name: Lint code + run: yarn test:lint + + - name: Run Flow + run: yarn test:flow + if: always() + + - name: Run Jest tests + run: yarn test:jest --coverage + if: always() + + - uses: codecov/codecov-action@v1 + with: + directory: ./coverage/ + + screenshotter_dispatcher: + runs-on: ubuntu-latest + if: | + (github.event_name != 'pull_request_target' || + (github.event.pull_request.head.repo.full_name != 'KaTeX/KaTeX' && + contains(github.event.pull_request.labels.*.name, 'test screenshots'))) && + !contains(toJSON(github.event.commits.*.message), '[skip ci]') && + !contains(toJSON(github.event.commits.*.message), '[ci skip]') + outputs: + matrix: ${{ steps.set-matrix.outputs.result }} + + steps: + - id: set-matrix + uses: actions/github-script@v2 + with: + github-token: ${{ secrets.GITHUB_TOKEN }} + script: | + const SELENIUM_BROWSERS = ["chrome", "firefox"]; + const BROWSERSTACK_BROWSERS = [{ + browserName: "safari", + browser_version: "13.1", + os: "OS X", + os_version: "Catalina", + }]; + + const include = []; + + // running selenium doesn't require access to secrets + if (context.eventName !== "pull_request_target") { + include.push(...SELENIUM_BROWSERS.map(name => ({ + browser: name, + services: {selenium: { + image: `selenium/standalone-${name}:3.141.59-20200525`, + ports: ["4444:4444"], + }}, + }))); + } + + // check access to Browserstack crendential secrets + if (context.eventName !== "pull_request" || + context.payload.pull_request.head.repo.full_name === "KaTeX/KaTeX") { + if (context.eventName === "pull_request_target") { + github.issues.removeLabel({ + issue_number: context.issue.number, + owner: context.repo.owner, + repo: context.repo.repo, + name: "test screenshots", + }); + } + include.push(...BROWSERSTACK_BROWSERS.map(capabilities => ({ + browser: capabilities.browserName, + services: {}, + browserstack: capabilities, + }))); + } + + return {browser: include.map(b => b.browser), include}; + + screenshotter: + runs-on: ubuntu-latest + needs: screenshotter_dispatcher + strategy: + matrix: ${{ fromJson(needs.screenshotter_dispatcher.outputs.matrix) }} + fail-fast: false + services: ${{ matrix.services }} + + steps: + - uses: actions/checkout@v2 + if: github.event_name != 'pull_request_target' + with: + submodules: recursive + - uses: actions/checkout@v2 + if: github.event_name == 'pull_request_target' + with: + # pull_request_target is run in the context of the base repository + # of the pull request, so the default ref is master branch and + # ref should be manually set to the head of the PR + ref: refs/pull/${{ github.event.pull_request.number }}/head + submodules: recursive + + - name: Use Node.js 12.x + uses: actions/setup-node@v1 + with: + node-version: '12' + + - name: Cache dependencies + uses: actions/cache@v2 + with: + path: | + .yarn/cache + .pnp.js + key: yarn-deps-v1-${{ hashFiles('yarn.lock') }} + restore-keys: | + yarn-deps-v1- + + - name: Install dependencies + run: yarn --immutable + + - name: Verify screenshots and generate diffs and new screenshots + run: yarn node dockers/screenshotter/screenshotter.js -b ${{ matrix.browser }} --verify --diff --new -c ${{ job.services.selenium.id }} + if: matrix.services.selenium + - name: Verify screenshots and generate diffs and new screenshots + run: yarn node dockers/screenshotter/screenshotter.js -b ${{ matrix.browser }} --verify --diff --new --browserstack --selenium-capabilities '${{ toJson(matrix.browserstack) }}' + if: matrix.browserstack + env: + BROWSERSTACK_USER: ${{ secrets.BROWSERSTACK_USER }} + BROWSERSTACK_ACCESS_KEY: ${{ secrets.BROWSERSTACK_ACCESS_KEY }} + + - name: Print Docker logs + run: docker logs ${{ job.services.selenium.id }} + if: always() && matrix.services.selenium + + - uses: actions/upload-artifact@v2 + if: failure() + with: + name: new-${{ matrix.browser }} + path: test/screenshotter/new + - uses: actions/upload-artifact@v2 + if: failure() + with: + name: diff-${{ matrix.browser }} + path: test/screenshotter/diff diff --git a/README.md b/README.md index 4b63b8ec..d8c41e4c 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,6 @@ # [KaTeX](https://katex.org/) [![npm](https://img.shields.io/npm/v/katex.svg)](https://www.npmjs.com/package/katex) -[![CircleCI](https://circleci.com/gh/KaTeX/KaTeX.svg?style=shield)](https://circleci.com/gh/KaTeX/KaTeX) +[![CI](https://github.com/KaTeX/KaTeX/workflows/CI/badge.svg?branch=master&event=push)](https://github.com/KaTeX/KaTeX/actions?query=workflow%3ACI) [![codecov](https://codecov.io/gh/KaTeX/KaTeX/branch/master/graph/badge.svg)](https://codecov.io/gh/KaTeX/KaTeX) [![Discussions](https://img.shields.io/badge/Discussions-join-brightgreen)](https://github.com/KaTeX/KaTeX/discussions) [![Dependabot Status](https://api.dependabot.com/badges/status?host=github&repo=KaTeX/KaTeX)](https://dependabot.com) diff --git a/dockers/screenshotter/screenshotter.js b/dockers/screenshotter/screenshotter.js index 21074eb9..75e6acfe 100644 --- a/dockers/screenshotter/screenshotter.js +++ b/dockers/screenshotter/screenshotter.js @@ -10,6 +10,7 @@ const pako = require("pako"); const path = require("path"); const selenium = require("selenium-webdriver"); const firefox = require("selenium-webdriver/firefox"); +const chrome = require("selenium-webdriver/chrome"); const istanbulLibCoverage = require('istanbul-lib-coverage'); const istanbulLibReport = require('istanbul-lib-report'); @@ -152,8 +153,12 @@ function guessDockerIPs() { return; } // Native Docker on Linux or remote Docker daemon or similar - const gatewayIP = cmd("docker", "inspect", - "-f", "{{.NetworkSettings.Gateway}}", opts.container); + // https://docs.docker.com/engine/tutorials/networkingcontainers/ + const gatewayIP = cmd("docker", "inspect", // using default bridge network + "-f", "{{.NetworkSettings.Gateway}}", opts.container) + || cmd("docker", "inspect", // using own network + "-f", "{{range .NetworkSettings.Networks}}{{.Gateway}}{{end}}", + opts.container); seleniumIP = seleniumIP || gatewayIP; katexIP = katexIP || gatewayIP; } @@ -277,6 +282,10 @@ function buildDriver() { ffProfile.setPreference("browser.startup.page", 0); const ffOptions = new firefox.Options().setProfile(ffProfile); builder.setFirefoxOptions(ffOptions); + } else if (opts.browser === "chrome") { + // https://stackoverflow.com/questions/48450594/selenium-timed-out-receiving-message-from-renderer + const chrOptions = new chrome.Options().addArguments("--disable-gpu"); + builder.setChromeOptions(chrOptions); } if (seleniumURL) { builder.usingServer(seleniumURL); diff --git a/package.json b/package.json index 9bbfa10b..0f45b572 100644 --- a/package.json +++ b/package.json @@ -36,7 +36,6 @@ "browserslist": "^4.13.0", "browserstack-local": "^1.4.5", "caniuse-lite": "^1.0.30001102", - "codecov": "^3.7.1", "css-loader": "^4.0.0", "cssnano": "^4.1.10", "eslint": "^7.4.0", diff --git a/yarn.lock b/yarn.lock index 93a3a8ad..bf5afe81 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1592,13 +1592,6 @@ __metadata: languageName: node linkType: hard -"@tootallnate/once@npm:1": - version: 1.1.2 - resolution: "@tootallnate/once@npm:1.1.2" - checksum: d030f3fb14e0373dbf5005d8f696ff34fda87bf56744bea611fc737449bfc0687ebcb28ee8ba4c6624877f51b18d701c0d417d793f406006a192f4721911d048 - languageName: node - linkType: hard - "@types/babel__core@npm:^7.0.0, @types/babel__core@npm:^7.1.7": version: 7.1.9 resolution: "@types/babel__core@npm:7.1.9" @@ -2054,15 +2047,6 @@ __metadata: languageName: node linkType: hard -"agent-base@npm:6": - version: 6.0.1 - resolution: "agent-base@npm:6.0.1" - dependencies: - debug: 4 - checksum: 5dbab2ce93cbf858c557c87a7401114ccf6afdd3d1c5c038831798de2be3873356bb1c09067a75e7e1f9a9ba84b4d979d3aec8cab3db87c776f05b5ae693323c - languageName: node - linkType: hard - "aggregate-error@npm:^3.0.0": version: 3.0.1 resolution: "aggregate-error@npm:3.0.1" @@ -2228,13 +2212,6 @@ __metadata: languageName: node linkType: hard -"argv@npm:0.0.2": - version: 0.0.2 - resolution: "argv@npm:0.0.2" - checksum: 09d26b0dc74bfdbc70feb354294d029e2e14cf274b404749b0369b917c0c993219f2b48285eb56b15441df1317b912a3ed09a82ae4f46bee640b68b769f01223 - languageName: node - linkType: hard - "arr-diff@npm:^4.0.0": version: 4.0.0 resolution: "arr-diff@npm:4.0.0" @@ -3384,21 +3361,6 @@ __metadata: languageName: node linkType: hard -"codecov@npm:^3.7.1": - version: 3.7.2 - resolution: "codecov@npm:3.7.2" - dependencies: - argv: 0.0.2 - ignore-walk: 3.0.3 - js-yaml: 3.13.1 - teeny-request: 6.0.1 - urlgrey: 0.4.4 - bin: - codecov: bin/codecov - checksum: ecddd837e3d7519f36aee902122d99cf6ba8bf07c6ffd2b5c8936193a9825d72e5699efd02b9ba87ae942cd14ee8b999034ad5cbfe85d8219387b775ce74d136 - languageName: node - linkType: hard - "collapse-white-space@npm:^1.0.2": version: 1.0.6 resolution: "collapse-white-space@npm:1.0.6" @@ -6047,17 +6009,6 @@ fsevents@^1.2.7: languageName: node linkType: hard -"http-proxy-agent@npm:^4.0.0": - version: 4.0.1 - resolution: "http-proxy-agent@npm:4.0.1" - dependencies: - "@tootallnate/once": 1 - agent-base: 6 - debug: 4 - checksum: 6703aeb5c5d398d93757c38eb0d77df10239ff3fefee27614aad2831f06f9ca6c8b21c43e9ff02464b5284cba3c6cedefffd210750871277ebf652cbe3230566 - languageName: node - linkType: hard - "http-proxy-middleware@npm:0.19.1": version: 0.19.1 resolution: "http-proxy-middleware@npm:0.19.1" @@ -6169,15 +6120,6 @@ fsevents@^1.2.7: languageName: node linkType: hard -"ignore-walk@npm:3.0.3": - version: 3.0.3 - resolution: "ignore-walk@npm:3.0.3" - dependencies: - minimatch: ^3.0.4 - checksum: 08394ce8c47dc086d44ef65a1e1d30352ff3d6605bdec90f59e985b710cc660aafa7975cb30312891d21d826d10b3a8b3210c5d68251678e2dcd366362865170 - languageName: node - linkType: hard - "ignore@npm:^4.0.6": version: 4.0.6 resolution: "ignore@npm:4.0.6" @@ -7422,18 +7364,6 @@ fsevents@^1.2.7: languageName: node linkType: hard -"js-yaml@npm:3.13.1": - version: 3.13.1 - resolution: "js-yaml@npm:3.13.1" - dependencies: - argparse: ^1.0.7 - esprima: ^4.0.0 - bin: - js-yaml: bin/js-yaml.js - checksum: 277157fdf235757b71cfbf24f6bef57576a26d9b4cf89b63d89c9044da7b0f9d16c3629c8b5fd549ae343523727a0df1598794e9a4429763cee4e17056ff8523 - languageName: node - linkType: hard - "js-yaml@npm:^3.12.1, js-yaml@npm:^3.13.1": version: 3.14.0 resolution: "js-yaml@npm:3.14.0" @@ -7671,7 +7601,6 @@ fsevents@^1.2.7: browserslist: ^4.13.0 browserstack-local: ^1.4.5 caniuse-lite: ^1.0.30001102 - codecov: ^3.7.1 commander: ^6.0.0 css-loader: ^4.0.0 cssnano: ^4.1.10 @@ -8590,13 +8519,6 @@ fsevents@^1.2.7: languageName: node linkType: hard -"node-fetch@npm:^2.2.0": - version: 2.6.0 - resolution: "node-fetch@npm:2.6.0" - checksum: dd9f586a9f7ddb7dd94d2aba9cb693d32f5001e9850098512fbc8a4cbdd56838afa08ed0a6725b9fce9b01ec12b713e622cbfc16d92762d8b937b238330a632a - languageName: node - linkType: hard - "node-forge@npm:0.9.0": version: 0.9.0 resolution: "node-forge@npm:0.9.0" @@ -11901,15 +11823,6 @@ fsevents@^1.2.7: languageName: node linkType: hard -"stream-events@npm:^1.0.5": - version: 1.0.5 - resolution: "stream-events@npm:1.0.5" - dependencies: - stubs: ^3.0.0 - checksum: faa9a327e3f8ac366ec7365e6cded01c5de44b834b429d2c8eea8039839269f437c0dd8afb996353b4eed016803292bf490fda78b87fdd8ae1437cff6649bd00 - languageName: node - linkType: hard - "stream-http@npm:^2.7.2": version: 2.8.3 resolution: "stream-http@npm:2.8.3" @@ -12123,13 +12036,6 @@ fsevents@^1.2.7: languageName: node linkType: hard -"stubs@npm:^3.0.0": - version: 3.0.0 - resolution: "stubs@npm:3.0.0" - checksum: 5d58c7b76aa6ac62149166ae43a1fc257cc093fffc2b584cbc09a49c5feb951f4ba05c6832453fad392a3c7665bbe62167c116a17edc8520637d0d65706bef0d - languageName: node - linkType: hard - "style-loader@npm:^1.2.1": version: 1.2.1 resolution: "style-loader@npm:1.2.1" @@ -12354,19 +12260,6 @@ fsevents@^1.2.7: languageName: node linkType: hard -"teeny-request@npm:6.0.1": - version: 6.0.1 - resolution: "teeny-request@npm:6.0.1" - dependencies: - http-proxy-agent: ^4.0.0 - https-proxy-agent: ^4.0.0 - node-fetch: ^2.2.0 - stream-events: ^1.0.5 - uuid: ^3.3.2 - checksum: 8721d128d0ac98043c985f67cd5511fe59f4e31ba0d93512e187c9cf348a51a82722e8f8ed7e1a949a5667f7317ddb40f2f15d57ded193881b3a19e271db0f6e - languageName: node - linkType: hard - "temp-fs@npm:^0.9.9": version: 0.9.9 resolution: "temp-fs@npm:0.9.9" @@ -13006,13 +12899,6 @@ fsevents@^1.2.7: languageName: node linkType: hard -"urlgrey@npm:0.4.4": - version: 0.4.4 - resolution: "urlgrey@npm:0.4.4" - checksum: 9c124ff8c1d705e2606e665c9a5307d85b233f887429bd3398d9dc171d14679f469e274ab8632e976c6faf05c678c39209f3a328932839dfec1837b47bd29d55 - languageName: node - linkType: hard - "use@npm:^3.1.0": version: 3.1.1 resolution: "use@npm:3.1.1"