skip to content

Actions & Marketplace

Consuming and authoring actions: composite, JavaScript and Docker action types, action.yml metadata, pinning by tag versus commit SHA, and the supply-chain exposure of third-party code. Asked because a marketplace action is arbitrary code running with your token and secrets.

part ofGitHub Actionsoverview, primer and where to startread it →
on this pageshow

questions

4

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

open as a page

What are the three types of GitHub Action you can author, and how do they differ?

level: middleimportance: should knowfreq 58%

basics

~20 s

Composite actions bundle a sequence of workflow steps in YAML. JavaScript actions run a Node script directly on the runner. Docker container actions run your code inside an image. The action.yml runs.using key picks which, and Docker actions run only on Linux runners.

open as a page

Why pin a third-party GitHub Action to a full commit SHA instead of a version tag?

level: middleimportance: should knowfreq 58%

basics

~20 s

Git tags are mutable and major tags like v4 are deliberately re-pointed on each release, so uses: acme/action@v4 can execute different code tomorrow. A full commit SHA is immutable, so the code you reviewed is the code that runs.

open as a page

What can a malicious third-party GitHub Action do inside your job, and how do you contain it?

level: seniorimportance: should knowfreq 47%

basics

~20 s

An action runs as arbitrary code in your job: it can read the workspace, every environment variable including secrets you expose, use the GITHUB_TOKEN against your repository, and send data anywhere the runner can reach. Containment means least-privilege token, minimal secret exposure, pinned refs, and job isolation.

open as a page