feat(pr): add checks command to show build status for PRs#15
feat(pr): add checks command to show build status for PRs#15avivsinai merged 6 commits intoavivsinai:masterfrom
Conversation
5b05911 to
309be29
Compare
Implements the `bkt pr checks <PR_ID>` command that displays CI/pipeline status for pull requests. Supports both Bitbucket Data Center and Cloud. Features: - Add CommitStatuses method to Cloud client with pagination support - Add checks subcommand to pr command group - Extract shared CommitStatus type to pkg/types for consistency - Color-coded output (green=success, red=failed, yellow=in-progress) - --wait flag for polling until all builds complete - --interval flag to configure polling frequency (default 10s) - --timeout flag for maximum wait time (default 30m) - Graceful Ctrl-C handling via signal.NotifyContext - Returns non-zero exit code when builds fail (useful for CI scripts) Usage: bkt pr checks 123 # Show current status bkt pr checks 123 --wait # Wait for completion bkt pr checks 123 --wait --timeout 5m bkt pr checks 123 --wait --interval 5s Closes avivsinai#14 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
309be29 to
1386aee
Compare
avivsinai
left a comment
There was a problem hiding this comment.
PR Review: feat(pr): add checks command
Thanks for this contribution @aviadshiber! This is a well-implemented feature that aligns with our gh CLI inspiration. The test coverage is excellent (36 cases), and the core design is solid.
After reviewing against the gh pr checks implementation, here are the required changes before we can merge:
🔴 Required Fixes
1. Empty checks should error, not loop forever
Problem: allBuildsComplete([]) returns false, causing infinite polling when no builds exist.
Fix in pkg/cmd/pr/pr.go:
func allBuildsComplete(statuses []types.CommitStatus) bool {
if len(statuses) == 0 {
return true // No builds = nothing to wait for
}
// ... rest unchanged
}And add early exit in runChecks for the --wait case:
statuses, err = fetchStatuses()
if err != nil {
return err
}
if len(statuses) == 0 {
// Print "no builds" message and exit - don't poll
return printStatuses(ios, opts.ID, commitSHA, statuses, colorEnabled)
}
// Only poll if there are actual builds
if opts.Wait {
statuses, err = pollUntilComplete(ctx, ios, opts, fetchStatuses, colorEnabled, commitSHA)
// ...
}2. Flag validation: --interval requires --wait
Problem: --interval 5s without --wait is silently ignored.
Fix in newChecksCmd:
cmd := &cobra.Command{
// ...
RunE: func(cmd *cobra.Command, args []string) error {
// Validate flag combinations
if cmd.Flags().Changed("interval") && !opts.Wait {
return fmt.Errorf("cannot use --interval without --wait")
}
if cmd.Flags().Changed("timeout") && !opts.Wait {
return fmt.Errorf("cannot use --timeout without --wait")
}
// ... existing logic
},
}🟡 Should Add (for gh parity)
3. Silent exit code 1 on build failure
Current: Returns fmt.Errorf("one or more builds failed") which prints error message.
Expected: Exit 1 silently (scripts check exit code, don't need message).
Add to pkg/cmdutil/errors.go:
// SilentError signals failure without printing an error message
var SilentError = errors.New("SilentError")Update command handler to recognize it (in root command or wherever errors are handled).
Then in runChecks:
if opts.Wait && anyBuildFailed(statuses) {
return cmdutil.SilentError
}4. Add --fail-fast flag
Exits watch mode immediately on first failure (useful for CI).
// In checksOptions:
FailFast bool
// In newChecksCmd:
cmd.Flags().BoolVar(&opts.FailFast, "fail-fast", false, "Exit watch mode on first check failure")
// In RunE validation:
if opts.FailFast && !opts.Wait {
return fmt.Errorf("cannot use --fail-fast without --wait")
}
// In pollUntilComplete, after printing statuses:
if opts.FailFast && anyBuildFailed(statuses) {
return statuses, nil
}🟢 Nice to Have (can be follow-up PRs)
5. Alternate screen buffer for cleaner watch output
Instead of printing --- separators, use terminal alternate screen:
if opts.Wait {
ios.StartAlternateScreenBuffer()
defer ios.StopAlternateScreenBuffer()
}This keeps the terminal clean and shows final result on original screen.
6. Pending exit code (exit 8)
gh uses exit code 8 when checks are still pending (e.g., timeout hit):
var PendingError = errors.New("PendingError") // exit code 87. Remove redundant test helpers
In pkg/bbcloud/client_test.go, replace custom testContains/findSubstring with strings.Contains.
Summary Checklist
- Fix empty checks infinite loop (#1)
- Add flag validation for
--interval/--timeout(#2) - Add
SilentErrorfor build failures (#3) - Add
--fail-fastflag (#4) - (Optional) Clean up test helpers (#7)
Once these are addressed, happy to approve! 🚀
Improves the `bkt pr checks --wait` command with adaptive polling: - Exponential backoff (1.5x multiplier) reduces API load during long builds - Random jitter (±15%) prevents thundering herd when multiple clients poll - New `--max-interval` flag caps backoff (default 2 minutes) - Automatic retry with backoff on transient errors (up to 3 attempts) - Uses crypto/rand for better jitter randomness distribution Polling progression with defaults (10s base, 2m max): Iteration 0: ~10s Iteration 1: ~15s Iteration 2: ~22s Iteration 3: ~34s Iteration 4: ~51s Iteration 5: ~76s Iteration 6+: ~120s (capped) 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Follow-up improvements from PR review: ## CANCELLED state support - Add CANCELLED to isTerminalState(), stateIcon(), stateColor() - Icon: ⊘ (circled slash), Color: yellow - Properly handle cancelled Bitbucket Cloud builds ## Code deduplication - Extract checksResult struct and executeStatusCheck() helper - Common logic for polling, error handling, output, exit codes - DC and Cloud branches now just set up client and call helper ## Integration tests for pollUntilComplete - TestPollUntilComplete_ImmediateSuccess - TestPollUntilComplete_MultipleIterations - TestPollUntilComplete_ContextCancellation - TestPollUntilComplete_Timeout - TestPollUntilComplete_FetchErrorRetry - TestPollUntilComplete_MaxConsecutiveErrors - TestPollUntilComplete_ErrorResetOnSuccess ## Empty statuses UX - Show "Waiting for builds to appear..." when no builds found - Clearer message than generic "Waiting for 0 build(s)" 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
- Add sentinel errors (ErrNoSourceCommit, ErrBuildsFailed) for better error handling with errors.Is - Add t.Parallel() to independent tests for faster test execution - Add TestSentinelErrors to verify error wrapping behavior - Update existing tests to use sentinel errors 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
- Add flag validation: --interval, --max-interval, --timeout, --fail-fast require --wait - Add --fail-fast flag to exit watch mode on first failure - Fix empty checks behavior: exit early when no builds exist - Use cmdutil.ErrSilent for build failures (silent exit code 1) - Clean up redundant test helpers in client_test.go - Add tests for new flag validation and behaviors 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
|
Hey @avivsinai! Thank you so much for this incredibly thorough and well-structured review! The level of detail and the clear examples made it a pleasure to work through each item. Really appreciate you taking the time to compare against the Updated ChecklistHere's the status of all your feedback: 🔴 Required Fixes
🟡 Should Add
🟢 Nice to Have
Additional EnhancementsBeyond your checklist, I also:
All tests pass with race detection enabled. Looking forward to your re-review! |
- Add alternate screen buffer for cleaner watch mode output - StartAlternateScreenBuffer/StopAlternateScreenBuffer in iostreams - ClearScreen for refreshing output in place - Only active when stdout is TTY - Add ErrPending sentinel for timeout with pending checks (exit code 8) - Matches gh pr checks behavior - Distinct from ErrSilent (exit code 1 for failures) - Add tests for new iostreams methods and ErrPending 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Follow-up: All Items Now Complete! 🎉Just pushed two more commits addressing the remaining "Nice to Have" items: 🟢 Nice to Have (Now Complete!)
Additional Enhancements Not in Original ReviewThese were implemented proactively to make the feature more robust:
Final Summary
All 7 items complete with comprehensive tests. Ready for final review! 🚀 |
avivsinai
left a comment
There was a problem hiding this comment.
All 7 review items addressed. Tests pass with race detection. LGTM 🚀
Summary
bkt pr checks <PR_ID>command that displays pipeline/CI status for pull requestsCommitStatustype topkg/typesfor consistency between API clientsFeatures
--waitflag: Poll until all builds complete (useful for CI scripts)--intervalflag: Configure initial polling frequency (default 10s)--max-intervalflag: Maximum polling interval with backoff (default 2m)--timeoutflag: Maximum time to wait for builds (default 30m, 0 for no timeout)signal.NotifyContextChanges
CommitStatuses()method with pagination supportcheckssubcommand with:ErrNoSourceCommit,ErrBuildsFailed)checksResultstruct to reduce DC/Cloud code duplicationpr checksexamplesUsage
Test plan
stateIconfunction (15 cases)stateColorfunction (11 cases, including CANCELLED/STOPPED)isTerminalStatefunction (14 cases)allBuildsCompletefunction (6 cases)anyBuildFailedfunction (5 cases)calculatePollInterval(5 cases)addJitter(3 test functions)pollUntilComplete(7 cases)go test -race)t.Parallel()for faster executionGo Best Practices Applied
errors.Is()supportgo test -racet.Parallel(),t.Run()subtestssignal.NotifyContextfor graceful Ctrl-C handling (Go 1.16+ pattern)IOStreams.ColorEnabled()🤖 Generated with Claude Code
Closes #14