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

# GitHub Actions

> Add the Dynamic CI filter job to a GitHub Actions workflow.

<Note>
  Before you start, [set up Trunk CI for GitHub Actions](/ci/get-started/github-actions)
  and store your organization's API token from
  [Settings → Organization → General](https://app.trunk.io/settings/organization/general)
  as a repository secret named `TRUNK_API_TOKEN`.
</Note>

## Add the filter job to the top of your workflow

List every job you want to gate under `outputs`.

```yaml theme={"system"}
jobs:
  dynamic-ci-filter:
    name: Dynamic CI Filter
    runs-on: ubuntu-latest
    timeout-minutes: 5
    if: github.event_name == 'pull_request'
    outputs:
      unit-tests: ${{ steps.ci-filter.outputs.unit-tests }}
      integration-tests: ${{ steps.ci-filter.outputs.integration-tests }}
    steps:
      - name: Run Dynamic CI Filter
        id: ci-filter
        uses: trunk-io/dynamic-ci@v1
        with:
          token: ${{ secrets.TRUNK_API_TOKEN }}
```

## Gate your jobs on it

```yaml theme={"system"}
unit-tests:
  name: Unit Tests
  runs-on: ubuntu-latest
  needs: [dynamic-ci-filter]
  if: >-
    !cancelled() &&
    needs.dynamic-ci-filter.outputs.unit-tests != 'false'
  steps:
    - run: make test
```

<Warning>
  Gate on `!= 'false'`, never on `== 'true'`. A job with no verdict has no
  output, and `!= 'false'` makes it run.
</Warning>

Use each job's key from `jobs:`, not its display `name:`.

<Accordion title="Gating a single job instead (pre-step mode)">
  A job can ask about itself by passing its own key in `job-keys`, and skip its
  own steps. It saves less, because the runner has already started.

  ```yaml theme={"system"}
  steps:
    - name: Run Dynamic CI Filter
      id: ci-filter
      uses: trunk-io/dynamic-ci@v1
      with:
        token: ${{ secrets.TRUNK_API_TOKEN }}
        job-keys: unit-tests
    - if: steps.ci-filter.outputs.unit-tests != 'false'
      run: make test
  ```
</Accordion>

## Next

[Turn on enforcement](/ci/dynamic-ci/getting-started) once the
recommendations look right. For how Dynamic CI behaves in a merge queue, see
[Merge queues](/ci/dynamic-ci/merge-queue).
