ci: Make forwardport job wait for zizmor check#5623
Conversation
There was a problem hiding this comment.
Pull request overview
This PR modifies the forwardport automation to wait for the zizmor security check before pushing to main. Previously, the workflow pushed directly to main, which could fail branch protection rules requiring the zizmor check. The new approach creates a temporary branch, opens a draft PR to trigger checks, waits for zizmor to pass, then fast-forwards main to the validated commit.
Changes:
- Added support for creating draft PRs and waiting for specific check runs to complete
- Modified forwardport logic to use a temporary branch and wait for zizmor validation
- Updated documentation to reflect the new automated workflow
Reviewed changes
Copilot reviewed 4 out of 4 changed files in this pull request and generated no comments.
| File | Description |
|---|---|
| tools/release/internal/github/client.go | Added Draft parameter to CreatePRParams, new WaitForCheckRun method to poll for check completion, and DeleteBranch method for cleanup |
| tools/release/internal/git/git.go | Added MergeFFOnly function to perform fast-forward-only merges |
| tools/release/forwardport-release-to-main/main.go | Refactored to create temp branch, open draft PR, wait for zizmor check, then merge to main |
| docs/developer/shepherding-releases.md | Updated documentation to describe the new automated workflow |
Comments suppressed due to low confidence (5)
tools/release/forwardport-release-to-main/main.go:189
- The success message states "forwardport PR #%d closed" but the code doesn't explicitly close the PR - it only deletes the branch. While GitHub will automatically close a PR when its head branch is deleted, this might be confusing. Consider either: (1) explicitly closing the PR before deleting the branch to match the message, or (2) updating the message to be more accurate, e.g., "forwardport PR #%d will be auto-closed" or simply removing the "closed" reference.
fmt.Printf("✅ Merged %s into main (forwardport PR #%d closed)\n", releaseBranch, draftPR.GetNumber())
tools/release/internal/github/client.go:390
- The context cancellation check using a select with default case may not respect the timeout promptly. The API call on line 387 happens after the context check, so if the context is cancelled between the check and the API call, the function will still make the API request. Consider checking
ctx.Err()before making the API call, or use the context directly in the API call which should respect cancellation. Additionally, the fixed 20-second sleep intervals mean the function could wait up to 20 seconds past the timeout before detecting cancellation.
for {
select {
case <-ctx.Done():
return fmt.Errorf("waiting for check %q: %w", checkName, ctx.Err())
default:
}
result, _, err := c.api.Checks.ListCheckRunsForRef(ctx, c.owner, c.repo, ref, opts)
if err != nil {
return fmt.Errorf("listing check runs for ref %s: %w", ref, err)
}
tools/release/forwardport-release-to-main/main.go:102
- If a previous forwardport run failed or was interrupted after pushing the branch but before cleanup, the forwardport branch (e.g.,
forwardport/v1.15) will already exist on the remote. When this workflow runs again,CreateBranchFromwill fail becausegit checkout -bcannot create a branch that already exists. Consider either: (1) checking if the branch exists and deleting it first, or (2) usinggit checkout -B(force create) instead of-b, or (3) adding error handling that suggests manual cleanup.
fmt.Printf("📌 Creating branch %s from main...\n", forwardportBranch)
if err := git.CreateBranchFrom(forwardportBranch, "main"); err != nil {
log.Fatalf("Failed to create branch %s: %v", forwardportBranch, err)
}
tools/release/forwardport-release-to-main/main.go:170
- The error message "Zizmor did not pass in time or failed" could be more specific. The underlying error from WaitForCheckRun already contains useful details (timeout, wrong conclusion, API errors), but this wrapper message uses "or" ambiguously. Consider either: (1) removing the wrapper and using just the underlying error, or (2) making the message more specific, e.g., "Zizmor check failed or timed out".
log.Fatalf("Zizmor did not pass in time or failed: %v", err)
tools/release/internal/github/client.go:411
- The check conclusion validation only accepts "success" but GitHub check runs can have other valid conclusions like "neutral" or "skipped" which might be acceptable in some workflows. If zizmor could legitimately return "neutral" or "skipped", this would cause the forwardport to fail unnecessarily. Consider whether other conclusions should be accepted, or at minimum, improve the error message to help diagnose why the check didn't succeed.
conclusion := found.GetConclusion()
if conclusion != "success" {
return fmt.Errorf("check %q completed with conclusion %q (expected success)", checkName, conclusion)
}
Fix the forwardport logic by pushing a temp branch and polling until the required zizmor check completes, then proceeding as per usual with the forwardport job. (cherry picked from commit 84eeb45)
## Backport of #5623 This PR backports #5623 to release/v1.13. ### Original PR Author @jharvey10 ### Description ### Brief description of Pull Request Fix the forwardport logic by pushing a temp branch and polling until the required zizmor check completes, then proceeding as per usual with the forwardport job. --- *This backport was created automatically.* Co-authored-by: Joe Harvey <51208233+jharvey10@users.noreply.github.com>
Fix the forwardport logic by pushing a temp branch and polling until the required zizmor check completes, then proceeding as per usual with the forwardport job.
Brief description of Pull Request
Fix the forwardport logic by pushing a temp branch and polling until the required zizmor check completes, then proceeding as per usual with the forwardport job.