Skip to content

feat(buds): per-BUD branch override for UAT / PROD tabs - #194

Merged
mickyarun merged 4 commits into
mainfrom
feat/bud-branch-overrides
Jun 3, 2026
Merged

feat(buds): per-BUD branch override for UAT / PROD tabs#194
mickyarun merged 4 commits into
mainfrom
feat/bud-branch-overrides

Conversation

@mickyarun

Copy link
Copy Markdown
Owner

Summary

Lets a BUD opt out of the repo-wide tracking branch and watch its own pattern instead. Useful for release-train BUDs whose PRs target release/* and one-off ad-hoc release branches that don't belong in the global repo setting.

Builds on top of #193 (PR 1). Without #193's broadened resolver + three-way query predicate the over-matching bug returns the moment you set any override — both PRs are needed for the full fix.

What changed

Backend

  • New branch_overrides JSONB column on bud_documents keyed by stage (uat / prod) with fnmatch-style values. Migration is autogenerated, nullable, no backfill needed.
  • BUDUpdate accepts branch_overrides and impacted_repos on PATCH. The validator rejects unknown stage keys (UAT, production, code_review), empty / whitespace patterns, and non-string values — typos surface as 422 at the API edge instead of fnmatching nothing.
  • PATCH handler merges branch_overrides per-key so saving a UAT override doesn't clobber an existing prod override. {stage: null} drops just that key; branch_overrides: null wipes both.
  • bud_prs.py::release_stage honors bud.branch_overrides[stage] before falling back to repo.uat_branch / repo.main_branch. Reuses the existing branch_matches() fnmatch utility — wildcard semantics stay consistent with release detection.
  • Editing impacted_repos after the planning phase logs bud_impacted_repos_edited_post_planning so post-deploy auditing of scope changes is possible.

Frontend

  • New BUDStageBranchOverride.vue widget rendered inside the shared BUDReleaseStagePanel.vue. Shows the currently-effective pattern (override or repo default), an Edit affordance opening an inline dialog, and — for multi-repo BUDs — a one-line applies to N repos disclosure so the per-stage scope is explicit before saving.
  • Save → PATCH /v1/buds/{id} with branch_overrides; the panel re-fetches its stage data on success and emits refresh-bud so BUDDetail pulls the new override into the prop feed.
  • Empty input on Save and the dedicated Clear override button both send {[stage]: null} — the merge-aware handler drops just that key, leaving the sibling stage untouched.

Test plan

  • cd backend && pytest tests/schemas/test_bud_branch_overrides.py -q — 16 cases pass (accept valid stage keys + clear signal; reject unknown keys, wrong case, empty / whitespace, non-string).
  • Full backend suite green: 1836 passed, 0 failed.
  • vue-tsc --noEmit clean.
  • In the UI: open a BUD's UAT tab → Edit tracking branch → set release/* → save. Confirm the PRs targeting release/2026-08-01 start appearing.
  • Set a UAT override, then set a Prod override — confirm the UAT one is still in place (merge, not replace).
  • Clear the Prod override via the Clear override button — confirm UAT is still in place and Prod falls back to the repo-wide main_branch.
  • Try to save " " — confirm the 422 must be a non-empty pattern or null surfaces inline.

Notes

  • Per-stage scope (not per-repo-per-stage) is a deliberate choice. Multi-repo BUDs whose repos disagree on the stage branch shape are expected to use a wildcard pattern (release/*) that matches all of them; the dialog explicitly tells the user this on multi-repo BUDs.
  • Migration is safe to deploy ahead of the new code: column is nullable and existing reads ignore it; new reads use (bud.branch_overrides or {}).get(stage) so the absence is the silent fallback path.

Lets a BUD opt out of the repo-wide tracking branch and watch its own
pattern instead. Release-train BUDs whose PRs target ``release/*`` can
be tracked without retraining the global repo setting; one-off ad-hoc
release branches can be pointed at directly.

Backend
- New ``branch_overrides`` JSONB column on ``bud_documents``, keyed by
  stage (``uat`` / ``prod``) with fnmatch-style values.
- ``BUDUpdate`` accepts ``branch_overrides`` and ``impacted_repos`` on
  PATCH. The validator rejects unknown stage keys, empty / whitespace
  patterns, and the wrong case (``"UAT"`` / ``"production"``) — typos
  surface as 422 at the API edge instead of silently missing in the
  filter.
- PATCH handler MERGES branch_overrides per-key so saving a UAT
  override doesn't clobber an existing prod override; ``{"uat": null}``
  is the clear-this-stage signal and drops the key from the merged
  column. Sending ``branch_overrides: null`` wipes both stages.
- ``bud_prs.py::release_stage`` honors ``bud.branch_overrides[stage]``
  before falling back to ``repo.uat_branch`` / ``repo.main_branch``.
  Reuses the existing ``branch_matches()`` fnmatch utility so wildcard
  semantics stay consistent with release detection.
- Editing ``impacted_repos`` after the planning phase logs
  ``bud_impacted_repos_edited_post_planning`` so an operator can audit
  scope changes that may leave PR rows linked to repos that no longer
  appear on the stage tab.

Frontend
- New ``BUDStageBranchOverride.vue`` widget rendered inside the shared
  ``BUDReleaseStagePanel.vue``. Shows the currently-effective pattern
  (override or repo default), an Edit affordance opening an inline
  dialog, and — for multi-repo BUDs — a one-line "applies to N repos"
  disclosure so the per-stage scope is explicit before saving.
- Save → PATCH ``/v1/buds/{id}`` with ``branch_overrides``; on success
  the panel re-fetches its stage data and emits ``refresh-bud`` so
  ``BUDDetail`` pulls the new override into the props feed.
- Empty input on Save and the dedicated Clear button both send
  ``{[stage]: null}`` — the merge-aware handler drops just that key.

Schema validator covered by 16 new parametrized tests.

Signed-off-by: Arun Rajkumar <mickyarunr@gmail.com>
mickyarun added 3 commits June 3, 2026 11:20
The override widget previously described only the fnmatch half of the
filter ("Open PRs are matched against this pattern on each impacted
repo. Supports fnmatch wildcards"). After the over-match fix added the
content guard that requires PRs to also reference the BUD by number,
that copy understated what surfaces a PR on a release-stage tab.

Rewrites the in-card hint and the dialog body so both rules are
visible. The example is now per-BUD: BUD-4 sees
``release/bud-001-bud-004``, BUD-12 sees ``release/bud-001-bud-012``.
Threads ``bud_number`` from BUDDetail → BUDReleaseStagePanel → the
override widget so the copy renders the actual reference, not a
placeholder.

Signed-off-by: Arun Rajkumar <mickyarunr@gmail.com>
The validator hardcoded ``{"uat", "prod"}`` next to a comment that
already pointed at the existing ``Literal["uat", "prod"]`` in
``app.schemas.bud_release``. Drop the duplicate and source the
allowed-keys set from ``get_args(ReleaseStage)`` so adding a new
release-stage tab in one place automatically widens the override
contract — no second edit needed, no drift possible.

Behaviour and tests unchanged (16 cases pass).

Signed-off-by: Arun Rajkumar <mickyarunr@gmail.com>
@mickyarun
mickyarun merged commit 6601034 into main Jun 3, 2026
14 checks passed
@mickyarun
mickyarun deleted the feat/bud-branch-overrides branch June 3, 2026 06:39
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