Appearance
Incident freeze and unfreeze
During an incident, merges must stop in minutes and be restored just as fast, with both directions on the record. Two profile files and one dispatch workflow do it. The freeze:
yaml
# .github/profiles/freeze.yml, incident INC-4471
rulesets:
- name: incident freeze
target: push
enforcement: active
rules:
- type: file_path_restriction
parameters:
restricted_file_paths: ["**/*"]
interaction_limits:
limit: collaborators_only
expiry: one_dayAnd the recovery:
yaml
# .github/profiles/recover.yml
rulesets:
- name: incident freeze
target: push
enforcement: disabled
rules:
- type: file_path_restriction
parameters:
restricted_file_paths: ["**/*"]
interaction_limits: nullyaml
name: Incident response
on:
workflow_dispatch:
inputs:
profile:
type: choice
options: [freeze, recover]
permissions:
contents: read
jobs:
incident:
runs-on: ubuntu-latest
environment: incident-response
steps:
- uses: actions/checkout@v7
- uses: Vivswan/github-settings-as-code@v2 # x-release-please-major
with:
token: ${{ secrets.INCIDENT_TOKEN }}
settings-file: .github/profiles/${{ inputs.profile }}.ymlHow to read the two files:
- The recovery file re-declares the same ruleset as
enforcement: disabledon purpose. Undeclared rulesets are kept by default, so a recovery file that simply omitted it would leave the freeze in place. - The interaction limit is the failsafe in the other direction. It self-expires after a day even if nobody runs the recover profile.
- One scope constraint. GitHub limits push rulesets to private and internal repositories (and their fork networks), so on a public repository this profile's brake is the interaction limit alone. Freeze a public repository with a branch ruleset instead, targeting
~ALLwith anupdaterule. - The audit trail comes free. Both runs are visible dispatches by a named actor on a gated environment.