Skip to content

feat(integration): show the remaining code lifetime in link create - #65

Merged
abhisek merged 12 commits into
mainfrom
claude/control-tower-pr-1168-u7ys0r
Sep 22, 2026
Merged

abhisek merged 12 commits into
mainfrom
claude/control-tower-pr-1168-u7ys0r

Conversation

@abhisek

@abhisek abhisek commented Sep 22, 2026 •

Copy link
Copy Markdown
Member

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

  • The table output of integration bitbucket link create shows the remaining lifetime under an EXPIRES column: in 15 min, in 1 h 30 min, expired, or in less than a minute.
  • The plain and JSON output keep expires_at in RFC 3339 for scripts.
  • The command doc states the split.

Tests

  • TestFormatExpiresIn covers expired, sub-minute, rounding, whole hours, and mixed durations.
  • go test and golangci-lint pass.

Note on history: the branch restarts from main after #63 squash-merged. An ours merge keeps the old branch commits in ancestry without content changes, so the diff against main is only this change.

🤖 Generated with Claude Code

https://claude.ai/code/session_01PZmWHi9y5aCpBaxxWXU6Fo


Generated by Claude Code

Summary by CodeRabbit

  • Improvements

    • Bitbucket link expiration details are now clearer across output formats.
    • Plain and JSON output show the exact expiration time in RFC 3339 UTC format.
    • Table output displays the remaining time in a human-readable format, such as “in 15m.”
    • Missing expiration values are handled safely in plain output.
  • Documentation

    • Added guidance on consistent time and duration formatting across output formats.

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
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

safedep Bot commented Sep 22, 2026 •

Copy link
Copy Markdown

SafeDep Report Summary

Green Malicious Packages Badge Green Vulnerable Packages Badge Green Risky License Badge

No dependency changes detected. Nothing to scan.

View complete scan results →

This report is generated by SafeDep GitHub App

@coderabbitai

coderabbitai Bot commented Sep 22, 2026 •

Copy link
Copy Markdown

Review in Change Stack →

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 configuration

Configuration used: Organization UI

Review profile: QUIET

Plan: Advanced

Run ID: e4a4b580-a3d9-4bdf-a7fb-cd1594bf3930

📥 Commits

Reviewing files that changed from the base of the PR and between 0be9c27 and 198e4a3.

📒 Files selected for processing (2)
  • AGENTS.md
  • docs/DEVGUIDE.md

Included review availability: Your plan provides up to 10 included reviews per hour; 7 remain after this review.


📝 Walkthrough

Walkthrough

Bitbucket 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.

Changes

Bitbucket expiration output

Layer / File(s) Summary
Expiration output formatting
internal/cmd/integration/bitbucket/link.go, docs/cmd/integration-bitbucket-link-create.md
Table output uses humanize.Time and an EXPIRES column. Plain output handles missing expiration values. Plain and JSON output retain RFC 3339 timestamps. Documentation describes these formats.
Humanized output guidance
AGENTS.md, docs/DEVGUIDE.md
The guidance requires shared humanizers for table time and duration values. It specifies exact values for plain and JSON output and render-time clocks for table output.

Priority: ⬇️ Low

Estimated code review effort: 2 (Simple) | ~10 minutes

Change: Feature

Merge Risk: ⚪ Minimal · up to 198e4

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)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning 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 … Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly summarizes the main change: showing the remaining link-code lifetime in the Bitbucket link create output.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
Full details: Docstring Coverage

Explanation

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.)

  • Fix all pre-merge checks with AI
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Commit to this branch
  • Create a new PR

Comment @coderabbitai help to get the list of available commands.

Comment thread internal/cmd/integration/bitbucket/link.go Outdated
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
@abhisek
abhisek merged commit a44d51e into main Sep 22, 2026
7 checks passed
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.

3 participants