Skip to content

Action reference

Generated by scripts/gen_action_reference.py. Edit action.yml, not this page. For the task-shaped version, see Setup.

Targets

target is the only required input and it has no default. Everything else is optional, and every one of them is checked against the target you picked: an input that belongs to another target is a hard error naming both, before the checkout.

Target What it does
github-pages Build an MkDocs site strictly and publish it to GitHub Pages
cloudflare-docs Build an MkDocs site strictly and publish it to Workers Static Assets
s3-cloudfront Sync a built static site to S3, invalidate CloudFront
lambda-zip Publish a Lambda package to S3, update the function, move an alias
terragrunt Discover, plan and (on an approval) apply Terragrunt stacks
ansible Run a playbook over SSH, then prove it is idempotent
cloudflare-workers Deploy a Worker and its static assets with Wrangler
azure-functions-zip Publish a zip to an Azure Function App, then wait for it to answer

Inputs

MagmaMoose/tremvok@v2 takes 120 inputs. target is the only one that is required.

Input Applies to Default Description
target the selector not set The deployment target. One of: github-pages Build an MkDocs site strictly and publish it to GitHub Pages. s3-cloudfront Sync a built static site to S3 and invalidate CloudFront. lambda-zip Publish a Lambda package to S3, update the function, move an alias. terragrunt Discover, plan and (on an approval) apply Terragrunt stacks. ansible Run an Ansible playbook over SSH, then prove it is idempotent. cloudflare-workers Deploy a Worker and its static assets with Wrangler. cloudflare-docs Build an MkDocs site strictly and publish it to Cloudflare Workers Static Assets, behind the docs router. azure-functions-zip Publish a zip to an Azure Function App, then wait for it to answer.
mode all auto What this run should do. One of: auto (default) push to the default branch = deploy, pull_request = preview, workflow_dispatch = deploy (pinned to the default branch). deploy Publish to the environment. preview Publish somewhere disposable; production is untouched. rollback Re-publish a previously published version.
environment all not set Logical environment name, surfaced in notifications and the deployment record. Defaults to "production" (deploy) or "preview".
working-directory all . Directory to run in. Paths in the other inputs are relative to it.
checkout all true Run actions/checkout first. Set false if the caller already checked out.
dry-run all false Resolve, plan and report without changing anything.
aws-region s3-cloudfront, lambda-zip, terragrunt not set s3-cloudfront, lambda-zip, terragrunt: AWS region. Falls back to the AWS_REGION environment variable.
aws-role-to-assume s3-cloudfront, lambda-zip, terragrunt not set s3-cloudfront, lambda-zip, terragrunt: IAM role ARN to assume with this run's GitHub OIDC token. Strongly preferred over stored keys: the credential expires in an hour and the role's trust policy decides which repository and ref may use it. Requires permissions: id-token: write. Leave empty to use credentials an earlier step already configured.
aws-role-duration-seconds s3-cloudfront, lambda-zip, terragrunt 3600 s3-cloudfront, lambda-zip, terragrunt: lifetime of the assumed-role session.
azure-client-id terragrunt, azure-functions-zip not set terragrunt, azure-functions-zip: client id of the Entra ID app registration this run signs in as, using the run's GitHub OIDC token. The Azure counterpart of aws-role-to-assume, and preferred over a stored publish profile or client secret for the same reasons: the session is minted per run and expires, and the app registration's federated credential decides which repository and which ref may mint one. A publish profile is a long-lived file carrying the deployment rights of the whole site, tied to nothing. Requires permissions: id-token: write. Leave empty to use a session an earlier step created. On terragrunt this is the PROVIDER's credential and has nothing to do with the state backend's, which terragrunt-stack-env carries. provider "azurerm" with no explicit auth falls through its default chain to the Azure CLI, so a run with a perfectly good ARM_ACCESS_KEY still fails at plan on a runner nobody signed in.
azure-tenant-id terragrunt, azure-functions-zip not set terragrunt, azure-functions-zip: Entra ID tenant the app registration lives in. Required whenever azure-client-id is set.
azure-subscription-id terragrunt, azure-functions-zip not set terragrunt, azure-functions-zip: subscription to select after login. Required whenever azure-client-id is set. On terragrunt it is the CLI default a stack inherits when its own provider block names no subscription.
gcp-workload-identity-provider terragrunt not set terragrunt: full resource name of the workload identity pool provider this run federates with, projects/<number>/locations/global/workloadIdentityPools/<pool>/providers/<provider>. The Google counterpart of aws-role-to-assume and azure-client-id, and preferred over a downloaded service-account key for the same reasons: the credential is minted per run and expires, and the pool provider's attribute condition decides which repository and which ref may mint one. A service-account key is a long-lived file, valid until somebody deletes it, tied to nothing. Requires permissions: id-token: write. Leave empty to use credentials an earlier step created. On terragrunt this is the PROVIDER's credential. If your state lives in GCS the backend needs one too, and the same federated credentials serve both.
gcp-service-account terragrunt not set terragrunt: service account to impersonate after federating, name@project.iam.gserviceaccount.com. Empty (default) uses the federated identity directly, which works when the IAM bindings name the pool's principalSet rather than a service account. Set, the pool's principalSet needs roles/iam.workloadIdentityUser on it.
gcp-project-id terragrunt not set terragrunt: project exported as GOOGLE_PROJECT and GOOGLE_CLOUD_PROJECT, for configurations that do not name one themselves. Optional.
pages-toolchain github-pages, cloudflare-docs auto github-pages, cloudflare-docs: how to install MkDocs, auto (default) | uv | pip. auto picks uv when a uv.lock is present, otherwise pip against pages-requirements. Detection exists so a caller does not have to declare per-repo what is already visible in the repo.
pages-dependency-group github-pages, cloudflare-docs docs github-pages, cloudflare-docs: uv dependency-group holding the docs tooling (uv toolchain only).
pages-requirements github-pages, cloudflare-docs docs/requirements.txt github-pages, cloudflare-docs: requirements file pinning the docs build (pip toolchain only).
pages-python-version github-pages, cloudflare-docs 3.12 github-pages, cloudflare-docs: Python version used to build the site.
pages-strict github-pages, cloudflare-docs true github-pages, cloudflare-docs: build with --strict, so a broken internal link or a nav entry pointing at a missing file fails rather than publishing a site with holes in it.
pages-site-dir github-pages, cloudflare-docs site github-pages, cloudflare-docs: directory the built site is written to.
pages-lint github-pages, cloudflare-docs true github-pages, cloudflare-docs: run the repo-shape checks (README budget and section order, licence agreement, link targets, Marketplace preflight, INHERIT clobber) before building. These are the rules nothing else covers; --strict already catches broken internal links.
pages-profile github-pages, cloudflare-docs auto github-pages, cloudflare-docs: repo profile for the shape checks, auto | action | service | spec.
pages-readme-budget github-pages, cloudflare-docs 0 github-pages, cloudflare-docs: override the README line budget. 0 uses the profile default.
pages-markdownlint github-pages, cloudflare-docs true github-pages, cloudflare-docs: run markdownlint-cli2 over docs/ and README.md when a markdownlint config is present. Runs here rather than under MegaLinter because MegaLinter's security flavor carries no markdown linter, and MARKDOWN_MARKDOWNLINT emits no SARIF, so it could never gate on net-new findings anyway.
pages-seo github-pages, cloudflare-docs true github-pages, cloudflare-docs: after the build, give every page its own meta description (from its first paragraph of prose; the home page keeps site_description), Open Graph and Twitter tags, a JSON-LD graph (WebSite, TechArticle, BreadcrumbList) and a markdown copy at <page>/index.md, linked from the page and from llms.txt. Configured by extra.seo in mkdocs.yml (locale, image, publisher, author, twitter), every key optional. A tag a page already carries is never replaced or doubled, so a site with its own SEO hook keeps it. false skips the step.
build-git-credentials github-pages, cloudflare-workers, cloudflare-docs not set github-pages, cloudflare-workers, cloudflare-docs: credentials for the private git hosts the build fetches from, one <host> <username>:<token> per line. Empty (default) changes nothing. git.example.invalid x-access-token:<a short-lived token> A docs build that installs its theme with pkg @ git+https://<host>/<org>/<repo>.git@<tag> needs that clone authenticated, and the clone is git's own, several processes below this action. Each line becomes one url.<credentialled>.insteadOf rewrite carried in GIT_CONFIG_COUNT / GIT_CONFIG_KEY_n / GIT_CONFIG_VALUE_n for the rest of this job, so the requirements file keeps pinning the plain URL and stays reviewable. Never git config --global: a self-hosted runner is shared and long-lived, and a global rewrite would leave the token in ~/.gitconfig for whatever runs on that machine next. The username is written out rather than assumed, because the forges disagree about it: x-access-token for a GitHub App token, oauth2 for a GitLab one. The split is at the FIRST :, which is the safe way round — the username is the half that cannot contain one, so a token that does survives intact. Every token is masked on receipt and never echoed. A malformed line is refused by index and host, and the host is only quoted when it looks like one, because a bare token pasted as a line would otherwise be printed into an annotation as public as the repository. Blank lines and # comments are ignored.
artifact-path s3-cloudfront, lambda-zip, cloudflare-workers, azure-functions-zip not set s3-cloudfront, lambda-zip, cloudflare-workers, azure-functions-zip: the built artifact, a directory for s3-cloudfront, a .zip for lambda-zip and azure-functions-zip.
s3-bucket s3-cloudfront, lambda-zip not set s3-cloudfront, lambda-zip: the bucket. For s3-cloudfront it serves the site; for lambda-zip it holds published artifacts.
s3-key-prefix s3-cloudfront, lambda-zip not set s3-cloudfront, lambda-zip: key prefix within the bucket. Previews are placed under <s3-key-prefix>/previews/<alias>/.
s3-delete-orphans s3-cloudfront auto s3-cloudfront: pass --delete to aws s3 sync, removing bucket objects with no local counterpart. auto (default) means yes.
cloudfront-distribution-id s3-cloudfront not set s3-cloudfront: CloudFront distribution to invalidate after the sync. Empty means no invalidation, the CDN keeps serving the old objects until its TTL expires.
cloudfront-site-url s3-cloudfront not set s3-cloudfront: base URL the distribution serves, used to build the URL reported in notifications.
lambda-function-name lambda-zip not set lambda-zip: the function to update.
lambda-function-alias lambda-zip live lambda-zip: the alias moved to the new version in deploy mode. A preview publishes a version and does not move it.
lambda-version-label lambda-zip not set lambda-zip: names the immutable S3 key (<s3-key-prefix>/<lambda-version-label>.zip). Defaults to the short commit SHA.
terragrunt-root terragrunt terraform terragrunt: directory the stacks live under.
terragrunt-exclude terragrunt modules _modules terragrunt: space-separated path segments that never hold a stack. modules _modules by default: a module has no state of its own, and guessing which stacks use it from a path is how a small module tidy-up ends up planning the whole estate. Add a stack that must only ever be applied by hand, one that rewrites a firewall wholesale, say, and the pipeline will not touch it.
terragrunt-scope terragrunt auto terragrunt: auto (changed stacks on a PR/push, all on a schedule or dispatch), all, or changed. With terragrunt-pull-request set, auto means the stacks that pull request touches even on a manual run; all stays legal and means plan the whole estate, gate on that pull request's approval, comment on that pull request.
terragrunt-pull-request terragrunt not set terragrunt: act on this pull request number instead of the one in the event payload. For a manual run that has to plan a named pull request. Its reviews are what the approval gate reads, its thread is where the plan comment goes, and its head commit is what the check run is published against, fetched from the API because a dispatch event carries no pull request. Empty (default) uses the event, which is every automatic run. Digits only. A manual run plans: dispatching a workflow is not approving a commit, so terragrunt-apply: force is what applies one by hand. Checking out the right tree stays yours: pass ref: refs/pull/<n>/merge to your own actions/checkout step and set checkout: false here, or the run plans whatever is on disk and says so.
terragrunt-apply terragrunt auto terragrunt: auto (apply on an independent approval), never (plan only), or force. Under auto the run must also be the event that asks for an apply: a pull-request review that is an approval, or a push with terragrunt-apply-on-merge on. A run that only finds an approval already standing plans and reports, because that approval was given for the commit it was given for and a commit pushed after it has not been reviewed. Re-approving applies. force skips the approval and is restricted to terragrunt-apply-operators.
terragrunt-apply-on-merge terragrunt false terragrunt: true lets a push to the default branch apply what was merged. false (the default) plans on a push and applies nothing, which is what this target has always done. A push event carries no pull request, so with this on the run asks the API which pull request the commit was merged from and reads that pull request's reviews: a merge that had an independent approval applies, a merge without one is reported and not applied. Off, none of that happens: no commit-to-pull-request lookup, no approval read, plan only. Separate from terragrunt-apply on purpose. That input answers "who may authorise an apply?"; this one answers "should a merge commit apply at all?". Stacking the two questions into one enum makes both harder to read.
terragrunt-apply-operators terragrunt not set terragrunt: comma-separated GitHub logins allowed to force an apply by hand (terragrunt-apply: force on a manual run). Empty means nobody can, so the manual path fails closed. The normal path is an independent pull-request approval and needs nothing here.
terragrunt-stack-env terragrunt not set terragrunt: environment applied per stack, one <glob> KEY=VALUE per line. The first matching line wins for a given KEY, so a specific pattern goes above the catch-all exactly as it would in a case. Blank lines and # comments are ignored. */prd/*|*/prod/* ARM_ACCESS_KEY=<the production state key> * ARM_ACCESS_KEY=<the shared state key> For an estate whose production state lives in a separate account from the rest, which is a deliberate blast-radius boundary rather than an accident: one credential cannot reach both, and without this the only options are a job per credential class or a pipeline that fails on the first stack of the other kind. The values are secrets, so they are passed to each run with env rather than exported into the shell, and one stack's credential never reaches the next stack's run. A malformed line is refused by index, glob and KEY: nothing at or after the first = is ever printed, because the annotation it would appear in is as public as the repository.
terragrunt-check-name terragrunt Terragrunt apply terragrunt: name of the check run published against the head commit. Make it a required check to enforce apply-before-merge.
terragrunt-install terragrunt auto terragrunt: install pinned, checksum-verified tofu and terragrunt binaries before running, auto (default) installs only what is not already on PATH, always reinstalls, never uses whatever the runner provides. The download is cached per version pair, so a bump lands in a new directory and can never pick up a stale binary.
terragrunt-version terragrunt 1.0.8 terragrunt: the Terragrunt version to install and run.
terragrunt-tofu-version terragrunt 1.12.1 terragrunt: the OpenTofu version to install and run under Terragrunt.
terragrunt-plugin-cache terragrunt ~/.terraform-plugin-cache terragrunt: directory to persist provider plugins in across stacks and runs, exported as TF_PLUGIN_CACHE_DIR. The workspace is wiped on each checkout, so without it every stack re-downloads every provider, the single biggest cost in a multi-stack run. Empty disables the cache.
terragrunt-refresh terragrunt auto terragrunt: refresh state from the provider before planning, auto (default) skips the refresh on a pull request and keeps it everywhere else, true always refreshes, false never does. Config-versus-state is enough for a pull-request check; the scheduled drift run is the one that has to ask the provider.
terragrunt-timeout terragrunt 900 terragrunt: seconds any one init, plan or apply may take before it is killed. 0 disables the timeout. A stalled provider call otherwise looks like a silent hang until the job limit.
terragrunt-preflight-urls terragrunt not set terragrunt: URLs to probe for reachability before the first plan, one per line. Empty (default) probes nothing. Terragrunt buffers plan output to a file, so a state backend or provider API the runner cannot reach is a silent wait until terragrunt-timeout rather than an error. One 8-second request per URL turns that into a red step naming the endpoint. https://&lt;your-state-backend-host&gt;/?comp=list https://&lt;your-provider-api-host&gt;/api/versions The rules worth knowing before you read a result: a code of 000 (DNS, connect, TLS or timeout) fails the run; any HTTP answer passes, 401 and 403 included, because an unauthenticated probe of a credentialed endpoint is supposed to be refused and being refused proves the endpoint is there. This proves reachability, not authorisation: a 403 does not mean the credential works. Blank lines and # comments are ignored. Put no credentials in these URLs; they are printed into the run log. A URL carrying userinfo is refused, and the refusal names the line by its index and shows the URL with the userinfo replaced rather than echoing the line.
terragrunt-credential-preflight terragrunt auto terragrunt: check that every cloud the discovered stacks name has a credential on this runner, before the first plan. auto (default) fails the run, warn annotates and plans anyway, off checks nothing. Exists because the credential a terragrunt run needs is not one credential. The state backend has its own (terragrunt-stack-env), and every provider block the stacks declare has another, from a different chain, that nothing in the workflow mentions. The failure that produces is the least readable in this action: state reads fine, init succeeds, and the plan dies inside a provider with could not configure AzureCli Authorizer: … Please run 'az login', twenty stacks in a row, which reads as a broken runner rather than as a credential nobody wired. What it reads: the provider "<name>" blocks in each discovered stack and in every parent directory up to terragrunt-root, so a generate block in a shared root.hcl counts. What it knows: azurerm/azuread/azapi, aws, google/google-beta, vcd. A provider it does not recognise is passed over in silence rather than guessed at. The state backend is deliberately not checked. A backend with no credential fails in init, immediately, naming the backend, which is already the good failure; and an ARM_ACCESS_KEY opens one storage account and cannot configure a provider, so counting it either way would be wrong. What it will NOT report: a provider block that configures its own authentication (client_id, client_secret, use_oidc, use_msi, access_key, credentials, token, api_token …), because that stack answers the question itself. That is the exemption that keeps this from being a check people turn off. This proves a credential is present, never that it is valid or that it can reach the subscription, project or account the stack names — the same distinction terragrunt-preflight-urls draws between reachable and authorised.
terragrunt-log-level terragrunt not set terragrunt: value for TF_LOG when diagnosing a stuck provider call (debug, trace). Empty (default) leaves provider logging off; the log is written next to the plan and redacted before it is shown.
terragrunt-extra-args terragrunt not set terragrunt: extra flags appended to every plan and apply, split on whitespace.
ansible-playbook ansible not set ansible: path to the playbook to run, relative to working-directory. Required for this target.
ansible-inventory ansible not set ansible: inventory path or comma-separated host list passed to -i. Required for this target.
ansible-version ansible 11.1.0 ansible: exact version of the ansible PyPI distribution to install. Pinned rather than floating, because a collection that changes behaviour between releases is indistinguishable from a change you made.
ansible-python-version ansible 3.12 ansible: Python version the control node runs on.
ansible-galaxy-requirements ansible not set ansible: path to a galaxy requirements file installed with ansible-galaxy install -r before the run. Empty skips it.
ansible-limit ansible not set ansible: value for --limit, restricting the run to a subset of the inventory.
ansible-tags ansible not set ansible: value for --tags.
ansible-skip-tags ansible not set ansible: value for --skip-tags.
ansible-check ansible auto ansible: run in check mode, auto (default) means check mode on a pull request and a real run everywhere else, true forces check mode, false forces a real run. A pull request that silently reconfigured a fleet would be a surprising way to find out what a diff does.
ansible-diff ansible true ansible: pass --diff, so the log shows what each changed task changed.
ansible-extra-vars ansible not set ansible: value for --extra-vars. Accepts key=value pairs or JSON.
ansible-extra-args ansible not set ansible: extra flags appended to the playbook run, split on whitespace.
ansible-ssh-private-key ansible not set ansible: SSH private key used to reach the inventory. Pass a secret, never a literal. It is masked on receipt, written to a 0600 file under RUNNER_TEMP, and removed when the step exits however it exits.
ansible-ssh-user ansible not set ansible: value for --user. Leave empty to let the inventory or ansible.cfg decide.
ansible-ssh-known-hosts ansible not set ansible: known_hosts entries for the inventory, one per line. Empty means host-key checking is disabled for the run, which is a real downgrade, supply this for anything reachable from a network you do not control.
ansible-vault-password ansible not set ansible: vault password. Pass a secret. Masked on receipt, written to a 0600 file under RUNNER_TEMP, removed when the step exits.
vault-addr ansible not set ansible: base URL of a HashiCorp Vault, e.g. https://vault.example.com:8200. Set it, with vault-token, to name secrets by Vault reference instead of passing them in. Only the fields you reference are read.
vault-token ansible not set ansible: Vault token the references are read with. Pass a secret. Needs read on the paths you reference and nothing else.
vault-namespace ansible not set ansible: Vault namespace (Enterprise). Empty is correct for open-source Vault.
ansible-vault-passthrough ansible false ansible: hand VAULT_ADDR, VAULT_TOKEN and VAULT_NAMESPACE to the ansible-playbook process, so the playbook can read its own secrets from the same HashiCorp Vault rather than having them copied into a second store that quietly stops being rotated. Needs vault-addr and vault-token; without both, nothing is passed. false by default, and the default removes them from the playbook's environment. A token scoped to the fields this action reads becomes a token every task, role and collection in the play can use, and widening a credential's blast radius should be a decision rather than something inherited by accident. Nothing to do with ansible-vault, the file-encryption tool; that is ansible-vault-password.
ansible-ssh-private-key-vault ansible not set ansible: Vault reference to the SSH private key, <path>#<field>, e.g. secret/data/team/app#ssh_private_key. The alternative to ansible-ssh-private-key, not a supplement: setting both fails. Use this where Vault is already the source of truth, so rotating there keeps rotating here and the key is not copied into a second store that quietly goes stale.
ansible-ssh-known-hosts-vault ansible not set ansible: Vault reference to the known_hosts entries, <path>#<field>. The alternative to ansible-ssh-known-hosts.
ansible-vault-password-vault ansible not set ansible: Vault reference to the ansible-vault password, <path>#<field>. The alternative to ansible-vault-password.
ansible-verify-idempotence ansible true ansible: after a successful real run, run the playbook again in check mode and fail if any task would still change. A zero exit only proves the playbook ran; this is what proves it converged. Skipped automatically when the run itself was check mode.
cloudflare-api-token cloudflare-workers, cloudflare-docs not set cloudflare-workers, cloudflare-docs: API token Wrangler authenticates with. Pass a secret, never a literal. Mint it from Cloudflare's "Edit Cloudflare Workers" template rather than a hand-picked permission list, or the first deploy of a custom domain fails on a permission nobody thought to grant.
cloudflare-account-id cloudflare-workers, cloudflare-docs not set cloudflare-workers, cloudflare-docs: Cloudflare account id. Pass a secret.
cloudflare-config cloudflare-workers, cloudflare-docs not set cloudflare-workers, cloudflare-docs: path to the Wrangler config (wrangler.toml or wrangler.jsonc), passed as --config. Empty lets Wrangler find it, which is the normal case. The config owns the asset directory, routes, custom domains and 404 handling; the inputs here are overrides for the few things a workflow legitimately varies.
cloudflare-worker-name cloudflare-workers, cloudflare-docs not set cloudflare-workers, cloudflare-docs: overrides the name in the Wrangler config, passed as --name.
cloudflare-env cloudflare-workers not set cloudflare-workers: Wrangler environment, passed as --env. This selects a config block, which is not the same thing as the shared environment input: that one is a label for notifications and the deployment record.
cloudflare-main cloudflare-workers not set cloudflare-workers: entry point for a Worker that runs code, passed as Wrangler's positional path argument. Leave empty for an assets-only Worker, which is the shape that serves static files straight from the edge with no cold start and no code in the request path.
cloudflare-build-command cloudflare-workers not set cloudflare-workers: command run in working-directory before Wrangler, for a Worker that needs bundling. Empty skips it.
cloudflare-compatibility-date cloudflare-workers not set cloudflare-workers: overrides the config, passed as --compatibility-date.
cloudflare-minify cloudflare-workers false cloudflare-workers: pass --minify. Applies to Worker code, not to static assets.
cloudflare-vars cloudflare-workers not set cloudflare-workers: plain (NOT secret) variables, one KEY=VALUE per line, each passed as --var. Wrangler deletes vars not present in the config unless --keep-vars is set, so anything set in the dashboard belongs in the config instead.
cloudflare-wrangler-version cloudflare-workers, cloudflare-docs 4.114.0 cloudflare-workers, cloudflare-docs: exact Wrangler version to run. Pinned, because the tool that publishes to production is not a floating dependency.
cloudflare-node-version cloudflare-workers, cloudflare-docs 24 cloudflare-workers, cloudflare-docs: Node version Wrangler runs on. Wrangler 4 declares engines.node >= 22, and on 20 it installs cleanly and then refuses to run, so this does not default to whatever the runner happens to ship.
cloudflare-docs-host cloudflare-docs not set cloudflare-docs: hostname the docs router serves, e.g. docs.magmamoose.com. The site Worker itself has no hostname — it is reachable only through the router's service binding — so this is the address the Access check and the reported URL are about. Required when cloudflare-docs-require-access is on.
cloudflare-docs-path cloudflare-docs not set cloudflare-docs: path segment this repository's site is served under on cloudflare-docs-host. Empty (default) uses the repository name, which is what the router dispatches on, so overriding it is for a repo whose site is mounted elsewhere.
cloudflare-docs-require-access cloudflare-docs false cloudflare-docs: refuse to publish unless a Cloudflare Access application actually covers <cloudflare-docs-host>/<cloudflare-docs-path>. This is enforcement, not convention: "the site is behind Access" is otherwise a belief nothing checks, and the deploy is the one moment something can ask Cloudflare and refuse. Needs the Access: Apps READ permission on cloudflare-api-token — the "Edit Cloudflare Workers" template does not include it, and a 403 is treated as "could not tell", never as "nothing covers it".
cloudflare-docs-index cloudflare-docs true cloudflare-docs: emit the docs corpus from the build: llms.txt and llms-full.txt written into the site before it is published, and a search index of every page for the documentation MCP servers. One pass over the markdown that was just rendered; no credentials and no network. Publishing the index is a separate opt-in, cloudflare-docs-index-bucket.
cloudflare-docs-index-bucket cloudflare-docs not set cloudflare-docs: R2 bucket the index is published to, as index/<repo>.json. Empty (default) writes llms.txt into the site and uploads no index. Only a deploy writes it, and only after the site itself deployed: a pull request would otherwise overwrite the shared corpus with an unmerged branch. Needs R2 object write on cloudflare-api-token, which the "Edit Cloudflare Workers" template does not include.
cloudflare-docs-capability-file cloudflare-docs capability.json cloudflare-docs: this repository's capability declaration, published to capability/<repo>.json in cloudflare-docs-index-bucket. Empty disables it; the default names a file most repositories do not have, and absent is the normal case — most repositories are not house tools. Present, it is validated against https://mcp.magmamoose.com/schema/capability.schema.json on every run, including a pull request, and a declaration that does not satisfy it FAILS THE RUN: the MCP reads a malformed document as private and reports it as unreadable, counted and never named, which is the one state invisible from the publishing side. private is republished from the repository's own visibility on the same fail-closed rule as the docs index, and commit and generated are stamped from the run.
cloudflare-extra-args cloudflare-workers not set cloudflare-workers: extra flags appended to the Wrangler invocation, split on whitespace.
cloudflare-verify-config cloudflare-workers true cloudflare-workers: before publishing, run wrangler deploy --dry-run and refuse to publish if Wrangler reports configuration it will not apply: an unexpected field (a misspelled [[r2_bucket]] is only a warning, and the Worker deploys with no bucket), or a top-level binding an --env deploy does not inherit. A binding is only real if Wrangler prints it. Costs one extra bundle per run.
functions-app-name azure-functions-zip not set azure-functions-zip: the Function App to publish to. Required for this target.
functions-resource-group azure-functions-zip not set azure-functions-zip: resource group the Function App lives in. Required for this target.
functions-slot azure-functions-zip not set azure-functions-zip: deployment slot to publish to instead of production. Empty (default) publishes to the app itself. This is also what a preview has to use. A pull request must not publish onto the routes production serves, and a slot is Azure's only destination that does not — but a Linux Consumption plan HAS NO SLOTS. So on Consumption a preview is refused and says why, rather than quietly shipping a branch to production. On Premium or Dedicated, set this and previews go to the slot.
functions-ready-attempts azure-functions-zip 30 azure-functions-zip: how many times to ask the app for an answer after publishing, before calling the deploy dead. Any HTTP status passes, including a 404 at the root, which is what a Function App whose only trigger is at /api/<name> normally returns. Only a connection failure and a 503 count as "not yet". The default is generous on purpose: a freshly created Consumption app 503s from both the site and its SCM endpoint until content is first published, so a FIRST deploy has to retry through that rather than fail on it.
functions-ready-delay azure-functions-zip 10 azure-functions-zip: seconds between readiness attempts. With the default attempts, that is a five-minute ceiling on a cold first publish.
verify-url all not set Post-deploy: the URL that must answer. Empty skips verification. Catches the deploy that uploaded but did not bind, the one failure that otherwise looks green.
verify-header all not set Post-deploy: a response header that must be present on verify-url (e.g. content-security-policy).
verify-header-match all not set Post-deploy: an extended regex the verify-header value must match.
verify-method all GET Post-deploy: HTTP method used to request verify-url. GET by default. Set it where a GET cannot verify the endpoint at all. A webhook receiver binds POST and nothing else, so a GET reaches no function and the platform answers 404 — which is also what an empty package returns, leaving the check unable to tell a working deploy from a broken one. verify-method: POST with verify-status: 401 asserts that the function is bound and that its signature check refuses an unsigned request, which is the thing worth knowing. The method survives a redirect: curl otherwise downgrades a redirected POST to a GET, which would quietly turn the assertion into a different one.
verify-status all 200 Post-deploy: expected HTTP status from verify-url.
verify-attempts all 6 Post-deploy: how many times to try verify-url before failing.
verify-delay all 10 Post-deploy: seconds between verification attempts.
notify all always always (default), on-success, or on-failure. Applies to the webhook sinks.
pr-comment all auto Sticky pull-request comment. auto (default) means on for preview mode and for the terragrunt target.
slack-webhook all not set Slack incoming-webhook URL. Empty means the sink is off. Never fails the deploy.
teams-webhook all not set Microsoft Teams incoming-webhook URL. Empty means the sink is off. Never fails the deploy.
api-url all not set Base URL of a Tremvok API to record this deployment with. Authenticates with a GitHub OIDC token, so the repository stores no credential. Empty means no record is kept.
api-audience all tremvok OIDC audience the Tremvok API expects.
auth-token all ${{ github.token }} Token used for the pull-request comment and the check run. Needs pull-requests: write and, for the terragrunt target, checks: write.
allow-fork-preview all false Let a fork pull request attempt a deploy. Off by default and almost always wrong: a fork cannot read secrets, so this only converts an honest skip into an auth error.
allow-dispatch-from-any-ref all false Let a manual run deploy from a branch other than the default one. Off by default, publishing a topic branch to production usually is not what "Run workflow" meant.

Outputs

Output Description
target The deployment target this run used.
mode The resolved mode: deploy, preview or rollback.
environment The resolved environment name.
url URL of what was deployed, when the target produces one.
deployed true when something was actually deployed (false for a skip).
verified true when post-deploy verification ran and passed.
skipped true when the run skipped (fork pull request, or no credential).
skip-reason Why it skipped, in a sentence.
record-id Identifier returned by the Tremvok API, when api-url is set.
site-dir github-pages: absolute path to the built site.
pages-toolchain github-pages: the toolchain actually used, uv or pip.
version-id lambda-zip, cloudflare-workers, azure-functions-zip: the published version. A Lambda version number, a Worker Version ID, or the package sha256 — Azure exposes no per-deploy content hash of its own.
stacks terragrunt: how many stacks this run discovered.
plan-changes terragrunt: how many of those stacks planned with a diff.
applied terragrunt: true when this run applied, false when it only planned.
changed-tasks ansible: how many tasks reported changed on the real run.
idempotent ansible: true when the second check-mode run found nothing left to change.

Required permissions

Declared by the caller, because a composite action cannot declare permissions:. Only what the target actually uses:

target: github-pages

permissions:
  contents: read        # checkout
  pages: write          # actions/deploy-pages, in the caller's own job
  id-token: write       # actions/deploy-pages

target: s3-cloudfront

permissions:
  contents: read        # checkout
  id-token: write       # assume the deploy role by OIDC
  pull-requests: write  # the sticky preview comment

target: lambda-zip

permissions:
  contents: read        # checkout
  id-token: write       # assume the deploy role by OIDC
  pull-requests: write  # the sticky preview comment

target: terragrunt

permissions:
  contents: read        # checkout
  id-token: write       # assume the deploy role by OIDC
  pull-requests: write  # the rolling plan comment
  checks: write         # the check run that makes apply-before-merge enforceable

target: ansible

permissions:
  contents: read        # checkout
  pull-requests: write  # the sticky run comment

target: cloudflare-workers

permissions:
  contents: read        # checkout
  pull-requests: write  # the sticky preview comment

target: cloudflare-docs

permissions:
  contents: read        # checkout
  pull-requests: write  # the sticky preview comment

target: azure-functions-zip

permissions:
  contents: read        # checkout
  id-token: write       # sign in to Azure by OIDC
  pull-requests: write  # the sticky preview comment

target: github-pages is the one target the action cannot finish on its own: actions/deploy-pages needs pages: write and the github-pages environment, and a composite action can declare neither. The action builds and stages the artifact; the caller's job runs actions/deploy-pages. Every other target completes inside the action.