CI/CD operations
GitHub Actions workflow_dispatch: safe manual inputs
A workflow_dispatch run is a manual trigger with a selected branch or tag and optional inputs. It does not itself approve a deployment or satisfy a pull request required check. Define typed inputs for operator intent, validate the actual values and run SHA before a credential-bearing step, and keep secrets out of the input form. After the run, compare its event, revision, check results, and destination's observed revision before declaring delivery complete.
Treat dispatch as a request to evaluate, not authority to ship
workflow_dispatch lets an authorized user or integration start a GitHub Actions workflow outside the ordinary push and pull request triggers. It is useful for a controlled retry, maintenance operation, or release candidate that needs an operator-selected target. The event does not answer whether the selected code passed the repository's required checks, whether a deployment is permitted, or whether the target is healthy. Those decisions belong to the workflow's policy and the destination's evidence. Design the manual path to inspect the same revision and gates as the automatic path, rather than treating the presence of a Run workflow button as an approval signal.
GitHub requires the workflow file to exist on the default branch for workflow_dispatch to receive events. The UI offers Run workflow for a workflow present there. After the workflow has run at least once, GitHub documents dispatching it against another branch or tag through the API or CLI. The dispatch ref is a branch or tag, and GITHUB_SHA identifies the commit associated with that run’s selected ref. A branch name is therefore an operator choice, not a durable revision identifier. Compare the actual run SHA with the reviewed candidate before using credentials or reusing an artifact.
An API caller needs the Actions repository permission with write access for the dispatch endpoint; classic personal access tokens use the documented repo scope. That permission authorizes creating the event, not the downstream cloud deployment. Keep dispatch permission scoped to the integration that needs it and let the workflow enforce its own event, ref, environment, and release gates. This guide describes the endpoint's contract only; it does not issue a dispatch request or imply that any particular account may deploy.
Sources: Events that trigger workflows — GitHub Docs · REST API endpoints for workflows — GitHub Docs · Manually running a workflow — GitHub Docs
Use typed fields to describe intent, then validate again
GitHub supports boolean, choice, number, environment, and string input types for workflow_dispatch. A boolean stays a Boolean in the inputs context but becomes a string in github.event.inputs; a choice resolves to a string. This distinction matters for a dry-run flag: comparing the string false as though it were a Boolean can send the job down the wrong path. Use the inputs context for typed decisions and test the false and true cases in a non-privileged run. Keep a small input surface with names that make the operator's decision legible.
A choice list or environment selector helps the UI present valid-looking options. It is not a substitute for authorization at the provider. The environment named by an input has no effect merely because the text was selected; the deployment job must reference the intended GitHub environment, and the job must satisfy that environment's configured protection rules. A cloud provider must still enforce its own role trust and target permissions. Validate the selected value against a maintained allowlist before requesting a deploy credential, because API callers and future workflow edits can bypass assumptions formed from today's form display.
Use a bounded attempt input only for a known operation. If an operator is retrying a failed candidate, choose an allowed attempt count such as one or two, record why the previous attempt failed, and require the same reviewed revision unless the new revision is reviewed separately. Do not put an API key, password, signed URL, or other secret in a dispatch input: inputs appear as workflow event data and are not the repository's secret storage mechanism. Use a narrow environment secret or a provider identity whose trust rule matches the approved job instead.
# Trigger fragment only; jobs and credentials are intentionally omitted.
on:
workflow_dispatch:
inputs:
target_environment:
description: Reviewed deployment target
required: true
type: choice
options: [staging, production]
dry_run:
description: Evaluate without delivery
required: true
type: boolean
default: true
max_attempts:
description: Bounded retry count
required: true
type: number
default: 1The fragment is deliberately incomplete; it does not make a runnable deployment workflow. A production job would still have to reject a target not in the reviewed allowlist, reject an attempt count outside the chosen range, prove the source revision, and acquire credentials only after those checks. The example chooses a choice input for the target to keep the available options visible, but a choice value is still string data. An environment-type input can provide a GitHub environment selector; it does not by itself attach protection rules to a job or grant access at the destination.
Sources: Workflow syntax for GitHub Actions — GitHub Docs · Deployment environments — GitHub Docs · REST API endpoints for workflows — GitHub Docs
Keep input strings out of generated shell code
An operator-entered string is data even when the operator is trusted to click a button. GitHub evaluates expressions before an inline run script reaches the shell. Placing an expression directly inside quoted shell text can still allow crafted characters to change the script that executes. Transfer an input through a step environment variable, validate it against the small set of accepted values, and then pass the quoted variable to a reviewed command. Do not interpolate an arbitrary ref, environment, or release note directly into executable shell text.
The allowlist must be enforced at runtime before the privileged operation, not only described in the UI. For the synthetic target field above, the decision is exactly staging or production; any other value fails without requesting a deployment identity. For max_attempts, accept only the explicitly supported integers and compare them as numbers. A dry-run path should avoid the credential request and external write altogether. This sequence keeps a malformed input from becoming a fallback target or a command argument with unexpected meaning.
A reviewed ref deserves similar care. A tag or branch name can be moved or replaced according to repository permissions, and the dispatch run records the commit it actually uses. Compare that SHA with the approved candidate and any artifact provenance. If they differ, stop and obtain a new decision for the new code rather than assuming the old review applies. Even when the inputs are valid, a credential-bearing release step should not execute untrusted fork code or downloaded data simply because the workflow was started manually.
Sources: Script injections — GitHub Docs · Secure use reference — GitHub Docs · Events that trigger workflows — GitHub Docs
Walk one manual release request to proof
Consider a synthetic operator request: run the workflow from reviewed main, target staging, dry_run false, and max_attempts one. Before dispatch, the operator records the intended source commit H2 and the reason a manual run is needed. After GitHub creates run R18, the job reads its event as workflow_dispatch, its selected ref as main, and its actual GITHUB_SHA as H2. These are illustrative labels, not real SHA or run IDs. The release job checks that the exact H2 candidate passed the appropriate automated gate and that staging is allowed by policy before it asks for the staging identity.
| Stage | Recorded evidence | Decision |
|---|---|---|
| Request | Reviewed ref main, expected H2, staging, dry_run false, one attempt | No authority inferred from form submission |
| Run | R18 reports workflow_dispatch and actual SHA H2 | Stop if event or SHA differs from approved request |
| Gate | Required verification for H2 and staging policy | Only then request the scoped delivery identity |
| Delivery | One provider operation for H2 with a recorded operation ID | Query uncertain result before retrying |
| Proof | Staging reports live revision H2 | Close the request only after target readback |
If the run reports H3 because main moved, the earlier H2 decision no longer covers it. If the job reaches its attempt limit after an uncertain provider response, query the provider operation and target rather than dispatching again merely to get a green badge. A selected environment can impose GitHub protection rules when the job actually references it, but the target itself must still report what it runs. A successful dispatch response means a run was requested; it is not a release receipt.
Sources: Events that trigger workflows — GitHub Docs · REST API endpoints for workflows — GitHub Docs · Deployment environments — GitHub Docs
Keep manual runs separate from pull request required checks
GitHub's required-check troubleshooting guidance says GitHub Actions job checks from workflow_dispatch on a pull request head do not appear in that pull request's checks section and do not satisfy a required status check in a branch ruleset, even when they pass on the same head commit. A manual rerun can help diagnose code, but it does not replace the eligible pull request event and required context. If a protected branch is waiting for a required check, repair or rerun the workflow on the event GitHub evaluates instead of adding a dispatch as a shortcut.
Document the separate intents. Pull request and merge queue checks answer whether a candidate can enter the protected branch. A workflow_dispatch job answers a specific operator request on a selected ref. A deployment job answers whether the approved revision reached a target. The same tests may be reused across these paths, but their event and source identity still matter. Link the manual run to the check and delivery records so an agent can explain exactly what was proven, what remains unknown, and which operation would close the gap.
Sources: Troubleshooting required status checks — GitHub Docs · Events that trigger workflows — GitHub Docs · Manually running a workflow — GitHub Docs
Sources and verification
- Events that trigger workflows — GitHub Docs
GitHub documents default-branch workflow existence, dispatch ref and SHA semantics, and API or CLI dispatch against branch or tag after initial run. Verified .
- Workflow syntax for GitHub Actions — GitHub Docs
GitHub documents workflow_dispatch input types, Boolean preservation in inputs versus event.inputs, choice strings, and input limits. Verified .
- REST API endpoints for workflows — GitHub Docs
GitHub documents dispatch ref and input parameters, required Actions write permission for fine-grained tokens, and the response as a created run. Verified .
- Manually running a workflow — GitHub Docs
GitHub documents the Run workflow UI, CLI ref selection, and REST dispatch with inputs and defaults. Verified .
- Deployment environments — GitHub Docs
GitHub environment protection rules apply when a job references an environment; they govern job start and environment secret access. Verified .
- Script injections — GitHub Docs
GitHub explains that expression substitution into inline shell scripts can turn untrusted context data into executed code. Verified .
- Secure use reference — GitHub Docs
GitHub recommends an intermediate environment variable for untrusted inline-script data and cautions against privileged execution of untrusted code. Verified .
- Troubleshooting required status checks — GitHub Docs
GitHub states GitHub Actions workflow_dispatch job checks on a pull request head do not satisfy branch-ruleset required checks. Verified .