Repository navigation
feat(integration): show the remaining code lifetime in link create - #65
Conversation
Add the CLI half of the Bitbucket Forge integration (safedep/control-tower#1137): - integration bitbucket link create issues the single-use workspace link code the Forge app settings page redeems. Re-running it re-generates the code and invalidates earlier unredeemed codes. - integration bitbucket link list lists linked workspaces with their link IDs. - integration bitbucket repository list lists a workspace's repositories with scan state and scan scope. - integration bitbucket allowlist update changes the scan scope and edits the repository allowlist. A fresh link is default deny, so this command is what turns scans on. Extract the cursor pagination helpers from internal/cmd/project into internal/paging: sibling command packages cannot import each other, and this is the second list-shaped domain that needs them. Bump the API SDK modules to 20260917155422-10bdf18a09ea.1 for the Bitbucket integration RPCs. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01PZmWHi9y5aCpBaxxWXU6Fo
A stray argument on `link create` reached the RPC and issued a new code, which invalidates every earlier unredeemed code. All four leaf commands take flags only, so they now use cobra.NoArgs. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01PZmWHi9y5aCpBaxxWXU6Fo
The sync command now serves both sources. A new --source flag picks the source, and the command infers it from --repository-id, --repository-uuid, or the tenant's links when only one source is linked. The Bitbucket path resolves workspace links and repository names, and syncs through SyncBitbucketProjects. Extract shared workspace-link resolution into internal/bitbucketlink so the project and integration commands reuse one implementation. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01PZmWHi9y5aCpBaxxWXU6Fo
Review findings: the Short text still named GitHub only, and the resolution section in the doc did not cover the Bitbucket selector. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01PZmWHi9y5aCpBaxxWXU6Fo
Review findings from #63: - A names-only sync no longer fails for a GitHub-only tenant when the control plane lacks the Bitbucket RPCs or the caller lacks the permission. The inference treats Unimplemented and PermissionDenied from the Bitbucket listing as a tenant with no Bitbucket links. - --link-id now picks the source that owns it, so a tenant with links to both sources can sync by name without --source. An unknown link ID fails with a clear not-found error. - The inference listing also resolves the link, so the per-source path no longer repeats the walk. bitbucketlink.PickSingle and pickGitHubLink apply the selection to links a caller already fetched. - The project Bitbucket path reuses bitbucketlink.PageSize. - integration/bitbucket callers use bitbucketlink directly. The forwarding wrappers and aliases in client.go are gone, and the single-caller error builder is inlined. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01PZmWHi9y5aCpBaxxWXU6Fo
Bitbucket shows repository UUIDs braced and uppercase, and the control plane requires the canonical lowercase, unbraced form. The allowlist update and project sync now parse every UUID value locally with google/uuid and send the canonical form, so a pasted value works and a malformed one fails before auth and link resolution. Normalization runs before validation, so two spellings of one UUID fail the duplicate check. --repository-uuid, --enable, and --disable now split comma-separated values, matching --repository-id. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01PZmWHi9y5aCpBaxxWXU6Fo
Unimplemented proves the control plane has no Bitbucket links, so the inference tolerates only that code. PermissionDenied now propagates: links the caller cannot inspect may exist, and picking GitHub then could sync a name against the wrong source. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01PZmWHi9y5aCpBaxxWXU6Fo
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01PZmWHi9y5aCpBaxxWXU6Fo
The table showed the raw RFC 3339 expiry. Show "in X min" instead. The plain and JSON output keep the RFC 3339 instant for scripts. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01PZmWHi9y5aCpBaxxWXU6Fo
PR #63 squash-merged this branch. The tree from main plus the expiry change is kept as-is. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01PZmWHi9y5aCpBaxxWXU6Fo
SafeDep Report SummaryNo dependency changes detected. Nothing to scan. This report is generated by SafeDep GitHub App |
|
Navigate logical layers of code changes, visualize relationships, and explore their blast radius. No actionable comments were generated in the recent review. 🎉 ℹ️ Recent review info⚙️ Run configurationConfiguration used: Organization UI Review profile: QUIET Plan: Advanced Run ID: 📒 Files selected for processing (2)
Included review availability: Your plan provides up to 10 included reviews per hour; 7 remain after this review. 📝 WalkthroughWalkthroughBitbucket link creation now shows relative expiration durations in table output while preserving RFC 3339 timestamps in plain and JSON output. Repository guidance defines shared humanization rules for table values. ChangesBitbucket expiration output
Priority: ⬇️ Low Estimated code review effort: 2 (Simple) | ~10 minutes Change: Feature Merge Risk: ⚪ Minimal · up to Table output now shows relative expiration lifetimes while plain and JSON output retain exact RFC 3339 timestamps. The change is mergeable with no identified production risk. 🚥 Pre-merge checks | ✅ 4 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (4 passed)
Full details: Docstring CoverageExplanation Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. Docstring coverage is scoped to functions touched by this diff. Analyzed 3 functions across 2 files. (2 skipped: 2 unsupported.)
✨ Finishing Touches🧪 Generate unit tests (beta)
Comment |
The CLI already renders relative time with dry/tui/humanize in auth status and the list commands. Drop the local formatter and use it, so the expiry cell reads like the rest of the CLI. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01PZmWHi9y5aCpBaxxWXU6Fo
DEVGUIDE gets a Humanized values section under Output: table cells go through dry/tui/humanize, plain and JSON keep exact values, and a new value shape extends the dry package rather than adding a local formatter. AGENTS.md gets the distilled line. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01PZmWHi9y5aCpBaxxWXU6Fo



Internal testing feedback on safedep/control-tower#1137: the link code expiry printed as a raw RFC 3339 instant (
2026-09-22T08:34:44Z).Change
integration bitbucket link createshows the remaining lifetime under anEXPIREScolumn:in 15 min,in 1 h 30 min,expired, orin less than a minute.expires_atin RFC 3339 for scripts.Tests
TestFormatExpiresIncovers expired, sub-minute, rounding, whole hours, and mixed durations.go testandgolangci-lintpass.Note on history: the branch restarts from
mainafter #63 squash-merged. Anoursmerge keeps the old branch commits in ancestry without content changes, so the diff againstmainis only this change.🤖 Generated with Claude Code
https://claude.ai/code/session_01PZmWHi9y5aCpBaxxWXU6Fo
Generated by Claude Code
Summary by CodeRabbit
Improvements
Documentation