| | |

Biome 18 🧹 CI Integration — biome ci and pipeline setup

⏱️ Estimated read: 14 min | |

The biome ci command is the read-only variant of the check command, and the distinction is the reason the CI pipeline uses it instead of the check. The check command accepts the --write flag, which means it can modify the files, and the modification is the hazard in the pipeline where the working tree is expected to be the immutable input. The ci command does not provide the --write or the --fix option, and the absence is the mechanism that guarantees the pipeline does not rewrite the source . The ci command also integrates with the specific runner: on the GitHub Actions, the diagnostics are printed as the GitHub annotations, and the annotations are the mechanism that surfaces the errors on the pull request without the log parsing.

The pipeline setup has two layers: the Biome binary and the configuration. The biomejs/setup-biome@v2 action installs the binary directly, and it reads the version from the lockfile, then from the package.json, so the pinned version is the mechanism that keeps the CI and the local tool in sync . The configuration layer is the biome.json, and if the configuration extends an external package, the pipeline must install the Node.js and the project dependencies before the Biome runs, because the external configuration is resolved from the node_modules .

Key point: biome ci is the read-only variant of biome check for the pipelines. It does not accept --write/--fix, it integrates with the runner’s annotations, it allows the thread control, and when the VCS integration is enabled, it uses the --changed flag instead of the --staged. The biomejs/setup-biome@v2 action installs the binary and reads the version from the lockfile. The external configuration requires the Node.js setup and the dependency install before the Biome runs.


Why the ci command exists

The write-hazard problem. The check command accepts the --write flag, and the flag is the convenience for the local development. In the pipeline, the write is the hazard: the job is supposed to verify the code, not to modify it, and the modification would leave the working tree dirty and the subsequent steps would operate on the modified code. The ci command is the mechanism that removes the write option, and the removal is the reason the pipeline uses the ci instead of the check .

The runner-integration problem. The GitHub Actions displays the annotations inline on the pull request when the tool emits the annotation format. The ci command detects the GitHub environment and prints the diagnostics using the GitHub annotations, and the detection is the mechanism that makes the errors visible without the manual log parsing . The check command does not have the same integration, and the integration is the reason the ci exists.

The VCS-integration problem. The pipeline checks the changed files against the default branch, not the staged files, because the remote repository does not have the concept of the staged files. The ci command uses the --changed flag when the VCS integration is enabled, and the --changed is the mechanism that limits the check to the files that differ from the base branch . The check command uses the --staged, which is the local concept, and the distinction is the reason the ci is the pipeline command.

The thread-control problem. The ci command allows the control of the number of the threads, and the control is the mechanism that tunes the resource usage on the CI runner. The check command does not have the same control, and the control is the reason the ci is the choice for the constrained runner.

The strictness problem. The ci command can be combined with the --error-on-warnings flag, and the flag is the mechanism that treats the warnings as the errors and fails the pipeline on the warning. The --max-diagnostics flag is the mechanism that caps the output, and the cap is the reason the pipeline log is readable when the project has many diagnostics . The combination is the strictness, and the strictness is the reason the pipeline catches the warnings that the local run ignores.


a. The GitHub Actions setup

The GitHub Actions is the first-party integration, and the biomejs/setup-biome@v2 action is the mechanism that installs the binary. The minimal workflow is the checkout, the setup, and the biome ci .

# .github/workflows/code-quality.yml — the minimal workflow
name: Code quality

on:
  push:
  pull_request:

jobs:
  quality:
    runs-on: ubuntu-latest
    permissions:
      contents: read
    steps:
      # The checkout fetches the repository.
      - name: Checkout
        uses: actions/checkout@v5

      # The setup-biome action installs the Biome binary.
      # It reads the version from the lockfile, then from package.json.
      - name: Setup Biome
        uses: biomejs/setup-biome@v2

      # The ci command runs the checks in the read-only mode.
      # The diagnostics are printed as the GitHub annotations.
      - name: Run Biome
        run: biome ci .

The biomejs/setup-biome@v2 is the mechanism that installs the binary directly, without the Node.js or the npm . The biome ci . is the mechanism that runs the formatter, the linter, and the import organizer in the read-only mode, and the read-only is the reason the pipeline does not dirty the working tree.

The external configuration is the case where the biome.json extends a package. The setup-biome installs the binary, but the external configuration is resolved from the node_modules, and the node_modules is installed by the package manager. The Node.js setup and the dependency install are the mechanism that makes the external configuration available .

# .github/workflows/code-quality.yml — the external configuration
name: Code quality

on:
  push:
  pull_request:

jobs:
  quality:
    runs-on: ubuntu-latest
    permissions:
      contents: read
    steps:
      - name: Checkout
        uses: actions/checkout@v5

      # The Node.js setup is required for the external configuration.
      - name: Setup Node.js
        uses: actions/setup-node@v4
        with:
          node-version: 22
          cache: "npm"

      # The dependency install provides the external configuration.
      - name: Install dependencies
        run: npm ci

      - name: Setup Biome
        uses: biomejs/setup-biome@v2

      - name: Run Biome
        run: biome ci .

The npm ci is the mechanism that installs the dependencies from the lockfile, and the lockfile is the reason the install is reproducible. The biome ci . runs after the install, and the order is the reason the external configuration is resolved.

The changed-files workflow is the mechanism that limits the check to the files that differ from the base branch. The --changed flag is the mechanism, and the --since flag is the override for the base branch .

# .github/workflows/lint-changed.yml — the changed files only
name: Lint Changed Files

on:
  pull_request:

jobs:
  biome:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
        with:
          fetch-depth: 0 # The full history for the diff

      - uses: biomejs/setup-biome@v2

      # The --changed flag checks only the files that differ from the base.
      - run: biome ci --changed --since=origin/main .

The --changed --since=origin/main is the mechanism that limits the check to the pull request’s changed files. The fetch-depth: 0 is the mechanism that fetches the full history, and the full history is the reason the diff is available.


b. The GitLab CI setup and the reporter

The GitLab CI uses the Biome Docker image, and the reporter is the mechanism that produces the GitLab code quality report. The biome ci --reporter=gitlab is the mechanism that emits the report, and the --colors=off is the mechanism that keeps the output parseable .

# .gitlab-ci.yml — the GitLab CI setup
stages:
  - quality

lint:
  # The Biome Docker image provides the binary.
  image:
    name: ghcr.io/biomejs/biome:latest
    entrypoint: [""] # The entrypoint override for the GitLab CI

  stage: quality

  script:
    # The gitlab reporter emits the code quality JSON.
    # The colors are disabled to keep the output parseable.
    - biome ci --reporter=gitlab --colors=off > /tmp/code-quality.json
    - cp /tmp/code-quality.json code-quality.json

  artifacts:
    paths:
      - code-quality.json # The artifact for the GitLab code quality

  rules:
    - if: $CI_COMMIT_BRANCH
    - if: $CI_MERGE_REQUEST_ID

The ghcr.io/biomejs/biome:latest is the mechanism that provides the binary, and the entrypoint: [""] is the override that makes the image work in the GitLab CI . The --reporter=gitlab is the mechanism that emits the JSON in the format that the GitLab reads, and the artifact is the reason the report appears in the merge request.

The --reporter flag is the general mechanism for the output format. The --reporter=github is the GitHub annotations, the --reporter=json is the raw JSON, and the --reporter=gitlab is the GitLab code quality format . The reporter is the mechanism that adapts the output to the consumer, and the adaptation is the reason the ci command is the pipeline command.

The --error-on-warnings and the --max-diagnostics are the strictness flags. The --error-on-warnings is the mechanism that fails the pipeline on the warning, and the --max-diagnostics=100 is the mechanism that caps the output to the first 100 diagnostics .

# The strict pipeline
- run: biome ci --error-on-warnings --max-diagnostics=100 .

The --error-on-warnings is the mechanism that treats the warnings as the errors, and the --max-diagnostics is the mechanism that keeps the log readable. The two flags are the strictness, and the strictness is the reason the pipeline catches the warnings.

The --enforce-assist flag is the additional mechanism. The import organizer is the assist action, and the assist actions are not the lint diagnostics by default, so the plain biome ci may pass even when the imports are unsorted. The --enforce-assist is the mechanism that makes the assist actions fail the pipeline, and the flag is the reason the imports are checked in the CI .

# The assist enforcement
- run: biome ci --enforce-assist .

The --enforce-assist is the mechanism that surfaces the assist action failures, and the surface is the reason the import order is checked in the pipeline.


Complete Example Session

# ============================================
# PART 1: THE MINIMAL GITHUB ACTIONS WORKFLOW
# ============================================

name: Code quality

on:
  push:
  pull_request:

jobs:
  quality:
    runs-on: ubuntu-latest
    permissions:
      contents: read
    steps:
      - name: Checkout
        uses: actions/checkout@v5
      - name: Setup Biome
        uses: biomejs/setup-biome@v2
      - name: Run Biome
        run: biome ci .

# ============================================
# PART 2: THE EXTERNAL CONFIGURATION WORKFLOW
# ============================================

name: Code quality

on:
  push:
  pull_request:

jobs:
  quality:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v5
      - uses: actions/setup-node@v4
        with:
          node-version: 22
          cache: "npm"
      - run: npm ci
      - uses: biomejs/setup-biome@v2
      - run: biome ci .

# ============================================
# PART 3: THE CHANGED FILES WORKFLOW
# ============================================

name: Lint Changed Files

on:
  pull_request:

jobs:
  biome:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
        with:
          fetch-depth: 0
      - uses: biomejs/setup-biome@v2
      - run: biome ci --changed --since=origin/main .

# ============================================
# PART 4: THE GITLAB CI SETUP
# ============================================

stages:
  - quality

lint:
  image:
    name: ghcr.io/biomejs/biome:latest
    entrypoint: [""]
  stage: quality
  script:
    - biome ci --reporter=gitlab --colors=off > /tmp/code-quality.json
    - cp /tmp/code-quality.json code-quality.json
  artifacts:
    paths:
      - code-quality.json
  rules:
    - if: $CI_COMMIT_BRANCH
    - if: $CI_MERGE_REQUEST_ID

# ============================================
# PART 5: THE STRICT FLAGS
# ============================================

- run: biome ci --error-on-warnings --max-diagnostics=100 .

# ============================================
# PART 6: THE ASSIST ENFORCEMENT
# ============================================

- run: biome ci --enforce-assist .

# ============================================
# PART 7: THE REPORTER FLAGS
# ============================================

# The GitHub annotations (auto-detected)
- run: biome ci --reporter=github .

# The JSON reporter
- run: biome ci --reporter=json .

# The GitLab code quality
- run: biome ci --reporter=gitlab --colors=off > code-quality.json

# ============================================
# PART 8: THE THREAD CONTROL
# ============================================

- run: biome ci --threads=4 .

# ============================================
# PART 9: THE VCS INTEGRATION
# ============================================

# biome.json
{
  "vcs": {
    "enabled": true,
    "clientKind": "git",
    "useIgnoreFile": true
  }
}

# ============================================
# PART 10: THE CHECKLIST
# ============================================

# [ ] The ci command is used, not the check.
# [ ] The setup-biome action is used.
# [ ] The Node.js setup is added for the external configuration.
# [ ] The changed flag is used for the incremental CI.
# [ ] The reporter flag is used for the runner integration.
# [ ] The enforce-assist flag is used for the import organization.

The ten parts covered the minimal workflow, the external configuration, the changed files, the GitLab CI, the strict flags, the assist enforcement, the reporter flags, the thread control, the VCS integration, and the checklist. The through-line is that the ci command is the read-only variant, and the reporter is the mechanism that adapts the output to the runner.


Quick Reference

The ci vs check

Aspectcheckci
The --writeYesNo
The runner integrationNoYes
The thread controlNoYes
The VCS flag--staged--changed
The use caseThe localThe pipeline

The GitHub Actions

StepAction
The checkoutactions/checkout@v5
The Biome setupbiomejs/setup-biome@v2
The runbiome ci .
The Node.js (external config)actions/setup-node@v4
The install (external config)npm ci

The reporter flags

FlagOutput
--reporter=githubThe GitHub annotations
--reporter=gitlabThe GitLab code quality
--reporter=jsonThe raw JSON

The strictness flags

FlagEffect
--error-on-warningsThe warnings fail the pipeline
--max-diagnostics=NThe cap on the diagnostics
--enforce-assistThe assist actions fail the pipeline

Best Practices

✅ Do This:

# Use the ci command in the pipeline
- run: biome ci .                                                  # ✅

# Use the setup-biome action
- uses: biomejs/setup-biome@v2                                     # ✅

# Add the Node.js setup for the external configuration
- uses: actions/setup-node@v4                                      # ✅

❌ Don’t Do This:

# Use the check --write in the pipeline
- run: biome check --write . # the pipeline modifies the tree      # ❌

# Use the check --staged in the pipeline
- run: biome check --staged . # the remote has no staged files     # ❌

Common Pitfalls

PitfallWhy It HappensFix
The pipeline modifies the treeThe check --write is usedUse the ci command
The external config is not resolvedThe Node.js setup is missingAdd the setup and the install
The GitLab report is missingThe --reporter=gitlab is missingAdd the reporter flag
The imports are not checkedThe assist is not enforcedUse the --enforce-assist

Real-World Examples

1. The minimal workflow

- uses: actions/checkout@v5
- uses: biomejs/setup-biome@v2
- run: biome ci .

2. The external configuration

- uses: actions/setup-node@v4
- run: npm ci
- uses: biomejs/setup-biome@v2
- run: biome ci .

3. The changed files

- run: biome ci --changed --since=origin/main .

4. The GitLab CI

- biome ci --reporter=gitlab --colors=off > code-quality.json

5. The strict mode

- run: biome ci --error-on-warnings .

6. The max diagnostics

- run: biome ci --max-diagnostics=100 .

7. The assist enforcement

- run: biome ci --enforce-assist .

8. The GitHub reporter

- run: biome ci --reporter=github .

9. The thread control

- run: biome ci --threads=4 .

10. The VCS integration

{ "vcs": { "enabled": true, "useIgnoreFile": true } }

Visual

The ci vs check

┌──────────────────────────────────────────────────────────────┐
│  THE CI VS CHECK                                             │
│                                                              │
│  biome check                    biome ci                    │
│  ┌─────────────────────┐       ┌─────────────────────┐     │
│  │ Accepts --write     │       │ Read-only           │     │
│  │ The local use       │       │ The pipeline use    │     │
│  │ The --staged        │       │ The --changed       │     │
│  └─────────────────────┘       └─────────────────────┘     │
└──────────────────────────────────────────────────────────────┘

The GitHub Actions

┌──────────────────────────────────────────────────────────────┐
│  THE GITHUB ACTIONS                                          │
│                                                              │
│  The checkout                                                │
│         │                                                    │
│         ▼                                                    │
│  ┌─────────────────────┐                                    │
│  │ setup-biome         │  The binary install                │
│  └──────────┬──────────┘                                    │
│             │                                                │
│             ▼                                                    │
│  ┌─────────────────────┐                                    │
│  │ biome ci .          │  The read-only check               │
│  └──────────┬──────────┘                                    │
│             │                                                │
│             ▼                                                    │
│  ┌─────────────────────┐                                    │
│  │ The GitHub          │  The annotations                   │
│  │ annotations         │                                    │
│  └─────────────────────┘                                    │
└──────────────────────────────────────────────────────────────┘

The Reporter

┌──────────────────────────────────────────────────────────────┐
│  THE REPORTER                                                │
│                                                              │
│  biome ci --reporter=github                                  │
│         │                                                    │
│         ▼                                                    │
│  ┌─────────────────────┐                                    │
│  │ The GitHub          │  The inline annotations            │
│  │ annotations         │                                    │
│  └─────────────────────┘                                    │
│                                                              │
│  biome ci --reporter=gitlab                                  │
│         │                                                    │
│         ▼                                                    │
│  ┌─────────────────────┐                                    │
│  │ The GitLab code     │  The merge request widget          │
│  │ quality JSON        │                                    │
│  └─────────────────────┘                                    │
└──────────────────────────────────────────────────────────────┘

Summary

ItemValue
The CI commandbiome ci
The differenceThe read-only, the runner integration, the thread control, the --changed
The GitHub Actionbiomejs/setup-biome@v2
The external configurationThe Node.js setup and the npm ci
The GitLab imageghcr.io/biomejs/biome:latest
The reporterThe github, the gitlab, the json
The strictnessThe --error-on-warnings, the --max-diagnostics, the --enforce-assist

Key takeaways:

  • biome ci is the read-only variant of biome check for the pipelines. It does not accept the --write/--fix, it integrates with the runner’s annotations, it allows the thread control, and it uses the --changed flag instead of the --staged . The read-only is the mechanism that prevents the pipeline from modifying the working tree.
  • The biomejs/setup-biome@v2 action installs the binary directly. It reads the version from the lockfile, then from the package.json, and the pinned version is the mechanism that keeps the CI and the local tool in sync . The external configuration requires the Node.js setup and the dependency install before the Biome runs.
  • The reporter flag adapts the output to the runner. The --reporter=github is the GitHub annotations, the --reporter=gitlab is the GitLab code quality JSON, and the --reporter=json is the raw JSON . The reporter is the mechanism that makes the diagnostics visible in the runner’s interface.
  • The strictness flags catch the warnings and the assist actions. The --error-on-warnings fails the pipeline on the warning, the --max-diagnostics caps the output, and the --enforce-assist surfaces the import organizer’s failures . The three flags are the mechanism that makes the pipeline the strict gate.
  • The GitLab CI uses the Biome Docker image. The ghcr.io/biomejs/biome:latest provides the binary, and the --reporter=gitlab produces the code quality report that the GitLab reads from the artifact . The image and the reporter are the mechanism that integrates the Biome with the GitLab pipeline.

Remember: The Biome CI integration is the biome ci command and the pipeline setup. The ci command is the read-only variant that does not modify the tree, and the reporter adapts the output to the runner. The setup-biome action installs the binary, and the external configuration requires the Node.js setup. The strictness flags make the pipeline the gate that catches the warnings and the assist failures.

Was this helpful?


Stop using slow, ad-bloated tool sites! 🤮

🔎 Search “KandZ Tools” on Google to use many professional utilities for free.

KandZ.me is the ultimate minimalist hub for:
✅ Finance (Mortgage, Interest, Inflation)
✅ Tech (Base64, JSON, Dev Suite, IP)
✅ Health (BMI, BMR, TDEE)
✅ Productivity (Timer, Workspace, QR)

⚡️ Fast & Private
🔒 No data leaves your device
💎 100% Free

🔗 Use it now: https://tools.kandz.me
🔖 Bookmark it—you’ll need it later!