CLI

Diff & CI

Compare a report against a base one and wire ranklint into GitLab or GitHub pipelines.

ranklint diff

FlagDescription
--basepath to report.json or a git ref — then the report is pulled from CI artifacts
--currentcurrent report.json
--reportermarkdown | json
--outputto a file

The CI adapter picks by environment: GITHUB_ACTIONS=true → GitHub (artifact by name, zip unpacked by a built-in reader), otherwise GitLab (branch job artifact). Error policy: no CI context → warn + full "first run" report (a missing base is not an error); a download error (401/403/network) → hard fail, so an expired token can't hide behind an eternal first run. Exit 1 only on new error issues.

Diff & CI

Issue key: checkId + url without origin + selector — stable across environments (UAT compares against prod). diffReports → newIssues / fixedIssues / pagesDelta.

Base report storages (ReportStorage): fs (a JSON directory, latest by mtime), GitLab artifacts (read-only, branch job artifact), GitHub artifacts (read-only: list by name, branch filter, zip download — a built-in minimal zip reader: EOCD → central directory → inflateRawSync; corrupt input → null, not an exception), S3 (for the monitor; the SDK is an optional peer, any S3-compatible endpoint).

Ready-made CI presets: presets/gitlab-ci/seo.yml (jobs .ranklint-audit, .ranklint-diff with an automatic MR comment, .ranklint-lighthouse, .ranklint-monitor with Playwright and report caches) and presets/github-actions/seo.yml, presets/github-actions/monitor.yml (scheduled monitor with report cache). The github reporter emits PR annotations.

SARIF for GitHub Code Scanning

SARIF is the format GitHub reads on the Security → Code scanning tab. ranklint audit --reporter sarif writes a SARIF 2.1.0 document: one run, tool.driver.name: ranklint, one rule per checkId with helpUri pointing at the rule docs, one result per issue with level (error / warning / note), the page URL as the location and a stable partialFingerprints.primaryLocationLineHash built from the diff key checkId + path + selector, so GitHub keeps an alert across runs instead of opening a new one.

ranklint audit --url "$URL" --reporter sarif --output ranklint.sarif
permissions:
  security-events: write

steps:
  - run: ranklint audit --url "$URL" --reporter sarif --output ranklint.sarif
  - uses: github/codeql-action/upload-sarif@v3
    with:
      sarif_file: ranklint.sarif
      category: ranklint

In the reusable preset presets/github-actions/seo.yml this is behind the sarif: true input. Findings land in Security → Code scanning, filtered by tool ranklint. Locations are absolute page URLs, not repository files, which SARIF allows; alerts are grouped by rule id.

Sticky comment in a PR or MR

markdownDiff starts its output with the hidden marker <!-- ranklint-diff -->. Presets use it to update the previous comment instead of appending a new one on every pipeline run.

GitLab: the .ranklint-diff job pages through GET /merge_requests/:iid/notes (per_page=100), picks the note whose body starts with the marker and sends PUT .../notes/:note_id; when nothing matches it falls back to POST. The lookup needs jq in the image and RANKLINT_GITLAB_TOKEN with api scope.

GitHub: the Comment on PR step lists the PR comments via actions/github-script, looks for the same marker and calls updateComment or createComment. The step needs pull-requests: write.

GitLab CI

include:
  - remote: 'https://raw.githubusercontent.com/aakazancev/ranklint/main/presets/gitlab-ci/seo.yml'

seo:audit:
  extends: .ranklint-audit
  needs: [deploy:uat]
  variables:
    RANKLINT_URL: $CI_ENVIRONMENT_URL

ranklint diff --base main resolves the base report from CI artifacts: in GitLab — from the branch's job artifact, in GitHub Actions — from the ranklint-report artifact (zip extraction is built in). A missing base is not an error: diff degrades to a full report.