Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
33 commits
Select commit Hold shift + click to select a range
e9b9676
fix: re-match policies when a branch grows a new object type
antoinekh Aug 31, 2026
d53c089
docs: add an administration guide with a permission matrix
antoinekh Aug 31, 2026
fe3f4e7
fix: attribute a change comment to its caller and keep it in its own …
antoinekh Aug 31, 2026
9960ddb
fix: queue only one automatic merge per branch
antoinekh Aug 31, 2026
5d8a47d
fix: make change request status derived, with permissioned abandon an…
antoinekh Aug 31, 2026
b2d3e91
fix: flatten comment threads on save and harden change comment valida…
antoinekh Aug 31, 2026
3e90d7a
fix: follow a branch rename onto its change request
antoinekh Aug 31, 2026
9404b51
fix: record merge check results in the changelog
antoinekh Aug 31, 2026
86f0c73
fix: require the change permission to submit a request for review
antoinekh Aug 31, 2026
5ef00d9
fix: stop list pages offering actions the plugin does not route
antoinekh Aug 31, 2026
9bb8dbf
ui: fix badge colours, the review form's decision and comment timestamps
antoinekh Aug 31, 2026
1b55701
perf: cache the conflict and readiness columns on the change request …
antoinekh Aug 31, 2026
7639ebd
docs: complete the permissions reference and correct the stale pages
antoinekh Aug 31, 2026
0bcc29e
fix: refresh the cached list columns on every event that moves them
antoinekh Aug 31, 2026
0996f0d
perf: skip the conflict test for a clean diff on an unflagged request
antoinekh Aug 31, 2026
ec6002e
fix: make the cached-state backfill read the live branch model
antoinekh Aug 31, 2026
a52754b
feat: index every model for global search
antoinekh Aug 31, 2026
217620a
feat: show a branch its change request on the branch page
antoinekh Aug 31, 2026
812dae5
perf: collapse a burst of refreshes into one per change request
antoinekh Aug 31, 2026
f0e980d
docs: show the branch page alerts
antoinekh Aug 31, 2026
d48b1ce
ui: name a rule's groups rather than expanding them into members
antoinekh Aug 31, 2026
72f40c3
chore: clear the small cleanups and raise the ruff target version
antoinekh Aug 31, 2026
21b1510
feat: add return to draft, and make draft a state the author holds
antoinekh Aug 31, 2026
1047cc1
chore: drop the four dead permissions on the policy binding table
antoinekh Sep 1, 2026
f7904fb
feat: choose where the branch page shows its change request
antoinekh Sep 1, 2026
24e1f2c
ui: put an alert's detail line under the sentence it explains
antoinekh Sep 1, 2026
90e2339
release: 0.3.0
antoinekh Sep 1, 2026
eba71d2
chore: drop the unused setuptools-scm build requirement and declare t…
antoinekh Sep 1, 2026
d7bc790
docs: describe the branch page card the screenshots will show
antoinekh Sep 1, 2026
218d0df
docs: re-shoot the branch page screenshots on the change request card
antoinekh Sep 1, 2026
eb88d8f
ci: run the tests from the checkout so the documentation tests can re…
antoinekh Sep 1, 2026
be6ad49
test: capture the deliberate traceback so a green run has no error an…
antoinekh Sep 1, 2026
c109930
chore: open an Unreleased section for the next change
antoinekh Sep 1, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 5 additions & 1 deletion .github/workflows/tests.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -90,7 +90,11 @@ jobs:
- name: Install netbox-branching and the plugin
run: |
pip install 'netboxlabs-netbox-branching~=1.1.3'
pip install ./netbox-change-control
# Editable, so the tests run from the checkout. Several of them read the pages in
# docs/ and compare them against the code, and a plain install copies the package
# into site-packages without the documentation beside it, where those tests can
# only fail. The release workflow builds and checks the real distribution.
pip install -e ./netbox-change-control

- name: Configure NetBox
run: |
Expand Down
49 changes: 46 additions & 3 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,54 @@

## Unreleased

## 0.3.0 - 2026-09-01

### Added

- **Return to draft.** A submitted change request can be pulled back out of review, by its author or anybody holding `change_changerequest`, from the page or `POST /change-requests/{id}/return-to-draft/`. The reviews are kept, and resubmitting picks up where it left off. Allowed from Approved too, so an author who spots a problem after approval need not race the merge; it only ever closes the merge gate.
- **Abandon** and **Reopen**, each behind its own permission (`abandon_changerequest`, `reopen_changerequest`), on the page and as `POST /change-requests/{id}/abandon/` and `/reopen/`. With **Submit for review** and **Return to draft** these are the four transitions a person makes; everything else is derived. `submit` is a REST action too, so an integration can drive the whole lifecycle now that `status` is read-only. [The lifecycle diagram](docs/change-requests.md#the-lifecycle) shows the rest.
- **Draft is a state the author holds** rather than one the evaluation computes. A review submitted against a draft moves nothing, and a request pulled back stays pulled back; without this, **Return to draft** would be undone by the next signal. Submitting is what leaves draft, matches the policies and notifies the reviewers.
- **The branch page shows its change request**: status, reference, what is outstanding and who can clear it, with a link. A branch with no change request is told it cannot merge until one is opened, with a button to open it, which is the case netbox-branching's merge form could only refuse.
- `branch_page_placement` decides where that panel sits: `right_page` for a card in the right-hand column, which is the default, `alerts` for a band across the top, both for both, `[]` for neither. Both render the same wording, so they cannot drift apart.
- **Global search** over change requests, policies, rules, reviews, checks and comments; the plugin registered no index at all before. A request is found by its reference first, so a ticket number finds the change it spawned, and one whose branch has been deleted is still found by the branch's name.
- A change comment can be **edited and deleted from the interface**. There was no view for either, so a typo in a review comment was permanent. Editing is restricted to the author, as a review is.

### Fixed

- **The policies governing a change request follow the branch.** They were matched at submission and never again, so an author could open a request on a low-risk branch, collect the light approval that attracted, then add the real change to the same branch and merge it under that approval. They are re-matched whenever the branch touches something new, and the merge gate re-matches for itself.
- **A change request's status can no longer be set by hand.** It is derived, but it was writable on the bulk edit form and over the REST API, and Completed is terminal, so one bulk edit blocked a branch from merging for good. It is now read-only on the API and gone from the form.
- **Submit for review** requires `change_changerequest`. It checked nothing, so any signed-in user could push somebody else's draft into review, attaching its policies and announcing the event.
- **The REST API no longer lets a caller forge the author of a change comment.** `author` was writable, so any token holding `add_changecomment` could fake a colleague's sign-off in the discussion a reviewer reads before approving. It is now always the caller, matching `reviewer` on a review.
- **Automatic merge no longer queues two jobs for one branch.** Becoming mergeable can be reached by two routes in one write, and the second arrival still saw Approved because the first had only queued. The second job then failed with "not ready to merge" on a change that had gone through. Auto-merge refuses to queue while a merge for that branch is pending, scheduled or running.
- **Check results reach the changelog.** They were written with a queryset update, which fires no `post_save`, so a required check going from failed to passed left no entry. A re-run finding the same answer still writes nothing.
- Every built-in check reports **skipped** on a change request whose branch is gone. `threads-resolved` failed instead, because threads outlive the branch, so a change nobody can merge was marked blocked for ever.
- A reply to a reply is flattened into its thread on every path. The flattening ran in `clean()`, which the REST serializer discards, so a reply posted over the API was stored as a grandchild and rendered nowhere at all.
- A change comment can no longer name a change in another request's branch, which was invisible on the tab it belonged to while blocking a request it did not describe, and one naming a change that no longer exists reports a validation error rather than a server error.
- Renaming a branch updates the name stored on its change request, which is what the branch filter and the search read, so a renamed branch could not be found by the name shown on its own page.
- List pages no longer offer **Import**, and reviews and merge checks no longer offer **Add** or **Edit Selected**. The plugin routes none of those and NetBox rendered them with `None` as their target, so clicking one gave a 404.
- Four badges used `text-bg-grey`, which NetBox does not define, so they rendered with no background. A test now checks every badge colour against the set NetBox ships.
- The review form opens on the reviewer's standing decision. It reset it to **Approve**, so a reviewer amending a **Request changes** was silently offered an approval.
- A review's own page says whether it has gone stale, and the Changes tab formats timestamps the way the rest of the interface does.
- The second line of the reconciled-conflicts and change-window alerts sits under the sentence it explains rather than beside it. An alert lays its direct children out in a row, so the detail was rendered as a column of its own.

### Changed

- The **Conflicts** column on the change request list now matches netbox-branching: a red octagon when there are conflicts, a dash when there are none.
- The README now warns that the plugin is below 1.0 and that models, settings and the REST API can still change.
- Improved the docs.
- **The change request list no longer gets slower as it gets longer.** **Conflicts** and **Ready to merge** were computed per row, about five hundred queries for a page of fifty. Both now read a cached field refreshed on the events that move them, the split the plugin already makes for `status`: a cache for display and filtering, never for a decision. The merge gate and the change request page still recompute in full. Both columns are now sortable and filterable, and **Reviews** no longer costs a query per row.
- **One user action refreshes a change request once**, not once per policy. A request governed by three policies recomputed its status and ran every check four times for an identical answer. Nothing is deferred past the commit.
- **A rule names its groups rather than expanding them into members.** A group of fifteen printed fifteen usernames on every rule it satisfied, burying the approval counts that are the point of the panel, and went stale as people joined and left. An empty group is still called out by name, because such a rule can never be satisfied.
- All four lifecycle actions sit on the control bar beside Edit and Delete. Submit and Abandon sat in the Applied policies card, where they read as something to do with the policies; that card now carries only policies.
- **Reopening an abandoned request returns it to Draft** rather than straight to review: its reviews may be stale and its policies may have moved while it was set aside.
- The **Conflicts** column matches netbox-branching: a red octagon when there are conflicts, a dash when there are none.
- Packaging: the build no longer pulls `setuptools-scm`, which it never read because the version is written by hand, and the package declares `Development Status :: 4 - Beta`, which is what the README says in prose.
- CI installs the plugin editable, so the tests that compare a documentation page against the code can find that page. A plain install copies the package into `site-packages` with no `docs/` beside it, and those tests could only error there.
- [Permissions](docs/permissions.md) is a complete reference, with a test comparing it against the models, and there is an [administration guide](docs/admin-guide.md). The README warns that the plugin is below 1.0. `docs/api.md`, `docs/design.md` and `docs/checks.md` are corrected: a wrong policy filter, a wrong value for requesting changes, a permission that does not exist, and two of the moments checks run.

### Removed

- **The policy binding table defines no permissions.** Django creates four for every model and no view reads any of them, so granting `delete_changerequestpolicy` to detach a policy by hand only got the binding back at the next re-match. Change the policy's scope instead.
- `ChangeRequestPolicy.matched` and `created`, and `Policy.applies_to_all_object_types`, none of which anything read. `matched` was always true, so the filter reading it was a no-op and the **Auto** badge it drove appeared on every policy.
- The `lock_matched_policies` setting, documented but read nowhere, so turning it off changed nothing. Nothing can attach or detach a policy by hand, which is what it claimed to control.
- `ReviewBulkEditForm`, used by no view, and the `INTERVAL_*` re-exports in `jobs.py`, referenced by nothing.

## 0.2.0 - 2026-08-27

Expand Down
5 changes: 3 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -72,7 +72,8 @@ Full documentation is in [`docs/`](docs/index.md).
| [Merging, windows and auto-merge](docs/merging.md) | Change windows and automatic merging. |
| [Protecting main](docs/protect-main.md) | Requiring a branch, optionally for part of NetBox only. |
| [Automatic behaviours](docs/automation.md) | Stale reviews, reevaluation, notifications. |
| [Permissions](docs/permissions.md) | What each role needs. |
| [Administration guide](docs/admin-guide.md) | Roles, the permission matrix, building policies, troubleshooting. |
| [Permissions](docs/permissions.md) | The short reference for every permission name. |
| [REST API](docs/api.md) | Every endpoint. |
| [Extending this plugin](docs/extending.md) | How another plugin adds content and checks. |
| [Design](docs/design.md) | Why it is built this way. |
Expand All @@ -83,7 +84,7 @@ Full documentation is in [`docs/`](docs/index.md).
|---|---|
| Policies containing rules with a minimum approval count | Done |
| Rules naming reviewer groups and individual reviewers | Done |
| Policies attached automatically, scope-matched from the branch contents and locked against the author | Done |
| Policies attached automatically, scope-matched from the branch contents, following it as it changes, and not selectable by the author | Done |
| Policy conditions, narrowing a policy on the values of the changed objects | Done |
| Change requests with status and priority | Done |
| Reviews with approve, request changes, and comment | Done |
Expand Down
Loading
Loading