Appearance
Inputs and outputs
Every with: input the action accepts, and the outputs it sets for the steps after it. Every input is optional (the required column is always false); a default of "" is an unset input, and the description says what an omitted input means. The token grant is on the permissions page, the undeclared policies on the undeclared policy page.
Inputs
| name | description | required | default |
|---|---|---|---|
token | Token used for the API calls. Most sections need a fine-grained PAT with Administration read/write on the repository - the default GITHUB_TOKEN can never hold that permission. | false | ${{ github.token }} |
repository | Target repository (owner/name). Defaults to the current repository. Single-repo mode only; cannot be combined with repos or repos-dir. | false | "" |
settings-file | Path to the settings YAML file: exactly one in apply and check. In mode: render, the ordered list of settings files to fold instead, newline- or comma-separated, lowest layer first. Newlines and commas are list separators in every mode, so a settings-file path can never contain a comma. Single-repo and render modes only; multi-repo targets read repos-dir files or each repository's own .github/settings.yml, so overriding it alongside repos or repos-dir fails the run. | false | .github/settings.yml |
mode | apply (mutate), check (report drift, exit 1 on any), render (fold the settings-file layers into one document written to rendered-file, with no token and no GitHub API call; render reads only settings-file, rendered-file, layering, and undeclared, ignores token, and rejects every other input set to a non-default value, since each controls an apply or check run), or snapshot (read the live settings of the target repositories back and write each as a settings document to snapshot-file or under snapshot-dir; nothing is written to GitHub, the document reaches only the file, and every input that controls an apply, a check, or a render is rejected). check makes no settings changes, though a private report may still be delivered. | false | apply |
rendered-file | mode: render only, and required there: the path the rendered settings document is written to (parent directories are created). The file holds exactly what apply would run: every section validated, each section that takes an undeclared policy in its policy-wrapper form with the policy made explicit, the other sections in their own shape, and the _layering directives dropped. Feed it to a later apply or check step as its settings-file. Must not name one of the settings-file layers (the render would overwrite it). Fails when set in apply or check. | false | "" |
snapshot-file | mode: snapshot only, and exactly one of snapshot-file and snapshot-dir is required there: the path one repository's live settings are written to as a settings document (parent directories are created). The target is the repository input, defaulting to the current repository, so it cannot be combined with repos or repos-dir. The header pins the schema, names the repository and the moment, and lists every section note; secret values GitHub never reveals become $NAME references to export before an apply. Must not be .github/settings.yml, the file apply and check read: the snapshot would overwrite the document you author, so write it beside that file and copy it over deliberately. Fails when set in apply, check, or render. | false | "" |
snapshot-dir | mode: snapshot only, and exactly one of snapshot-file and snapshot-dir is required there: the directory the multi-repo targets' live settings are written under, one owner/name.yml per target (the repos-dir layout, so the directory can later serve as a repos-dir). The targets come from repos and repos-dir exactly as in a multi-repo apply, discovery filters included; defaults-file does not apply. Must be disjoint from the repos-dir (not the same directory, not above it, not below it): the snapshots would overwrite the central files or be read back as central files. Fails when set in apply, check, or render. | false | "" |
on-missing-permission | fail (default) or warn. Under warn, sections the token cannot access are skipped with a warning and the run stays green (partial success). | false | fail |
required-sections | Comma-separated section names that must fully apply even under on-missing-permission: warn (minimum requirements). Every name must also be allowed by the "sections" input when that allowlist is set; a required section the allowlist excludes is rejected up front, because the run could never attempt it. | false | "" |
sections | Optional comma-separated allowlist of sections to process; unset, every declared section is processed. apply, check, and snapshot only: mode: render writes every section its layers declare, so the allowlist belongs on the step that runs the rendered document and fails the render when set. | false | "" |
api-version | X-GitHub-Api-Version header value. Override to opt into a newer REST API version before this action defaults to it. | false | 2022-11-28 |
repos | Multi-repo remote mode: comma- or newline-separated owner/name targets, each applied from its own .github/settings.yml (default branch), or "*" alone to discover every repository the token's user owns, filterable via the visibility, archived, forks, exclude, topics, and affiliation inputs. Combinable with repos-dir; a repos-dir file for the same repository wins. | false | "" |
repos-dir | Multi-repo central mode: a directory in the checked-out admin repository holding per-repo settings files - name.yml (same owner as this repository) or owner/name.yml. Requires actions/checkout. | false | "" |
defaults-file | YAML settings document applied to every multi-repo target that has no settings file of its own (a repos target without .github/settings.yml, which is otherwise skipped). A target with its own file is applied as written; the defaults are never merged into it. With repos: "*" every discovered repository without a settings file receives the defaults; run mode: check first. Multi-repo mode only; fails when set without repos or repos-dir. | false | "" |
layering | mode: render only: replace, shallow, or deep (default), the run-wide default for how every list section's entries combine with the layers below them, each section by its own key (a label's name, a ruleset's name, a secret's name, and so on). replace lets the higher list win wholesale; shallow unions the entries by key and swaps a same-key entry for the higher one; deep unions by key and merges a same-key pair field by field, a nested keyed list (a ruleset's rules, by type) unioning the same way. A layer's own _layering directive, at its top level or on a section's {entries} wrapper, overrides it per file or per section. Lists outside the list sections are replaced by the higher layer's. Fails when set in apply or check. | false | "" |
undeclared | keep or delete: the run-wide fallback for what apply does to a live resource a list does not declare, for every list that takes the _undeclared knob (the sixteen knobbed sections and an environment's variables, secrets, deployment branch policies, and deployment protection rules). Unset by default, so each list's own default applies. A list's wrapper _undeclared wins over the file's top-level _undeclared, which wins over this input. In mode: render the resolved policy is written into every list of the rendered document, so a later apply of that document needs no undeclared input of its own. Rejected in mode: snapshot. | false | "" |
private-repos | redact (default) or show. Under redact, private and internal targets are hidden from this run's public logs, summary, and outputs: their slug becomes a "private repository #N" placeholder, live values and error bodies are replaced with "hidden (private repository)", and each slug is registered with the runner's secret masker. A target equal to GITHUB_REPOSITORY is never redacted. show reveals everything (today's behavior); only use it when the run's logs are not publicly readable. | false | redact |
private-report | none (default), issue, issue-on-failure, or artifact. Delivers the full unredacted report only for redacted targets the visibility probe proves private or internal (an unknown visibility is redacted but excluded from delivery). Under issue, each such target's report is delivered as a reused, marker-labelled issue on that target repository itself (the one GitHub-private channel a public run has): the body is replaced every run, and the issue is opened when the target fails or drifts and closed when it is healthy. issue-on-failure is the quiet variant: a failing or drifting target gets the same issue, but a healthy run only closes a still-open issue from a previous failure and otherwise writes nothing - no issue ever appears on a repository that never needed attention (though a declared labels section still creates the marker label, and a manually-removed marker label defers the close: the next failing run reattaches it, and the first healthy run after that closes the issue). Under artifact, those reports are concatenated, age-encrypted to report-public-key, and uploaded as one workflow artifact (settings-as-code-private-report) for readers who hold the key but no GitHub access to the targets; the artifact channel needs the Actions artifact service, so on GitHub Enterprise Server it warns and uploads nothing. Applies only to redacted targets, so it is rejected alongside private-repos: show. Report delivery writes even in mode: check, and its failure never changes the run's result. | false | none |
report-public-key | The age recipient (a public key starting with age1) the artifact channel encrypts every report to; safe to commit in the workflow. Generate a keypair with "age-keygen -o key.txt", keep key.txt secret, and decrypt a downloaded artifact with "age -d -i key.txt private-report.md.age". Required when private-report is artifact and rejected otherwise. | false | "" |
visibility | Keeps only repositories of this visibility in repos: "*" discovery. One of all (default), public, private, or internal; internal is matched client-side (Enterprise only). Fails if set outside that discovery. | false | "" |
archived | Archived-repository policy for repos: "*" discovery. One of skip (default; settings writes fail on archived repositories), include, or only (mostly useful with mode: check). Fails if set outside that discovery. | false | "" |
forks | Fork policy for repos: "*" discovery. One of include (default), exclude, or only. Fails if set outside that discovery. | false | "" |
exclude | Comma- or newline-separated wildcard patterns removing repositories from repos: "*" discovery. An asterisk matches any characters; a pattern containing "/" matches the full owner/name, any other the name alone. Case-insensitive. Fails if set outside that discovery. | false | "" |
topics | Comma- or newline-separated topics; repos: "*" discovery keeps only repositories carrying at least one of them. Unrelated to the topics settings section. Fails if set outside that discovery. | false | "" |
affiliation | Comma-separated affiliations for repos: "*" discovery, passed to the GitHub /user/repos listing. Any of owner (default), collaborator, organization_member; the list replaces the default, so use owner,collaborator to widen rather than move discovery. Fails if set outside that discovery. | false | "" |
The discovery-only inputs apply to repos: "*"; the multi-repo guide covers the filters and the two sourcing modes.
Outputs
result:failed/drift/partial/skipped/applied/clean/snapshot/rendered, worst first across the run's targets; the exit code is 1 exactly when it isfailed, ordriftin mode: check. The snapshot guide says what each snapshot word means.skipped-sections: the sections skipped for missing permissions underon-missing-permission: warn, comma-separated (a deduped union across targets in multi-repo mode); empty when none.repos-result: a JSON map ofowner/nameto{result, source, skipped-sections}, one entry per target of a multi-repo run (repos,repos-dir, or thesnapshot-dirform ofmode: snapshot); the empty map{}for a run over one repository or a render. A redacted private target is keyed by itsprivate repository #Nplaceholder instead of its slug; see Private repositories.
All three outputs are set on every run, whatever the mode and however it ended.
A check run exits 1 on any drift, so a downstream step usually reads result only when the step runs with continue-on-error: true or through if: always(). Check mode has the exit-code table.
Environment variables
Beside the inputs, a run reads these from its environment. The Actions runner sets every GITHUB_* and ACTIONS_* name below except GITHUB_TOKEN, which a workflow exports itself; the command line reads GITHUB_TOKEN, GITHUB_REPOSITORY, GITHUB_API_URL, and GSAC_RETRY_BASE_MS under the same names.
| Variable | Read for |
|---|---|
GITHUB_TOKEN | The token when the token input (or --token) is empty |
GITHUB_REPOSITORY | The default repository, the owner of a bare <name>.yml under repos-dir, and the one target never redacted |
GITHUB_SERVER_URL, GITHUB_RUN_ID | The run link a private report carries |
GITHUB_API_URL | The REST base URL (GitHub Enterprise Server) |
GITHUB_OUTPUT, GITHUB_STEP_SUMMARY | Where the action writes its outputs and the step summary (the command line prints them instead) |
ACTIONS_RUNTIME_TOKEN | The artifact service's credential; private-report: artifact warns and uploads nothing without it (GitHub Enterprise Server) |
GSAC_RETRY_BASE_MS | A test knob: the real milliseconds in one retry-backoff second. Set, it also selects the immediate scheduler, so the rate-limit Retry-After waits and the write limiter's spacing are skipped rather than scaled. Unset, a second is a second and the waits are real. Set it only to make a retry scenario finish in milliseconds against a mock; against GitHub the skipped waits earn the next secondary rate limit |