Skip to content

feat!: vendor the Bitbucket 10.2 OpenAPI spec - #309

Open
vriesdemichael wants to merge 5 commits into
feat/pr-review-visibilityfrom
chore/openapi-10.2
Open

feat!: vendor the Bitbucket 10.2 OpenAPI spec#309
vriesdemichael wants to merge 5 commits into
feat/pr-review-visibilityfrom
chore/openapi-10.2

Conversation

@vriesdemichael

@vriesdemichael vriesdemichael commented Aug 10, 2026

Copy link
Copy Markdown
Owner

The vendored API reference was Bitbucket 9.4 while the local stack and the live suite run 10.2.1. That drift is why the CLI still called endpoints Atlassian removed in 8.0 — the spec described a server nobody tested against.

This vendors the Atlassian 10.2 document, regenerates models and client from it, and keeps the generated client in step with the server the live suite runs against.

What the delta actually is

Almost entirely additive: 17 new paths, 17 new schemas, 0 schemas removed, and one removed mirroring endpoint the CLI does not use.

The cost is elsewhere: 10.2 marks far more fields required — 27 required entries across all schemas became 117 — so oapi-codegen emits value types where 9.4 emitted pointers. That is what drives the ~30 call-site adjustments here. They are mechanical, but they are not nothing.

Two changes are behavioural rather than mechanical:

  • Fork-sync status moved from GetStatus2 to GetStatus. Seven operationIds swap endpoints between the two specs; this is the only one the CLI calls, and the compiler caught it. The other six are in areas the CLI does not touch (admin/license, rate-limit, logs, basicauth, tsv).
  • RestReviewerGroup dropped displayName, so default-reviewer listings fall back to name.

Two sanitizer defects found on the way

The generated client was not reproducible. Collision suffixes were assigned while ranging over the spec's paths map, and Go randomises map iteration — so the same spec produced a different operationId→endpoint mapping on every run. Whether Get3WithResponse called /basicauth/latest/config or a pull-request endpoint was decided by chance at generation time. Three consecutive runs produced three different mappings.

Nothing caught it because models:verify and client:verify were never run by CI. Both now are, and both pass.

Suffixing assumed the suffixed name was free. Bitbucket 10.2 ships an operation already called get_2, so renaming a second get collided with it and the client failed to compile with duplicate Get2 declarations. Suffixes now increment until the canonical id is unused.

Registered as OPENAPI-009; regression tests in tools/openapi-sanitize/main_test.go run the sanitizer eight times and assert an identical mapping.

One deliberate exclusion

Bitbucket 10.2 added four schemas — Comment, CommentThread, PullRequest, PullRequestParticipant — that reference each other through required, non-nullable $refs (Comment.threadCommentThread.rootComment). Go cannot size such a value cycle, so generating them does not compile.

They are unreachable: no path operation references any of them, and only the four reference each other. The wire types remain RestComment and RestPullRequest. Both codegen configs exclude them (OPENAPI-010). If a future spec makes them reachable, generation fails loudly rather than silently omitting a used type.

Version policy

Rebased onto main, which adopted ADR 042 — track the newest containerisable version, state a supported version nowhere, and keep no default target in configuration.

My first pass predated that and set the target to 10.2.1, which contradicted it. This version complies: the spec is vendored as provenance for the generated client, not as a claim about which servers work. defaultBitbucketVersionTarget stays empty, ADR 018 stays superseded, and the only version statement remains the image tag in docker/compose.yml.

Guard against test pollution

Also included: internal/git/gittest fails any package whose tests reconfigure the repository they run inside.

Backend.Clone persists http.extraHeader into the repository it clones into, and a test that once pointed it at the working copy wrote this into this repo's .git/config:

http.extraheader = Authorization: Basic <base64 of dummy-user:dummy-password>
user.name        = Test User
user.email       = test@example.local

An unscoped http.extraHeader beats any credential helper, so every git push sent dummy-user:dummy-password and was rejected with "Password authentication is not supported for Git operations" — a message that reads like a bad token and sends you hunting in the wrong place. The identity override authored real commits as Test User <test@example.local>, including some already on main.

The tests were isolated with t.TempDir() afterwards, but nothing detected the damage or would catch a recurrence. Snapshotting repository-scoped config before and after each package does, and comparing before against after means a developer's own settings are invisible to the guard.

⚠️ Breaking change

Models generated from the 10.2 spec expose required fields as values rather than pointers, and RestReviewerGroup drops displayName. Consumers of internal/openapi/generated and internal/models/generated must adjust.

The bb command surface and JSON output are unchanged.

Verification

  • Full unit suite and full live suite green against Bitbucket 10.2.1
  • models:verify and client:verify both clean — generated code reproduces byte-identically
  • Patch coverage 95.21%, combined scoped 91.05%
  • Spec coverage, docs, and quality artifacts regenerated

🤖 Generated with Claude Code

vriesdemichael and others added 5 commits August 11, 2026 00:24
Two defects in the spec sanitizer, both found while moving to the Bitbucket
10.2 OpenAPI document.

Collision suffixes were assigned while ranging over the spec paths map. Go
randomises map iteration, so the same spec produced a different
operationId-to-endpoint mapping on every run: whether Get3WithResponse called
/basicauth/latest/config or a pull request endpoint was decided by chance at
generation time. Nothing caught it because models:verify and client:verify are
not run by CI. Paths are now sorted, making the mapping a pure function of the
spec.

Suffixing also assumed the suffixed name was free. Bitbucket 10.2 ships an
operation already called "get_2", so renaming a second "get" collided with it
and the generated client failed to compile with duplicate Get2 declarations.
Suffixes now increment until the canonical id is unused.

Bitbucket 10.2 also adds four schemas -- Comment, CommentThread, PullRequest,
PullRequestParticipant -- that reference each other through required,
non-nullable $refs. Go cannot size such a value cycle. No operation references
any of them, so both codegen configurations exclude them; the wire types are
still RestComment and RestPullRequest.

Registered as OPENAPI-009 and OPENAPI-010.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
The vendored API reference was Bitbucket 9.4 while the container stack and the
live suite run 10.2.1. That drift is why the CLI still called endpoints
Atlassian removed in 8.0: the spec described a server nobody tested against.

Vendors the Atlassian 10.2 document and regenerates models and client from it.
The delta is almost entirely additive -- 17 new paths, 17 new schemas, one
removed mirroring endpoint the CLI does not use -- but 10.2 marks far more
fields required (27 required entries across all schemas became 117), so
oapi-codegen emits value types where 9.4 emitted pointers. Call sites are
adjusted accordingly.

Two changes are behavioural rather than mechanical:

- Fork synchronization status moves from GetStatus2 to GetStatus. Seven
  operationIds swap endpoints between the two specs, and this is the only one
  the CLI calls; the compiler caught it. The other six are in areas the CLI
  does not touch.
- RestReviewerGroup no longer carries displayName, so default reviewer listings
  fall back to name.

Per ADR 042 this states no supported version: the spec is the provenance of the
generated client, not a claim about which servers work, and the version under
test remains recorded only in docker/compose.yml. The configuration default
stays unset.

CI now runs models:verify and client:verify. Generated code was never verified,
which is how the sanitizer nondeterminism fixed in the previous commit survived.

BREAKING CHANGE: models generated from the Bitbucket 10.2 spec expose required
fields as values rather than pointers, and RestReviewerGroup drops displayName.
Consumers of internal/openapi/generated and internal/models/generated must
adjust. The bb command surface and JSON output are unchanged.

Verified with the full unit and live suites against the container stack.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Backend.Clone persists http.extraHeader into the repository it clones into so
later fetches carry authentication. A test that pointed it at the working copy
rather than a temporary directory wrote this into the project own .git/config:

    http.extraheader    = Authorization: Basic <base64 dummy-user:dummy-password>
    user.name           = Test User
    user.email          = test@example.local
    remote.upstream.url = https://example.local/scm/PRJ/upstream.git

An unscoped http.extraHeader is attached to every HTTP request git makes, and an
explicit Authorization header takes precedence over any credential helper, so
every push to GitHub sent dummy-user:dummy-password and came back with
"Password authentication is not supported for Git operations" -- a message that
reads like a bad token and sends you looking in the wrong place. The identity
override meanwhile authored real commits as Test User <test@example.local>,
which reached published history.

The tests were later isolated with t.TempDir, but nothing detected the original
damage or would catch a recurrence.

internal/git/gittest snapshots the repository-scoped configuration before a
package tests and compares it afterwards. TestMain in internal/git/execgit,
internal/cli and tests/integration/live fails the package when anything changed,
naming the exact keys and how to undo them. Comparing before against after means
a developer own repository-scoped settings are invisible to the guard, and any
new key is caught rather than only the values that leaked the first time.

Verified by reproducing the original bug: the offending test still passes and
the package fails with the added key named.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
… guard

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
@vriesdemichael
vriesdemichael force-pushed the feat/pr-review-visibility branch from 170f3d0 to cf20d2c Compare August 10, 2026 22:37
@vriesdemichael vriesdemichael changed the title feat!: target Bitbucket 10.2 and vendor its OpenAPI spec feat!: vendor the Bitbucket 10.2 OpenAPI spec Aug 10, 2026
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.

1 participant