Home Features Pricing Documentation Services Contact DOWNLOAD
← Back to docs

Quality gates in CI

Use the sitecmd CLI to fail a build when site health regresses. Examples for GitHub Actions, GitLab CI, CircleCI, and more.

A quality gate is a check that fails your build if your site is in worse shape than you’ll accept. SiteCMD’s CLI gives you a simple, exit-code-driven way to do this from any CI system.

This page is the recipe collection. For the underlying CLI behavior, see CLI reference.

The basic gate

sitecmd init https://staging.example.com --yes
sitecmd scan --fail-under 85

That’s the entire pattern. init writes a .sitecmd/config.json if one isn’t there. scan runs the checks. If the score is below 85, the command exits with code 1 and the CI job fails. If the score is 85 or above, it exits 0 and the job passes.

Most teams start here and iterate.

Picking a threshold

Some defaults that work for most projects:

  • Marketing site, low-stakes: --fail-under 75. Catches bad regressions, doesn’t block on polish.
  • Production product site: --fail-under 85. Reasonable bar for a launched site you care about.
  • High-stakes (financial, health, anything regulated): --fail-under 90 and add --type security --fail-under 95 as a second gate.

You can also pin a category-specific gate. sitecmd scan --type security --fail-under 95 only checks security category, and only fails if security drops below 95.

Multi-gate strategy

Run the same scan multiple times with different filters when you want different bars for different categories:

sitecmd scan --type security --fail-under 95
sitecmd scan --type accessibility --fail-under 80
sitecmd scan --fail-under 75

Each line is its own gate. Any one failing fails the build.

GitHub Actions

name: Site health

on:
  push:
    branches: [main]
  pull_request:

jobs:
  sitecmd:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4

      - name: Install SiteCMD CLI
        run: |
          # at launch: install via package manager or direct binary download
          curl -fsSL https://sitecmd.com/install.sh | sh

      - name: Scan staging
        run: |
          sitecmd init https://staging.example.com --yes
          sitecmd scan --fail-under 85

The CLI’s exit code is what GitHub Actions reads. No extra adapter needed.

GitLab CI

sitecmd:
  image: sitecmd/cli:latest
  script:
    - sitecmd init https://staging.example.com --yes
    - sitecmd scan --fail-under 85

The Docker image keeps the CI job light.

CircleCI

jobs:
  sitecmd:
    docker:
      - image: sitecmd/cli:latest
    steps:
      - checkout
      - run:
          name: Scan
          command: |
            sitecmd init https://staging.example.com --yes
            sitecmd scan --fail-under 85

Pre-deploy vs. post-deploy

Two common shapes:

  • Pre-deploy gate: scan staging before promoting to production. Fails the deploy if staging regressed.
  • Post-deploy verification: scan production right after deploy. Surfaces regressions immediately, even if they only manifest with real production config (CDN headers, env vars, third-party scripts).

You typically want both, run independently.

deploy:
  needs: [pre-deploy-scan]
  # ...

pre-deploy-scan:
  run: sitecmd scan --url https://staging.example.com --fail-under 85

post-deploy-scan:
  needs: [deploy]
  run: sitecmd scan --url https://example.com --fail-under 85

Comparing scans (regression-only gates)

If you want to fail on new regressions rather than absolute thresholds, use --diff:

sitecmd scan --diff

This compares against .sitecmd/last-scan.json from the previous run. If new criticals appeared, the exit code reflects that. Useful when you’re improving a site over time and don’t want to block on the legacy baseline.

You’ll need to persist .sitecmd/last-scan.json between CI runs for --diff to work. Most CI tools support cache or artifact-passing for this.

Performance and Core Web Vitals

CWV checks need a headless Chrome and add 5-15 seconds per page. Don’t put them in every PR scan. A typical pattern is a separate nightly job:

nightly-cwv:
  schedule: "0 4 * * *" # daily at 4 AM
  run: sitecmd scan --cwv --fail-under 85

For PR scans, --no-browser explicitly skips browser-based checks and keeps the run fast:

pr-scan:
  run: sitecmd scan --no-browser --fail-under 85

Output for humans

By default, sitecmd scan prints a readable summary to stdout. CI logs are perfectly fine for the day-to-day. If you want structured output for a script or a custom report:

sitecmd scan --json > results.json

Then parse with jq or pipe into whatever you want.

Output for pull request comments

The fix prompts surface is designed for this:

sitecmd fix --all > fixes.md

In GitHub Actions, you can post fixes.md as a PR comment:

- name: Comment fixes on PR
  if: github.event_name == 'pull_request'
  uses: actions/github-script@v7
  with:
    script: |
      const fs = require("fs");
      const body = fs.readFileSync("fixes.md", "utf8");
      github.rest.issues.createComment({
        issue_number: context.issue.number,
        owner: context.repo.owner,
        repo: context.repo.repo,
        body
      });

When the gate fails

A failed gate doesn’t tell you what to fix. The CI log shows the headline (“Score 72, below threshold of 85”) but the details live in the scan JSON. Common pattern:

  1. CI fails the build with the score.
  2. Developer opens the SiteCMD desktop app (if they have it), or downloads results.json from the CI artifacts.
  3. Triage from there.

You can also wire the post-failure step to upload the scan JSON as an artifact:

- name: Upload scan results
  if: failure()
  uses: actions/upload-artifact@v4
  with:
    name: sitecmd-scan
    path: .sitecmd/last-scan.json

What CI gates can’t do

  • They can’t verify fixes. The CLI runs against a URL; it doesn’t know whether your fix in this PR actually shipped to the URL it’s scanning. If your CI scans staging and the PR hasn’t been merged into staging, you’re scanning the previous version of the site.
  • They can’t use tier-gated features. Cross-source correlation, fix guides, and ticket mirroring are desktop-app features tied to a license. The CLI runs free-tier checks only.
  • They can’t replace a real review. A passing gate means the site cleared the threshold, not that it’s perfect. Use gates as a floor, not a ceiling.