> ## 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.

# Upload impacted targets from GitHub Actions

> Authenticate a GitHub Actions job to the v2 API with no Trunk secret, fork pull requests included.

A GitHub Actions job can [upload a pull request's impacted targets](https://docs.trunk.io/merge-queue/reference/api-v2/impactedtargets/upload-a-pull-requests-impacted-targets.md) without a Trunk API key. The [`trunk-io/login`](https://github.com/trunk-io/login) action exchanges the run's own GitHub credential for a short-lived Trunk login, so there's no Trunk secret to manage. It also works on fork pull requests, which get no repository secrets.

The login can upload impacted targets only, and only for the run's own repository. For anything else, use an [API key](https://docs.trunk.io/merge-queue/reference/api-v2/authentication.md#create-an-api-key).

## Before you start

* The Trunk GitHub App is installed on the repository, and the repository has a merge queue in `graph` mode ([**Parallel**](https://docs.trunk.io/merge-queue/optimizations/parallel-queues) in the app). Only those queues wait for impacted targets.
* Your CI can list the build targets a pull request changes, one per line. The workflow below uses the changed top-level folders. This works in any repository but treats folders as independent, and changes that interact across folders may be tested separately. A build system such as Bazel or Nx gives dependency-aware targets.
* The job runs on Linux (x64 or arm64) or macOS (arm64). Windows is not supported.
* To accept uploads from fork pull requests, an admin must first [allow them](#allow-uploads-from-forks). Pull requests from the repository itself need no setup in Trunk.

## Set up the workflow

Add a workflow file, for example `.github/workflows/impacted-targets.yaml`:

```yaml theme={"system"}
on:
  pull_request:
    branches: [main] # the branches that have a merge queue in graph mode

permissions:
  contents: read
  id-token: write # the run's OIDC token, for pull requests from this repository

jobs:
  impacted-targets:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
        with:
          fetch-depth: 0

      # One target per top-level folder (or root file) the pull request changes.
      # Replace this with your build system's impacted targets.
      - name: Compute impacted targets
        shell: bash # fails the step if git diff fails
        run: |
          git diff --name-only "origin/${{ github.base_ref }}...HEAD" \
            | cut -d/ -f1 | sort -u > targets.txt

      - uses: trunk-io/login@v1

      - name: Upload impacted targets
        shell: bash
        run: |
          if [ -s targets.txt ]; then
            trunk mergequeue upload-impacted-targets --targets-file targets.txt
          else
            trunk mergequeue upload-impacted-targets --none
          fi
        env:
          GITHUB_TOKEN: ${{ github.token }} # lets a long job on a fork pull request renew its login
```

`trunk-io/login` installs the `trunk` CLI, puts it on `PATH`, and logs in. On a pull request, `trunk mergequeue upload-impacted-targets` takes the repository, pull request number, head commit, and target branch from the run. On other events, such as `push`, `merge_group`, or `workflow_dispatch`, the run doesn't name a pull request, so pass `--pr <number>`, `--sha <head commit>`, and `--target-branch <branch>` yourself.

The `branches:` filter skips a stacked pull request that targets another pull request's branch. To upload for those, widen the filter and pass `--target-branch <queue branch>`, since the CLI otherwise uses the pull request's own base branch.

Impacted targets apply to one commit. Upload again on every push, or the queue keeps the pull request waiting. The `pull_request` trigger above does this.

The upload answers `202`, and the queue picks the targets up shortly after. To confirm, once the pull request is in a queue, read it with an API key through [`GET /v2/pull-requests/{id}`](https://docs.trunk.io/merge-queue/reference/api-v2/pullrequests/get-a-pull-request) and check that `queueEntry.readiness.hasImpactedTargets` is `true`.

The actions on the [Bazel](https://docs.trunk.io/merge-queue/optimizations/parallel-queues/bazel.md) and [Nx](https://docs.trunk.io/merge-queue/optimizations/parallel-queues/nx.md) pages compute and upload in one step through the v1 API, with a stored Trunk token. The Nx action can also upload from fork pull requests through v1. Neither action outputs a targets file. To use the GitHub Actions login instead, compute the targets with your build tool, for example `bazel-diff` or `nx show projects --affected`, write them to a file, and upload it as above.

Declare the pull request's impact with one of:

| Option | Uploads |
| - | - |
| `--targets-file <file>` | The target names in the file, one per line. An empty file is refused, since it usually means the computation failed. If the pull request really impacts nothing, use `--none`. |
| `--none` | An empty list, for a pull request that impacts nothing. This still satisfies the queue's impacted-targets check. |
| `--all` | The pull request impacts every target. |

## How the job authenticates

The credential depends on the event that started the run:

| Run | Credential | Needs |
| - | - | - |
| `pull_request` from this repository, `merge_group`, `push`, `workflow_dispatch` | The run's GitHub OIDC token | `permissions: id-token: write` |
| `pull_request` or `pull_request_target` from a fork | The job's `GITHUB_TOKEN`, bound to that pull request and its head commit | [Impacted targets from fork PRs](#allow-uploads-from-forks) turned on for the repository |

* The CLI sends `GITHUB_TOKEN` to Trunk only on a fork pull request.
* A `pull_request_target` run for a pull request from the repository itself is refused. A `pull_request_target` job runs with the base repository's permissions and secrets, so never check out and run a fork's code in one. Prefer `pull_request` for forks too.
* A fork pull request's login needs the pull request to be open and the run's commit to be its current head, so an older run fails after a new push.
* Other events from the repository itself, such as `issue_comment`, `workflow_run`, and `schedule`, are refused, because some of them let someone outside the repository run code with the repository's OIDC token.
* GitHub may hold a first-time contributor's run until a maintainer approves it.

### Allow uploads from forks

Uploads from fork pull requests are off until an organization admin who can change queue settings turns them on. In the Trunk app, open the **Merge Queue** tab, navigate to **\[any of the repository's queues]** → **Settings** → **GitHub**, and toggle **Impacted targets from fork PRs** on.

* The setting applies to every merge queue on the repository, and matters only for `graph` mode queues.
* It can be turned on only for public repositories.
* If it is off, the login on a fork pull request fails with `Trunk refused this run's GitHub credential`. The error calls this setting **Fork PR CI access**.

## Security

* The login isn't exported to the job's environment; the action only adds the CLI to `PATH`. The login is saved to the CLI's credential file (mode `0600`). Only the upload uses it, but later steps running as the same user can read it.
* The login expires after at most 15 minutes. The CLI renews it when it is close to expiring. On a fork pull request, renewing needs `GITHUB_TOKEN` in the upload step's `env`, as in the example above.
* Each login is tied to the workflow run that made it, so a job from a different run on a reused self-hosted runner doesn't pick it up.

## Action inputs

| Input | Default | Description |
| - | - | - |
| `api-url` | `https://api.trunk.io/v2` | The Trunk API root. Unlike the API's base URL, this includes `/v2`. |
| `cli-version` | Pinned per release | The `trunk` CLI version to install. |
| `cli-channel` | `prod` | The release channel `cli-version` is published on. |
| `github-token` | `${{ github.token }}` | The job's GitHub token. Sent to Trunk only on a fork pull request. |
