Let a closed pull request's check builds go, and show what each walk build did
mesh/merge-gate head 66d22dd53eff merged into trunk 7ed119995f69 — pass: builds mesh-delivery → novox; no bus step; every machine composes with the change …
mesh/repo-check head 66d22dd53eff merged into trunk 7ed119995f69 — pass: its merge-check.sh passed, each part in its toolchain: merge-check.sh (go), merge-…
mesh/delivery delivered
mesh/delivery-group group feat/350-one-build-per-change delivered: every member is delivered
mesh/merge-gate head 66d22dd53eff merged into trunk 7ed119995f69 — pass: builds mesh-delivery → novox; no bus step; every machine composes with the change …
mesh/repo-check head 66d22dd53eff merged into trunk 7ed119995f69 — pass: its merge-check.sh passed, each part in its toolchain: merge-check.sh (go), merge-…
mesh/delivery delivered
mesh/delivery-group group feat/350-one-build-per-change delivered: every member is delivered
A check's builds for its merge have no taker once the pull request closes unmerged, and the delivery should say whether a module was reused from the check or built, and why (novox/hq ADR 0350 §4, §5).
This commit is contained in:
@@ -0,0 +1,50 @@
|
|||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"reflect"
|
||||||
|
"strings"
|
||||||
|
"testing"
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
|
// One build per change (novox/hq ADR 0350): a pull request closed unmerged has its check builds let go of, and a
|
||||||
|
// walk's record says per module whether its build reused a check's build or built, and why.
|
||||||
|
|
||||||
|
// A pull request closed unmerged asks the controller to let go of what its checks built for its merge (§5); one
|
||||||
|
// the controller cannot answer is said, never fatal.
|
||||||
|
func TestAPullRequestClosedUnmergedLetsGoOfItsCheckBuilds(t *testing.T) {
|
||||||
|
w := newWorldOver(t, newMemStore(), newFakeController(), newFakeForge(), atCC)
|
||||||
|
cc := pr(cat, 101, headCC, "feat/claude-code", "modules/claude-code/module.json")
|
||||||
|
w.h.PullUpdated(cc)
|
||||||
|
w.h.PullClosed(cc)
|
||||||
|
if want := []string{"novox/mesh-catalog#101"}; !reflect.DeepEqual(w.ctl.forgot, want) {
|
||||||
|
t.Fatalf("closed unmerged, the controller was asked to let go of %v", w.ctl.forgot)
|
||||||
|
}
|
||||||
|
if s := w.state(IDOf(cat, headCC)); s != Stopped {
|
||||||
|
t.Fatalf("closed unmerged it is %s", s)
|
||||||
|
}
|
||||||
|
|
||||||
|
w.ctl.down = true
|
||||||
|
du := pr(cat, 102, headDu, "feat/dunst", "modules/dunst/module.json")
|
||||||
|
w.h.PullUpdated(du)
|
||||||
|
w.h.PullClosed(du)
|
||||||
|
if s := w.state(IDOf(cat, headDu)); s != Stopped {
|
||||||
|
t.Fatalf("with the controller down, a closed delivery is %s", s)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// The walk's record says per module what its build did, and the delivery keeps it beside its walk (§4).
|
||||||
|
func TestAWalksRecordSaysWhetherEachModuleWasReusedOrBuilt(t *testing.T) {
|
||||||
|
walk := catWalk("plan-1", mergeCC, atCC.Add(time.Minute), "claude-code", "dunst")
|
||||||
|
walk.Modules["claude-code"].Made = "reused from the check of novox/mesh-catalog#101 at head cccccccccccc " +
|
||||||
|
"(merged into trunk 553b71910000): tools sha256:aa"
|
||||||
|
walk.Modules["dunst"].Made = "built: no check of novox/mesh-catalog#101 built it"
|
||||||
|
seen := walk.Seen()
|
||||||
|
if !strings.HasPrefix(seen.Built["claude-code"], "reused from the check of novox/mesh-catalog#101") ||
|
||||||
|
seen.Built["dunst"] != "built: no check of novox/mesh-catalog#101 built it" {
|
||||||
|
t.Fatalf("the delivery keeps %v", seen.Built)
|
||||||
|
}
|
||||||
|
if catWalk("plan-2", mergeCC, atCC, "dunst").Seen().Built != nil {
|
||||||
|
t.Fatal("a walk from a controller that says nothing of its builds reads as if it said something")
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -110,6 +110,18 @@ type fakeController struct {
|
|||||||
onCheck func()
|
onCheck func()
|
||||||
// planned is what Plan answers instead of "builds app", when set.
|
// planned is what Plan answers instead of "builds app", when set.
|
||||||
planned *DeliveryPlan
|
planned *DeliveryPlan
|
||||||
|
// forgot are the pull requests whose check builds were let go of (novox/hq ADR 0350 §5), "owner/repo#n".
|
||||||
|
forgot []string
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *fakeController) ForgetChecks(owner, repo string, number int) error {
|
||||||
|
c.mu.Lock()
|
||||||
|
defer c.mu.Unlock()
|
||||||
|
if c.down {
|
||||||
|
return errors.New("down")
|
||||||
|
}
|
||||||
|
c.forgot = append(c.forgot, fmt.Sprintf("%s/%s#%d", owner, repo, number))
|
||||||
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func newFakeController() *fakeController {
|
func newFakeController() *fakeController {
|
||||||
|
|||||||
@@ -593,9 +593,12 @@ func verdictOfState(s string) string {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// PullClosed takes a pull request closed unmerged: its open deliveries are stopped.
|
// PullClosed takes a pull request closed unmerged: its open deliveries are stopped.
|
||||||
|
//
|
||||||
|
// **And what its checks built for its merge goes** (novox/hq ADR 0350 §5): nothing will take it now. Asked of the
|
||||||
|
// controller once the deliveries are settled, outside the lock; a refusal is said, and the build seat's holders let
|
||||||
|
// go of it within the week anyway.
|
||||||
func (h *Holder) PullClosed(p PullEvent) {
|
func (h *Holder) PullClosed(p PullEvent) {
|
||||||
h.mu.Lock()
|
h.mu.Lock()
|
||||||
defer h.mu.Unlock()
|
|
||||||
for _, d := range h.deliveries {
|
for _, d := range h.deliveries {
|
||||||
if d.Repository == p.repository() && d.Number == p.Number && !d.State.Final() && d.MergedAs == "" {
|
if d.Repository == p.repository() && d.Number == p.Number && !d.State.Final() && d.MergedAs == "" {
|
||||||
d.ClosedUnmerged = true
|
d.ClosedUnmerged = true
|
||||||
@@ -605,6 +608,16 @@ func (h *Holder) PullClosed(p PullEvent) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
h.mu.Unlock()
|
||||||
|
if h.Controller == nil || p.Owner == "" || p.Repo == "" || p.Number <= 0 {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if err := h.Controller.ForgetChecks(p.Owner, p.Repo, p.Number); err != nil {
|
||||||
|
h.mu.Lock()
|
||||||
|
h.refuse(fmt.Errorf("%s#%d closed, and its check builds stay until the week's sweep: %w", p.repository(),
|
||||||
|
p.Number, err))
|
||||||
|
h.mu.Unlock()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// CheckedEvent is the controller's `checked`.
|
// CheckedEvent is the controller's `checked`.
|
||||||
|
|||||||
@@ -61,7 +61,7 @@ func TestItClaimsTheDeliverySeatAndFollowsWhatItHandles(t *testing.T) {
|
|||||||
}
|
}
|
||||||
// It calls exactly what its ports ask: the controller's six verbs and the forge's four tools.
|
// It calls exactly what its ports ask: the controller's six verbs and the forge's four tools.
|
||||||
want := []string{}
|
want := []string{}
|
||||||
for _, v := range []string{"delivery-plan", "delivery-order", "delivery-check", "deliver", "delivery-stop", "delivery-walks"} {
|
for _, v := range []string{"delivery-plan", "delivery-order", "delivery-check", "deliver", "delivery-stop", "delivery-walks", "forget-checks"} {
|
||||||
want = append(want, "seat:"+ControllerSeat+"."+v)
|
want = append(want, "seat:"+ControllerSeat+"."+v)
|
||||||
}
|
}
|
||||||
want = append(want, "gitea.gitea_note_append", "gitea.gitea_delivery_view", "gitea.gitea_commit_status",
|
want = append(want, "gitea.gitea_note_append", "gitea.gitea_delivery_view", "gitea.gitea_commit_status",
|
||||||
|
|||||||
@@ -402,6 +402,9 @@ type WalkModule struct {
|
|||||||
Build string `json:"build,omitempty"`
|
Build string `json:"build,omitempty"`
|
||||||
Gate *WalkGate `json:"gate,omitempty"`
|
Gate *WalkGate `json:"gate,omitempty"`
|
||||||
GatedBy string `json:"gated_by,omitempty"`
|
GatedBy string `json:"gated_by,omitempty"`
|
||||||
|
// Made is what the walk's build of it did (novox/hq ADR 0350 §4): "reused from <check build>", naming the pull
|
||||||
|
// request's head, the trunk commit and the digests, or "built: <why>". Empty from a controller before it.
|
||||||
|
Made string `json:"made,omitempty"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// WalkGate is a module's judging on its first machine.
|
// WalkGate is a module's judging on its first machine.
|
||||||
@@ -592,6 +595,9 @@ type WalkSeen struct {
|
|||||||
// Unchanged are the modules the walk said matched what runs, with why (novox/hq issue 611): not built, and
|
// Unchanged are the modules the walk said matched what runs, with why (novox/hq issue 611): not built, and
|
||||||
// nothing for it to deliver.
|
// nothing for it to deliver.
|
||||||
Unchanged map[string]string `json:"unchanged,omitempty"`
|
Unchanged map[string]string `json:"unchanged,omitempty"`
|
||||||
|
// Built is, per module, what the walk's build of it did (novox/hq ADR 0350 §4): "reused from <check build>" or
|
||||||
|
// "built: <why>" — what `show` says beside the build phase.
|
||||||
|
Built map[string]string `json:"built,omitempty"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// Seen is a walk as a delivery keeps it.
|
// Seen is a walk as a delivery keeps it.
|
||||||
@@ -616,6 +622,12 @@ func (w Walk) Seen() *WalkSeen {
|
|||||||
if m != nil && (m.SentAt != nil || m.Gate != nil && m.Gate.Verdict == "passed") {
|
if m != nil && (m.SentAt != nil || m.Gate != nil && m.Gate.Verdict == "passed") {
|
||||||
s.Moved = append(s.Moved, name)
|
s.Moved = append(s.Moved, name)
|
||||||
}
|
}
|
||||||
|
if m != nil && m.Made != "" {
|
||||||
|
if s.Built == nil {
|
||||||
|
s.Built = map[string]string{}
|
||||||
|
}
|
||||||
|
s.Built[name] = m.Made
|
||||||
|
}
|
||||||
}
|
}
|
||||||
sort.Strings(s.Moved)
|
sort.Strings(s.Moved)
|
||||||
s.MovedSaid = true
|
s.MovedSaid = true
|
||||||
|
|||||||
@@ -5,6 +5,7 @@ import (
|
|||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
"sort"
|
"sort"
|
||||||
|
"strconv"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
stdio "git.novox.be/novox/mesh-sdk/go"
|
stdio "git.novox.be/novox/mesh-sdk/go"
|
||||||
@@ -70,6 +71,8 @@ type Controller interface {
|
|||||||
StopWithout(walk, why, by string, without []string) (StopAnswer, error)
|
StopWithout(walk, why, by string, without []string) (StopAnswer, error)
|
||||||
// Walks are the walks the controller keeps — one, given its id — and whether this seat has a holder on record.
|
// Walks are the walks the controller keeps — one, given its id — and whether this seat has a holder on record.
|
||||||
Walks(walk string) (bool, []Walk, error)
|
Walks(walk string) (bool, []Walk, error)
|
||||||
|
// ForgetChecks lets go of the builds a pull request's merge checks made for its merge (novox/hq ADR 0350 §5).
|
||||||
|
ForgetChecks(owner, repo string, number int) error
|
||||||
}
|
}
|
||||||
|
|
||||||
// Forge is what the forge's holder does for a delivery.
|
// Forge is what the forge's holder does for a delivery.
|
||||||
@@ -271,6 +274,11 @@ func (c seatController) Deliver(walk, why string) error {
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (c seatController) ForgetChecks(owner, repo string, number int) error {
|
||||||
|
_, err := c.verb("forget-checks", map[string]any{"owner": owner, "repo": repo, "number": strconv.Itoa(number)})
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
func (c seatController) Stop(walk, why, by string) error {
|
func (c seatController) Stop(walk, why, by string) error {
|
||||||
_, err := c.verb("delivery-stop", map[string]any{"plan": walk, "why": why, "by": by})
|
_, err := c.verb("delivery-stop", map[string]any{"plan": walk, "why": why, "by": by})
|
||||||
return err
|
return err
|
||||||
|
|||||||
@@ -41,6 +41,7 @@
|
|||||||
"seat:mesh-controller.deliver",
|
"seat:mesh-controller.deliver",
|
||||||
"seat:mesh-controller.delivery-stop",
|
"seat:mesh-controller.delivery-stop",
|
||||||
"seat:mesh-controller.delivery-walks",
|
"seat:mesh-controller.delivery-walks",
|
||||||
|
"seat:mesh-controller.forget-checks",
|
||||||
"gitea.gitea_note_append",
|
"gitea.gitea_note_append",
|
||||||
"gitea.gitea_delivery_view",
|
"gitea.gitea_delivery_view",
|
||||||
"gitea.gitea_commit_status",
|
"gitea.gitea_commit_status",
|
||||||
|
|||||||
Reference in New Issue
Block a user