GitHub Actions Triggers, Events, Caching, Artifacts and Outputs (GH-200)
Workflow triggers are the events under a workflow's on: key that decide when GitHub Actions runs it — repository activity such as a push or pull request, a cron schedule, a manual workflow_dispatch, an external repository_dispatch webhook, another workflow finishing, or a call from a reusable workflow. This lesson covers the trigger and execution/output parts of the GH-200 "Author and manage workflows" domain: choosing and filtering events safely, defining workflow_dispatch and workflow_call inputs and secrets, and managing what a run produces — caches, artifacts and their retention, step and job outputs, job summaries, status badges and environment protections.
On this page10 sections
- Which events can start a workflow
- Scheduled workflows: cron, time zones and the default branch
- Manual and external runs: workflow_dispatch and repository_dispatch
- Filtering repository events: activity types, branches, tags and paths
- Choosing events safely: fork pull requests and the GITHUB_TOKEN
- Chaining workflows with workflow_run
- Reusable workflows: workflow_call inputs, secrets and outputs
- Passing data between steps and jobs: GITHUB_ENV, GITHUB_OUTPUT and artifacts
- Caching, artifacts and retention policies
- Job summaries, status badges and environment protections
- Configure workflows for repository, scheduled, manual and external webhook events, and explain which triggers only read workflow files from the default branch
- Filter events with activity types, branch, tag and path filters, and predict whether a given push or pull request starts a run
- Choose between pull_request, pull_request_target and workflow_run for fork pull requests, and predict when an event caused by the GITHUB_TOKEN starts a new run
- Define and validate typed workflow_dispatch inputs and pass inputs, secrets and outputs across a workflow_call boundary
- Pass data with GITHUB_ENV, GITHUB_OUTPUT, job outputs and artifacts, and decide when to use a cache instead
- Set artifact and log retention and delete logs, artifacts and runs through the REST API
- Publish job summaries and status badges, and protect deployments with environment rules and secrets
Which events can start a workflow
A workflow starts when an event named under its on: key happens. The GH-200 guide groups these into four kinds, plus two that link workflows together:
| Kind | Trigger | Started by |
|---|---|---|
| Repository events | push, pull_request, issues, release, create and many more | Activity in the repository itself |
| Scheduled | schedule with one or more cron entries | The clock |
| Manual | workflow_dispatch | A person (Run workflow button), the REST API or gh workflow run |
| Webhook (external) | repository_dispatch | Any system outside GitHub that calls the REST API |
| Chaining | workflow_run | Another workflow being requested, in progress or completed |
| Reuse | workflow_call | A caller workflow that uses this file as a reusable workflow |
A workflow can list several events, and each event can carry its own filters:
on:
push:
branches: [main]
pull_request:
workflow_dispatch:
schedule:
- cron: '30 2 * * 1-5'
Several of these triggers — schedule, workflow_dispatch, repository_dispatch and workflow_run — only consider workflow files that exist on the default branch. A workflow that lives only on a feature branch can be triggered by pushes or pull requests on that branch, but never by these. That single rule explains many "my workflow never ran" scenarios.
Scheduled workflows: cron, time zones and the default branch
A schedule trigger runs a workflow on a POSIX cron expression (minute, hour, day of month, month, day of week), and it behaves differently from every other trigger in five ways:
- Default branch only. Scheduled runs use the workflow file and the latest commit on the default branch. A cron added only on a feature branch never fires until it is merged.
- UTC unless you say otherwise. Cron is evaluated in UTC by default. You can add a
timezonekey with an IANA zone name (for exampleEurope/London) beside a cron entry; the schedule then follows that zone, including daylight saving changes. A hand-converted UTC time drifts by an hour twice a year. On the night clocks spring forward, a time inside the skipped hour runs at the next valid time (02:30 becomes 03:00) instead of being skipped. - Five minutes is the shortest interval. Schedules more frequent than every five minutes are not supported.
- Not exact. Runs can be delayed at times of high load (the start of every hour is busiest), and very delayed runs may be dropped. Pick an odd minute when timing matters.
- Auto-disabled in quiet public repositories. In a public repository, GitHub disables scheduled workflows after 60 days with no repository activity. The symptom is no runs at all — not failing runs. Re-enable the workflow in the Actions tab (or via the API) once the repository is active again.
Manual and external runs: workflow_dispatch and repository_dispatch
workflow_dispatch lets a person or a script start a workflow on demand and pass it typed inputs. Each input can have a description, required, default and a type:
| Type | What the operator gets |
|---|---|
string | Free text (the default type) |
boolean | A checkbox, true or false |
number | A numeric field |
choice | A drop-down limited to the values in options; default sets the preselected one |
environment | A drop-down of all the repository's environments — you cannot narrow it to a subset |
on:
workflow_dispatch:
inputs:
region:
description: Region to rebuild
type: choice
options: [eu-west, us-east, ap-south]
default: eu-west
verbose:
type: boolean
required: false
default: false
How inputs are validated. GitHub checks the declared inputs on every dispatch, not only in the form. The Run workflow form will not submit while a required input is empty, offers only the listed options for a choice input, and pre-fills each default. A dispatch through the REST API or gh workflow run is rejected (the API returns 422 Unprocessable Entity and no run is created) when it omits a required input that has no default, sends a value that is not in a choice input's options, or names an input the workflow does not declare. An omitted optional input takes its default. API callers send every value as JSON; the type declared in the workflow decides how it appears in the inputs context. Anything the type system cannot express — a version format, a value range — is validated in a step that fails the job.
Where the file must be. The Run workflow button only appears for workflow files on the default branch, and API dispatches of a file that is not there fail. Once a file with the trigger exists on the default branch, you can dispatch it against any ref (UI branch picker, ref in the API, gh workflow run --ref), and that run uses the workflow file and code from the chosen ref. The usual way to test a new manual workflow is therefore to merge a minimal stub to the default branch, then dispatch the feature branch.
Read inputs from the inputs context. inputs.<name> keeps each input's type, so a boolean is a real boolean. The older github.event.inputs.<name> turns every value into a string. Because expressions coerce mismatched types to numbers when comparing, github.event.inputs.verbose == false compares the string 'false' (which becomes NaN) with false and is never true; inputs.verbose == false works.
External systems: repository_dispatch
repository_dispatch is the trigger for activity that happens outside GitHub: a registry publishing a package, an incident tool, a CMS. The external system sends POST /repos/{owner}/{repo}/dispatches with a token, an event_type it chooses, and an optional client_payload of arbitrary JSON. GitHub answers 204 No Content, which only means the event was created — not that any workflow matched it.
on:
repository_dispatch:
types: [package-published, package-yanked]
jobs:
report:
runs-on: ubuntu-latest
steps:
- run: echo "${{ github.event.action }} ${{ github.event.client_payload.version }}"
typesfilters onevent_type. A workflow withouttypesruns for every dispatch; a workflow withtypesignores event types it does not list.- The event type is available as
github.event.action, so one workflow can branch its jobs per signal. - Payload fields are read from
github.event.client_payload.<field>— not frominputs, which belongs toworkflow_dispatchandworkflow_call.
workflow_dispatch | repository_dispatch | |
|---|---|---|
| Targets | One specific workflow file, on a chosen ref | The repository; every workflow whose filter matches runs |
| Data | Declared, typed inputs | Free-form client_payload JSON plus an event_type |
| Typical caller | People, scripts, the CLI | External systems via the REST API |
Choosing events safely: fork pull requests and the GITHUB_TOKEN
Picking the right event is also a security decision, because each event decides which code runs, with which token and which secrets.
| Trigger | Runs in the context of | Fork PR: GITHUB_TOKEN | Fork PR: secrets | Safe use |
|---|---|---|---|---|
pull_request | The PR's merge commit (PR code and workflow file) | Read-only | Not passed | Build and test untrusted code |
pull_request_target | The base repository's default branch (workflow file from there) | Can write | Available | Label, comment or triage without checking out or running PR code |
workflow_run | The base repository's default branch | Can write | Available | Privileged follow-up that only consumes the first run's artifacts |
The dangerous combination is pull_request_target plus a checkout of github.event.pull_request.head.sha plus a build or test step: anything the fork controls — build scripts, test configuration, package-manager install hooks — then executes with the repository's secrets and a write-capable token. The workflow YAML itself still comes from the base branch, which is why the risk is easy to miss.
The standard pattern when untrusted code must be tested and something privileged must happen afterwards (posting results as a comment, for example) is a split: run the tests under pull_request, upload the results as an artifact, and let a second workflow on workflow_run download that artifact and post the comment — without ever executing fork code.
Events caused by the GITHUB_TOKEN
To prevent recursive runs, most events caused by the GITHUB_TOKEN — pushing a tag, pushing a commit outside a pull request, creating a release or an issue — do not start new workflow runs. The documented exceptions are workflow_dispatch and repository_dispatch, which a workflow creates deliberately. Pull requests are a special case: when a workflow creates or updates a pull request with the GITHUB_TOKEN (including pushing to its branch), the pull_request runs are created but wait until someone with write access approves them. When a follow-on workflow must run for something a workflow did, authenticate that action with a GitHub App installation token or a personal access token instead; extra permissions on the GITHUB_TOKEN do not change the rule.
Scope the GITHUB_TOKEN with permissions
Scope and permissions belong to the same decision as the event: choose the narrowest event and filters that do the job, and give the token only the scopes the jobs need. The token starts from the default set in the repository or organization settings — permissive (read and write for most scopes) or restricted (read access to contents and packages only); new organizations and repositories default to restricted. A permissions: key overrides that default at workflow level or for one job, and the moment you list any scope, every scope you did not list becomes none:
permissions:
contents: read # workflow default for every job
jobs:
triage:
runs-on: ubuntu-latest
permissions:
contents: read
issues: write # this job only: comment on issues
steps:
- run: echo "triage"
Each scope takes read, write or none (write includes read). Pick the scope named after the object the job changes — issues for issue comments, contents for commits, tags and releases, pull-requests for pull request comments and reviews, actions for runs and artifacts — and only the access level it needs. Write access never makes the token's own events start new runs. Token hardening in depth is covered in the "Secure and optimize automation" domain.
Chaining workflows with workflow_run
workflow_run starts a workflow when another workflow, named in workflows, is requested, in_progress or completed. Use it when two workflows must stay separate files — needs only links jobs inside one workflow.
on:
workflow_run:
workflows: [CI]
types: [completed]
branches: [main]
jobs:
deploy:
if: github.event.workflow_run.conclusion == 'success'
runs-on: ubuntu-latest
completedfires for every outcome — success, failure, cancelled — so checkgithub.event.workflow_run.conclusionbefore deploying.branchesfilters on the branch the triggering workflow ran on.- Chains stop after three levels: with A → B → C → D → E → F, workflows E and F never run, even when everything succeeds. Long pipelines belong in one workflow as jobs connected with
needs, or in reusable workflows. - Which commit it runs on: like
scheduleandrepository_dispatch(but unlikeworkflow_dispatch, which runs on the ref it was dispatched on), aworkflow_runrun getsGITHUB_SHAandGITHUB_REFfor the latest commit on the default branch, so a plainactions/checkout(andgithub.sha) gives you the default branch — not the code the triggering run tested. To build or deploy that exact commit, check outgithub.event.workflow_run.head_sha.head_branchonly names the branch, and its tip may have moved on since the triggering run. - To use files from the triggering run, download its artifacts with its run id (
github.event.workflow_run.id) — see the artifacts section below.
- uses: actions/checkout@v4
with:
ref: ${{ github.event.workflow_run.head_sha }}
Checking out the triggering commit suits runs that came from your own branches. When the triggering run came from a fork pull request, that commit is untrusted code, so a privileged workflow_run job should only consume its artifacts, never build or run it.
Reusable workflows: workflow_call inputs, secrets and outputs
on: workflow_call turns a workflow into a reusable one that a job in another workflow calls with uses:. Data crosses the boundary only through what the called workflow declares.
Inputs
workflow_call inputs accept only three types: boolean, number and string (no choice or environment — validate the value inside the called workflow instead). Each can be required or have a default. The caller passes values with with:. Environment variables set in the caller's env are not propagated to the called workflow, and a calling job cannot set env or runs-on at all — it accepts keys such as with, secrets, needs, if, strategy and permissions. Per-caller values therefore always travel as inputs.
Secrets
A called workflow receives only the secrets its caller passes. The caller either maps them one by one, or uses secrets: inherit to pass all of its secrets implicitly — available when the reusable workflow is in the same organization or enterprise. A secret the called workflow declares as required: true must be supplied by one of these two methods or the call fails.
# caller
jobs:
deploy:
uses: acme/platform/.github/workflows/publish.yml@v1
with:
channel: beta
secrets:
registry_token: ${{ secrets.REGISTRY_TOKEN }} # or: secrets: inherit
Outputs: three hops out, one hop in
Inside the reusable workflow, a value leaves in three hops — a step writes it to GITHUB_OUTPUT, the job maps it under jobs.<job>.outputs, and on.workflow_call.outputs exposes it with a value that references jobs.<job>.outputs. A workflow-level output cannot reference steps directly, because steps are scoped to a job.
on:
workflow_call:
outputs:
report_url:
value: ${{ jobs.scan.outputs.url }}
jobs:
scan:
runs-on: ubuntu-latest
outputs:
url: ${{ steps.upload.outputs.link }}
steps:
- id: upload
run: echo "link=https://reports.example.com/r/88" >> "$GITHUB_OUTPUT"
The caller treats the whole call as one job: a dependent job declares needs: <calling-job> and reads the workflow-level output name from it, here needs.<calling-job>.outputs.report_url. The called workflow's internal job and output names are invisible to the caller.
Passing data between steps and jobs: GITHUB_ENV, GITHUB_OUTPUT and artifacts
What you pass and how far it must travel decide the mechanism:
| Need | Mechanism | Reach |
|---|---|---|
| An environment variable for later steps | Append NAME=value to $GITHUB_ENV | Subsequent steps of the same job only |
| A named string value | Append name=value to $GITHUB_OUTPUT in a step with an id | Later steps via steps.<id>.outputs; other jobs once mapped to job outputs |
| A string for another job | Job outputs mapped from a step output | Jobs that needs it, via needs.<job>.outputs |
| Files for another job or for people | Artifacts (upload-artifact / download-artifact) | Other jobs, other runs, downloads |
GITHUB_ENV takes effect from the next step. The step that writes the variable cannot read the new value through its own environment — use a shell variable there. Values never reach other jobs; each job runs on its own runner.
- name: Pick channel
run: |
ch=nightly
echo "CHANNEL=$ch" >> "$GITHUB_ENV"
echo "Using $ch" # use the shell variable in this step
- run: echo "Publishing to $CHANNEL" # set from this step on
Across jobs, both halves are required: a step with an id writes to GITHUB_OUTPUT, and the producing job maps it under outputs; the consumer declares needs and reads needs.<job>.outputs.<name>.
Multi-line values use a delimiter, in GITHUB_OUTPUT as in GITHUB_ENV:
- id: notes
run: |
{
echo "body<<EOF"
cat notes.md
echo "EOF"
} >> "$GITHUB_OUTPUT"
The old ::set-output workflow command is deprecated; use the environment files. Workflow commands in general (annotations, masking, grouping) are covered with workflow structure.
Caching, artifacts and retention policies
Caching and artifacts both store files, but for opposite purposes: a cache reuses files that rarely change — dependencies — between runs to make them faster; an artifact keeps files a run produced — binaries, test reports — so later jobs, other workflows or people can download them.
actions/cache: keys and restore-keys
- uses: actions/cache@v4
with:
path: ~/.cache/pip
key: pip-${{ runner.os }}-${{ hashFiles('requirements*.txt') }}
restore-keys: |
pip-${{ runner.os }}-
- The action first looks for an exact match on
key(outputcache-hitistrue). - On a miss, it tries each
restore-keysentry as a prefix and restores the most recently created matching cache;cache-hitisfalse. - When the primary key missed, the action's post step saves a new cache under that key at the end of the job — by default only when the job succeeds, so a failed job leaves no cache behind. Caches are immutable — an existing entry is never updated, so a changing key (such as a lockfile hash) is how content is refreshed.
- Caches not accessed for 7 days are evicted, and a repository has a total size limit beyond which the oldest caches are removed.
Which caches a run may restore: those created on its own branch and on the default branch; a pull request run can also restore caches from its base branch. Caches from sibling or child branches — including other pull requests — are isolated. Many setup actions (such as actions/setup-python with cache: pip) wrap the same mechanism.
Artifacts with upload-artifact / download-artifact v4
- Files between jobs: upload in the producing job, download in a job that
needsit. Job outputs andGITHUB_ENVcarry strings, not files, and runner disks do not persist between jobs. - Names are unique and immutable per run. Several jobs cannot upload into one artifact. In a matrix, put a matrix value in the name (
test-results-${{ matrix.os }}).overwrite: truereplaces an earlier artifact of the same name — it does not merge. retention-dayson an upload sets that artifact's lifetime, within the repository's maximum.- From another run (for example in a
workflow_runworkflow),download-artifact@v4needs bothrun-id— the triggering run's id,github.event.workflow_run.id, notgithub.run_id— andgithub-token, a token withactions: readon that repository.
Retention policies through the REST API
Retention decides how long a run's artifacts and logs are kept; the Actions REST API lets you set it once for an organization or a repository and clean up individual objects.
| Goal | REST call |
|---|---|
| Set artifact and log retention for every repository in an organization | PUT /orgs/{org}/actions/permissions/artifact-and-log-retention with days |
| Set it for one repository | PUT /repos/{owner}/{repo}/actions/permissions/artifact-and-log-retention |
| Read the current setting | GET on the same endpoints |
| Remove one run's logs but keep the run in history | DELETE /repos/{owner}/{repo}/actions/runs/{run_id}/logs |
| Remove a whole run | DELETE /repos/{owner}/{repo}/actions/runs/{run_id} |
| Remove one artifact | DELETE /repos/{owner}/{repo}/actions/artifacts/{artifact_id} |
| Remove caches | DELETE /repos/{owner}/{repo}/actions/caches (by key) or …/caches/{cache_id} |
Three tools, three jobs: the retention endpoint changes the policy for future runs, the delete endpoints clean up what already exists, and retention-days on an upload overrides the lifetime of a single artifact. A repository cannot keep data longer than its organization (or enterprise) allows. Finding and downloading artifacts and logs in the UI and API is covered in "Interpret workflow results, artifacts and logs".
Job summaries, status badges and environment protections
A job summary is GitHub-flavored Markdown that a step writes to the file at $GITHUB_STEP_SUMMARY; it appears on the workflow run's summary page, grouped by job, so tables, headings and links are visible without opening logs or downloading anything.
- run: |
echo "## Test results" >> "$GITHUB_STEP_SUMMARY"
echo "| Suite | Result |" >> "$GITHUB_STEP_SUMMARY"
echo "| --- | --- |" >> "$GITHUB_STEP_SUMMARY"
echo "| api | passed |" >> "$GITHUB_STEP_SUMMARY"
- Each step has its own file. When the step finishes, its summary is uploaded and later steps cannot change it; the job summary shows the step summaries in order.
>>appends to the current step's summary;>overwrites only the current step's content, never an earlier step's.- Each step's summary is limited to 1 MiB, and secrets are masked just as in logs.
- Compare: annotations carry short messages,
GITHUB_OUTPUTcarries data for other steps, artifacts must be downloaded.
Status badges
A status badge is an SVG image showing the latest result of a workflow, addressed by the workflow file name — not its display name:
https://github.com/OWNER/REPO/actions/workflows/deploy.yml/badge.svg
https://github.com/OWNER/REPO/actions/workflows/deploy.yml/badge.svg?branch=main
https://github.com/OWNER/REPO/actions/workflows/deploy.yml/badge.svg?event=push
Without parameters the badge reflects the default branch; branch selects another branch and event limits it to runs from one event type. The Actions tab can generate the Markdown for you. A badge for a private repository is visible only to people who can access the repository, so it cannot be embedded on or linked from an external site.
Environment protections
Environment protections gate deployment jobs. A job opts in with environment: <name>, and the environment's protection rules are evaluated before the job starts:
| Rule | Effect |
|---|---|
| Required reviewers | The job waits until one listed person or team member approves (self-review can be prevented) |
| Wait timer | The job waits a fixed number of minutes, then proceeds automatically — no click needed |
| Deployment branches and tags | Only the listed branches or tags (or protected branches) may deploy to the environment |
| Custom deployment protection rules | A GitHub App (for example a monitoring or change-management system) approves or rejects the deployment |
By default, repository administrators can bypass these rules; the environment setting that disallows administrator bypass makes the rules apply to everyone.
Environment secrets are released only to jobs that reference the environment, and only after its rules pass. A job without environment: gets an empty value and triggers no review; moving the secret to repository level would "work" but bypass the gate. Compare near-twins: branch protection governs merges, not deployments; timeout-minutes cancels long jobs; concurrency allows one run or job in a group at a time (queueing or cancelling the others) but sets no fixed wait.
Tip. Expect scenario questions built on short YAML snippets: a workflow that never ran, a value that arrives empty, an upload that fails, or a security review of a trigger. You are asked to predict which event starts a run, to spot the missing piece in an output or secret chain, or to choose the trigger, filter, token, API call or environment setting that meets every stated constraint. Wrong options are usually real Actions features used in the wrong place — a near-twin trigger, GITHUB_ENV where an output is needed, a repository secret where an environment secret is required — and some items ask you to select two answers.
- schedule, workflow_dispatch, repository_dispatch and workflow_run only use workflow files on the default branch; a dispatch can then target any ref.
- Cron is UTC unless a timezone key is set, runs can be delayed, and public repositories disable schedules after 60 days without activity.
- Setting types on pull_request replaces the defaults (opened, synchronize, reopened); branch and path filters must both match, and paths-ignore skips only when every file is ignored.
- Fork pull requests on pull_request get a read-only token and no secrets; pull_request_target and workflow_run run with base-repository privileges, so never run fork code in them. Scope the token with permissions: — once any scope is listed, the rest become none.
- Most events caused by the GITHUB_TOKEN (tags, releases, issues, pushes outside a pull request) start no new runs, except workflow_dispatch and repository_dispatch; pull_request runs it causes wait for approval. Use a GitHub App token or PAT for follow-on runs.
- workflow_call inputs are boolean, number or string only; required secrets must be passed or inherited (same org or enterprise); caller env never propagates.
- Outputs travel step (GITHUB_OUTPUT) → job outputs → needs; reusable workflows add on.workflow_call.outputs, and callers read needs.<calling-job>.outputs.
- Cache dependencies (key, newest prefix match via restore-keys, immutable, 7-day eviction); use artifacts for files a run produces, with unique names per run in v4.
- Set retention with the artifact-and-log-retention endpoints at org or repo level; DELETE runs/{id}/logs removes logs but keeps the run.
- GITHUB_STEP_SUMMARY is per step; badges are addressed by workflow file name; environment secrets reach only jobs with environment: after protection rules pass.
- A workflow_run run checks out the default branch by default; to ship the commit the triggering run tested, check out github.event.workflow_run.head_sha (head_branch's tip may have moved).
Frequently asked questions
Why does my scheduled GitHub Actions workflow never run?
The most common causes are that the workflow file exists only on a non-default branch (schedules only read the default branch), that the repository is public and has had no activity for 60 days so GitHub disabled the schedule, or that the cron is evaluated in UTC rather than the local time you expected. Runs can also be delayed under load, but a delay never removes every run.
What input types does workflow_dispatch support compared with workflow_call?
workflow_dispatch inputs can be string, boolean, number, choice (a drop-down limited to listed options) or environment (a drop-down of all the repository's environments). workflow_call inputs accept only boolean, number and string, so a reusable workflow that needs a restricted value must validate it itself.
What is the difference between workflow_dispatch and repository_dispatch?
workflow_dispatch starts one specific workflow file on a chosen ref with declared, typed inputs, from the UI, CLI or API. repository_dispatch is sent to the repository by an external system through the REST API with an event_type and a free-form client_payload; every workflow whose types filter matches the event_type runs, and the payload is read from github.event.client_payload.
Is pull_request_target safe to use with pull requests from forks?
It is safe only when the workflow never checks out or executes the pull request's code. pull_request_target runs in the base repository's context with access to secrets and a GITHUB_TOKEN that can write, which suits labelling or commenting. Checking out the pull request head and building or testing it hands untrusted code those secrets and that token; test under pull_request and do privileged follow-up in a workflow_run workflow instead.
Why doesn't a tag or commit pushed by my workflow trigger another workflow?
GitHub does not start workflow runs for most events caused by the GITHUB_TOKEN, to prevent recursive runs: a tag, a release, an issue or a commit pushed outside a pull request triggers nothing. The exceptions are workflow_dispatch and repository_dispatch events. A pull request created or updated with the GITHUB_TOKEN does create pull_request runs, but they wait until someone with write access approves them. To make a push, tag or release created by a workflow start other workflows, perform it with a GitHub App installation token or a personal access token.
When should I use a cache instead of an artifact in GitHub Actions?
Use a cache (actions/cache or a setup action's cache option) to reuse files that rarely change, such as downloaded dependencies, between runs so they start faster. Use an artifact for files a run produces, such as binaries or test reports, that a later job, another workflow or a person needs to download. Caches are evicted after 7 days without access; artifacts follow the retention period.
How do I pass a value from one job to another in GitHub Actions?
In the producing job, a step with an id appends name=value to the file at GITHUB_OUTPUT, and the job maps it under its outputs key. The consuming job declares needs on the producer and reads needs.<job>.outputs.<name>. GITHUB_ENV does not work for this because its variables only reach later steps of the same job; files need artifacts instead.
How do I change artifact and log retention for a whole organization?
Send PUT /orgs/{org}/actions/permissions/artifact-and-log-retention with the number of days; the repository-level equivalent is PUT /repos/{owner}/{repo}/actions/permissions/artifact-and-log-retention. These change the policy for future runs. To remove existing data, use the delete endpoints for a run's logs, a whole run, an artifact or caches.
Source
This lesson covers the "Author and manage workflows" domain of the official GH-200 exam guide. Vendors revise their guides — check the source for the current version.
Sign up free to mark lessons complete, bookmark topics and track your exam readiness.