fix(ci): dispatch frozen-line builds with GITHUB_TOKEN (#4888)

## What this PR does

The first RC freeze hit HTTP 403 when dispatching `build-release.yaml`
because the CI app has no Actions write permission
([run](https://github.com/cozystack/cozystack/actions/runs/38229949424)).
I switched this dispatch to `GITHUB_TOKEN` with job-scoped `actions:
write`. Tag and branch pushes still use the app token.

Verified with `make unit-tests`, zizmor 1.26.1 and the commit trailer
check. The regression test fails on the old workflow and passes with the
fix, including warning and manual recovery on dispatch failure. Live
dispatch with `GITHUB_TOKEN` has not been tested.

### Downstream repositories

- [x] No downstream repository is affected by this change
- [ ] [cozystack/website](https://github.com/cozystack/website) -
follow-up:
- [ ]
[cozystack/terraform-provider-cozystack](https://github.com/cozystack/terraform-provider-cozystack)
- follow-up:
- [ ]
[cozystack/ansible-cozystack](https://github.com/cozystack/ansible-cozystack)
- follow-up:
- [ ] [cozystack/ccp](https://github.com/cozystack/ccp) - follow-up:
- [ ] [cozystack/talm](https://github.com/cozystack/talm) - follow-up:
- [ ] [cozystack/cozyhr](https://github.com/cozystack/cozyhr) -
follow-up:
- [ ] [cozystack/cozy-proxy](https://github.com/cozystack/cozy-proxy) -
follow-up:
- [ ]
[cozystack/cozystack-telemetry-server](https://github.com/cozystack/cozystack-telemetry-server)
- follow-up:
- [ ]
[cozystack/external-apps-example](https://github.com/cozystack/external-apps-example)
- follow-up:
- [ ] [cozystack/examples](https://github.com/cozystack/examples) -
follow-up:
- [ ] [cozystack/community](https://github.com/cozystack/community) -
follow-up:

### Release note

```release-note
fix(ci): use GITHUB_TOKEN to start the initial packages build when freezing a release line
```


<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->

## Summary by CodeRabbit

* **Chores**
* Updated release automation to use appropriate permissions and
authentication for CI triggers.
* Improved warnings and recovery guidance when automated package builds
cannot be started.

* **Tests**
* Added automated checks covering release dispatch permissions, branch
targeting, successful execution, and non-blocking failure handling.

<!-- end of auto-generated comment: release notes by coderabbit.ai -->
This commit is contained in:
myasnikovdaniil authored and GitHub committed 2026-10-21 14:17:15 +05:00
commit c6fcb4e7c6
2 files changed
+40 -4

No files matched your search

+7 -4
View File
@@ -48,8 +48,7 @@ concurrency:
group: cut-prerelease
cancel-in-progress: false
# The tag write happens via the app token, not GITHUB_TOKEN, so the job needs
# only read scope on the default token.
# Tag and branch writes use the app token so their push events trigger CI.
permissions:
contents: read
@@ -58,6 +57,9 @@ jobs:
name: Cut ${{ inputs.tag }}
runs-on: ubuntu-latest
timeout-minutes: 10
permissions:
contents: read
actions: write
steps:
# Pure-input validation first — no token minted, no checkout, so a bad tag
@@ -345,13 +347,14 @@ jobs:
- name: Build the frozen line's packages artifact
if: steps.freeze.outputs.created == 'true'
env:
GH_TOKEN: ${{ steps.app-token.outputs.token }}
# workflow_dispatch can trigger a run with GITHUB_TOKEN.
GH_TOKEN: ${{ github.token }}
BRANCH: release-${{ steps.parse.outputs.line }}
run: |
if gh workflow run build-release.yaml --ref "$BRANCH"; then
echo "Dispatched build-release.yaml for $BRANCH; it publishes cozystack-packages:$BRANCH."
else
echo "::warning::Could not dispatch build-release.yaml for $BRANCH (the CI app may lack 'actions: write')."
echo "::warning::Could not dispatch build-release.yaml for $BRANCH."
echo "::warning::Until it runs, cozystack-packages:$BRANCH does not exist, and PRs onto $BRANCH test the image refs committed in the tree rather than the line's own build."
echo "::warning::Start it by hand: gh workflow run build-release.yaml --ref $BRANCH"
fi
+33
View File
@@ -204,6 +204,39 @@ step_line() {
[ "${count:-0}" -eq 1 ]
}
@test "initial line build dispatch has Actions access and preserves failure recovery" {
step="$(STEP_NAME="Build the frozen line's packages artifact" yq -o=json '.jobs.cut.steps[] | select(.name == strenv(STEP_NAME))' "$CUT")"
script="$(printf '%s\n' "$step" | yq -r '.run')"
token="$(printf '%s\n' "$step" | yq -r '.env.GH_TOKEN')"
[ -n "$script" ] && [ "$script" != null ]
case "$token" in
'${{ github.token }}'|'${{ secrets.GITHUB_TOKEN }}') token=workflow-token ;;
'${{ steps.app-token.outputs.token }}') token=app-token ;;
*) echo "Unknown dispatch credential: $token"; return 1 ;;
esac
actions="$(yq -r '(.jobs.cut.permissions // .permissions).actions // "none"' "$CUT")"
# The installed CI app has no Actions permission; the workflow token only
# gets it when the effective job permissions request it.
gh() {
[ "$*" = 'workflow run build-release.yaml --ref release-1.7' ] || return 2
[ "$GH_TOKEN" = workflow-token ] && [ "$ACTIONS_PERMISSION" = write ] || return 1
return "$DISPATCH_STATUS"
}
export -f gh
run env GH_TOKEN="$token" ACTIONS_PERMISSION="$actions" BRANCH=release-1.7 \
DISPATCH_STATUS=0 bash -e -c "$script"
[ "$status" -eq 0 ]
[[ "$output" == *'Dispatched build-release.yaml for release-1.7;'* ]]
run env GH_TOKEN="$token" ACTIONS_PERMISSION="$actions" BRANCH=release-1.7 \
DISPATCH_STATUS=1 bash -e -c "$script"
[ "$status" -eq 0 ]
[[ "$output" == *'::warning::Could not dispatch build-release.yaml for release-1.7'* ]]
[[ "$output" == *'Start it by hand: gh workflow run build-release.yaml --ref release-1.7'* ]]
}
@test "build-release refuses a dispatch from anything but a line branch" {
BUILD_RELEASE="$REPO_ROOT/.github/workflows/build-release.yaml"
[ -f "$BUILD_RELEASE" ]