Lumen in CI
lumen is built for CI: one command, one exit-code contract, and two CI-native render formats — --format sarif (2.1.0, validated against the official schema) and --format md (a job summary). Previews on localhost work with --allow-private (the Action infers it for loopback URLs automatically).
GitHub Actions
The composite action under nitishagar/lumen/action starts an optional preview server, audits it, uploads the SARIF artifact, and writes the Markdown summary to $GITHUB_STEP_SUMMARY:
$ - uses: nitishagar/lumen/action@v0.4.0
with:
url: https://example.com
fail-threshold: warning
baseline: lumen.baseline.json # gate only on NEW findings
To surface findings as PR annotations, upload the artifact to code scanning with github/codeql-action/upload-sarif@v3 — see the action README for the full snippet and the pinning note.
Scheduled monitoring (recipe)
lumen has no daemon — the recipe is a cron workflow that commits audit history to a dedicated branch. It's yours to own (copy, adjust the schedule, pin the version):
# .github/workflows/lumen-monitor.yml (your repo)
on:
schedule: [{ cron: "0 6 * * 1" }] # weekly
permissions: { contents: write }
jobs:
audit:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@<pinned-sha>
- run: npx -y @lumen-seo/cli@0 audit https://example.com \
--max-pages 100 --fail-threshold off \
&& npx -y @lumen-seo/cli@0 history audit --json > trend.json
- run: |
git config user.name lumen-monitor && git config user.email bot@users.noreply.github.com
git checkout -B lumen-history
git add trend.json && git commit -m "audit $(date -u +%F)" || true
git push origin lumen-history
Trend lines come from lumen history audit — and the branch history doubles as a shareable record. (--fail-threshold off keeps monitoring non-gating; drop it if you want the cron to alarm.)
SARIF
--format sarif emits one rule per lumen rule (with its rules-reference anchor as helpUri), one result per finding, and the run's provenance (version, budgets, started/completed, incomplete/stopReason) in run.properties. With --out, the artifact is written and stdout stays silent; without it, the document goes to stdout.
$ npx @lumen-seo/cli audit https://example.com --format sarif --out lumen.sarif
--source-map site/src/pages/** adds a best-effort page-URL → source-file mapping for annotations; when the mapping is ambiguous the result stays URL-only — never guessed. The audit seed's own subpath is stripped first, so previews served under a base (Astro base: '/lumen') map to the same files; GitHub code scanning rejects http locations, so a source map is required for annotation uploads.
Job summary
$ npx @lumen-seo/cli audit "$SITE_URL" --format md >> "$GITHUB_STEP_SUMMARY"
The summary carries the score, the top rule groups with fixes (linking the rules reference), and — when baselined — the new/fixed/unknown breakdown.
Baseline gate
Adopt lumen on a site with existing findings: record a baseline once, commit it, and gate only on regressions in CI:
$ npx @lumen-seo/cli audit "$SITE_URL" --update-baseline lumen.baseline.json
# commit lumen.baseline.json, then in CI:
$ npx @lumen-seo/cli audit "$SITE_URL" --baseline lumen.baseline.json
# exit 1 only when NEW findings appear at/above failThreshold
Fingerprints are rule + normalized URL + selector — message tweaks never un-baseline an issue; pages not audited in a run are unknown, never counted as fixed. Compare any two saved reports with lumen diff a.json b.json.
GitLab CI
$ audit:
script:
- npx -y @lumen-seo/cli@0 audit "$SITE_URL" --format sarif --out lumen.sarif
artifacts:
paths: [lumen.sarif]
Any shell
$ npx -y @lumen-seo/cli@0 audit https://example.com \
--format sarif --out lumen.sarif --fail-threshold warning
echo "exit $? # 0 ok · 1 findings at/above threshold · 2 config error"
Exit codes
The contract is stable across every renderer: 0 under threshold, 1 findings at/above failThreshold (or new findings with --baseline; an incomplete run always gates),2 usage/config error.