Skip to content

Environments

Deployment environments, matched by name: protection rules plus environment-scoped secrets/variables (same shape as the top-level secrets and variables sections, but scoped to the environment).

Field Type Default Notes
name string required Match key
wait_timer int unmanaged Minutes to wait before allowing deployment
reviewers list[reviewer] unmanaged Required reviewers; see below
prevent_self_review bool unmanaged
deployment_branch_policy.protected_branches bool unmanaged Mutually exclusive with custom_branch_policies
deployment_branch_policy.custom_branch_policies bool unmanaged Mutually exclusive with protected_branches
deployment_branch_policy.patterns list of {name, type} unmanaged The custom branch/tag patterns allowed to deploy; requires custom_branch_policies: true
secrets list[secret] unmanaged Same shape as the top-level secrets section
variables list[variable] unmanaged Same shape as the top-level variables section

Setting both protected_branches: true and custom_branch_policies: true is a config error at load time — GitHub's API rejects that combination too, but with an opaque error, so it's caught earlier here.

Each entry in patterns has a name (required, non-empty — a branch/tag name or glob like v* or releases/*) and a type (branch | tag, default branch).

Note

custom_branch_policies: true alone enables custom policies but registers no patterns, so nothing can deploy until at least one pattern is declared. patterns is an authoritative set — the manager creates/deletes to match it exactly (patterns have no update, only create/delete); an empty list removes all patterns, and leaving patterns unset leaves them unmanaged. Declaring patterns requires custom_branch_policies: true (a config-load error otherwise).

environments:
  - name: pypi
    deployment_branch_policy:
      custom_branch_policies: true
      patterns:
        - {name: "v*", type: tag}
        - {name: "releases/*", type: branch}

Each reviewer is a User (by login) or a Team (by slug), resolved to the numeric ID GitHub's API requires when the plan runs — a Team reviewer requires an org-owned repository. login/slug must be a valid GitHub identifier (letters, digits, and internal hyphens only — no /), since the value is used directly in the resolution request.

environments:
  - name: production
    wait_timer: 10
    reviewers:
      - {type: User, login: octocat}
      - {type: Team, slug: platform}
    prevent_self_review: true
    deployment_branch_policy: {protected_branches: true}
    secrets:
      - {name: DEPLOY_TOKEN, value_from_env: PROD_DEPLOY_TOKEN}
    variables:
      - {name: REGION, value: us-east-1}

Note

GitHub's create-environment endpoint is a single call covering wait timer, reviewers, self-review prevention, and branch policy together — there's no partial-update form. An unset field on an existing environment is preserved at its current live value rather than reset, the same policy settings and actions use for their own unmanaged fields. For a brand-new environment, an unset field falls back to GitHub's own defaults (wait_timer: 0, no reviewers, prevent_self_review: false, no branch policy) instead.

For a brand-new environment there's nothing yet to diff its secrets/variables against, so its protection rules and its declared secrets/variables are folded into one CREATE change: applying it creates the environment first, then pushes each secret/variable against the newly-created environment.