# In CI

In CI, hushrun skips the checks this exact code already passed anywhere, on a laptop, in an agent's sandbox or in an earlier run, and records the rest for everyone else to reuse.

## GitHub Actions

CI needs no secret. When `package.json` names the backend, as `npx hushrun init` writes it, it needs no variable either. Give each job that runs a wrapped check an ID token:

```yaml title=".github/workflows/ci.yml"
jobs:
  test:
    runs-on: ubuntu-latest
    permissions:
      contents: read
      id-token: write
    steps:
      - uses: actions/checkout@v4
      - uses: actions/setup-node@v4
        with:
          node-version: 22
      - run: npm ci
      - run: npm run lint
      - run: npm test
```

`npm ci` installs hushrun with your other `devDependencies`, and `npm test` goes through `hushrun run` as it does everywhere else. init adds `id-token: write` for you, to the job's `permissions:` or the workflow's. When neither has one, it adds a block with `contents: read` too, since a `permissions:` block replaces the token's defaults: add any other permission the job needs.

### How CI is recognized

hushrun asks GitHub for an ID token made for your organization's backend, and the backend checks which repository the job runs for. hushrun's hosted service accepts a repository that your organization connected, which init does with your sign-in. The first result it accepts pins that repository's GitHub id. A repository deleted and created again under the same name has another id, so it is refused, with a message that says why; a renamed repository keeps working.

- **Pull requests from forks** get no ID token, so their checks run and nothing is recorded.
- **A job without `id-token: write`** has no credential either. Its checks run, and its log ends with `hushrun: results aren't shared with acme: …` and the backend's reason.

### Without package.json

For a repository without `package.json`, or a job that doesn't install your Node.js dependencies, install hushrun in the job and name the backend there:

```yaml title=".github/workflows/ci.yml"
jobs:
  test:
    runs-on: ubuntu-latest
    permissions:
      contents: read
      id-token: write
    env:
      HUSHRUN_BACKEND: https://api.hushrun.com/o/acme
    steps:
      - uses: actions/checkout@v4
      - run: npm install --global hushrun
      - run: hushrun run unit -- cargo test
```

Your organization's URL is in `hushrun whoami` on a signed-in machine.

## CI reuses only CI's results

By default, CI skips a check that a laptop or an agent already passed on the same files. An admin can stop that for one repository: on its settings page in the dashboard, turn on **CI reuses only CI's results**. CI then skips a check only on passes CI itself recorded, while passes from laptops and agents still speed up people and agents. hushrun says so on each line:

```text
hushrun: unit skipped: this exact code passed in CI 3 min ago (saves 41s); skipped only on CI's passes
hushrun: unit running: no CI pass yet for this exact code; skipped only on CI's passes
```

The server enforces it; nothing in the repository can turn it off.

## Other CI

GitLab CI, CircleCI, Buildkite, Jenkins and the rest record with an agent token and the key it comes with. An organization admin creates them, signed in with `hushrun login`, in the repository:

```bash
hushrun token create --agent gitlab-ci --expires 365d
```

It prints the token and the key once. Store the token as a masked, protected secret named `HUSHRUN_TOKEN` in the CI's settings, and the key, the whole PEM block, as a protected secret named `HUSHRUN_MACHINE_KEY`. Where the CI can't mask a value of several lines, as on GitLab, store the key as a File variable named `HUSHRUN_MACHINE_KEY_FILE` instead: GitLab writes it to a file and sets the variable to the file's path. Then run the checks as usual:

```yaml title=".gitlab-ci.yml"
test:
  image: node:22
  script:
    - npm ci
    - npm test
```

- The token records and reads results for that one repository, as `agent:gitlab-ci`. `--org-wide` makes one for every repository of the organization.
- The results it records carry the key's signature: without the key, checks still run, but their results aren't recorded.
- It expires after 30 days unless `--expires` says otherwise, 365 days at most. Before it does, create the next one under the same name, update both secrets, and revoke the old one with `hushrun token revoke`.
- An agent token follows `package.json`'s backend only on hushrun's hosted service. For a backend on another server, set `HUSHRUN_BACKEND` in the job too.

The dashboard's **Agents** page creates the same tokens, each with its key, and lists and revokes them. [Agents and sandboxes](https://hushrun.com/docs/agents.md) has the details, which apply to CI too.
