Skip to content

v4.1.0

Choose a tag to compare

@github-actions github-actions released this 16 Sep 09:28
· 500 commits to main since this release

v4.1.0

bb v4.1.0 adds bb doctor for configuration problems and closes most of the remaining gap with gh for pull requests and repositories. It also makes failures say what actually happened: whether a request that never got an answer may have been applied, whether a listing was cut short, and which file or policy refused a command.

It is a minor release with no breaking changes. Most of the behaviour that changes is a fix: v4.0.0's decision records and release notes already specified it, and this release makes the binary keep those decisions everywhere.

Highlights

  • bb doctor (ADR-086, #597): Checks the stored, workspace and system configuration files and reports every problem at once:

    • schema violations, with their key and line;
    • keys a file never reads, such as require_keyring in a user's own file;
    • where each effective setting comes from;
    • whether a required OS keyring answers.

    It needs no Bitbucket host, makes no network call, and exits 1 when there is anything to fix.

  • Closer to gh (ADR-050, #575):

    • bb pr view, bb pr edit and bb pr close resolve, and bb pr checks shows a pull request's build statuses.
    • bb pr ready marks a draft ready for review without asking for its version, and --undo turns it back into a draft (#602).
    • bb repo get, also spelled bb repo view, describes a repository and prints its README (#605).
  • An interrupted change is no longer reported as safe to retry (ADR-011, #574):

    • The problem. Suppose bb creates a pull request or a comment, or merges one, and the connection drops, times out, or a gateway answers 502 before Bitbucket does. Bitbucket may already have applied it. bb used to report that as transient, exit 10, which is the code retry wrappers act on, so a retry could create the same pull request or comment twice.
    • Now. Such a failure is unknown_outcome and exits 13, outside the retry range, and the message says to check whether the change landed before sending it again.
    • What still exits 10. A failure before the request left the machine, and a delete or an update whose answer is lost: repeating those cannot create a second anything, so bb retries them itself (ADR-009).
  • Releases you can verify, and a self-update that stays on https unless you allow otherwise. This matters if you check what you install or mirror releases internally; the steps are in Release Verification.

    • Per-archive SBOMs (#584). Each release archive now has its own SBOM, published beside it as <archive name>.spdx.json: the list of every module the binary inside it is built from, which vulnerability scanners and compliance reviews read. The .deb and .rpm are covered by the _noupdate archive's.
      • It is generated from that binary, because each platform links different modules.
      • It is attested against the archive, so gh attestation verify proves the SBOM describes that archive.
      • It replaces the single sbom.spdx.json, which inventoried the build checkout rather than any binary.
    • https for update mirrors (ADR-059). bb update fetches release mirrors over https, because over plain HTTP anyone on the network path can read or hold back what the mirror serves.
      • A mirror without TLS needs bb update --allow-http or BB_ALLOW_HTTP_UPDATE=1; without one of them the update is refused (exit 2).
      • In system policy, allow_http_update: true permits it for every user without the flag, and allow_http_update: false refuses it for every user (exit 3).
    • Pinned actions. The workflow that builds and signs releases runs GitHub Actions pinned to exact commits, so a moved tag cannot change what builds the binary.
    • Windows and macOS CI. The unit suite runs natively on Windows and macOS as well as Linux.

Fixes: v4.0.0 decisions the binary now keeps everywhere

  1. Every destructive delete confirms (ADR-073).
    • The rule: destructive commands confirm when a person is present and require --yes when not, and --yes is inert on a repository inferred from the git remote.
    • v4.0.0 enforced it for bb repo delete, bb repo admin delete and bb auth gpg-key clear. Every command whose last word is delete, remove, revoke or clear now follows it: 37 of them, including bb branch delete, bb tag delete, bb webhook delete, bb build delete, bb insights report delete, bb pr review reviewer remove, bb repo permissions revoke and bb auth token revoke.
    • In a pipeline, pass --yes and name the target explicitly: --repo PROJECT/slug, or BITBUCKET_PROJECT_KEY and BITBUCKET_REPO_SLUG. A repository bb took from the git remote does not count, and --json needs --yes even at a terminal.
  2. Invalid arguments exit 2, as the v4.0.0 notes promised. A subcommand that a command group does not know printed the group's help and exited 0, which put prose on stdout under --json. It now exits 2 (validation), and the help goes to stderr.
  3. Error kinds match the taxonomy (ADR-011, #574).
    • A rejected TLS certificate or a DNS failure is permanent (exit 1). It is not transient (exit 10), which invites a retry that cannot succeed.
    • A 401 that refuses a known user is authorization, not authentication.
  4. A capped listing says it was capped (ADR-074, #573). --limit has capped the total since v4.0.0. Every listing that takes it now also reports meta.limitReached, and MCP results (limit_reached) and text output say so too. bb insights annotation list also applies the default limit of 25 it had been ignoring; pass --all for every annotation.

Other fixes worth knowing

  • Error messages no longer carry raw Bitbucket responses.
    • An upstream response body is summarised, and credentials in it are redacted, before it reaches a message. Some bodies ran to 18 KB.
    • error.details carries upstreamStatus and, when Bitbucket names one, upstreamException.
    • --full-error-body prints the whole body when you need it.
  • --dry-run changes nothing on the local machine either (#571). Twelve commands used to apply their change under --dry-run and now preview it: bb auth login, logout, alias add, alias discover, alias remove, server use, setup-git, bb ai skill install and skill remove, bb clone, bb repo clone and bb pr checkout. bb ai mcp serve --dry-run is refused rather than started, because a running server cannot be previewed (#568).
  • A configuration file bb cannot read is an error, not an empty file (#567).
    • Commands name the file, point at bb doctor, and stop with permanent (exit 1), instead of reporting that you are not logged in with validation (exit 2).
    • bb auth login and bb auth logout no longer rewrite a file they could not read, which removed every other host.
    • BB_DISABLE_STORED_CONFIG=1 no longer reads the stored file at all.

Deprecation

  • bb bulk is deprecated and is removed in v5.0.0 (ADR-084).
    • It warns once per invocation on stderr, so --json output stays clean.
    • Permissions, webhooks and default tasks set at project level already apply to every repository in the project. For anything else, loop over bb repo list.

The full change ledger follows.

Changes since v4.0.0.

Compare: v4.0.0...v4.1.0

All 196 changes

Features

  • update: update URLs are https unless plain HTTP is explicitly allowed (7c3c01e)
  • bb doctor checks the configuration bb would load (fe6962d)
  • repo: bb repo get describes a repository and prints its README, with gh's view as an alias (3152a9a)
  • pr: bb pr ready marks a draft ready for review without asking for its version (dc27824)
  • errors: unknown_outcome, for a request whose result never came back (7dfbf6b)
  • safety: install the destructive confirmation from one walk (a3621a2)
  • safety: confirm before deleting a branch, a tag or a webhook (56ed0a4)
  • bulk: deprecate bb bulk, and add the machinery that remembers (7d6332d)
  • pr: register the gh spellings so muscle memory resolves (bbd53fa)

Fixes

  • release: ask jq for the one conclusion instead of piping into head (72b82fa)
  • release: keep a prerelease tag from deciding the next release (b6de809)
  • pr: let each half of an alias name the other, and prove doctor asks nothing (812a9b9)
  • errors: ignore a status that is not one, and tidy what the refactors left (1f726b5)
  • quality: count the endpoints bb calls from outside the services (09d6385)
  • errors: a 2xx is not proof that a mutation landed (0518320)
  • update: read the mirror as policy, and warn on the plain HTTP it reaches (ed04460)
  • pr: read the version bb pr update needs instead of demanding it (e0d699b)
  • cli: answer a request for help instead of refusing it (44a66a4)
  • cli: make the destructive confirmation say what it means (7c0b200)
  • errors: keep the transport's classification through three wrappers (3b1f04f)
  • auth: give bb auth status its TLS remedy back (d15f3a0)
  • errors: a status that arrived is the outcome (6d4346c)
  • errors: a server that refuses the handshake is permanent (54f3790)
  • doctor: report the refusals bb update would meet (51bad61)
  • config: do not let an unreadable registry policy switch a control off (7fe30c9)
  • errors: close the redaction gaps an upstream body can use (1eaf15a)
  • git: keep a clone credential out of git's command line (4243b75)
  • config: bind a stored credential to the host it was stored for (ba23a13)
  • auth: keep the git remote out of a token's scope (d3f093c)
  • cli: answer an interrupt that finds bb blocked on a read (8757850)
  • errors: show a JSON envelope under --full-error-body too (edcf1ea)
  • test: wait for Bitbucket to rescope the pull request before rebasing it (56ffc9f)
  • update: say which address was refused, and which policy refused it (a6c3502)
  • config: find a path part's name on disk in one pass that Linux runs in full (7963ded)
  • config: one keyring key per config file where the file system ignores case (e439e43)
  • errors: a 401 that refuses a known caller is authorization (b1060e6)
  • doctor: bb doctor exits 1 for any issue, with every issue in error.details (ac68f3b)
  • pr: review complete without a draft review names the command that sets the status (af1d650)
  • mcp: list_pull_requests answers with your own pull requests, and the tool listing says which tools write (0d8778c)
  • webhook: a create bb could not hear the answer to looks before reporting (f7d8427)
  • describe: an echoed flag publishes the flag's own values, and bb bulk describes its full artifacts (4784c1b)
  • repo compare --diff shows what the branch adds, and an old keyring token no longer overrides a new login (7dc2323)
  • config: logout, bb update and the CI variable no longer run past a damaged config (13a3d80)
  • errors: every command reports a rejected certificate as permanent and a lost mutation as exit 13 (a29b051)
  • output: insights annotation list honours --limit, and three listings report reaching it correctly (b8fd5a8)
  • output: a capped result says so in MCP results and text output too (209dca0)
  • output: meta is published open to new fields, and pr comment list reports meta.limitReached (7be0759)
  • output: every capped listing says so, and the envelope has a published contract (34928ea)
  • errors: redact an upstream body before it becomes a message (dd583fc)
  • errors: summarise an upstream body, and stop calling a rejected certificate transient (b693064)
  • describe: publish the vocabulary the flag accepts, and the shape bulk emits (11bfe39)
  • config: a hand-edited profile whose URL and map key differ keeps its stored credential (674bafb)
  • cli: say what a command wanted, and give compare the diff it promises (096fbe3)
  • pr,config: name the pull request that was approved, and scope the keyring (3c63562)
  • config: a file bb cannot read is not a file that is not there (0a400f2)
  • test: give each test server its own transport, not the shared one (9a70af5)
  • test: bump the version before the target moves, not after (b00e853)
  • quality: find a generated call by its method, not by its receiver's name (cfd0984)
  • test: derive the project name, so one aborted run does not block the rest (1ffc7a7)
  • dry-run: stop the local registry meaning "run it anyway" (6d2a048)
  • test: a live test that cannot run its case now fails instead of skipping (e2bb3b5)
  • test: make the webhook delivery tests actually test delivery (75867f7)
  • pr: keep the rebase call visible to the spec-coverage detector (8320d4f)
  • pr: recover from a stale version bb read itself when rebasing (a7450cd)
  • test: clone into a temp directory, not into the checkout (c544838)
  • test: seal the packages that were reading the developer's own config (e825cef)
  • cli: fail when a command group is handed an unknown subcommand (cb37d0e)

Performance

  • paging: stop walking a filtered listing one row at a time (92d87ed)

Refactors

  • cli: make the interrupt answer testable (06cea58)
  • errors: keep --full-error-body in an atomic, not a global the root command races on (c18f675)
  • deprecation: ask the registry question of a list, not a global (c713845)

Docs

  • say which registry spellings policy accepts, and what an unreadable one does (383b064)
  • adr: say what the release process and the deprecation window actually do (77ea917)
  • name the Windows registry values policy is read from (eb217cd)
  • stop teaching the deprecated command, and write verification once (d0662ae)
  • fix the small things that were wrong (acb8471)
  • state what is true rather than what changed (0cb75a0)
  • document the .env files bb reads (72b2c2a)
  • close the gaps a gh user falls into (c87999b)
  • describe the llms.txt that exists, and document bb ai skill install (72d1429)
  • add the three failures people hit most, and fix the keyring remedy (9ed3cec)
  • link troubleshooting and gh parity from where a reader is stuck (b46adcd)
  • correct the bb auth status examples against the binary (e872fb0)
  • say bb bulk is deprecated where agents are sent to it (d3f82f2)
  • record that a credential is bound to its host (a0e9af7)
  • rebase onto next, which is where every change goes (ac80155)
  • fix claims that do not match the binary or the release (85e8018)
  • correct the v4.1.0 release notes (34ef3e4)
  • name go.mod as the Go version contributors need (fcb3e1b)
  • explain exit code 13 and the release integrity work in the v4.1.0 notes (56db8d4)
  • frame the v4.1.0 behaviour changes as the fixes they are (bd2ad90)
  • introduce the v4.1.0 release (0c52f52)
  • render the cheatsheet's shell completion tabs (bbef023)
  • put the plain-HTTP refusal where users look, and release verification out of the quickstart (c05691c)
  • name the SBOM the .deb and .rpm are attested with, and where nfpm runs (652ae31)
  • pr: say what bb pr ready does, not the chore it replaces (78175ad)
  • adr: ADR-062 says why list_pull_requests alone takes an optional repository (1adb06e)
  • adr: the MCP gating rule and the project-scoped dashboard, as the code has them (9f7b8c2)
  • say what bb does, not what changed (5b32900)
  • records and guides describe the error, config, keyring and truncation changes (bacc45e)
  • tell the human audience that Cloud is unsupported (3a3dcd8)
  • keep the deprecation out of the record and out of the ranking (34149a1)
  • show what bulk and dry-run actually print (7ef40b1)
  • grow the bulk and dry-run pages into guides (88ea4d7)
  • bb api is the same thing as gh api, so stop saying it is not (ccb7c92)
  • correct what stands in for gh repo view (78a51d8)
  • collapse the global flags, and print one open copy at the top (ccc030d)
  • say what changes when you arrive from gh (e10f682)
  • stop putting counts and reassurance in a hand-written page (4d19a79)
  • generate the MCP tool reference from the registry that serves it (85e9dcd)
  • regenerate the ADR page the merge order left behind (456d039)
  • boost the troubleshooting page in search (4d5ee12)
  • weight search so task pages outrank decision records (7c447aa)

Tests

  • releasetags: guard the repository the tag test builds (58bf53e)
  • config: assert on both platforms instead of skipping on one (df5dd91)
  • quality: gate the mocked-server inventory and let it see every server (9f45b73)
  • live: fail where the suite used to skip, and find what that hid (d0aa54b)
  • bulk: run the deprecation warning instead of trusting it (589a651)
  • testsupport: catch the names a clock reaches through assembly (2eeac73)
  • pr: prove the stale-version retry reads the version again (483c429)
  • cli: run the delete confirmation rather than check its flag (1b343e4)
  • ai: expect the project skill path under the working directory the OS reports (7dc16fc)
  • update: remove the WSL-only Windows swap smoke test (1b3bb42)
  • live: the label and watch lifecycle runs alone (7f7121a)
  • openapi: the retry test sends through its server's own transport (0aec4a9)
  • point every transport fault at a port no listener is handed (6fd023a)
  • cover the failure paths of bb pr ready and bb repo get (4f41e8f)
  • docs: check quoted error messages against the source that prints them (962f6c4)
  • webhook: name the create's command words where command-reach reads them (f3f0138)
  • cover the failure paths the review fixes added (8cb9f43)
  • webhook: a create response lost to Bitbucket's serialisation race is not a failed create (74d1635)
  • transport: close the responses the outcome tests discard, and justify the doer's G704 (22de971)
  • the clock-name guard reads the code, and ADR-085 says what it enforces (6356f4e)
  • output: check every JSON write under --limit, not whether the command reports once (685edc2)
  • a test fixture is named at random rather than from the clock (ADR-085) (5f1cce9)
  • pr: make the rebase transport-fault test reach the rebase call (de90514)
  • pr: cover the rebase retry the live suite cannot reach (2f909c0)
  • deprecation: cover what a malformed version does (2005d0f)
  • live: read bulk output from stdout alone (8326664)
  • cli: cover the cases UnknownSubcommandError must stay quiet for (8262b4d)

Build

  • docs: silence Material's MkDocs 2.0 advisory in every docs task (d1a761f)
  • stack: stop at 2h58m, and restart an instance 2h40m old before a live run (1a8e9fa)
  • stack: quieter pruning, and no empty row in the instance cap message (59d0e7c)
  • stack: one local Bitbucket instance per checkout (35698fb)
  • stack: the local Bitbucket stops itself before its licence runs out, and test:live starts it (25c080a)
  • error-registry: each row's kind comes from bb's own mapping (83217b6)
  • record the readme operation bb repo get calls (e1135d9)
  • publish the docs under each release's major version (77b5b06)
  • docs:verify-generated reports stale docs instead of hanging (c866d51)

CI

  • measure a patch against the branch it targets (1190aa5)
  • release: let a prerelease be a prerelease (593e9f0)
  • release: keep the docs toolchain out of the job that can sign a release (dbb15b9)
  • ask for the check the promotion push skips, and stop expanding a branch name (b9153d4)
  • make the Dependabot base hold fail when it stops holding (5825e5b)
  • propose Dependabot updates against next (45c68d9)
  • release: generate each archive's SBOM from the binary inside it (1a7314a)
  • pin every GitHub Action to the commit of its latest release (518899a)
  • run the unit suite natively on Windows and macOS (ced6604)
  • a restarted local stack refused every live test and said nothing useful (a44d9d3)

Chores

  • run govulncheck v1.8.0, which can analyse go1.27 code (698f427)
  • run the gofmt and golangci-lint the pinned toolchain needs (e0515be)
  • deps: build with go1.27.1 and format for its gofmt (a2fd62f)
  • deps: bump the gomod-minor-patch group across 1 directory with 12 updates (4bcfa06)
  • deps: bump atlassian/bitbucket in /docker/harness (0bb98c8)
  • deps: bump astral-sh/setup-uv from 10.0.1 to 10.1.0 (49098a4)
  • deps: bump the gomod-minor-patch group with 13 updates (ac5cc7e)

Other

  • State that the schema exists, and stop prescribing how to run it (b7f9859)
  • Validate config with what the reader already has (1a8e230)
  • Name what a trace keeps, and validate config instead of reading it (019271c)
  • Say what a diagnostic file contains before asking anyone to send it (4038c52)
  • Propose a deprecation window, for a decision rather than as one (d07fc61)
  • Put troubleshooting where a stuck person looks for it (fe107ab)
  • Move the self-update builds to the guide that deploys them (23d15e0)
  • Stop certifying a command the shell will not run (c566d40)
  • Explain the build variant most installs actually deliver (432ef74)
  • Give the system policy keys a reference page (ecefffd)
  • Put back an example I moved under a claim it contradicts (7bfdff8)
  • Correct the two comments that said llms.txt could not be macroed (b192b87)
  • Render macros into the files mkdocs copies without rendering (5845d92)
  • Macro the other version the docs had typed out by hand (a6f09fe)
  • Read the tested Bitbucket version from the stack that is tested (1827b62)
  • Separate the two compatibility questions, and keep next alive between bundles (84201c7)
  • Say which Bitbucket versions bb is tested against (de0d457)
  • Point at the contributor guide, and stop naming tests that were deleted (0f813c2)
  • Print the live coverage number instead of computing and withholding it (340c6c7)
  • Ship the Apache licence rather than a paraphrase of it (8491938)
  • Stop directing Arch users at a package nobody has registered (dda59ac)
  • An ADR may not send work to a branch the gate refuses (40893db)
  • Teach docs-lint that a version can hide in a filename (b3299c4)
  • Document the fleet controls an administrator cannot currently find (836f5e1)
  • Say what the threat model can actually claim (b90448a)
  • An accepted ADR may not name a flag bb does not have (36bfaa0)
  • Record the v4 credential decision instead of editing the old one (f6420e5)
  • Teach docs-lint the two things it could not see (8256737)
  • Correct documented outputs that no longer match what the commands emit (d547520)
  • Correct v4 docs that still describe removed flags and fields (50097d8)