skip to content

In a GitHub Actions step, what does uses: reference, and what forms can that reference take?

level: juniorimportance: must knowfreq 75%

answer

  1. The alternative to run:
  2. A ref is never optional
  3. One form points inside your own repository
  4. There is a container form too
  5. Inputs live under a sibling key

basics

~10 s

uses: runs a packaged action instead of a shell command. It points at a public repository plus a ref (actions/checkout@v4), a path inside your own repository (./.github/actions/setup), or a Docker image (docker://alpine:3.19).

solid answer

~40 s

A step either runs a command with `run:` or invokes a packaged action with `uses:`. The reference has three shapes: **`owner/repo@ref`** for an action in another repository, where `ref` is a tag, branch or commit SHA and is mandatory (`actions/checkout@v4`); **`owner/repo/path@ref`** when the action lives in a subdirectory of that repository; and **`./path`** for an action in the current repository, which requires that the repository has already been checked out. `docker://image:tag` runs a published container image directly as the action. Parameters go under `with:` and are declared by the action's `action.yml`. Whatever the form, `uses:` means "run someone's packaged code in my job" — which is why the ref you choose is a supply-chain decision, not cosmetic.

code

yaml · 9 lines
yaml
steps:
  - uses: actions/checkout@v4              # owner/repo@ref
  - uses: acme/tools/setup-cli@v2          # subdirectory of a repo
  - uses: ./.github/actions/configure      # local, needs checkout first
    with:
      environment: staging
  - uses: docker://alpine:3.19             # published container image
    with:
      args: echo hello

go deeper

for a junior

Be able to write both step kinds and recall the owner/repo@ref form plus the fact that the ref is mandatory.

for a middle

Explain all the reference forms including local and docker://, how with: maps to declared inputs, and how step outputs are read back via an id.

for a senior

Frame uses: as executing third-party code inside your job and connect the ref choice to supply-chain exposure and reproducibility.

for a principal

Own the organisational convention: where shared actions live, whether they are vendored locally or referenced across repositories, and how references are pinned and updated at scale.

## Two kinds of step GitHub Actions steps come in exactly two flavours. `run:` executes a shell command on the runner. `uses:` invokes an **action** — a reusable unit of code packaged with an `action.yml` metadata file that declares its inputs, outputs and how it executes. ```yaml steps: - uses: actions/checkout@v4 - run: ./gradlew build ``` A single step cannot have both keys. ## The reference forms **Public or private repository:** `owner/repo@ref`. The ref is required — there is no implicit default branch — and may be a release tag (`@v4`), a branch (`@main`), or a full commit SHA. GitHub fetches that repository at that ref and executes the action defined by the `action.yml` at its root. **Subdirectory:** `owner/repo/path/to/action@ref`. Many organisations keep several actions in one repository; the path selects which one, and the ref still applies to the whole repository. **Local:** `./.github/actions/setup-toolchain`. The path is relative to the repository root of the *workspace*, so the step only works after `actions/checkout` has run. This is the cheapest way to factor repeated steps out of one repository's workflows without publishing anything. **Docker image:** `docker://alpine:3.19` runs a published image directly, with `with.args` becoming the container arguments. Docker-based steps only work on Linux runners. ## Passing data Inputs go under `with:`, keyed by the names the action's `action.yml` declares; unknown keys are ignored rather than rejected, which is a classic source of "my setting had no effect". Environment variables go under `env:`. Outputs come back via the step's `id`: ```yaml - id: meta uses: acme/build-meta@v2 with: prefix: release - run: echo "tag is ${{ steps.meta.outputs.tag }}" ``` ## Why the ref matters The reference is the entire security boundary. `uses:` downloads and executes code from another repository *inside your job*, with access to the job's environment, the workspace, the network, and whatever secrets you have passed in. A mutable ref like `@main`, or a major tag like `@v4` that its owner re-points on each release, means the code you run today is not necessarily the code you reviewed. Pinning to a full commit SHA freezes it. That is why security-conscious repositories pin third-party actions by SHA and keep the human-readable version in a trailing comment. ## Common mistakes - Omitting the ref (`uses: actions/checkout`) — the workflow fails to parse the step. - Using a local `./` action before `actions/checkout`, so the path does not exist yet. - Assuming `with:` keys are validated: a typo'd input is silently dropped unless the action itself checks. - Believing an action is "just configuration". It is arbitrary code — JavaScript, a container, or a bundle of shell steps.

  • What happens if you reference a local action with ./ before running actions/checkout?
    The step fails because the path does not exist: the workspace is empty until checkout populates it. Local actions are read from the checked-out working tree, not fetched from GitHub, so any job using one must check out the repository first — including jobs that otherwise need no source code.
  • If you pass a with: key the action does not declare, what happens?
    Nothing visible. Undeclared inputs are simply not surfaced to the action, so a misspelled key silently takes no effect and the step runs with the default. The failure mode is a configuration that appears applied but is not, which is why reading the action's action.yml beats guessing input names.

saying these in an interview costs you the question

  • Omitting the ref after owner/repo
  • Treating an action as inert configuration
  • Using a local ./ action without checking out first
  • Assuming misspelled with: keys raise an error
  • Thinking run: and uses: can share one step

context