forge: Add Bitbucket Data Center support to the bitbucket forge#1230
Conversation
119a73d to
20e2220
Compare
|
Thanks @kanchev1, this makes sense to support but I'm reluctant to register a separate forge for this. We could then do: default is: bitbucket.org with API 2.0, but you can provide a custom URL and we'll switch to API 1.0 (with a configuration option to stay on 2.0 even with a custom URL). I haven't thought the design through fully, but that's my initial gut reaction. There's a backlog of LLM-assisted PRs so I'm going to come back to this as time permits. Footnotes
|
20e2220 to
1ef15dd
Compare
|
Thanks for the feedback, it pointed me in the right direction. I've reworked the PR along those lines. There's no longer a separate forge. It's now just the single The configuration is fairly flexible while still doing the sensible thing by default. With no URL set, or I've been running this live against our Data Center instance for day-to-day work, so I can confirm the submit, edit, merge, and comment paths and the URL auto-detection all work well. I'm also using it together with #1237 (merge-based restack, not merged yet). The two of them together have made git-spice a really useful and helpful tool for me, which is why I wanted to share both upstream in case they're useful to others as well. Thanks for the great tool. Let me know if you have any remarks on the implementation and I'll adjust or fix anything. |
550c414 to
3b8309a
Compare
Support self-hosted Atlassian Bitbucket Data Center / Server instances. Unlike Bitbucket Cloud (REST 2.0), Data Center speaks the REST 1.0 API, so the bitbucket forge now selects one of two API gateways per repository, instead of registering a separate forge. The product is inferred from the instance URL: no URL or bitbucket.org selects Cloud, and any other URL selects Data Center. spice.forge.bitbucket.kind (cloud or datacenter) overrides the inference, e.g. to keep the Cloud API behind a custom URL. With spice.forge.kind=bitbucket, the instance URL itself is derived from the Git remote URL (including context paths for HTTP(S) remotes), so a single configuration key suffices for self-hosted instances. Authentication uses an HTTP access token sent as a Bearer token, entered via `gs auth login --forge bitbucket` or provided in BITBUCKET_TOKEN; tokens are stored per instance URL. A single forge.Repository implementation serves both products through a gateway contract (ports and adapters): the product-neutral gateway interface and its DTOs live in their own package, and one adapter package per product implements the contract over its wire API, hiding the differences: pagination models, reviewer identity (account UUIDs vs usernames), Data Center's optimistic-locking versions with one refetch-and-retry on conflict, comment reads from the pull request activity feed (Data Center has no comment-listing endpoint), merge vetoes, and draft-support probing. Pull request updates on Data Center carry the current title, description, and reviewers because the update endpoint replaces those fields wholesale. Optional capabilities default to a no-op "unsupported" implementation that the forge reports and ignores, e.g. toggling draft status after creation. Adding support for another API version means adding an adapter package; the forge itself is closed against new products. Both products fill new pull request descriptions from pull request template files on the repository's default branch. Bitbucket Cloud behavior, configuration keys, stored credentials, and persisted PR metadata are unchanged. Platform limitations match the documented Bitbucket Cloud ones where applicable: no PR labels, no assignees, draft state cannot be toggled after creation (and needs Data Center 8.18+), and no cross-repository (fork) pull requests. - internal/gateway/bitbucket: gateway contract shared by both products - internal/gateway/bitbucket/cloud: Bitbucket Cloud adapter (REST 2.0) - internal/gateway/bitbucket/server: Bitbucket Data Center adapter (REST 1.0) - internal/forge/bitbucket: one forge over the gateway contract Implements abhinav#1229 Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
3b8309a to
9090881
Compare
|
Thanks, @kanchev1, apologies for the delay. I'll follow up on this as soon as I'm able. |
Merge main into feat/bitbucket-server-forge after resolving the Bitbucket gateway conflicts. The resolution keeps a single Bitbucket forge backed by neutral Cloud and Data Center gateways. This keeps main's named change checks and mergeability model while preserving the shared Bitbucket forge boundary.
Bitbucket Data Center repository IDs carry the instance URL derived from the remote. Open the server gateway from that repository URL so API and browser targets stay tied to the parsed repository identity. Keep spice.forge.bitbucket.api-url as an explicit override for callers that need a separate REST root.
Bitbucket Data Center personal repositories use a tilde-prefixed
project key in project-centric REST paths. Keep browser URLs on
the bare user slug, but add the tilde when building REST requests
so personal repositories do not call /projects/{user}/repos.
The regression opens a personal repository through the forge and
exercises template lookup, protecting the parsed remote-to-gateway
path. Import giturl in the forge package so that contract builds.
Bitbucket Cloud and Data Center expose mergeability through different REST shapes. Model mergeability as a gateway operation so the forge asks for that decision directly instead of reading it from pull request metadata. Data Center now uses the documented merge probe to report ready, blocked, and conflict states. Veto text remains display-only, so blocked non-conflict responses keep an unknown reason.
Register forge definitions instead of using registry-held forge instances as command execution state. Definitions own the option storage populated by CLI and environment parsing, and command paths construct fresh Forge values before auth, remote parsing, or metadata handling. Implicit remote matching now goes through InferFromRemoteURL. Explicit selection uses Registry.New with optional remote context, so auth can construct a selected forge without parsing a repository path.
Merge the neutral forge construction refactor into the Bitbucket feature branch. The integration keeps Bitbucket Data Center support on the feature branch while replacing remote URL mutation with definition-based forge construction. Bitbucket remote-derived instance URLs are now applied to fresh forge instances from New, leaving the registry definition options unchanged.
Bring the merged forge definition construction work into the Bitbucket Server branch. Keep the generic registry, auth, remote, and existing forge definitions from main. Adapt the Bitbucket forge so Definition owns CLI options and constructs a resolved Forge from the remote URL while preserving Data Center URL derivation and gateway selection.
Resolve Bitbucket Cloud versus Data Center once in Definition.New. The constructed Forge now stores the resolved web URL, API URL, product kind, and product behavior object instead of recalculating those values from Options on each operation. Use one concrete RepositoryID for both Bitbucket products. The selected product parses repository paths into that shared identity and opens repositories through the matching gateway implementation. Move ChangeTemplatePaths next to ListChangeTemplates so template path knowledge stays with template lookup.
Clean up the Bitbucket construction refactor so it passes the repository lint rules. Use the staticcheck-preferred switch form, keep the construction error string lowercase, and remove the duplicate REST project-key helper that became unused after RepositoryID was unified.
Bitbucket forge construction now resolves the selected product during Definition.New and stores only the fields the Forge needs at runtime. The Cloud and Data Center product objects own their gateway inputs, so repository opening no longer sends the whole Forge back through the product abstraction. The Forge keeps the resolved base URL, API URL, kind, token, logger, and product instead of retaining Options after construction. Repository path parsing for Data Center lives with the Data Center product because the path form is product-specific. giturl.URL now exposes the normalized parsed scheme so Bitbucket URL derivation can preserve HTTP-style clone ports and derive git+http and git+https remotes without re-reading the raw remote string.
Bitbucket gateway conformance tests now declare the product names and wire state spellings before the scenarios that use them. Per-scenario pull request IDs, comment IDs, commit hashes, and template paths live in the tests that own those values, so the shared harness only keeps the product and repository coordinates it actually needs. Bitbucket Data Center now implements SetChangeDraft directly by returning bitbucket.ErrUnsupported. The shared UnsupportedGateway type is removed so optional capability refusals stay on the product gateway that owns them. The Bitbucket integration template skip now documents the current blocker: the harness can exercise fixed template files, but the Bitbucket template fixtures have not been recorded.
Bitbucket forge construction now resolves the prefixed logger locally when it builds the selected product object. The Forge logger accessor keeps the same nil-safe behavior inline, matching the shape used by the neighboring forge implementations. This removes the extra bitbucketLogger helper without changing the logger contract for repositories or gateways.
The Bitbucket forge now imports the Bitbucket gateway package with its natural package name instead of the gw alias. This keeps the import consistent with the Go guide: the current file package name does not require an alias for another imported package selector.
Bitbucket gateway conformance tests now keep single-use fake-server setup inside the scenario that depends on it. The suite still shares gateway construction and pull-request wire rendering, because those boundaries hide repeated product setup rather than simple path or assertion plumbing. This removes the path-builder helpers, the one-off template and comment stubs, and the helper that stripped product-specific fields from pull request assertions.
Bitbucket conformance pull-request fixtures now render product wire payloads through methods on conformancePR. Both renderers return JSON-shaped maps, so the Cloud and Data Center fake responses have the same test-local shape. The conformance gateway constructor now builds the selected product gateway itself instead of delegating to product-specific helpers. The FindChangesByBranch assertion keeps its changeSummary type local to the scenario that uses it.
Bitbucket Cloud and Data Center clients now keep HTTP transport separate from JSON decoding. The private do method applies shared request headers and returns an open response body to callers that own the lifecycle. doJSON handles JSON response checking and streaming decode. getRaw remains the only success path that reads a full response body because raw template and file endpoints return bytes instead of JSON. Bitbucket Cloud pagination still flows through Response.NextURL by copying the decoded list response Next field after JSON decode.
|
Thanks! Will try to make time for #1237 soon as well. |
Resolves #1229. Reworked per the design feedback: one
bitbucketforge, two API clients — and now a single repository implementation over a gateway interface, replacing the previous revision's two parallel implementations.What's added
bitbucketforge now serves both Bitbucket Cloud (REST 2.0) and Bitbucket Data Center / Server (REST 1.0), selecting the client per repository when opening it. No separate forge is registered.bitbucket.orgselects Cloud; any custom URL selects Data Center.spice.forge.bitbucket.kind(cloud/datacenter, aliasserver) overrides the inference, e.g. to stay on the Cloud API behind a custom URL.spice.forge.kind=bitbucket, the instance URL is derived from the Git remote URL (context paths included), so a single configuration key suffices for self-hosted setups;spice.forge.bitbucket.urlalways wins when set. Implemented as a small optionalforge.RemoteURLConfigurerinterface used by the remote resolver andgs auth login— separable if you'd rather require the explicit URL.PULL_REQUEST_TEMPLATE-style files on the repository's default branch.Design notes
internal/gateway/bitbucket: the package itself defines the product-neutralGatewaycontract (interface + request/response types), and one adapter package per product —cloud/(REST 2.0) andserver/(REST 1.0) — implements it, each containing its thin REST client alongside the translator. Supporting a future API version means adding an adapter package; the forge is closed against new products.internal/forge/bitbucketcontains oneforge.Repositoryimplementation, written against theGatewaycontract. The two adapters hide every product difference: pagination models, reviewer identity (account UUIDs vs usernames), Data Center's optimistic-locking versions with one refetch-and-retry on conflict, comment reads via the activity feed, merge vetoes, and draft-support probing. All shared workflow and policy — warnings, checks aggregation, comment counting and filtering, limits, template lookup, metadata — lives once in the repository implementation.UnsupportedGatewaybase supplying "unsupported" defaults (the gRPCUnimplementedXServer/ GiteaNullDownloaderpattern); the shared repository warns and continues. Today that covers toggling draft status after creation, which Data Center cannot do.#0).Compatibility
spice.forge.bitbucket.urlnow selects the Data Center API (previously it was parsed as Cloud). Noted in aChangedchangelog entry;spice.forge.bitbucket.kind=cloudrestores the old behavior.bitbucket-serverforge ID andBITBUCKET_SERVER_*variables never shipped and are gone.Testing