> ## Documentation Index
> Fetch the complete documentation index at: https://extension.js.org/llms.txt
> Use this file to discover all available pages before exploring further.

# CI templates for extension builds

> Automate lint, build, and E2E checks for browser extensions with ready-to-use CI templates for GitHub Actions and other platforms with Extension.js.

Automate lint, build, and end-to-end (E2E) checks so extension changes stay releasable.

Use continuous integration (CI) to run the same quality gates on every pull request and release branch.

## CI capabilities

| Capability                | What it gives you                                        |
| ------------------------- | -------------------------------------------------------- |
| Quality gates             | Enforce lint, build, and test checks before merge        |
| Browser-target confidence | Build and validate multiple browser targets consistently |
| Reproducible environments | Lock toolchain versions across local and CI runs         |
| Debuggable failures       | Publish test reports and artifacts for investigation     |

## Core pipeline stages

1. Install dependencies
2. Run lint/check commands
3. Build browser targets
4. Run automated tests (including Playwright where applicable)
5. Upload artifacts/reports

## Minimal GitHub Actions example

```yaml theme={null}
name: ci

on:
  pull_request:
  push:
    branches: [main]

jobs:
  validate:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - uses: pnpm/action-setup@v4
      - uses: actions/setup-node@v4
        with:
          node-version: 20
          cache: pnpm
      - run: pnpm install --frozen-lockfile
      - run: pnpm lint
      - run: pnpm extension build --browser=chromium
```

## Build matrix example

```yaml theme={null}
strategy:
  matrix:
    browser: [chromium, firefox]
steps:
  - run: pnpm extension build --browser=${{ matrix.browser }}
```

## Playwright in CI

* Install browsers in CI job (`playwright install` or equivalent).
* Keep retries slightly higher on CI than local.
* Publish Playwright reports/artifacts for failed runs.
* Use `dist/extension-js/<browser>/ready.json` as readiness contract before launching Playwright.
* Avoid parsing human-readable logs in CI automation scripts.
* Prefer the built-in wait commands: `extension dev --wait --browser=<browser>` (watch/dev) or `extension start --wait --browser=<browser>` (production/start).
* For scripts or CI automation, prefer `--wait-format=json`.

### Example readiness gate

```bash theme={null}
pnpm extension start --wait --browser=chromium --wait-timeout=60000 --wait-format=json
```

## Test handlers without a browser UI

You don't always need Playwright. For event handlers — the toolbar action, keyboard commands — Extension.js can fire them directly against a dev session and you assert on the side effect. It's deterministic and headless, so it's a reliable gate.

```yaml theme={null}
- run: pnpm extension dev --browser=chromium --allow-control --wait --wait-format=json
- run: pnpm extension open action --browser=chromium --output json
- run: |
    pnpm extension storage get --key lastClickedAt --browser=chromium --output json \
      | node -e 'process.exit(JSON.parse(require("fs").readFileSync(0)).value?.lastClickedAt ? 0 : 1)'
```

The same approach replays keyboard commands (`extension open command --name <cmd>`). See [Trigger actions and commands](/docs/debugging/trigger-actions-and-commands) for the result payloads and the one caveat (replay carries no user gesture, so `activeTab` is not granted).

## Docker and container CI

When CI runs inside Docker or a dev container, pass `--host 0.0.0.0` so external processes can reach the dev server. If another process already occupies the default port, use `--port 0` for automatic port assignment.

```bash theme={null}
pnpm extension dev --host 0.0.0.0 --port 0 --no-browser
```

For slow or resource-constrained containers, increase browser transport timeouts via environment variables (see [browser transport tuning variables](/docs/features/environment-variables#browser-transport-tuning-variables)).

## Common pitfalls

* CI scripts diverging from local scripts over time
* Building only one browser target while shipping to multiple engines
* Missing artifact upload for failed E2E jobs
* Mixing unpinned Node/package manager versions across workflows

Your repository includes an E2E workflow example at:

* `.github/workflows/e2e.yml`

## Best practices

* Keep CI commands close to local scripts to reduce drift.
* Fail fast on lint/config errors before expensive browser jobs.
* Cache dependencies and browser binaries when possible.
* Version lock CI Node and package manager versions for reproducibility.

## Next steps

* Add or refine [Playwright E2E tests](/docs/workflows/playwright-e2e).
* Review [commands reference](/docs/commands/dev) for script alignment.
