Skip to content

Allow intentional protected-workflow changes via PR label#14

Open
kazukinakai wants to merge 1 commit into
mainfrom
feat/allow-protected-workflow-change-label
Open

Allow intentional protected-workflow changes via PR label#14
kazukinakai wants to merge 1 commit into
mainfrom
feat/allow-protected-workflow-change-label

Conversation

@kazukinakai

Copy link
Copy Markdown
Member

Motivating case

`detect_native_gates.sh` hard-fails any PR that modifies a workflow the base
revision already declares as a required gate (`repo-quality-gate`, or
`db-tests` when the repo has `supabase/config.toml`). There is no label,
allowlist, or bypass, so such a workflow is permanently unimprovable through
the normal PR lane.

Real case in `agiletec-inc/agiletec`: `.github/workflows/db-tests.yml` skips
its pgTAP job on every `main` push, because its own change-detection diffs
`github.event.before` under `fetch-depth: 1`, so the diff never sees the
merge's files. Fixing that requires editing the protected workflow file —
which this guard forbids — so a gate that is provably not running cannot be
repaired.

What changes

  • `.github/scripts/detect_native_gates.sh`: when a protected workflow diff is
    detected, check a new `PR_LABELS` env var (space-separated label names) for
    `allow-protected-workflow-change`. If present, print
    `Protected workflow changed under allow-protected-workflow-change: `
    and continue instead of failing. Absent the label, behavior is byte-for-byte
    identical to today (same failure message, same exit code).
  • `.github/workflows/quality-gate.yml`: wire `PR_LABELS` at the existing
    `detect_native_gates.sh` invocation (step id `native`) from
    `github.event.pull_request.labels.*.name`.
  • `tests/test_native_required_gates.py`: new cases for (a) protected workflow
    modified without the label still fails with the existing message, (b) same
    change with the label succeeds and prints the notice, (c) an unrelated
    workflow change is unaffected either way, (d) the label has no effect when
    there's no protected-workflow change, (e) empty/absent `PR_LABELS` (the
    `merge_group` shape, which carries no labels) keeps strict behavior
    unchanged.

Why the label can't be forged

The label comes from the `pull_request` event context
(`github.event.pull_request.labels.*.name`), which is metadata GitHub attaches
to the event and is not derived from any file in the head commit. A head
branch cannot grant itself the label by editing tracked repository content —
only a human (or automation with label-write permission) applying the label
via the GitHub UI/API can. `merge_group` events carry no labels at all, so
`PR_LABELS` is empty there and the strict rejection path is unchanged.

What is explicitly NOT weakened

  • Without the label, the guard's behavior, failure message, and exit code are
    identical to before this change.
  • The `required_contexts` output logic (job name / workflow path pairing used
    by `evaluate_required_checks.mjs`) is untouched.
  • The label only suppresses the hard-fail; it does not skip or alter the
    actual required check runs — the changed workflow still has to execute and
    report the same protected context name for the check to pass.
  • This does not add a bypass usable by the PR author unilaterally: applying a
    label to a PR is itself a reviewable, audited GitHub action separate from
    pushing commits.

Test output

```
$ python3 -m unittest discover -s tests
...............................................

Ran 47 tests in 2.648s

OK
```

Also ran `bash -n .github/scripts/detect_native_gates.sh` (clean) and
`actionlint .github/workflows/quality-gate.yml` (clean).

Not done

Auto-merge intentionally not enabled — this is a governance change for
the owner to review.

A workflow declaring repo-quality-gate/db-tests as of the base revision is
permanently unimprovable through the normal PR lane: detect_native_gates.sh
hard-fails any PR that touches it, with no escape hatch. This bit a real case
in agiletec/agiletec: db-tests.yml skips its pgTAP job on every main push
because its own change-detection diffs github.event.before under
fetch-depth: 1, which never sees the merge's files. Fixing that requires
editing the protected workflow, which the guard forbids.

Add a deliberate, auditable escape hatch: the PR label
allow-protected-workflow-change lets a protected-workflow diff through and
prints a notice naming each changed path, instead of failing. Absent the
label, behavior is unchanged. The label is threaded in from the
pull_request event context (github.event.pull_request.labels.*.name) via
the PR_LABELS env var, never from repository file content, so a head cannot
self-authorize by editing tracked files. merge_group events carry no
labels, so PR_LABELS is empty there and strict behavior still applies.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant