diff --git a/modules/gitea/README.md b/modules/gitea/README.md index 119e7c92d..c946a02b0 100644 --- a/modules/gitea/README.md +++ b/modules/gitea/README.md @@ -197,7 +197,7 @@ the module's own object store `incoming`, its transfers in the state `transfers` side), `versions` and `remove` (a tagged version, by the builder). Before anything is uploaded the holder asks the builder whether it numbered the version with that digest; a package goes up as npm's publish sends it, a Go module's zip to the Go upload. Every outcome is said on the seat's events (`published`, `refused`, `removed`), a Go package's -with its module path. Who may call each verb is the trusted config value `callers` (empty: design 51's list). +with its module path. Who may call each verb is the seat definition's `callers`, which the grants part grants by; the holder keeps no list. ## The package registry's retention (novox/hq ADR 0251 §4, to-be 51) diff --git a/modules/gitea/cmd/package-registry/retention_test.go b/modules/gitea/cmd/package-registry/retention_test.go index 93d77ace9..10116919d 100644 --- a/modules/gitea/cmd/package-registry/retention_test.go +++ b/modules/gitea/cmd/package-registry/retention_test.go @@ -488,7 +488,7 @@ func TestToolsAreTheOnesTheReadmeNamesAndSayWhatTheyDo(t *testing.T) { t.Errorf("transfers has options (a lifetime): %s", req) } // The retention asks the builder's versions; the forge's other bundles ask the catalog half and record a re-read. - if strings.Join(m.Invokes, ",") != "seat:mesh-catalog.revision,seat:mesh-hand-acts.hand-act,mesh-delivery-builder.builder_versions" { + if strings.Join(m.Invokes, ",") != "seat:mesh-catalog.revision,seat:mesh-catalog.registrations,seat:mesh-hand-acts.hand-act,mesh-delivery-builder.builder_versions" { t.Errorf("the manifest invokes %v", m.Invokes) } if len(m.Tools) != 0 { diff --git a/modules/gitea/cmd/package-registry/seats.go b/modules/gitea/cmd/package-registry/seats.go index cb4bce4df..5b409e603 100644 --- a/modules/gitea/cmd/package-registry/seats.go +++ b/modules/gitea/cmd/package-registry/seats.go @@ -276,11 +276,7 @@ var boundCap = parts.BusCap(packageseats.IncomingObjects) // busHolder is the holder over the runtime's bus state, the builder's numbering and the forge. func busHolder(forge func() (*Forge, error)) (*packageseats.Holder, error) { - callers, err := packageseats.ConfiguredCallers() - if err != nil { - return nil, err - } - return &packageseats.Holder{Callers: callers, Objects: parts.BusObjects(packageseats.IncomingObjects), + return &packageseats.Holder{Objects: parts.BusObjects(packageseats.IncomingObjects), Transfers: parts.BusTransfers[packageseats.Opening](packageseats.TransfersState), Builder: builds.BuilderVersions{Ask: stdio.Ask}, Store: seatStore{forge}, Say: say, Finding: finding, Cap: boundCap, Record: recordAct, Idle: parts.BusArm(packageseats.IdleState)}, nil diff --git a/modules/gitea/cmd/package-registry/whole_integration_test.go b/modules/gitea/cmd/package-registry/whole_integration_test.go index 64ef99a8c..6c766e8af 100644 --- a/modules/gitea/cmd/package-registry/whole_integration_test.go +++ b/modules/gitea/cmd/package-registry/whole_integration_test.go @@ -6,7 +6,7 @@ package main // the built package-registry bundle, played over its real stdin and stdout as the node's tool runtime plays it, its // `incoming` an object store and its `transfers` and `findings` key-value buckets of a nats-server of this test alone // (so the publish in parts runs through mesh-sdk's parts.BusObjects and BusTransfers, ruling 4 of the lead's 14:0x), the -// builder's builder_versions answered on mesh/ask, the config value `callers` watched on mesh/provision.watch, and the forge an +// builder's builder_versions answered on mesh/ask, and the forge an // httptest server that takes reads only with the tools' token and writes only with the packages account's. // // A Go package the builder numbered goes up in two chunks, is uploaded whole as the packages account and says @@ -113,7 +113,6 @@ func (p *player) handle(m wire) { Data []byte `json:"data"` Subject string `json:"subject"` Body json.RawMessage `json:"body"` - Kind string `json:"kind"` TTL int `json:"ttl_seconds"` } _ = json.Unmarshal(m.Params, &a) @@ -123,12 +122,6 @@ func (p *player) handle(m wire) { _ = json.Unmarshal(a.Body, &q) d, ok := p.numbered[q.Name+"@"+q.Version] p.answer(m.ID, map[string]any{"recorded": ok && d == q.Digest, "why": "not numbered with that digest"}, nil) - case m.Method == "mesh/provision.watch" && a.Kind == "config": - // The watch of `callers`, as the runtime answers it: the value now as one change (mesh/provision, - // named by the watch's id) handed over before the watch is answered, here unset, so design 51's list holds. - p.send(map[string]any{"id": "change-" + watchID(m.ID), "method": "mesh/provision", "params": map[string]any{ - "watch": watchID(m.ID), "change": map[string]any{"kind": "config", "key": a.Key}}}) - p.answer(m.ID, map[string]any{}, nil) case m.Method == "mesh/seat.publish": p.answer(m.ID, map[string]any{}, p.nc.Publish(a.Subject, a.Body)) case m.Method == "mesh/publish": @@ -142,15 +135,6 @@ func (p *player) handle(m wire) { } } -// watchID is the watch's id as a string, the form the change names it by. -func watchID(raw json.RawMessage) string { - var id string - if json.Unmarshal(raw, &id) != nil { - return string(raw) - } - return id -} - func (p *player) objects(m wire, local, name string, data []byte) { os, err := p.js.ObjectStore(p.ctx, "gitea_"+local) if err != nil { diff --git a/modules/gitea/internal/packageseats/holder.go b/modules/gitea/internal/packageseats/holder.go index 70c98c080..7b03101fb 100644 --- a/modules/gitea/internal/packageseats/holder.go +++ b/modules/gitea/internal/packageseats/holder.go @@ -2,21 +2,18 @@ // `go-package-registry` (novox/hq design 51 *The four registry seats*, ADR 0605, ADR 0611). A build module publishes // a package through `publish` and `publish-part`: the package's one file lands in the forge module's own object store // `incoming`, is hashed, checked against the builder's numbering and then uploaded into the forge's package registry -// with the module's own write side. The receiving side is mesh-sdk's seat-neutral parts.Receiver and the caller list -// mesh-sdk's VerbCallers (ADR 0627 §2): this package binds both to the two seats and to the forge. +// with the module's own write side. The receiving side is mesh-sdk's seat-neutral parts.Receiver: this package binds it to the two seats and to the +// forge. Who may call a verb is the seat definition's `callers`, which the grants part grants by (design 47 §68, §321). package packageseats import ( - "encoding/json" "errors" "fmt" "strings" - "sync" "time" "git.novox.be/novox/mesh-contracts/health-and-repair/go/clients/meshhandactsclient" "git.novox.be/novox/mesh-delivery/sdk/parts" - stdio "git.novox.be/novox/mesh-sdk/go" ) // The seats the forge module holds. @@ -67,103 +64,6 @@ const ( ReasonRetention = "retention" ) -// Callers is the trusted config value `callers`, read as mesh-sdk's list per verb. -type Callers = stdio.VerbCallers - -// DefaultCallers is design 51's list for the package seats: publish and publish-part the two build modules that -// publish packages; remove the builder (a tagged version whose branch ended); versions anyone on the mesh, as the -// registry's reads are anonymous. -func DefaultCallers() Callers { - builds := []string{"mesh-build-go", "mesh-build-node"} - return Callers{VerbPublish: builds, VerbPublishPart: builds, VerbRemove: {"mesh-delivery-builder"}, - VerbVersions: {stdio.AnyCaller}} -} - -// ConfiguredCallers is the config value `callers` as mesh-config resolved it, its lines of the package seats' verbs; unset, -// or with none of them, is DefaultCallers. It is held from a watch of the value and replaced on each change, so a call -// never asks the runtime (issue 1966). The same value carries the lines of the other seat the module holds (git, served -// by its TypeScript side), so each holder takes its own verbs' lines and neither refuses the other's. -func ConfiguredCallers() (Callers, error) { return configured() } - -var ( - watchMu sync.Mutex - heldMu sync.Mutex - heldList Callers - heldBad error -) - -// configured reads the held list; the first call starts the watch, and a change that cannot be read keeps the list last -// read, said and never widened. -func configured() (Callers, error) { - if l, err := read(); l != nil || err != nil { - return l, err - } - watchMu.Lock() - defer watchMu.Unlock() - if l, err := read(); l != nil || err != nil { - return l, err - } - if err := stdio.WatchConfig("callers", func(c stdio.ProvisionChange) error { - heldMu.Lock() - defer heldMu.Unlock() - if c.Refused != "" { - heldBad = fmt.Errorf("the config value callers is not answered: %s", c.Refused) - return nil - } - l, err := OwnCallers(c.Value) - if err != nil { - heldBad = fmt.Errorf("the config value callers cannot be read: %w", err) - return nil - } - heldList, heldBad = l, nil - return nil - }); err != nil { - return nil, fmt.Errorf("the config value callers cannot be read: %w", err) - } - l, err := read() - if l == nil && err == nil { - err = errors.New("the config value callers cannot be read: the runtime handed over no value") - } - return l, err -} - -func read() (Callers, error) { - heldMu.Lock() - defer heldMu.Unlock() - if heldList != nil { - return heldList, nil - } - return nil, heldBad -} - -// OwnCallers reads the package seats' lines of the config value as the runtime answered it: a JSON object of verb to -// callers, or that object written as a string. A value that cannot be read is an error, never the defaults. -func OwnCallers(raw json.RawMessage) (Callers, error) { - text := strings.TrimSpace(string(raw)) - var quoted string - if json.Unmarshal(raw, "ed) == nil { - text = strings.TrimSpace(quoted) - } - if text == "" || text == "null" { - return DefaultCallers(), nil - } - var all map[string]json.RawMessage - if err := json.Unmarshal([]byte(text), &all); err != nil { - return nil, fmt.Errorf("callers is not a JSON object of verb to callers: %w", err) - } - own := map[string]json.RawMessage{} - for _, verb := range Verbs { - if line, ok := all[verb]; ok { - own[verb] = line - } - } - if len(own) == 0 { - return DefaultCallers(), nil - } - b, _ := json.Marshal(own) - return stdio.ParseVerbCallers(string(b), Verbs, DefaultCallers()) -} - // Publish is a publish as the seats' contract names it (mesh-contracts npmpackageregistry and gopackageregistry share // one shape): what the tools bundle reads from either seat's PublishInput. type Publish struct { @@ -227,7 +127,6 @@ type Store interface { // Holder answers the verbs of both seats. type Holder struct { - Callers Callers Objects parts.Objects Transfers parts.Transfers[Opening] Builder Builder @@ -316,9 +215,6 @@ func (h *Holder) answer(transfer string, r parts.Received) Answer { // Publish is `publish` on seat for the proved caller: a package is one file, its digest the file's sha256. func (h *Holder) Publish(seat, caller string, p Publish) (Answer, error) { - if err := h.Callers.Allowed(VerbPublish, caller); err != nil { - return Answer{}, err - } if err := checkPublish(seat, p); err != nil { return Answer{}, err } @@ -349,9 +245,6 @@ func (h *Holder) Publish(seat, caller string, p Publish) (Answer, error) { // PublishPart is `publish-part`: one chunk, from the caller that opened the transfer alone. func (h *Holder) PublishPart(seat, caller, transfer, part string, sequence int, last bool, data []byte) (Answer, error) { - if err := h.Callers.Allowed(VerbPublishPart, caller); err != nil { - return Answer{}, err - } got, err := h.receiver().Take(key(seat, transfer), caller, part, sequence, last, data, func(t Transfer) error { if t.Shape.Seat != seat { return fmt.Errorf("transfer %s is %s's, not %s's", transfer, t.Shape.Seat, seat) @@ -366,9 +259,6 @@ func (h *Holder) PublishPart(seat, caller, transfer, part string, sequence int, // Versions is `versions`: every version the seat holds of name, newest first. func (h *Holder) Versions(seat, caller, name string) ([]Version, error) { - if err := h.Callers.Allowed(VerbVersions, caller); err != nil { - return nil, err - } if _, ok := Seats[seat]; !ok { return nil, fmt.Errorf("versions: the forge holds no seat %s", seat) } @@ -385,9 +275,6 @@ func (h *Holder) Versions(seat, caller, name string) ([]Version, error) { // Remove is `remove`: the builder removes a tagged version (its branch ended); an untagged version goes only by the // forge's own retention, never by a call. A version already gone is said removed all the same, so a retry ends. func (h *Holder) Remove(seat, caller, name, version, reason string) (Outcome, error) { - if err := h.Callers.Allowed(VerbRemove, caller); err != nil { - return Outcome{}, err - } if _, ok := Seats[seat]; !ok { return Outcome{}, fmt.Errorf("remove: the forge holds no seat %s", seat) } diff --git a/modules/gitea/internal/packageseats/holder_test.go b/modules/gitea/internal/packageseats/holder_test.go index 91e062cdc..63ebf4de8 100644 --- a/modules/gitea/internal/packageseats/holder_test.go +++ b/modules/gitea/internal/packageseats/holder_test.go @@ -10,7 +10,6 @@ import ( "git.novox.be/novox/mesh-contracts/health-and-repair/go/clients/meshhandactsclient" "git.novox.be/novox/mesh-delivery/sdk/parts" - stdio "git.novox.be/novox/mesh-sdk/go" ) type memObjects map[string][]byte @@ -102,7 +101,7 @@ type bed struct { func newBed(numbered bool) *bed { b := &bed{forge: &forge{files: map[string]map[string][]byte{}}, findings: map[string]bool{}} - b.h = &Holder{Callers: DefaultCallers(), Objects: memObjects{}, Transfers: memTransfers{}, Builder: builder{ok: numbered}, + b.h = &Holder{Objects: memObjects{}, Transfers: memTransfers{}, Builder: builder{ok: numbered}, Store: b.forge, Now: func() time.Time { return time.Unix(1e9, 0) }, Say: func(seat, event string, o Outcome) error { b.said = append(b.said, said{seat, event, o}); return nil }, Finding: func(kind, subject, _ string, raised bool) { b.findings[kind+" "+subject] = raised }} @@ -212,14 +211,6 @@ func TestTheForgeHoldsBothPackageSeats(t *testing.T) { do func(b *bed) error want string }{ - {"a caller not on publish's list", func(b *bed) error { - _, err := b.h.Publish(NpmSeat, "ace.claude-code", pub("x", "0.1.0", file)) - return err - }, "not on the holder's caller list"}, - {"an unproven caller", func(b *bed) error { - _, err := b.h.Publish(NpmSeat, stdio.Unproven, pub("x", "0.1.0", file)) - return err - }, "no caller the tool runner proved"}, {"a seat the forge does not hold", func(b *bed) error { _, err := b.h.Publish("cargo-package-registry", goBuild, pub("x", "0.1.0", file)) return err @@ -244,7 +235,6 @@ func TestTheForgeHoldsBothPackageSeats(t *testing.T) { _, err := b.h.PublishPart(GoSeat, nodeBuild, "t-x", digest(file), 0, true, file) return err }, "not open"}, - {"remove by a build module", func(b *bed) error { _, err := b.h.Remove(GoSeat, goBuild, "x", "0.1.1-feat.1", "tag-gone"); return err }, "not on the holder's caller list"}, {"remove of a version with no tag", func(b *bed) error { _, err := b.h.Remove(GoSeat, "novox.mesh-delivery-builder", "x", "0.1.1", "tag-gone") return err @@ -304,29 +294,4 @@ func TestTheForgeHoldsBothPackageSeats(t *testing.T) { } }) - t.Run("the callers value: its own verbs' lines, the other seat's left to it, unset or none of its own the defaults", func(t *testing.T) { - manifest := `"{\"publish\":[\"mesh-build-go\"],\"remove\":[\"mesh-delivery-builder\"],\"pull\":[\"mesh-delivery-gate\"]}"` - for _, row := range []struct { - raw string - publish []string - fails bool - }{ - {raw: manifest, publish: []string{"mesh-build-go"}}, - {raw: `{"pull":["mesh-delivery-gate"]}`, publish: DefaultCallers()[VerbPublish]}, - {raw: `""`, publish: DefaultCallers()[VerbPublish]}, - {raw: `{"publish":"mesh-build-go"}`, fails: true}, - {raw: `"not json"`, fails: true}, - } { - got, err := OwnCallers([]byte(row.raw)) - if row.fails != (err != nil) { - t.Fatalf("%s: err %v", row.raw, err) - } - if !row.fails && strings.Join(got[VerbPublish], ",") != strings.Join(row.publish, ",") { - t.Errorf("%s: publish %v, want %v", row.raw, got[VerbPublish], row.publish) - } - if _, theirs := got["pull"]; theirs { - t.Errorf("%s: the seat git's line is not the package seats'", row.raw) - } - } - }) } diff --git a/modules/gitea/module.json b/modules/gitea/module.json index dd462ecbe..02c8fcd63 100644 --- a/modules/gitea/module.json +++ b/modules/gitea/module.json @@ -248,13 +248,6 @@ "default": [ "jschoubben" ] - }, - "callers": { - "type": "string", - "trusted": true, - "runtime": true, - "default": "{\"publish\":[\"mesh-build-go\",\"mesh-build-node\"],\"publish-part\":[\"mesh-build-go\",\"mesh-build-node\"],\"remove\":[\"mesh-delivery-builder\"],\"versions\":[\"*\"],\"pull\":[\"mesh-delivery-forge-bridge\",\"mesh-delivery-gate\"],\"branch\":[\"mesh-delivery-forge-bridge\",\"mesh-delivery-indexer\"],\"open-pulls\":[\"mesh-delivery-forge-bridge\",\"mesh-delivery-indexer\",\"mesh-delivery-gate\"],\"tags\":[\"mesh-delivery-forge-bridge\"],\"trees\":[\"mesh-delivery-forge-bridge\",\"mesh-delivery-checker\",\"mesh-delivery-indexer\"],\"file\":[\"mesh-delivery-forge-bridge\",\"mesh-delivery-checker\",\"mesh-delivery-indexer\"],\"contains\":[\"mesh-delivery-indexer\",\"mesh-delivery-gate\"],\"statuses\":[\"mesh-delivery-gate\"],\"status\":[\"mesh-delivery-forge-bridge\"],\"comment\":[\"mesh-delivery-forge-bridge\"],\"view\":[\"mesh-delivery-forge-bridge\"],\"note\":[\"mesh-delivery-forge-bridge\"],\"merge-pull\":[\"mesh-delivery-forge-bridge\"],\"tag\":[\"mesh-delivery-builder\"]}", - "description": "per verb of the seats the module holds, the callers that may call it (JSON object of verb to callers; * every proved caller, operator the operator's session); each holder reads the lines of its own verbs. The package seats' lines are design 51's (empty is that list for them); the seat git's are design 47's: its reads the parts that declare them in invokes, its acts (status, comment, view, note, merge-pull) the forge bridge alone, and a verb of git with no line is called by no one" } }, "facts": { diff --git a/modules/gitea/seat.ts b/modules/gitea/seat.ts index 5715098d3..8ba0d9725 100644 --- a/modules/gitea/seat.ts +++ b/modules/gitea/seat.ts @@ -2,10 +2,8 @@ // delivery part makes of the forge is a verb of the seat, served here as a tool `git.` against the seat's generated // contract, and no part names this module's own tools. // -// **Who may call which verb** is the holder's trusted config value `callers` (ADR 0627 §2), the one value the module's -// package seats read too: each holder takes the lines of the verbs it serves, so neither refuses the other's. Unset, no -// one may call a verb of `git`: the list is the manifest's, never a list in this code. The caller is the one the tool -// runner proved in the call's `_meta` (mesh-sdk `callers.callerOf`), never an argument. +// **Who may call which verb** is the seat definition's `callers`, which the grants part grants by: the bus refuses a caller a +// definition does not name (design 47 §68, §313, §321), so this holder judges no caller and keeps no list of its own. // // **A failed read answers an error, never an empty answer**: the forge's 404 is the kind `not-found`, its refusal (another // 4xx, or a refusal of this holder) the kind `refused`, a forge that failed or did not answer the kind `failed`; an empty list or an absent file is answered @@ -16,8 +14,6 @@ // (design 47 §314): only into a base whose protection refuses a push, and only that head. import type { ToolDefinition } from "@novox/mesh-sdk/tools"; -import { callerOf, refusal, parseVerbCallers, type VerbCallers } from "@novox/mesh-sdk/callers"; -import { config } from "@novox/mesh-sdk/runtime"; import { seat as GIT, verbs as GIT_VERBS, type BranchAnswer, type ContainsAnswer, type CommentAnswer, type ErrorKind, type FileAnswer, type MergePullAnswer, type Merging, type NoteAnswer, @@ -33,9 +29,6 @@ import { appendNote, viewBody, viewComment, type Runner } from "./delivery.js"; /** The verbs of the seat, in its definition's order. */ export const VERBS: readonly string[] = Object.values(GIT_VERBS); -/** The config value whose lines per verb say who may call it. */ -export const CALLERS_KEY = "callers"; - /** How many tags a page of `tags` holds. */ export const TAGS_PAGE = 50; @@ -50,31 +43,13 @@ export interface HeadStatuses { } /** What the holder reaches beside the forge's API: git in the forge's container (the note), the head's one writer of its - * statuses, and the callers' list. */ + * statuses, and whether the first registrations are written. */ export interface SeatDeps { run: Runner; container: string; statuses: HeadStatuses; /** Whether the first registrations were written (registrations.ts): until then merge-pull refuses every merge (design 47 §95). */ registered: () => boolean; - /** The config value `callers` as the runtime answers it; the runtime's own config read by default. */ - callers?: () => Promise; -} - -/** The lines of `raw` (the config value, an object or that object as a string) for the seat's verbs; unset is none. */ -export function gitCallers(raw: unknown): VerbCallers { - if (raw === undefined || raw === null || raw === "") return {}; - let all: unknown = raw; - if (typeof raw === "string") { - try { - all = JSON.parse(raw); - } catch (err) { - throw new Error(`callers is not a JSON object of verb to callers: ${err instanceof Error ? err.message : String(err)}`); - } - } - if (!all || typeof all !== "object" || Array.isArray(all)) throw new Error("callers is not a JSON object of verb to callers"); - const mine = Object.fromEntries(Object.entries(all as Record).filter(([verb]) => VERBS.includes(verb))); - return parseVerbCallers(JSON.stringify(mine), VERBS, {}); } /** A failure as the contract says it: the forge's or this holder's refusal, or a forge that failed. */ @@ -146,17 +121,14 @@ async function mergingOf(gitea: GiteaClient, owner: string, repo: string, p: Git return { mergeable: p.mergeable, protected: !!rule, required, writes }; } -/** The seat's verbs as tools `git.`, each judging its caller first and answering in the seat's contract. */ +/** The seat's verbs as tools `git.`, each answering in the seat's contract. */ export function seatTools(gitea: GiteaClient, deps: SeatDeps): ToolDefinition[] { - const listOf = deps.callers ?? (() => config(CALLERS_KEY)); const verb = (name: string, description: string, act: (args: Record) => Promise): ToolDefinition => ({ name: `${GIT}.${name}`, description, input: {}, run: async (args: Record) => { try { - const why = refusal(gitCallers(await listOf()), name, callerOf(args)); - if (why) return failure(new Refused(why.message)); return await act(args); } catch (err) { return failure(err); diff --git a/modules/gitea/test/guard.test.ts b/modules/gitea/test/guard.test.ts index 1474a1aec..d939bcfb9 100644 --- a/modules/gitea/test/guard.test.ts +++ b/modules/gitea/test/guard.test.ts @@ -431,19 +431,14 @@ test("the two new tools are named for what mesh_search is asked, and gitea_api s assert.ok("refused" in judgeApiCall("PATCH", "/repos/a/b/pulls/7", { base: "x" })); }); -// **The seat git's verbs judge their caller, and merge-pull judges the Approve's head and no check** (design 47 *The seat -// git*, ADR 0661 §3): a caller the trusted list does not name is refused before the forge is asked; merge-pull merges the +// **merge-pull judges the head it is asked at and no check** (design 47 *The seat git*, §314): merge-pull merges the // head it is named into a base that takes no push, whatever its statuses say, and an id said again after the merge is // answered performed. -test("the seat git's verbs judge their caller, and merge-pull merges the Approve's head and judges no check", async () => { - const { seatTools, gitCallers } = await import("../dist/seat.js"); +test("merge-pull merges the head the caller names and judges no check", async () => { + const { seatTools } = await import("../dist/seat.js"); const { runAs } = await import("@novox/mesh-sdk/callers"); const bridge = "ace.mesh-delivery-forge-bridge"; let registered = true; - const list = JSON.stringify({ publish: ["mesh-build-go"], "merge-pull": ["mesh-delivery-forge-bridge"], - pull: ["mesh-delivery-forge-bridge", "mesh-delivery-gate"], status: ["mesh-delivery-forge-bridge"] }); - assert.deepEqual(Object.keys(gitCallers(list)).sort(), ["merge-pull", "pull", "status"], "only the seat git's lines are its"); - assert.deepEqual(gitCallers(undefined), {}, "unset, no one may call"); // The head's one writer of its statuses (gate.ts), over a forge whose first write is still in flight when the next is asked. const { GateWriter } = await import("../dist/gate.js"); const landed: string[] = []; @@ -457,15 +452,13 @@ test("the seat git's verbs judge their caller, and merge-pull merges the Approve }; const gates = new GateWriter(slow); const seat = seatTools(Object.assign(Object.create(client), { setCommitStatus: slow.setCommitStatus }), - { run: async () => ({ stdout: "", code: 0 }), container: "gitea", callers: async () => list, statuses: gates, registered: () => registered }); + { run: async () => ({ stdout: "", code: 0 }), container: "gitea", statuses: gates, registered: () => registered }); const call = (verb: string, args: Record, caller = bridge) => runAs({ "mesh/caller": caller }, args, () => seat.find((t: any) => t.name === `git.${verb}`)!.run(args)) as Promise; const merge = (extra: Record = {}, caller?: string) => call("merge-pull", { id: "act-1", repository: "novox/mesh-catalog", number: 7, head: REVIEWED, ...extra }, caller); const rows: { name: string; set: () => void; caller?: string; extra?: Record; want: RegExp | "merged" | "held"; loud?: boolean; kind?: string }[] = [ { name: "no registration written yet", set: () => { registered = false; }, want: /no module is registered yet/, kind: "no-registrations" }, - { name: "a caller not on the list", set: () => {}, caller: "ace.mesh-delivery-checker", want: /refused for ace.mesh-delivery-checker/ }, - { name: "an unproven caller", set: () => {}, caller: "", want: /no caller the tool runner proved/ }, { name: "a head that moved", set: () => { state.head = MOVED; }, want: /its head is 3c4d5e6f7a8b, not the abc1234def56/, kind: "head-moved" }, { name: "a base no rule guards", set: () => { state.rules = []; }, want: /guarded by no protection rule/, kind: "unprotected" }, { name: "a base whose protection was removed after the last branch-moved", set: () => { state.rules = [protectedMain()]; state.rules = []; }, want: /guarded by no protection rule/, kind: "unprotected" }, @@ -555,11 +548,10 @@ test("every read and act of the seat git answers its contract, and an error is a }; const run = async (_f: string, args: string[]) => args.includes("show") || args.includes("--ref") && !args.includes("append") ? { stdout: noteShown, code: noteShown ? 0 : 1 } : (noteWritten++, { stdout: "", code: 0 }); - const seat = seatTools(forge as any, { run: run as any, container: "gitea", callers: async () => JSON.stringify({}), statuses: { status: async () => {} }, registered: () => true }); - const everyone = JSON.stringify(Object.fromEntries(seat.map((t: any) => [t.name.slice(4), ["*"]]))); + const seat = seatTools(forge as any, { run: run as any, container: "gitea", statuses: { status: async () => {} }, registered: () => true }); const call = (verb: string, args: Record, over: Record = {}) => { const t = seat.find((x: any) => x.name === `git.${verb}`)!; - const wrapped = seatTools(Object.assign(Object.create(forge), over) as any, { run: run as any, container: "gitea", callers: async () => everyone, statuses: { status: async () => {} }, registered: () => true }); + const wrapped = seatTools(Object.assign(Object.create(forge), over) as any, { run: run as any, container: "gitea", statuses: { status: async () => {} }, registered: () => true }); return runAs({ "mesh/caller": "ace.x" }, args, () => wrapped.find((x: any) => x.name === t.name)!.run(args)) as Promise; }; const rows: [string, string, Record, Record, (a: any) => unknown, unknown][] = [