name: CI on: push: branches: [main] # Releases are tag pushes (`scripts/release.sh` tags bare `x.y.z`). A # `branches` filter alone matches no tag ref, so without this both the # tag-gated `publish` job and the coverage publish step never fire. tags: ["*"] pull_request: branches: [main] workflow_dispatch: {} jobs: build: runs-on: ubuntu-latest # Bind-mount the shared pages tree so the coverage step below can write # into it. The runner whitelists this path via `container.valid_volumes` # (docker-space `setup/gitea.sh`); `image` is omitted on purpose so the # runner keeps using its default job image. container: volumes: - /data/gitea-pages:/data/gitea-pages steps: - uses: actions/checkout@v4 - uses: actions/setup-node@v4 with: node-version-file: .node-version cache: "npm" - run: npm ci - run: npm run build - run: npm run check - run: npm run test:ci # Publish this tag's coverage to the self-hosted pages server, # served read-only at # https://pages.e1nsnull.de////coverage/. Wipe only # this tag's `coverage/`, so sibling docs/landing trees and older # tags survive; pruning stale tags is a manual chore. The # precompress pass emits `.br` / `.gz` / `.zst` sidecars next to # every text asset, so `static-web-server` can serve the precompressed # variant and keep the original as fallback. - name: Publish coverage to the pages server if: startsWith(gitea.ref, 'refs/tags/') env: REPO: ${{ github.repository }} REF: ${{ gitea.ref }} run: | TAG="${REF#refs/tags/}" DEST="/data/gitea-pages/${REPO}/${TAG}/coverage" rm -rf "${DEST}" mkdir -p "${DEST}" cp -R coverage/. "${DEST}/" node --strip-types scripts/precompress.ts "${DEST}" echo "Coverage: https://pages.e1nsnull.de/${REPO}/${TAG}/coverage/" # Fast, offline packaging gate. `attw` stays in `publish` (it needs # a pack + full resolution matrix); `publint` packs too but is cheap # enough to run on every push so a packaging break fails here, not # at release time. - run: npm run publish:publint # Persist the exact dist/ that `check`, `test:ci` and `publint` were # run against, so `publish` ships those bytes instead of rebuilding # (which could in principle differ and would pay the build twice). - uses: actions/upload-artifact@v4 with: name: dist path: dist/ # Advisory scans (dead code, dependency freshness). Non-blocking: surfaced in # the Actions tab for visibility, but must never gate a merge — so # continue-on-error and intentionally NOT in `publish`'s `needs`. maintain: runs-on: ubuntu-latest continue-on-error: true steps: - uses: actions/checkout@v4 - uses: actions/setup-node@v4 with: node-version-file: .node-version cache: "npm" - run: npm ci - run: npm run maintain publish: if: startsWith(gitea.ref, 'refs/tags/') needs: build runs-on: ubuntu-latest # The release page is created with the run's automatic Gitea token # (`github.token`), not `NPM_TOKEN`, so it needs `contents: write`. permissions: contents: write steps: # Double-gate: publish only runs on a tag *and* aborts here if NPM_TOKEN # is unset, so a tag push never silently no-ops (or half-publishes). Set # NPM_TOKEN in the Gitea repo: Settings → Actions → Secrets. - name: Assert NPM_TOKEN is configured run: | if [ -z "${{ secrets.NPM_TOKEN }}" ]; then echo "::error::NPM_TOKEN secret is not set — refusing to publish." exit 1 fi - uses: actions/checkout@v4 - uses: actions/setup-node@v4 with: node-version-file: .node-version registry-url: "https://registry.npmjs.org/" - run: npm ci # Consume the dist/ that `build` produced and gated, instead of # rebuilding here — `publish` must ship the tested artifact. - uses: actions/download-artifact@v4 with: name: dist path: dist/ - run: npm run publish:publint - run: npm run publish:attw # The Gitea release page is created *before* `npm publish` on # purpose: a broken page then fails CI without burning an npm # version. The page is cheap to retry, a published version is not. # The body is the matching Keep-a-Changelog section; an unknown tag # makes the extractor exit non-zero, so the page can never go up # empty. - name: Extract release notes from CHANGELOG.md env: TAG_REF: ${{ gitea.ref }} run: ./scripts/release-notes.sh "${TAG_REF#refs/tags/}" > release-notes.md - uses: https://gitea.com/actions/gitea-release-action@v1 with: body_path: release-notes.md - run: npm publish --access public env: NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }}