Skip to content

fix: restore branch protections in cleanup trap on push failure - #303

Open
rafistrauss wants to merge 2 commits into
CasperWA:mainfrom
rafistrauss:fix/restore-protections-on-push-failure
Open

fix: restore branch protections in cleanup trap on push failure#303
rafistrauss wants to merge 2 commits into
CasperWA:mainfrom
rafistrauss:fix/restore-protections-on-push-failure

Conversation

@rafistrauss

Copy link
Copy Markdown

Summary

Fixes #302

When unprotect_reviews: true is set and the push step fails (e.g. due to permissions issues, a branch update race condition, or a rejected push), branch protections were left disabled because protect() was only ever called in the happy-path flow after a successful push.

Root cause

entrypoint.sh calls unprotectpush_to_targetprotect in sequence. The cleanup() EXIT trap only removed the temporary branch. If push_to_target failed (causing bash set -e to trigger EXIT), the trap ran but skipped protect().

Changes

  • Introduces a PUSH_PROTECTED_REVIEWS_REMOVED flag that is set to yes after unprotect_reviews succeeds and cleared after protect_reviews succeeds.
  • Extends the cleanup() EXIT trap to call protect_reviews when the flag is still set, ensuring protections are always restored regardless of whether the push succeeded or failed.
  • On failure, logs a GitHub Actions ::warning:: when restoring in the error path and a loud ::error:: with manual remediation guidance if restoration itself fails.

Acceptance criteria (from issue)

  • ✅ With unprotect_reviews=true, protections are restored after a successful push (unchanged behaviour; flag is cleared before exit).
  • ✅ With unprotect_reviews=true, protections are also restored after any push failure (new behaviour; cleanup trap handles it).

rafistrauss and others added 2 commits June 24, 2026 12:14
When unprotect_reviews=true and the push step fails (due to permissions,
race conditions, etc.), branch protections were left disabled because the
protect() function was only called in the happy path after a successful push.

This fix:
- Introduces PUSH_PROTECTED_REVIEWS_REMOVED flag set after unprotect()
  succeeds and cleared after protect() succeeds.
- Extends the cleanup() EXIT trap to call protect_reviews when the flag
  is still set, ensuring protections are always restored regardless of
  whether the push succeeded or failed.
- Logs a warning when restoring in the error path and an explicit error
  (with manual remediation guidance) if restoration itself fails.

Fixes CasperWA#302

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
In the existing force-pushing CI job, after the expected push failure
(non-fast-forwardable without --force, with unprotect_reviews=true),
add a step that queries the GitHub API to assert the pull-request review
protection is still present on the 'protected' branch.

This exercises the cleanup() trap fix from the previous commit: if
protections were not restored by the trap, the curl check would return
a response without 'required_approving_review_count' and the step fails.

Also add a sourcing guard to entrypoint.sh so the file can be sourced
in isolation (e.g. future shell-level tests) without executing the main
body or registering the EXIT trap.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR fixes a reliability bug in the action’s unprotect_reviews flow where required review protections could remain disabled if the push step failed, by ensuring protections are restored from the EXIT cleanup trap. It also adds CI verification that branch protections are restored after a simulated push failure.

Changes:

  • Track whether review protections have been removed and restore them in cleanup() when exiting early due to push failure.
  • Clear the “protections removed” flag on successful re-protection.
  • Add a CI workflow step to verify required review protections are present after a failed push attempt.

Reviewed changes

Copilot reviewed 2 out of 2 changed files in this pull request and generated 2 comments.

File Description
entrypoint.sh Adds a flag and extends the EXIT trap cleanup to restore review protections on failure paths; adds a sourcing guard for tests.
.github/workflows/ci_tests.yml Adds a CI assertion that required review protections are restored after a push failure scenario.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread entrypoint.sh
Comment on lines +154 to +156
# Allow this file to be sourced (e.g. in unit tests) to load function
# definitions without registering the trap or running the main body.
[[ "${BASH_SOURCE[0]}" != "${0}" ]] && return 0
Comment on lines +156 to +162
response=$(curl --silent \
--header "Authorization: Bearer ${{ secrets.CI_PUSH_TO_PROTECTED_BRANCH }}" \
--header "Accept: application/vnd.github.v3+json" \
"https://api.github.com/repos/${GITHUB_REPOSITORY}/branches/protected/protection/required_pull_request_reviews")
echo "Protection response: ${response}"
# If required_pull_request_reviews is present, protections were restored
echo "${response}" | python3 -c "import sys, json; d = json.load(sys.stdin); sys.exit(0 if 'required_approving_review_count' in d else 1)"
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.

Bug: unprotect_reviews=true can leave branch protections disabled when push fails

2 participants