Skip to content

Build and test code #1007

Build and test code

Build and test code #1007

Workflow file for this run

name: Build and test code
on:
push:
branches: [main]
pull_request:
# lets a maintainer re-run the main baseline on demand
workflow_dispatch:
# tide merges with github.token don't trigger push runs on main;
# the daily schedule keeps a build-test baseline on the merged result.
# GitHub emails scheduled-run failures to whoever last edited the cron line.
schedule:
- cron: '17 6 * * *'
permissions:
contents: read
# One run per pull request: a newer push supersedes its pending run. The PR
# number keys the group, so same-named branches across forks never collide.
# Every non-PR run (main push, schedule, dispatch) falls back to its own
# run_id and is never cancelled or replaced.
concurrency:
group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.run_id }}
cancel-in-progress: true
jobs:
build-test:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
- uses: actions/setup-node@949feb2413d6458794dcd2491c4babbbce0c15c1 # v7.1.0
with:
node-version-file: package.json
cache: npm
- run: npm ci
- run: npm run build
- run: npm run lint
- run: npm run test:coverage
- name: Coverage summary
if: always()
run: |
[ -f coverage/coverage-summary.json ] || exit 0
# shellcheck disable=SC2016
node -e '
const t = require("./coverage/coverage-summary.json").total;
const row = k => `| ${k} | ${t[k].pct}% | ${t[k].covered}/${t[k].total} |`;
console.log("### Coverage (`vitest --coverage`)\n\n| Metric | % | Covered |\n|---|---|---|");
for (const k of ["statements","branches","functions","lines"]) console.log(row(k));
' >> "$GITHUB_STEP_SUMMARY"
- uses: actions/upload-artifact@cf430e030ddbb5b0abf93d22962f4752f3646cd9 # v7.0.2
if: always()
with:
name: coverage-${{ github.sha }}
path: coverage/
retention-days: 14
if-no-files-found: ignore
- name: Verify committed dist/ matches source
run: |
npm run pack
if [ -n "$(git status --porcelain dist/)" ]; then
echo "::error::dist/ is out of date. Run 'npm run pack' and commit the result."
git --no-pager diff --stat dist/
exit 1
fi
# Dependabot cannot run `npm run pack`, so bundled-dependency bumps always
# fail the dist/ drift gate. The rebuild is split across two jobs so the
# write token never coexists with dependency code (the bumped packages run
# during npm ci/build/pack): this job holds no secrets — credentials are not
# persisted and install scripts are disabled — and hands dist/ to the
# token-holding job below as an artifact.
repack-dist:
if: >-
github.event_name == 'pull_request' &&
github.event.pull_request.user.login == 'dependabot[bot]' &&
github.event.pull_request.head.repo.full_name == github.repository
runs-on: ubuntu-latest
outputs:
changed: ${{ steps.drift.outputs.changed }}
steps:
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
with:
ref: ${{ github.event.pull_request.head.sha }}
persist-credentials: false
- uses: actions/setup-node@949feb2413d6458794dcd2491c4babbbce0c15c1 # v7.1.0
with:
node-version-file: package.json
cache: npm
- run: npm ci --ignore-scripts
- run: npm run build
- run: npm run pack
- name: Detect dist/ drift
id: drift
run: |
if [ -n "$(git status --porcelain dist/)" ]; then
echo "changed=true" >> "$GITHUB_OUTPUT"
else
echo "dist/ is up to date"
echo "changed=false" >> "$GITHUB_OUTPUT"
fi
- uses: actions/upload-artifact@cf430e030ddbb5b0abf93d22962f4752f3646cd9 # v7.0.2
if: steps.drift.outputs.changed == 'true'
with:
name: repacked-dist-${{ github.event.pull_request.head.sha }}
path: dist/
retention-days: 1
# Commits the rebuilt dist/ to the dependabot branch. Runs no npm: the only
# inputs are the artifact from repack-dist and a short-lived App token,
# minted here and scoped by the app installation (contents:write). The App
# must be installed on this repository and its id/key stored as the
# Dependabot secrets REPACK_APP_ID / REPACK_APP_PRIVATE_KEY (dependabot-
# triggered runs only see Dependabot secrets). Pushing with a non-default
# token is also what makes the pushed commit re-trigger build-test.
push-repacked-dist:
needs: repack-dist
if: needs.repack-dist.outputs.changed == 'true'
runs-on: ubuntu-latest
steps:
- uses: actions/create-github-app-token@bcd2ba49218906704ab6c1aa796996da409d3eb1 # v3.2.0
id: app-token
with:
app-id: ${{ secrets.REPACK_APP_ID }}
private-key: ${{ secrets.REPACK_APP_PRIVATE_KEY }}
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
with:
ref: ${{ github.event.pull_request.head.ref }}
token: ${{ steps.app-token.outputs.token }}
- name: Refuse if the branch moved since the rebuild
run: |
if [ "$(git rev-parse HEAD)" != '${{ github.event.pull_request.head.sha }}' ]; then
echo "::error::${{ github.event.pull_request.head.ref }} moved since the rebuild; re-run the workflow"
exit 1
fi
- uses: actions/download-artifact@9000827ccba6bdab643e8b6fd33ac0654aef8333 # v8.0.2
with:
name: repacked-dist-${{ github.event.pull_request.head.sha }}
path: dist/
- name: Commit rebuilt dist/ if it changed
run: |
# the artifact may only touch dist/; refuse anything else
if git status --porcelain | grep -qvE '^.. dist/'; then
echo "::error::artifact changed paths outside dist/"
git status --porcelain | grep -vE '^.. dist/'
exit 1
fi
if [ -z "$(git status --porcelain dist/)" ]; then
echo "dist/ is up to date"; exit 0
fi
git config user.name 'github-actions[bot]'
git config user.email '41898282+github-actions[bot]@users.noreply.github.com'
git add dist/
git commit -s -m 'chore: repack dist/ for dependency bump'
git push --force-with-lease origin 'HEAD:refs/heads/${{ github.event.pull_request.head.ref }}'