From 3f39f340beeb2520d598c203b13dbed2015ebe94 Mon Sep 17 00:00:00 2001 From: Caner Alp <109098482+alpCaner@users.noreply.github.com> Date: Fri, 14 Aug 2026 19:19:08 +0000 Subject: [PATCH] fix(ci): grant python-gates the issues:write parity.yml statically needs (#752) release.yml's python-gates called parity.yml with permissions {contents: read}. parity.yml's notify-planner-drift job declares {issues: write}. A called workflow may never exceed its caller's grant, and that check is STATIC -- it runs before any job and ignores the if: that keeps notify-planner-drift to repository_dispatch. So every v* tag failed the entire run with startup_failure and zero jobs. Reproduced by the v0.6.0-rc1 tag: run 31830969596, startup_failure, no jobs, no release object, no assets published. Granted here rather than removed from notify-planner-drift, which genuinely needs it on its own trigger to file the planner-drift tracker. A sweep of every local caller/callee pair now reports zero conflicts. Co-authored-by: Caner Alp --- .github/workflows/release.yml | 14 ++++++++++++++ changelog.d/752.fixed.md | 12 ++++++++++++ 2 files changed, 26 insertions(+) create mode 100644 changelog.d/752.fixed.md diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index c0ef0d9c..b28941d6 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -265,6 +265,20 @@ jobs: python_only: true permissions: contents: read + # `issues: write` is required even though NOTHING here writes an issue. + # `parity.yml`'s `notify-planner-drift` job declares + # `permissions: {issues: write}`, and a called workflow may never exceed + # the grant its caller gives it. GitHub checks that STATICALLY, before + # any job runs and regardless of the `if:` that keeps that job to + # `repository_dispatch` -- so a caller granting only `contents: read` + # fails the ENTIRE run with `startup_failure` and zero jobs, which is + # what v0.6.0-rc1's first tag push did (run 31830969596). + # + # Grant it here rather than dropping it from `notify-planner-drift`: + # that job genuinely needs it on its own `repository_dispatch` trigger, + # where it files/refreshes the planner-drift tracker. A tag build simply + # never reaches it. + issues: write # One PyInstaller freeze of `python/` per runner. There is no cross-build step # and there cannot be one: PyInstaller freezes the interpreter it is running diff --git a/changelog.d/752.fixed.md b/changelog.d/752.fixed.md new file mode 100644 index 00000000..6f28a2d4 --- /dev/null +++ b/changelog.d/752.fixed.md @@ -0,0 +1,12 @@ +- **A `v*` tag no longer fails the whole release run with `startup_failure` + before any job starts.** `release.yml`'s `python-gates` called + `parity.yml` granting only `permissions: {contents: read}`, but + `parity.yml`'s `notify-planner-drift` job statically declares + `permissions: {issues: write}`. A called workflow may never exceed its + caller's grant, and GitHub evaluates that BEFORE any job runs -- regardless + of the `if:` that keeps `notify-planner-drift` to `repository_dispatch`, so + a tag build that never reaches the job still failed on it. The v0.6.0-rc1 + tag reproduced it exactly: run `31830969596`, `startup_failure`, zero jobs, + no release object and no assets. `python-gates` now grants `issues: write` + alongside `contents: read`; a sweep of every local caller/callee pair + reports zero remaining conflicts.