From a8f8c8b0f741094a61ca900353aff01b14f67c6a Mon Sep 17 00:00:00 2001 From: Pelle Wessman Date: Thu, 1 Aug 2024 16:25:11 +0200 Subject: [PATCH] ci: github actions workflow improvements (#5177) * ci: use `npm ci` consistently * ci: use `actions/setup-node` cache mechanism * ci: reusable workflow for DRY:er setup * ci: remove unused `delete-runs.yml` * ci: fix matrix definitions * ci: fix matrix definitions, take 2 * ci: ensure they all scripts can use `npm run` See also #5128 * ci: add node 22 to `test-node` * ci: have the coverage run always happen on `lts/*` * ci: set permissions * ci: lock down all non-github actions by hash * ci: use reusable workflow for full tests as well * ci: remove redundant activity types * ci: add one more permissions * ci: use full sha hashes for third party workflows * ci: be consistent in naming * ci: generate the coverage report using nps for now See #5126 * ci: parallelize, run local browser test right away * ci: disable saucelabs for now, too noisy --- .github/workflows/browser-test.yml | 34 +--- .github/workflows/ci.yml | 16 -- .github/workflows/compliance.yml | 2 +- .github/workflows/delete-runs.yml | 21 -- .github/workflows/mocha.yml | 227 ++++------------------ .github/workflows/nightly-site-deploy.yml | 7 +- .github/workflows/npm-script.yml | 94 +++++++++ package.json | 2 + 8 files changed, 152 insertions(+), 251 deletions(-) delete mode 100644 .github/workflows/ci.yml delete mode 100644 .github/workflows/delete-runs.yml create mode 100644 .github/workflows/npm-script.yml diff --git a/.github/workflows/browser-test.yml b/.github/workflows/browser-test.yml index f9535b1669..9e7db64ee7 100644 --- a/.github/workflows/browser-test.yml +++ b/.github/workflows/browser-test.yml @@ -11,34 +11,20 @@ permissions: jobs: test-browser: - # TODO: configure to retain build artifacts in `.karma/` dir name: 'Browser Tests' - timeout-minutes: 20 - runs-on: ubuntu-latest if: contains(github.event.pull_request.labels.*.name, 'run-browser-test') + uses: ./.github/workflows/npm-script.yml + secrets: inherit + with: + npm-script: test.browser + + remove-label: + needs: test-browser + if: always() + runs-on: ubuntu-latest steps: - - uses: actions/setup-node@v4 - with: - node-version: lts/* - - uses: actions/checkout@v4 - with: - ref: ${{ github.event.pull_request.head.sha }} - persist-credentials: false - - name: 'Cache node_modules' - uses: actions/cache@v4 - with: - path: '~/.npm' - key: "ubuntu-latest-node-full-lts-${{ hashFiles('**/package-lock.json') }}" - - name: Install Dependencies - run: npm ci - - name: Run Browser Tests - run: npm start test.browser - env: - SAUCE_USERNAME: '${{secrets.SAUCE_USERNAME}}' - SAUCE_ACCESS_KEY: '${{secrets.SAUCE_ACCESS_KEY}}' - name: remove 'run-browser-test' label - uses: buildsville/add-remove-label@v2.0.1 - if: ${{ always() }} + uses: buildsville/add-remove-label@ac59c9f0aeb66eb12d6366eb1d69ec1906e9ef9a with: token: ${{secrets.GITHUB_TOKEN}} label: run-browser-test diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml deleted file mode 100644 index 31e3b3d9f9..0000000000 --- a/.github/workflows/ci.yml +++ /dev/null @@ -1,16 +0,0 @@ -jobs: - lint: - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@v4 - - uses: actions/setup-node@v4 - - run: npm install --ignore-scripts - - run: npm run lint - -name: CI - -on: - pull_request: ~ - push: - branches: - - main diff --git a/.github/workflows/compliance.yml b/.github/workflows/compliance.yml index 99afe754fa..cf346a3898 100644 --- a/.github/workflows/compliance.yml +++ b/.github/workflows/compliance.yml @@ -2,7 +2,7 @@ jobs: compliance: runs-on: ubuntu-latest steps: - - uses: mtfoley/pr-compliance-action@main + - uses: mtfoley/pr-compliance-action@11b664f0fcf2c4ce954f05ccfcaab6e52b529f86 with: body-auto-close: false ignore-team-members: false diff --git a/.github/workflows/delete-runs.yml b/.github/workflows/delete-runs.yml deleted file mode 100644 index 326f8424b7..0000000000 --- a/.github/workflows/delete-runs.yml +++ /dev/null @@ -1,21 +0,0 @@ -name: Delete workflow runs -on: - workflow_dispatch: - inputs: - days: - description: 'Number of days' - type: number - required: true - default: 180 - -jobs: - del_runs: - runs-on: ubuntu-latest - steps: - - name: Delete workflow runs - uses: Mattraks/delete-workflow-runs@main - with: - token: ${{ github.token }} - repository: ${{ github.repository }} - retain_days: ${{ github.event.inputs.days }} - keep_minimum_runs: 10 diff --git a/.github/workflows/mocha.yml b/.github/workflows/mocha.yml index 33e357fd9a..3897f1dac2 100644 --- a/.github/workflows/mocha.yml +++ b/.github/workflows/mocha.yml @@ -1,205 +1,56 @@ name: Tests + on: push: branches: - - '**' - - '!mochajs.org' + - main paths-ignore: ['*.md', 'docs/**'] - tags-ignore: - - '**' pull_request: - types: [opened, synchronize, reopened] + branches: + - main paths-ignore: ['*.md', 'docs/**'] +permissions: + contents: read + jobs: - prevent-double-run: - # skip 'push' event when an open PR exists - name: Prevent double run - runs-on: ubuntu-latest - outputs: - pr-id: ${{ steps.findPr.outputs.number }} - steps: - - name: Check event pull_request - if: github.event_name == 'pull_request' - run: 'echo pull_request: run workflow' - - uses: actions/checkout@v4 - if: github.event_name == 'push' - with: - persist-credentials: false - - name: Check event push - id: findPr - if: github.event_name == 'push' - uses: jwalton/gh-find-current-pr@v1 - with: - state: open + lint: + uses: ./.github/workflows/npm-script.yml + with: + npm-script: lint smoke: - name: 'Smoke [Node.js v${{ matrix.node }} / ${{ matrix.os }}]' - needs: prevent-double-run - if: needs.prevent-double-run.outputs.pr-id == '' - runs-on: '${{ matrix.os }}' - strategy: - matrix: - os: - - ubuntu-latest - - windows-2019 - node: - - 14 - - 16 - - 18 - - 20 - steps: - - uses: actions/checkout@v4 - with: - persist-credentials: false - - uses: actions/setup-node@v4 - with: - node-version: '${{ matrix.node }}' - - run: npm install --production - - run: npm run test:smoke - - lint: - name: Linting code and markdown - runs-on: ubuntu-latest - needs: smoke - steps: - - uses: actions/setup-node@v4 - with: - node-version: lts/* - - uses: actions/checkout@v4 - with: - persist-credentials: false - - name: 'Cache node_modules' - uses: actions/cache@v4 - with: - path: '~/.npm' - key: "ubuntu-latest-node-lts-${{ hashFiles('**/package-lock.json') }}" - - name: Install Dependencies - run: npm ci --ignore-scripts - - name: 'Check lint' - run: npm start lint + uses: ./.github/workflows/npm-script.yml + with: + os: 'ubuntu-latest,windows-2019' + node-versions: '14,16,18,20,22' + npm-script: test:smoke test-node: - name: 'Node.js [v${{ matrix.node }} / ${{ matrix.os }}]' + # TODO: Restore "mocha-github-actions-reporter" style reporting without relying on third party module + uses: ./.github/workflows/npm-script.yml needs: smoke - runs-on: '${{ matrix.os }}' - env: - NODE_OPTIONS: '--trace-warnings' - strategy: - matrix: - os: - - ubuntu-latest - - windows-2019 - node: - - 14 - - 16 - - 18 - - 20 - include: - - os: ubuntu-latest - node: 16 - env: - COVERAGE: 1 - steps: - - uses: actions/setup-node@v4 - with: - node-version: '${{ matrix.node }}' - - uses: actions/checkout@v4 - with: - persist-credentials: false - - name: Get npm cache directory in Windows - id: npm-cache - if: ${{ matrix.os == 'windows-2019' }} - run: | - echo "dir=$(npm config get cache)" >> $env:GITHUB_OUTPUT - - name: 'Cache node_modules' - uses: actions/cache@v4 - with: - path: ${{ matrix.os == 'ubuntu-latest' && '~/.npm' || steps.npm-cache.outputs.dir }} - key: "${{ matrix.os }}-node-v${{ matrix.node }}-${{ hashFiles('**/package-lock.json') }}" - - name: Install Dependencies - run: npm ci --ignore-scripts - - name: Install Annotation Support - run: npm install mocha-github-actions-reporter - - name: Run All Node.js Tests - run: npm start test.node - env: - COVERAGE: '${{ matrix.env.COVERAGE }}' - MOCHA_REPORTER: mocha-github-actions-reporter - # this is so mocha-github-actions-reporter can find mocha - NODE_PATH: lib - - name: Generate Coverage Report (Linux + Node.js latest) - if: ${{ matrix.env.COVERAGE }} - run: npm start coverage-report-lcov - - name: Upload Coverage to Coveralls (Linux + Node.js latest) - if: ${{ matrix.env.COVERAGE }} - uses: coverallsapp/github-action@master - with: - github-token: '${{ secrets.GITHUB_TOKEN }}' + with: + os: 'ubuntu-latest,windows-2019' + node-versions: '14,16,18,20,22' + npm-script: test:node + coverage: true test-browser-local: - name: Browser Test [ChromeHeadless] - needs: smoke - runs-on: ubuntu-latest - timeout-minutes: 20 - # Don't run forked 'pull_request' without saucelabs token - if: github.event_name == 'push' || !github.event.pull_request.head.repo.fork - steps: - - uses: actions/setup-node@v4 - with: - node-version: lts/* - - uses: actions/checkout@v4 - with: - persist-credentials: false - - name: 'Cache node_modules' - uses: actions/cache@v4 - with: - path: '~/.npm' - # this key is different than above, since we are running scripts - # (builds, postinstall lifecycle hooks, etc.) - key: "ubuntu-latest-node-full-lts-${{ hashFiles('**/package-lock.json') }}" - - name: Install Dependencies - run: npm ci - - name: Run Browser Tests - run: npm start test.browser - env: - BROWSER: ChromeHeadless + uses: ./.github/workflows/npm-script.yml + with: + browsers: ChromeHeadless + npm-script: test:browser - test-browser-saucelabs: - name: Browser Tests on SauceLabs [${{ matrix.browser }}] - needs: - - smoke - - test-browser-local - runs-on: ubuntu-latest - timeout-minutes: 20 - strategy: - matrix: - browser: - - firefox@latest - - chrome@latest - - MicrosoftEdge@latest - - safari@latest - # Don't run forked 'pull_request' without saucelabs token - if: github.event_name == 'push' || !github.event.pull_request.head.repo.fork - steps: - - uses: actions/setup-node@v4 - with: - node-version: lts/* - - uses: actions/checkout@v4 - with: - persist-credentials: false - - name: 'Cache node_modules' - uses: actions/cache@v4 - with: - path: '~/.npm' - # this key is different than above, since we are running scripts - # (builds, postinstall lifecycle hooks, etc.) - key: "ubuntu-latest-node-full-lts-${{ hashFiles('**/package-lock.json') }}" - - name: Install Dependencies - run: npm ci - - name: Run Browser Tests - run: npm start test.browser - env: - SAUCE_USERNAME: ${{ secrets.SAUCE_USERNAME }} - SAUCE_ACCESS_KEY: ${{ secrets.SAUCE_ACCESS_KEY }} - BROWSER: ${{ matrix.browser }} + # test-browser-saucelabs: + # # Don't run forked 'pull_request' without saucelabs token + # if: github.event_name == 'push' || !github.event.pull_request.head.repo.fork + # needs: + # - lint + # - smoke + # - test-browser-local + # uses: ./.github/workflows/npm-script.yml + # secrets: inherit + # with: + # browsers: 'firefox@latest,chrome@latest,MicrosoftEdge@latest,safari@latest' + # npm-script: test:browser diff --git a/.github/workflows/nightly-site-deploy.yml b/.github/workflows/nightly-site-deploy.yml index bd1ac0e3af..f81d2fc814 100644 --- a/.github/workflows/nightly-site-deploy.yml +++ b/.github/workflows/nightly-site-deploy.yml @@ -2,15 +2,20 @@ # This updates the list of supporters name: Nightly mochajs.org Deploy + on: schedule: - cron: '0 0 * * *' + +permissions: + contents: read + jobs: deploy: runs-on: ubuntu-latest steps: - name: Webhook Action - uses: joelwmale/webhook-action@2.3.2 + uses: joelwmale/webhook-action@448a17bf857ead98546cfbdbe3b9d4cf979dda95 env: data: '' WEBHOOK_URL: ${{ secrets.NETLIFY_NIGHTLY_DEPLOY_URL }} diff --git a/.github/workflows/npm-script.yml b/.github/workflows/npm-script.yml new file mode 100644 index 0000000000..bed511cf60 --- /dev/null +++ b/.github/workflows/npm-script.yml @@ -0,0 +1,94 @@ +name: Reusable npm script runner + +on: + workflow_call: + inputs: + npm-script: + description: 'npm script' + required: true + type: string + browsers: + description: 'A comma separated list of browser names to test with.' + required: false + type: string + node-versions: + description: 'A comma separated list of Node versions to test with.' + required: false + type: string + os: + description: 'A comma separated list of operating systems to test on.' + required: false + type: string + coverage: + description: 'Whether to set up coverage reporting or not' + required: false + type: boolean + +permissions: + contents: read + +jobs: + resolve-inputs: + runs-on: ubuntu-latest + outputs: + browsers: ${{ steps.split-browsers.outputs.splitted }} + nodeVersions: ${{ steps.split-node-versions.outputs.splitted }} + os: ${{ steps.split-os.outputs.splitted }} + steps: + - id: split-browsers + if: inputs.browsers + run: echo "splitted=$(echo '${{ inputs.browsers }}' | jq -R -c 'split(",")')" >> $GITHUB_OUTPUT + - id: split-node-versions + if: inputs.node-versions + run: echo "splitted=$(echo '${{ inputs.node-versions }}' | jq -R -c 'split(",")')" >> $GITHUB_OUTPUT + - id: split-os + if: inputs.os + run: echo "splitted=$(echo '${{ inputs.os }}' | jq -R -c 'split(",")')" >> $GITHUB_OUTPUT + + script: + name: ${{ inputs.npm-script }}${{ needs.resolve-inputs.outputs.browsers && format('[{0}]', matrix.browser) }}${{ needs.resolve-inputs.outputs.nodeVersions && format(' with node.js {0}', matrix.node_version) }}${{ needs.resolve-inputs.outputs.os && format(' on {0}', matrix.os) }} + runs-on: ${{ matrix.os }} + timeout-minutes: 20 + needs: + - resolve-inputs + strategy: + fail-fast: false + matrix: + node_version: ${{ fromJson(needs.resolve-inputs.outputs.nodeVersions || '["lts/*"]') }} + os: ${{ fromJson(needs.resolve-inputs.outputs.os || '["ubuntu-latest"]') }} + browser: ${{ fromJson(needs.resolve-inputs.outputs.browsers || '[""]') }} + steps: + - uses: actions/checkout@v4 + with: + persist-credentials: false + - uses: actions/setup-node@v4 + with: + node-version: ${{ matrix.node_version }} + cache: 'npm' + - run: npm ci --ignore-scripts + - run: npm run ${{ inputs.npm-script }} + env: + BROWSER: ${{ matrix.browser }} + COVERAGE: ${{ inputs.coverage && '1'}} + NODE_OPTIONS: '--trace-warnings' + SAUCE_USERNAME: ${{ secrets.SAUCE_USERNAME }} + SAUCE_ACCESS_KEY: ${{ secrets.SAUCE_ACCESS_KEY }} + - name: Generate coverage report + if: always() && inputs.coverage + run: npm start coverage-report-lcov + - name: Coveralls Parallel + if: always() && inputs.coverage + uses: coverallsapp/github-action@v2 + with: + flag-name: run-${{ join(matrix.*, '-') }} + parallel: true + + upload-coverage: + needs: script + if: always() && inputs.coverage + runs-on: ubuntu-latest + steps: + - name: Coveralls Finished + uses: coverallsapp/github-action@v2 + with: + parallel-finished: true diff --git a/package.json b/package.json index a638d3cc95..590c1fb9e5 100644 --- a/package.json +++ b/package.json @@ -48,6 +48,8 @@ "start": "nps", "test": "nps test", "version": "nps version", + "test:browser": "nps test.browser", + "test:node": "nps test.node", "test:smoke": "node ./bin/mocha --no-config test/smoke/smoke.spec.js" }, "dependencies": {