Merge b2/registry into b7/registry: the Delivery contracts, sdk and forgemsg homes (D-12..D-22 base; issue 1966)
This commit is contained in:
@@ -0,0 +1,63 @@
|
||||
// Package containerimageregistryclient is the container-image-registry seat's client (novox/hq ADR 0627 §1, §2; ADR 0611, design 51
|
||||
// *Publishing*): it publishes a file in parts to the seat's holder. `publish` opens the transfer naming every part,
|
||||
// the holder answers the parts it lacks, and only those travel by `publish-part`, in chunks a bus message carries; a
|
||||
// cut transfer goes on from what the holder answers when the same transfer is opened again. The sending in parts is
|
||||
// mesh-sdk's seat-neutral runtime (parts); this client binds it to this seat's verbs and types alone. The call is the
|
||||
// caller's own (a bundle's stdio.Ask, a process's call of a seat verb), so it holds no transport.
|
||||
package containerimageregistryclient
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/base64"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
|
||||
seat "git.novox.be/novox/mesh-delivery/contracts/go/seats/containerimageregistry"
|
||||
"git.novox.be/novox/mesh-delivery/sdk/parts"
|
||||
)
|
||||
|
||||
// Call asks one verb of the seat, `seat:<seat>.<verb>`, with a body, and answers its JSON.
|
||||
type Call func(key string, body any) (json.RawMessage, error)
|
||||
|
||||
// Sent is what a Publish did: the transfer, whether the holder held its digest already, the parts it lacked and were
|
||||
// sent, and the chunks they took.
|
||||
type Sent struct {
|
||||
Transfer string
|
||||
parts.Sent
|
||||
}
|
||||
|
||||
// Publish opens (or opens again) the transfer in on the seat and sends the parts the holder answers it lacks, each in
|
||||
// chunks of at most parts.MaxChunk, in sequence, the last marked. in.Parts is written from parts. A part whose bytes
|
||||
// do not hash to its digest is refused before anything of it is sent; a part the holder does not name is never read.
|
||||
// Its outcome, published or refused, is the holder's event, which the caller waits for.
|
||||
func Publish(ctx context.Context, call Call, in seat.PublishInput, ps []parts.Part) (Sent, error) {
|
||||
open := func(_ context.Context, ps []parts.Part) (parts.Opened, error) {
|
||||
in.Parts = make([]seat.Part, 0, len(ps))
|
||||
for _, p := range ps {
|
||||
in.Parts = append(in.Parts, seat.Part{Digest: p.Digest, Size: p.Size})
|
||||
}
|
||||
raw, err := call("seat:"+seat.Seat+"."+seat.VerbPublish, in)
|
||||
if err != nil {
|
||||
return parts.Opened{}, fmt.Errorf("%s's publish of %s: %w", seat.Seat, in.Name, err)
|
||||
}
|
||||
var a seat.PublishAnswer
|
||||
if err := json.Unmarshal(raw, &a); err != nil {
|
||||
return parts.Opened{}, fmt.Errorf("%s's answer to publish: %w", seat.Seat, err)
|
||||
}
|
||||
return parts.Opened{Held: a.Held, Lacks: a.Lacks}, nil
|
||||
}
|
||||
chunk := func(_ context.Context, part string, sequence int, last bool, data []byte) error {
|
||||
raw, err := call("seat:"+seat.Seat+"."+seat.VerbPublishPart, seat.PublishPartInput{Transfer: in.Transfer, Part: part,
|
||||
Sequence: int64(sequence), Last: last, Data: base64.StdEncoding.EncodeToString(data)})
|
||||
if err != nil {
|
||||
return fmt.Errorf("%s's publish-part: %w", seat.Seat, err)
|
||||
}
|
||||
var a seat.PublishPartAnswer
|
||||
if err := json.Unmarshal(raw, &a); err != nil {
|
||||
return fmt.Errorf("%s's answer to publish-part: %w", seat.Seat, err)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
sent, err := parts.Send(ctx, open, chunk, ps)
|
||||
return Sent{Transfer: in.Transfer, Sent: sent}, err
|
||||
}
|
||||
@@ -0,0 +1,57 @@
|
||||
package containerimageregistryclient
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
"crypto/sha256"
|
||||
"encoding/base64"
|
||||
"encoding/hex"
|
||||
"encoding/json"
|
||||
"io"
|
||||
"testing"
|
||||
|
||||
seat "git.novox.be/novox/mesh-delivery/contracts/go/seats/containerimageregistry"
|
||||
"git.novox.be/novox/mesh-delivery/sdk/parts"
|
||||
)
|
||||
|
||||
// The client publishes over its own seat's verbs alone (ADR 0627 §1): publish names every part, and only the part the
|
||||
// holder lacks travels by publish-part, its bytes whole, the last chunk marked.
|
||||
func TestPublishTalksToItsOwnSeat(t *testing.T) {
|
||||
held, lacked := []byte("held"), []byte("lacked")
|
||||
part := func(b []byte) parts.Part {
|
||||
sum := sha256.Sum256(b)
|
||||
return parts.Part{Digest: "sha256:" + hex.EncodeToString(sum[:]), Size: int64(len(b)),
|
||||
Open: func() (io.ReadCloser, error) { return io.NopCloser(bytes.NewReader(b)), nil }}
|
||||
}
|
||||
ps := []parts.Part{part(held), part(lacked)}
|
||||
var asked []string
|
||||
var got []byte
|
||||
call := func(key string, body any) (json.RawMessage, error) {
|
||||
asked = append(asked, key)
|
||||
raw, _ := json.Marshal(body)
|
||||
switch key {
|
||||
case "seat:container-image-registry.publish":
|
||||
var in seat.PublishInput
|
||||
_ = json.Unmarshal(raw, &in)
|
||||
if len(in.Parts) != 2 {
|
||||
t.Errorf("publish names %d parts, want 2", len(in.Parts))
|
||||
}
|
||||
return json.Marshal(seat.PublishAnswer{Transfer: in.Transfer, Lacks: []string{ps[1].Digest}})
|
||||
case "seat:container-image-registry.publish-part":
|
||||
var in seat.PublishPartInput
|
||||
_ = json.Unmarshal(raw, &in)
|
||||
data, _ := base64.StdEncoding.DecodeString(in.Data)
|
||||
if in.Part != ps[1].Digest || !in.Last || in.Transfer != "t1" {
|
||||
t.Errorf("publish-part %+v", in)
|
||||
}
|
||||
got = append(got, data...)
|
||||
return json.Marshal(seat.PublishPartAnswer{Transfer: in.Transfer, Part: in.Part})
|
||||
}
|
||||
t.Fatalf("asked %s", key)
|
||||
return nil, nil
|
||||
}
|
||||
sent, err := Publish(context.Background(), call, seat.PublishInput{Transfer: "t1", Name: "x"}, ps)
|
||||
if err != nil || sent.Transfer != "t1" || sent.Chunks != 1 || len(sent.Parts) != 1 || !bytes.Equal(got, lacked) || len(asked) != 2 {
|
||||
t.Errorf("sent %+v, %q arrived, asked %v, %v", sent, got, asked, err)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,18 @@
|
||||
module git.novox.be/novox/mesh-delivery/contracts/go/clients/containerimageregistryclient
|
||||
|
||||
go 1.26.0
|
||||
|
||||
require (
|
||||
git.novox.be/novox/mesh-delivery/contracts/go/seats/containerimageregistry v0.0.0
|
||||
git.novox.be/novox/mesh-delivery/sdk v0.0.0
|
||||
git.novox.be/novox/mesh-sdk/go v0.1.14-0.20261104174118-f94abb95aaef
|
||||
)
|
||||
|
||||
require (
|
||||
github.com/klauspost/compress v1.20.1 // indirect
|
||||
github.com/nats-io/nats.go v1.54.0 // indirect
|
||||
github.com/nats-io/nkeys v0.4.16 // indirect
|
||||
github.com/nats-io/nuid v1.0.1 // indirect
|
||||
golang.org/x/crypto v0.57.0 // indirect
|
||||
golang.org/x/sys v0.48.0 // indirect
|
||||
)
|
||||
@@ -0,0 +1,18 @@
|
||||
git.novox.be/novox/mesh-contracts/go v0.0.0-20261104172218-347a3053580a h1:E9ERPqR4uZXypudHGxv2/wNIOYcder9torVqJwJq/DI=
|
||||
git.novox.be/novox/mesh-contracts/go v0.0.0-20261104172218-347a3053580a/go.mod h1:0dD1XEJBJ0G5UYDQxSZI84nuujb6m3tOpJl4C8rQVvg=
|
||||
git.novox.be/novox/mesh-delivery/contracts/go/seats/containerimageregistry v0.0.0-20261105000000-000000000000 h1:BTqpa7dCCTQH9m08Jle8KVFwyIHiHPSUbwWxuqSg2yA=
|
||||
git.novox.be/novox/mesh-delivery/contracts/go/seats/containerimageregistry v0.0.0-20261105000000-000000000000/go.mod h1:pPxwhTMb3hIk7UUrBicJnGQCqfTEoBGJvNFspEK2U18=
|
||||
git.novox.be/novox/mesh-sdk/go v0.1.14-0.20261104174118-f94abb95aaef h1:/Jic5ho0xz32Z9DH2NSUqVxCF1KyfIOWuRTrX1aLjus=
|
||||
git.novox.be/novox/mesh-sdk/go v0.1.14-0.20261104174118-f94abb95aaef/go.mod h1:2wHG7ntLCG8Tpm/SCfgp524a5emUJm1gAC4KrRYsU9s=
|
||||
github.com/klauspost/compress v1.20.1 h1:T7kKElXUMXrUJ2E9QhQhxFtcK5rPyLdsGZvdbLMPdiQ=
|
||||
github.com/klauspost/compress v1.20.1/go.mod h1:LUdAzn7YLVvxLpc7y3V1m40wESHTgc1422pwwBSKYuI=
|
||||
github.com/nats-io/nats.go v1.54.0 h1:vsXoOxjHp/GmPUN+EcI7uOf/uB+iAP+kEsAFNQN0yzA=
|
||||
github.com/nats-io/nats.go v1.54.0/go.mod h1:y+DZoD1oBOYfZTU681eTUiUjI0vbqYGixNVFHcjHJ0k=
|
||||
github.com/nats-io/nkeys v0.4.16 h1:rd5oAuLOb8mnAycB0xleuEBNS1pVVnN0fv/FF34Eypg=
|
||||
github.com/nats-io/nkeys v0.4.16/go.mod h1:llLgWoI0o4z/Q57q2R1kHfmocyhGV6VG/U18Glg1Afs=
|
||||
github.com/nats-io/nuid v1.0.1 h1:5iA8DT8V7q8WK2EScv2padNa/rTESc1KdnPw4TC2paw=
|
||||
github.com/nats-io/nuid v1.0.1/go.mod h1:19wcPz3Ph3q0Jbyiqsd0kePYG7A95tJPxeL+1OSON2c=
|
||||
golang.org/x/crypto v0.57.0 h1:3ZVCjf8Ggz7zneR/EHRVx68Ctf+2pmIMP2UFhh9cC6M=
|
||||
golang.org/x/crypto v0.57.0/go.mod h1:Fdz0i5U6CoizGwLda9DttjSk6qlZo25zYNtR+ycvuZA=
|
||||
golang.org/x/sys v0.48.0 h1:bbX/i/6MgT9BVLM9RT1thmxL04yeTAhbEz4SyadbXoo=
|
||||
golang.org/x/sys v0.48.0/go.mod h1:hNLxWAXmnKAxqDtdwIYC4bM9oQPEecfsnNMuSxOs3og=
|
||||
@@ -0,0 +1,63 @@
|
||||
// Package gopackageregistryclient is the go-package-registry seat's client (novox/hq ADR 0627 §1, §2; ADR 0611, design 51
|
||||
// *Publishing*): it publishes a file in parts to the seat's holder. `publish` opens the transfer naming every part,
|
||||
// the holder answers the parts it lacks, and only those travel by `publish-part`, in chunks a bus message carries; a
|
||||
// cut transfer goes on from what the holder answers when the same transfer is opened again. The sending in parts is
|
||||
// mesh-sdk's seat-neutral runtime (parts); this client binds it to this seat's verbs and types alone. The call is the
|
||||
// caller's own (a bundle's stdio.Ask, a process's call of a seat verb), so it holds no transport.
|
||||
package gopackageregistryclient
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/base64"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
|
||||
seat "git.novox.be/novox/mesh-delivery/contracts/go/seats/gopackageregistry"
|
||||
"git.novox.be/novox/mesh-delivery/sdk/parts"
|
||||
)
|
||||
|
||||
// Call asks one verb of the seat, `seat:<seat>.<verb>`, with a body, and answers its JSON.
|
||||
type Call func(key string, body any) (json.RawMessage, error)
|
||||
|
||||
// Sent is what a Publish did: the transfer, whether the holder held its digest already, the parts it lacked and were
|
||||
// sent, and the chunks they took.
|
||||
type Sent struct {
|
||||
Transfer string
|
||||
parts.Sent
|
||||
}
|
||||
|
||||
// Publish opens (or opens again) the transfer in on the seat and sends the parts the holder answers it lacks, each in
|
||||
// chunks of at most parts.MaxChunk, in sequence, the last marked. in.Parts is written from parts. A part whose bytes
|
||||
// do not hash to its digest is refused before anything of it is sent; a part the holder does not name is never read.
|
||||
// Its outcome, published or refused, is the holder's event, which the caller waits for.
|
||||
func Publish(ctx context.Context, call Call, in seat.PublishInput, ps []parts.Part) (Sent, error) {
|
||||
open := func(_ context.Context, ps []parts.Part) (parts.Opened, error) {
|
||||
in.Parts = make([]seat.Part, 0, len(ps))
|
||||
for _, p := range ps {
|
||||
in.Parts = append(in.Parts, seat.Part{Digest: p.Digest, Size: p.Size})
|
||||
}
|
||||
raw, err := call("seat:"+seat.Seat+"."+seat.VerbPublish, in)
|
||||
if err != nil {
|
||||
return parts.Opened{}, fmt.Errorf("%s's publish of %s: %w", seat.Seat, in.Name, err)
|
||||
}
|
||||
var a seat.PublishAnswer
|
||||
if err := json.Unmarshal(raw, &a); err != nil {
|
||||
return parts.Opened{}, fmt.Errorf("%s's answer to publish: %w", seat.Seat, err)
|
||||
}
|
||||
return parts.Opened{Held: a.Held, Lacks: a.Lacks}, nil
|
||||
}
|
||||
chunk := func(_ context.Context, part string, sequence int, last bool, data []byte) error {
|
||||
raw, err := call("seat:"+seat.Seat+"."+seat.VerbPublishPart, seat.PublishPartInput{Transfer: in.Transfer, Part: part,
|
||||
Sequence: int64(sequence), Last: last, Data: base64.StdEncoding.EncodeToString(data)})
|
||||
if err != nil {
|
||||
return fmt.Errorf("%s's publish-part: %w", seat.Seat, err)
|
||||
}
|
||||
var a seat.PublishPartAnswer
|
||||
if err := json.Unmarshal(raw, &a); err != nil {
|
||||
return fmt.Errorf("%s's answer to publish-part: %w", seat.Seat, err)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
sent, err := parts.Send(ctx, open, chunk, ps)
|
||||
return Sent{Transfer: in.Transfer, Sent: sent}, err
|
||||
}
|
||||
@@ -0,0 +1,57 @@
|
||||
package gopackageregistryclient
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
"crypto/sha256"
|
||||
"encoding/base64"
|
||||
"encoding/hex"
|
||||
"encoding/json"
|
||||
"io"
|
||||
"testing"
|
||||
|
||||
seat "git.novox.be/novox/mesh-delivery/contracts/go/seats/gopackageregistry"
|
||||
"git.novox.be/novox/mesh-delivery/sdk/parts"
|
||||
)
|
||||
|
||||
// The client publishes over its own seat's verbs alone (ADR 0627 §1): publish names every part, and only the part the
|
||||
// holder lacks travels by publish-part, its bytes whole, the last chunk marked.
|
||||
func TestPublishTalksToItsOwnSeat(t *testing.T) {
|
||||
held, lacked := []byte("held"), []byte("lacked")
|
||||
part := func(b []byte) parts.Part {
|
||||
sum := sha256.Sum256(b)
|
||||
return parts.Part{Digest: "sha256:" + hex.EncodeToString(sum[:]), Size: int64(len(b)),
|
||||
Open: func() (io.ReadCloser, error) { return io.NopCloser(bytes.NewReader(b)), nil }}
|
||||
}
|
||||
ps := []parts.Part{part(held), part(lacked)}
|
||||
var asked []string
|
||||
var got []byte
|
||||
call := func(key string, body any) (json.RawMessage, error) {
|
||||
asked = append(asked, key)
|
||||
raw, _ := json.Marshal(body)
|
||||
switch key {
|
||||
case "seat:go-package-registry.publish":
|
||||
var in seat.PublishInput
|
||||
_ = json.Unmarshal(raw, &in)
|
||||
if len(in.Parts) != 2 {
|
||||
t.Errorf("publish names %d parts, want 2", len(in.Parts))
|
||||
}
|
||||
return json.Marshal(seat.PublishAnswer{Transfer: in.Transfer, Lacks: []string{ps[1].Digest}})
|
||||
case "seat:go-package-registry.publish-part":
|
||||
var in seat.PublishPartInput
|
||||
_ = json.Unmarshal(raw, &in)
|
||||
data, _ := base64.StdEncoding.DecodeString(in.Data)
|
||||
if in.Part != ps[1].Digest || !in.Last || in.Transfer != "t1" {
|
||||
t.Errorf("publish-part %+v", in)
|
||||
}
|
||||
got = append(got, data...)
|
||||
return json.Marshal(seat.PublishPartAnswer{Transfer: in.Transfer, Part: in.Part})
|
||||
}
|
||||
t.Fatalf("asked %s", key)
|
||||
return nil, nil
|
||||
}
|
||||
sent, err := Publish(context.Background(), call, seat.PublishInput{Transfer: "t1", Name: "x"}, ps)
|
||||
if err != nil || sent.Transfer != "t1" || sent.Chunks != 1 || len(sent.Parts) != 1 || !bytes.Equal(got, lacked) || len(asked) != 2 {
|
||||
t.Errorf("sent %+v, %q arrived, asked %v, %v", sent, got, asked, err)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,18 @@
|
||||
module git.novox.be/novox/mesh-delivery/contracts/go/clients/gopackageregistryclient
|
||||
|
||||
go 1.26.0
|
||||
|
||||
require (
|
||||
git.novox.be/novox/mesh-delivery/contracts/go/seats/gopackageregistry v0.0.0
|
||||
git.novox.be/novox/mesh-delivery/sdk v0.0.0
|
||||
git.novox.be/novox/mesh-sdk/go v0.1.14-0.20261104174118-f94abb95aaef
|
||||
)
|
||||
|
||||
require (
|
||||
github.com/klauspost/compress v1.20.1 // indirect
|
||||
github.com/nats-io/nats.go v1.54.0 // indirect
|
||||
github.com/nats-io/nkeys v0.4.16 // indirect
|
||||
github.com/nats-io/nuid v1.0.1 // indirect
|
||||
golang.org/x/crypto v0.57.0 // indirect
|
||||
golang.org/x/sys v0.48.0 // indirect
|
||||
)
|
||||
@@ -0,0 +1,18 @@
|
||||
git.novox.be/novox/mesh-contracts/go v0.0.0-20261104172218-347a3053580a h1:E9ERPqR4uZXypudHGxv2/wNIOYcder9torVqJwJq/DI=
|
||||
git.novox.be/novox/mesh-contracts/go v0.0.0-20261104172218-347a3053580a/go.mod h1:0dD1XEJBJ0G5UYDQxSZI84nuujb6m3tOpJl4C8rQVvg=
|
||||
git.novox.be/novox/mesh-delivery/contracts/go/seats/gopackageregistry v0.0.0-20261105000000-000000000000 h1:Zl6CzMg9AZtk7RU/DSJxSGGoUg5fSW3t7Yf8zyfXbVY=
|
||||
git.novox.be/novox/mesh-delivery/contracts/go/seats/gopackageregistry v0.0.0-20261105000000-000000000000/go.mod h1:Y5kgBh0hBbL/hgc+abCF9Vbhov+vCsB0ckpRzUw1D+0=
|
||||
git.novox.be/novox/mesh-sdk/go v0.1.14-0.20261104174118-f94abb95aaef h1:/Jic5ho0xz32Z9DH2NSUqVxCF1KyfIOWuRTrX1aLjus=
|
||||
git.novox.be/novox/mesh-sdk/go v0.1.14-0.20261104174118-f94abb95aaef/go.mod h1:2wHG7ntLCG8Tpm/SCfgp524a5emUJm1gAC4KrRYsU9s=
|
||||
github.com/klauspost/compress v1.20.1 h1:T7kKElXUMXrUJ2E9QhQhxFtcK5rPyLdsGZvdbLMPdiQ=
|
||||
github.com/klauspost/compress v1.20.1/go.mod h1:LUdAzn7YLVvxLpc7y3V1m40wESHTgc1422pwwBSKYuI=
|
||||
github.com/nats-io/nats.go v1.54.0 h1:vsXoOxjHp/GmPUN+EcI7uOf/uB+iAP+kEsAFNQN0yzA=
|
||||
github.com/nats-io/nats.go v1.54.0/go.mod h1:y+DZoD1oBOYfZTU681eTUiUjI0vbqYGixNVFHcjHJ0k=
|
||||
github.com/nats-io/nkeys v0.4.16 h1:rd5oAuLOb8mnAycB0xleuEBNS1pVVnN0fv/FF34Eypg=
|
||||
github.com/nats-io/nkeys v0.4.16/go.mod h1:llLgWoI0o4z/Q57q2R1kHfmocyhGV6VG/U18Glg1Afs=
|
||||
github.com/nats-io/nuid v1.0.1 h1:5iA8DT8V7q8WK2EScv2padNa/rTESc1KdnPw4TC2paw=
|
||||
github.com/nats-io/nuid v1.0.1/go.mod h1:19wcPz3Ph3q0Jbyiqsd0kePYG7A95tJPxeL+1OSON2c=
|
||||
golang.org/x/crypto v0.57.0 h1:3ZVCjf8Ggz7zneR/EHRVx68Ctf+2pmIMP2UFhh9cC6M=
|
||||
golang.org/x/crypto v0.57.0/go.mod h1:Fdz0i5U6CoizGwLda9DttjSk6qlZo25zYNtR+ycvuZA=
|
||||
golang.org/x/sys v0.48.0 h1:bbX/i/6MgT9BVLM9RT1thmxL04yeTAhbEz4SyadbXoo=
|
||||
golang.org/x/sys v0.48.0/go.mod h1:hNLxWAXmnKAxqDtdwIYC4bM9oQPEecfsnNMuSxOs3og=
|
||||
@@ -0,0 +1,63 @@
|
||||
// Package npmpackageregistryclient is the npm-package-registry seat's client (novox/hq ADR 0627 §1, §2; ADR 0611, design 51
|
||||
// *Publishing*): it publishes a file in parts to the seat's holder. `publish` opens the transfer naming every part,
|
||||
// the holder answers the parts it lacks, and only those travel by `publish-part`, in chunks a bus message carries; a
|
||||
// cut transfer goes on from what the holder answers when the same transfer is opened again. The sending in parts is
|
||||
// mesh-sdk's seat-neutral runtime (parts); this client binds it to this seat's verbs and types alone. The call is the
|
||||
// caller's own (a bundle's stdio.Ask, a process's call of a seat verb), so it holds no transport.
|
||||
package npmpackageregistryclient
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/base64"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
|
||||
seat "git.novox.be/novox/mesh-delivery/contracts/go/seats/npmpackageregistry"
|
||||
"git.novox.be/novox/mesh-delivery/sdk/parts"
|
||||
)
|
||||
|
||||
// Call asks one verb of the seat, `seat:<seat>.<verb>`, with a body, and answers its JSON.
|
||||
type Call func(key string, body any) (json.RawMessage, error)
|
||||
|
||||
// Sent is what a Publish did: the transfer, whether the holder held its digest already, the parts it lacked and were
|
||||
// sent, and the chunks they took.
|
||||
type Sent struct {
|
||||
Transfer string
|
||||
parts.Sent
|
||||
}
|
||||
|
||||
// Publish opens (or opens again) the transfer in on the seat and sends the parts the holder answers it lacks, each in
|
||||
// chunks of at most parts.MaxChunk, in sequence, the last marked. in.Parts is written from parts. A part whose bytes
|
||||
// do not hash to its digest is refused before anything of it is sent; a part the holder does not name is never read.
|
||||
// Its outcome, published or refused, is the holder's event, which the caller waits for.
|
||||
func Publish(ctx context.Context, call Call, in seat.PublishInput, ps []parts.Part) (Sent, error) {
|
||||
open := func(_ context.Context, ps []parts.Part) (parts.Opened, error) {
|
||||
in.Parts = make([]seat.Part, 0, len(ps))
|
||||
for _, p := range ps {
|
||||
in.Parts = append(in.Parts, seat.Part{Digest: p.Digest, Size: p.Size})
|
||||
}
|
||||
raw, err := call("seat:"+seat.Seat+"."+seat.VerbPublish, in)
|
||||
if err != nil {
|
||||
return parts.Opened{}, fmt.Errorf("%s's publish of %s: %w", seat.Seat, in.Name, err)
|
||||
}
|
||||
var a seat.PublishAnswer
|
||||
if err := json.Unmarshal(raw, &a); err != nil {
|
||||
return parts.Opened{}, fmt.Errorf("%s's answer to publish: %w", seat.Seat, err)
|
||||
}
|
||||
return parts.Opened{Held: a.Held, Lacks: a.Lacks}, nil
|
||||
}
|
||||
chunk := func(_ context.Context, part string, sequence int, last bool, data []byte) error {
|
||||
raw, err := call("seat:"+seat.Seat+"."+seat.VerbPublishPart, seat.PublishPartInput{Transfer: in.Transfer, Part: part,
|
||||
Sequence: int64(sequence), Last: last, Data: base64.StdEncoding.EncodeToString(data)})
|
||||
if err != nil {
|
||||
return fmt.Errorf("%s's publish-part: %w", seat.Seat, err)
|
||||
}
|
||||
var a seat.PublishPartAnswer
|
||||
if err := json.Unmarshal(raw, &a); err != nil {
|
||||
return fmt.Errorf("%s's answer to publish-part: %w", seat.Seat, err)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
sent, err := parts.Send(ctx, open, chunk, ps)
|
||||
return Sent{Transfer: in.Transfer, Sent: sent}, err
|
||||
}
|
||||
@@ -0,0 +1,57 @@
|
||||
package npmpackageregistryclient
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
"crypto/sha256"
|
||||
"encoding/base64"
|
||||
"encoding/hex"
|
||||
"encoding/json"
|
||||
"io"
|
||||
"testing"
|
||||
|
||||
seat "git.novox.be/novox/mesh-delivery/contracts/go/seats/npmpackageregistry"
|
||||
"git.novox.be/novox/mesh-delivery/sdk/parts"
|
||||
)
|
||||
|
||||
// The client publishes over its own seat's verbs alone (ADR 0627 §1): publish names every part, and only the part the
|
||||
// holder lacks travels by publish-part, its bytes whole, the last chunk marked.
|
||||
func TestPublishTalksToItsOwnSeat(t *testing.T) {
|
||||
held, lacked := []byte("held"), []byte("lacked")
|
||||
part := func(b []byte) parts.Part {
|
||||
sum := sha256.Sum256(b)
|
||||
return parts.Part{Digest: "sha256:" + hex.EncodeToString(sum[:]), Size: int64(len(b)),
|
||||
Open: func() (io.ReadCloser, error) { return io.NopCloser(bytes.NewReader(b)), nil }}
|
||||
}
|
||||
ps := []parts.Part{part(held), part(lacked)}
|
||||
var asked []string
|
||||
var got []byte
|
||||
call := func(key string, body any) (json.RawMessage, error) {
|
||||
asked = append(asked, key)
|
||||
raw, _ := json.Marshal(body)
|
||||
switch key {
|
||||
case "seat:npm-package-registry.publish":
|
||||
var in seat.PublishInput
|
||||
_ = json.Unmarshal(raw, &in)
|
||||
if len(in.Parts) != 2 {
|
||||
t.Errorf("publish names %d parts, want 2", len(in.Parts))
|
||||
}
|
||||
return json.Marshal(seat.PublishAnswer{Transfer: in.Transfer, Lacks: []string{ps[1].Digest}})
|
||||
case "seat:npm-package-registry.publish-part":
|
||||
var in seat.PublishPartInput
|
||||
_ = json.Unmarshal(raw, &in)
|
||||
data, _ := base64.StdEncoding.DecodeString(in.Data)
|
||||
if in.Part != ps[1].Digest || !in.Last || in.Transfer != "t1" {
|
||||
t.Errorf("publish-part %+v", in)
|
||||
}
|
||||
got = append(got, data...)
|
||||
return json.Marshal(seat.PublishPartAnswer{Transfer: in.Transfer, Part: in.Part})
|
||||
}
|
||||
t.Fatalf("asked %s", key)
|
||||
return nil, nil
|
||||
}
|
||||
sent, err := Publish(context.Background(), call, seat.PublishInput{Transfer: "t1", Name: "x"}, ps)
|
||||
if err != nil || sent.Transfer != "t1" || sent.Chunks != 1 || len(sent.Parts) != 1 || !bytes.Equal(got, lacked) || len(asked) != 2 {
|
||||
t.Errorf("sent %+v, %q arrived, asked %v, %v", sent, got, asked, err)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,18 @@
|
||||
module git.novox.be/novox/mesh-delivery/contracts/go/clients/npmpackageregistryclient
|
||||
|
||||
go 1.26.0
|
||||
|
||||
require (
|
||||
git.novox.be/novox/mesh-delivery/contracts/go/seats/npmpackageregistry v0.0.0
|
||||
git.novox.be/novox/mesh-delivery/sdk v0.0.0
|
||||
git.novox.be/novox/mesh-sdk/go v0.1.14-0.20261104174118-f94abb95aaef
|
||||
)
|
||||
|
||||
require (
|
||||
github.com/klauspost/compress v1.20.1 // indirect
|
||||
github.com/nats-io/nats.go v1.54.0 // indirect
|
||||
github.com/nats-io/nkeys v0.4.16 // indirect
|
||||
github.com/nats-io/nuid v1.0.1 // indirect
|
||||
golang.org/x/crypto v0.57.0 // indirect
|
||||
golang.org/x/sys v0.48.0 // indirect
|
||||
)
|
||||
@@ -0,0 +1,18 @@
|
||||
git.novox.be/novox/mesh-contracts/go v0.0.0-20261104172218-347a3053580a h1:E9ERPqR4uZXypudHGxv2/wNIOYcder9torVqJwJq/DI=
|
||||
git.novox.be/novox/mesh-contracts/go v0.0.0-20261104172218-347a3053580a/go.mod h1:0dD1XEJBJ0G5UYDQxSZI84nuujb6m3tOpJl4C8rQVvg=
|
||||
git.novox.be/novox/mesh-delivery/contracts/go/seats/npmpackageregistry v0.0.0-20261105000000-000000000000 h1:JdEqNAv+s0ut6JEcCtXN5RIoGpuYg0jIVteFZSJJ36s=
|
||||
git.novox.be/novox/mesh-delivery/contracts/go/seats/npmpackageregistry v0.0.0-20261105000000-000000000000/go.mod h1:JRtTjIjMTkzn59IN4vCYei9ZwJGFnN1Puwame4V/sD0=
|
||||
git.novox.be/novox/mesh-sdk/go v0.1.14-0.20261104174118-f94abb95aaef h1:/Jic5ho0xz32Z9DH2NSUqVxCF1KyfIOWuRTrX1aLjus=
|
||||
git.novox.be/novox/mesh-sdk/go v0.1.14-0.20261104174118-f94abb95aaef/go.mod h1:2wHG7ntLCG8Tpm/SCfgp524a5emUJm1gAC4KrRYsU9s=
|
||||
github.com/klauspost/compress v1.20.1 h1:T7kKElXUMXrUJ2E9QhQhxFtcK5rPyLdsGZvdbLMPdiQ=
|
||||
github.com/klauspost/compress v1.20.1/go.mod h1:LUdAzn7YLVvxLpc7y3V1m40wESHTgc1422pwwBSKYuI=
|
||||
github.com/nats-io/nats.go v1.54.0 h1:vsXoOxjHp/GmPUN+EcI7uOf/uB+iAP+kEsAFNQN0yzA=
|
||||
github.com/nats-io/nats.go v1.54.0/go.mod h1:y+DZoD1oBOYfZTU681eTUiUjI0vbqYGixNVFHcjHJ0k=
|
||||
github.com/nats-io/nkeys v0.4.16 h1:rd5oAuLOb8mnAycB0xleuEBNS1pVVnN0fv/FF34Eypg=
|
||||
github.com/nats-io/nkeys v0.4.16/go.mod h1:llLgWoI0o4z/Q57q2R1kHfmocyhGV6VG/U18Glg1Afs=
|
||||
github.com/nats-io/nuid v1.0.1 h1:5iA8DT8V7q8WK2EScv2padNa/rTESc1KdnPw4TC2paw=
|
||||
github.com/nats-io/nuid v1.0.1/go.mod h1:19wcPz3Ph3q0Jbyiqsd0kePYG7A95tJPxeL+1OSON2c=
|
||||
golang.org/x/crypto v0.57.0 h1:3ZVCjf8Ggz7zneR/EHRVx68Ctf+2pmIMP2UFhh9cC6M=
|
||||
golang.org/x/crypto v0.57.0/go.mod h1:Fdz0i5U6CoizGwLda9DttjSk6qlZo25zYNtR+ycvuZA=
|
||||
golang.org/x/sys v0.48.0 h1:bbX/i/6MgT9BVLM9RT1thmxL04yeTAhbEz4SyadbXoo=
|
||||
golang.org/x/sys v0.48.0/go.mod h1:hNLxWAXmnKAxqDtdwIYC4bM9oQPEecfsnNMuSxOs3og=
|
||||
@@ -0,0 +1,63 @@
|
||||
// Package ociartifactregistryclient is the oci-artifact-registry seat's client (novox/hq ADR 0627 §1, §2; ADR 0611, design 51
|
||||
// *Publishing*): it publishes a file in parts to the seat's holder. `publish` opens the transfer naming every part,
|
||||
// the holder answers the parts it lacks, and only those travel by `publish-part`, in chunks a bus message carries; a
|
||||
// cut transfer goes on from what the holder answers when the same transfer is opened again. The sending in parts is
|
||||
// mesh-sdk's seat-neutral runtime (parts); this client binds it to this seat's verbs and types alone. The call is the
|
||||
// caller's own (a bundle's stdio.Ask, a process's call of a seat verb), so it holds no transport.
|
||||
package ociartifactregistryclient
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/base64"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
|
||||
seat "git.novox.be/novox/mesh-delivery/contracts/go/seats/ociartifactregistry"
|
||||
"git.novox.be/novox/mesh-delivery/sdk/parts"
|
||||
)
|
||||
|
||||
// Call asks one verb of the seat, `seat:<seat>.<verb>`, with a body, and answers its JSON.
|
||||
type Call func(key string, body any) (json.RawMessage, error)
|
||||
|
||||
// Sent is what a Publish did: the transfer, whether the holder held its digest already, the parts it lacked and were
|
||||
// sent, and the chunks they took.
|
||||
type Sent struct {
|
||||
Transfer string
|
||||
parts.Sent
|
||||
}
|
||||
|
||||
// Publish opens (or opens again) the transfer in on the seat and sends the parts the holder answers it lacks, each in
|
||||
// chunks of at most parts.MaxChunk, in sequence, the last marked. in.Parts is written from parts. A part whose bytes
|
||||
// do not hash to its digest is refused before anything of it is sent; a part the holder does not name is never read.
|
||||
// Its outcome, published or refused, is the holder's event, which the caller waits for.
|
||||
func Publish(ctx context.Context, call Call, in seat.PublishInput, ps []parts.Part) (Sent, error) {
|
||||
open := func(_ context.Context, ps []parts.Part) (parts.Opened, error) {
|
||||
in.Parts = make([]seat.Part, 0, len(ps))
|
||||
for _, p := range ps {
|
||||
in.Parts = append(in.Parts, seat.Part{Digest: p.Digest, Size: p.Size})
|
||||
}
|
||||
raw, err := call("seat:"+seat.Seat+"."+seat.VerbPublish, in)
|
||||
if err != nil {
|
||||
return parts.Opened{}, fmt.Errorf("%s's publish of %s: %w", seat.Seat, in.Name, err)
|
||||
}
|
||||
var a seat.PublishAnswer
|
||||
if err := json.Unmarshal(raw, &a); err != nil {
|
||||
return parts.Opened{}, fmt.Errorf("%s's answer to publish: %w", seat.Seat, err)
|
||||
}
|
||||
return parts.Opened{Held: a.Held, Lacks: a.Lacks}, nil
|
||||
}
|
||||
chunk := func(_ context.Context, part string, sequence int, last bool, data []byte) error {
|
||||
raw, err := call("seat:"+seat.Seat+"."+seat.VerbPublishPart, seat.PublishPartInput{Transfer: in.Transfer, Part: part,
|
||||
Sequence: int64(sequence), Last: last, Data: base64.StdEncoding.EncodeToString(data)})
|
||||
if err != nil {
|
||||
return fmt.Errorf("%s's publish-part: %w", seat.Seat, err)
|
||||
}
|
||||
var a seat.PublishPartAnswer
|
||||
if err := json.Unmarshal(raw, &a); err != nil {
|
||||
return fmt.Errorf("%s's answer to publish-part: %w", seat.Seat, err)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
sent, err := parts.Send(ctx, open, chunk, ps)
|
||||
return Sent{Transfer: in.Transfer, Sent: sent}, err
|
||||
}
|
||||
@@ -0,0 +1,57 @@
|
||||
package ociartifactregistryclient
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
"crypto/sha256"
|
||||
"encoding/base64"
|
||||
"encoding/hex"
|
||||
"encoding/json"
|
||||
"io"
|
||||
"testing"
|
||||
|
||||
seat "git.novox.be/novox/mesh-delivery/contracts/go/seats/ociartifactregistry"
|
||||
"git.novox.be/novox/mesh-delivery/sdk/parts"
|
||||
)
|
||||
|
||||
// The client publishes over its own seat's verbs alone (ADR 0627 §1): publish names every part, and only the part the
|
||||
// holder lacks travels by publish-part, its bytes whole, the last chunk marked.
|
||||
func TestPublishTalksToItsOwnSeat(t *testing.T) {
|
||||
held, lacked := []byte("held"), []byte("lacked")
|
||||
part := func(b []byte) parts.Part {
|
||||
sum := sha256.Sum256(b)
|
||||
return parts.Part{Digest: "sha256:" + hex.EncodeToString(sum[:]), Size: int64(len(b)),
|
||||
Open: func() (io.ReadCloser, error) { return io.NopCloser(bytes.NewReader(b)), nil }}
|
||||
}
|
||||
ps := []parts.Part{part(held), part(lacked)}
|
||||
var asked []string
|
||||
var got []byte
|
||||
call := func(key string, body any) (json.RawMessage, error) {
|
||||
asked = append(asked, key)
|
||||
raw, _ := json.Marshal(body)
|
||||
switch key {
|
||||
case "seat:oci-artifact-registry.publish":
|
||||
var in seat.PublishInput
|
||||
_ = json.Unmarshal(raw, &in)
|
||||
if len(in.Parts) != 2 {
|
||||
t.Errorf("publish names %d parts, want 2", len(in.Parts))
|
||||
}
|
||||
return json.Marshal(seat.PublishAnswer{Transfer: in.Transfer, Lacks: []string{ps[1].Digest}})
|
||||
case "seat:oci-artifact-registry.publish-part":
|
||||
var in seat.PublishPartInput
|
||||
_ = json.Unmarshal(raw, &in)
|
||||
data, _ := base64.StdEncoding.DecodeString(in.Data)
|
||||
if in.Part != ps[1].Digest || !in.Last || in.Transfer != "t1" {
|
||||
t.Errorf("publish-part %+v", in)
|
||||
}
|
||||
got = append(got, data...)
|
||||
return json.Marshal(seat.PublishPartAnswer{Transfer: in.Transfer, Part: in.Part})
|
||||
}
|
||||
t.Fatalf("asked %s", key)
|
||||
return nil, nil
|
||||
}
|
||||
sent, err := Publish(context.Background(), call, seat.PublishInput{Transfer: "t1", Name: "x"}, ps)
|
||||
if err != nil || sent.Transfer != "t1" || sent.Chunks != 1 || len(sent.Parts) != 1 || !bytes.Equal(got, lacked) || len(asked) != 2 {
|
||||
t.Errorf("sent %+v, %q arrived, asked %v, %v", sent, got, asked, err)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,18 @@
|
||||
module git.novox.be/novox/mesh-delivery/contracts/go/clients/ociartifactregistryclient
|
||||
|
||||
go 1.26.0
|
||||
|
||||
require (
|
||||
git.novox.be/novox/mesh-delivery/contracts/go/seats/ociartifactregistry v0.0.0
|
||||
git.novox.be/novox/mesh-delivery/sdk v0.0.0
|
||||
git.novox.be/novox/mesh-sdk/go v0.1.14-0.20261104174118-f94abb95aaef
|
||||
)
|
||||
|
||||
require (
|
||||
github.com/klauspost/compress v1.20.1 // indirect
|
||||
github.com/nats-io/nats.go v1.54.0 // indirect
|
||||
github.com/nats-io/nkeys v0.4.16 // indirect
|
||||
github.com/nats-io/nuid v1.0.1 // indirect
|
||||
golang.org/x/crypto v0.57.0 // indirect
|
||||
golang.org/x/sys v0.48.0 // indirect
|
||||
)
|
||||
@@ -0,0 +1,18 @@
|
||||
git.novox.be/novox/mesh-contracts/go v0.0.0-20261104172218-347a3053580a h1:E9ERPqR4uZXypudHGxv2/wNIOYcder9torVqJwJq/DI=
|
||||
git.novox.be/novox/mesh-contracts/go v0.0.0-20261104172218-347a3053580a/go.mod h1:0dD1XEJBJ0G5UYDQxSZI84nuujb6m3tOpJl4C8rQVvg=
|
||||
git.novox.be/novox/mesh-delivery/contracts/go/seats/ociartifactregistry v0.0.0-20261105000000-000000000000 h1:fhQ+q40fnPwuBBU+7Bpv13ktzoPxHtQiO6mpHR9oVoo=
|
||||
git.novox.be/novox/mesh-delivery/contracts/go/seats/ociartifactregistry v0.0.0-20261105000000-000000000000/go.mod h1:ooQa2CiP8JOvhP0bdQvwurV5wskRE7Cm68ecURBbCwU=
|
||||
git.novox.be/novox/mesh-sdk/go v0.1.14-0.20261104174118-f94abb95aaef h1:/Jic5ho0xz32Z9DH2NSUqVxCF1KyfIOWuRTrX1aLjus=
|
||||
git.novox.be/novox/mesh-sdk/go v0.1.14-0.20261104174118-f94abb95aaef/go.mod h1:2wHG7ntLCG8Tpm/SCfgp524a5emUJm1gAC4KrRYsU9s=
|
||||
github.com/klauspost/compress v1.20.1 h1:T7kKElXUMXrUJ2E9QhQhxFtcK5rPyLdsGZvdbLMPdiQ=
|
||||
github.com/klauspost/compress v1.20.1/go.mod h1:LUdAzn7YLVvxLpc7y3V1m40wESHTgc1422pwwBSKYuI=
|
||||
github.com/nats-io/nats.go v1.54.0 h1:vsXoOxjHp/GmPUN+EcI7uOf/uB+iAP+kEsAFNQN0yzA=
|
||||
github.com/nats-io/nats.go v1.54.0/go.mod h1:y+DZoD1oBOYfZTU681eTUiUjI0vbqYGixNVFHcjHJ0k=
|
||||
github.com/nats-io/nkeys v0.4.16 h1:rd5oAuLOb8mnAycB0xleuEBNS1pVVnN0fv/FF34Eypg=
|
||||
github.com/nats-io/nkeys v0.4.16/go.mod h1:llLgWoI0o4z/Q57q2R1kHfmocyhGV6VG/U18Glg1Afs=
|
||||
github.com/nats-io/nuid v1.0.1 h1:5iA8DT8V7q8WK2EScv2padNa/rTESc1KdnPw4TC2paw=
|
||||
github.com/nats-io/nuid v1.0.1/go.mod h1:19wcPz3Ph3q0Jbyiqsd0kePYG7A95tJPxeL+1OSON2c=
|
||||
golang.org/x/crypto v0.57.0 h1:3ZVCjf8Ggz7zneR/EHRVx68Ctf+2pmIMP2UFhh9cC6M=
|
||||
golang.org/x/crypto v0.57.0/go.mod h1:Fdz0i5U6CoizGwLda9DttjSk6qlZo25zYNtR+ycvuZA=
|
||||
golang.org/x/sys v0.48.0 h1:bbX/i/6MgT9BVLM9RT1thmxL04yeTAhbEz4SyadbXoo=
|
||||
golang.org/x/sys v0.48.0/go.mod h1:hNLxWAXmnKAxqDtdwIYC4bM9oQPEecfsnNMuSxOs3og=
|
||||
@@ -0,0 +1,528 @@
|
||||
// Code generated by seatgen from the seat definitions; DO NOT EDIT.
|
||||
|
||||
// Package containerimageregistry is the contract of the seat "container-image-registry", defined whole (seats/container-image-registry/seat.json): The mesh's container images, in the one artifact store: the OCI distribution protocol's read side, an image's tags and mirroring an outside image.
|
||||
package containerimageregistry
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"strings"
|
||||
)
|
||||
|
||||
// Seat is the seat's name.
|
||||
const Seat = "container-image-registry"
|
||||
|
||||
const (
|
||||
VerbPublish = "publish"
|
||||
VerbPublishPart = "publish-part"
|
||||
VerbVersions = "versions"
|
||||
VerbRemove = "remove"
|
||||
VerbMirror = "mirror"
|
||||
VerbTags = "tags"
|
||||
VerbHold = "hold"
|
||||
VerbRelease = "release"
|
||||
VerbList = "list"
|
||||
)
|
||||
|
||||
const (
|
||||
EventPublished = "published"
|
||||
EventRefused = "refused"
|
||||
EventRemoved = "removed"
|
||||
EventKept = "kept"
|
||||
EventHeld = "held"
|
||||
EventReleased = "released"
|
||||
)
|
||||
|
||||
// Verbs is every verb the seat serves, in its definition's order: a new copy each call.
|
||||
func Verbs() []string {
|
||||
return []string{VerbPublish, VerbPublishPart, VerbVersions, VerbRemove, VerbMirror, VerbTags, VerbHold, VerbRelease, VerbList}
|
||||
}
|
||||
|
||||
// Events is every event published on the seat's subject: a new copy each call.
|
||||
func Events() []string {
|
||||
return []string{EventPublished, EventRefused, EventRemoved, EventKept, EventHeld, EventReleased}
|
||||
}
|
||||
|
||||
// Family is where the seat's events are kept: the shared events stream, filtered to the seat's subjects.
|
||||
const (
|
||||
Family = "container-image-registry.events"
|
||||
FamilyMaxAgeDays = 7
|
||||
)
|
||||
|
||||
// SubjectPublished is where published is published.
|
||||
const SubjectPublished = "mesh.seat.container-image-registry.event.published"
|
||||
|
||||
// SubjectRefused is where refused is published.
|
||||
const SubjectRefused = "mesh.seat.container-image-registry.event.refused"
|
||||
|
||||
// SubjectRemoved is where removed is published.
|
||||
const SubjectRemoved = "mesh.seat.container-image-registry.event.removed"
|
||||
|
||||
// SubjectKept is where kept is published.
|
||||
const SubjectKept = "mesh.seat.container-image-registry.event.kept"
|
||||
|
||||
// SubjectHeld is where held is published.
|
||||
const SubjectHeld = "mesh.seat.container-image-registry.event.held"
|
||||
|
||||
// SubjectReleased is where released is published.
|
||||
const SubjectReleased = "mesh.seat.container-image-registry.event.released"
|
||||
|
||||
const (
|
||||
StateHolds = "holds"
|
||||
StateWindows = "windows"
|
||||
)
|
||||
|
||||
// HoldsBySeatNameDigestKey is the key of one value of the state holds under the form "<seat>.<name>.<digest>".
|
||||
func HoldsBySeatNameDigestKey(seat, name, digest string) string {
|
||||
return seat + "." + name + "." + digest
|
||||
}
|
||||
|
||||
// ParseHoldsBySeatNameDigestKey reads the parts of a key of the form "<seat>.<name>.<digest>", in the order of the form; the last result is false for a key of another form.
|
||||
func ParseHoldsBySeatNameDigestKey(key string) (string, string, string, bool) {
|
||||
rest := key
|
||||
v0, after0, found0 := strings.Cut(rest, ".")
|
||||
if !found0 || v0 == "" {
|
||||
return "", "", "", false
|
||||
}
|
||||
rest = after0
|
||||
v2, after2, found2 := strings.Cut(rest, ".")
|
||||
if !found2 || v2 == "" {
|
||||
return "", "", "", false
|
||||
}
|
||||
rest = after2
|
||||
if rest == "" {
|
||||
return "", "", "", false
|
||||
}
|
||||
v4 := rest
|
||||
rest = ""
|
||||
if rest != "" {
|
||||
return "", "", "", false
|
||||
}
|
||||
return v0, v2, v4, true
|
||||
}
|
||||
|
||||
// DecodeHoldsBySeatNameDigestValue reads one value of the state holds under the form "<seat>.<name>.<digest>", refusing a field HoldsBySeatNameDigestValue does not know and any text after the value.
|
||||
func DecodeHoldsBySeatNameDigestValue(raw []byte) (HoldsBySeatNameDigestValue, error) {
|
||||
var v HoldsBySeatNameDigestValue
|
||||
dec := json.NewDecoder(bytes.NewReader(raw))
|
||||
dec.DisallowUnknownFields()
|
||||
if err := dec.Decode(&v); err != nil {
|
||||
return v, err
|
||||
}
|
||||
if dec.More() {
|
||||
return v, errors.New("text after the value")
|
||||
}
|
||||
return v, nil
|
||||
}
|
||||
|
||||
// WindowsByCallerKey is the key of one value of the state windows under the form "<caller>".
|
||||
func WindowsByCallerKey(caller string) string { return caller }
|
||||
|
||||
// ParseWindowsByCallerKey reads the parts of a key of the form "<caller>", in the order of the form; the last result is false for a key of another form.
|
||||
func ParseWindowsByCallerKey(key string) (string, bool) {
|
||||
rest := key
|
||||
if rest == "" {
|
||||
return "", false
|
||||
}
|
||||
v0 := rest
|
||||
rest = ""
|
||||
if rest != "" {
|
||||
return "", false
|
||||
}
|
||||
return v0, true
|
||||
}
|
||||
|
||||
// DecodeWindowsByCallerValue reads one value of the state windows under the form "<caller>", refusing a field WindowsByCallerValue does not know and any text after the value.
|
||||
func DecodeWindowsByCallerValue(raw []byte) (WindowsByCallerValue, error) {
|
||||
var v WindowsByCallerValue
|
||||
dec := json.NewDecoder(bytes.NewReader(raw))
|
||||
dec.DisallowUnknownFields()
|
||||
if err := dec.Decode(&v); err != nil {
|
||||
return v, err
|
||||
}
|
||||
if dec.More() {
|
||||
return v, errors.New("text after the value")
|
||||
}
|
||||
return v, nil
|
||||
}
|
||||
|
||||
// Artifact is one published thing, as every registry event and answer names it.
|
||||
type Artifact struct {
|
||||
// the source commit it was built from
|
||||
Commit string `json:"commit,omitempty"`
|
||||
// its digest, sha256:<hex> (a Go package's h1: hash beside it in the version's record)
|
||||
Digest string `json:"digest"`
|
||||
// the build's fingerprint
|
||||
Fingerprint string `json:"fingerprint,omitempty"`
|
||||
// what it is
|
||||
Kind string `json:"kind"`
|
||||
// the package's, image's, bundle's or archive's name; a package's is its name in its own language (design 32): a Go package's the go.mod module path (git.novox.be/novox/mesh-sdk/go), an npm package's package.json's name (@novox/mesh-sdk)
|
||||
Name string `json:"name"`
|
||||
// the tag it carries, for a tagged version
|
||||
Tag string `json:"tag,omitempty"`
|
||||
// the version the builder gave it (design 51 *The versions*); empty for a bundle or archive published by its digest alone
|
||||
Version string `json:"version,omitempty"`
|
||||
}
|
||||
|
||||
// The values of Artifact's kind.
|
||||
const (
|
||||
ArtifactKindPackage = "package"
|
||||
ArtifactKindImage = "image"
|
||||
ArtifactKindBundle = "bundle"
|
||||
ArtifactKindArchive = "archive"
|
||||
ArtifactKindUpstream = "upstream"
|
||||
)
|
||||
|
||||
// Hold is the body of held and released.
|
||||
type Hold struct {
|
||||
// when, RFC 3339
|
||||
At string `json:"at,omitempty"`
|
||||
// the caller the holder proved
|
||||
By string `json:"by,omitempty"`
|
||||
// the digest
|
||||
Digest string `json:"digest"`
|
||||
// the module holding the seat
|
||||
Holder string `json:"holder"`
|
||||
// the name
|
||||
Name string `json:"name"`
|
||||
// why
|
||||
Reason string `json:"reason,omitempty"`
|
||||
}
|
||||
|
||||
// Listed is a type of the contract.
|
||||
type Listed struct {
|
||||
// for a manifest keeping one bundle or archive, the digest of that blob: what a reference names it by
|
||||
Artifact string `json:"artifact,omitempty"`
|
||||
// the manifest's digest, sha256:<hex>
|
||||
Digest string `json:"digest"`
|
||||
// a hold keeps it from retention
|
||||
Held bool `json:"held"`
|
||||
// it is an index, naming a manifest per platform
|
||||
Index bool `json:"index"`
|
||||
// for an index, the digests of the manifests it names
|
||||
Platforms []string `json:"platforms,omitempty"`
|
||||
// its bytes: its own content and every blob it names
|
||||
Size int64 `json:"size"`
|
||||
// the tags naming it, none for an untagged one
|
||||
Tags []string `json:"tags,omitempty"`
|
||||
}
|
||||
|
||||
// Outcome is the body of published, refused and removed.
|
||||
type Outcome struct {
|
||||
// the source commit it was built from
|
||||
Commit string `json:"commit,omitempty"`
|
||||
// its digest, sha256:<hex> (a Go package's h1: hash beside it in the version's record)
|
||||
Digest string `json:"digest"`
|
||||
// the build's fingerprint
|
||||
Fingerprint string `json:"fingerprint,omitempty"`
|
||||
// the module holding the seat that said it
|
||||
Holder string `json:"holder"`
|
||||
// what it is
|
||||
Kind string `json:"kind"`
|
||||
// the Go module path a Go package is fetched under, equal to name for a Go package (go-package-registry's published); empty for every other kind of artifact
|
||||
Module string `json:"module,omitempty"`
|
||||
// the package's, image's, bundle's or archive's name; a package's is its name in its own language (design 32): a Go package's the go.mod module path (git.novox.be/novox/mesh-sdk/go), an npm package's package.json's name (@novox/mesh-sdk)
|
||||
Name string `json:"name"`
|
||||
// why: a refusal's cause, a removal's (retention, tag-gone, by-hand), or why a removal was kept (shared-digest)
|
||||
Reason string `json:"reason,omitempty"`
|
||||
// the reference the seat answers: <registry>/<module>/<artifact>@sha256:<digest>, or <name>@<version> for a package (ADR 0605 §9)
|
||||
Reference string `json:"reference,omitempty"`
|
||||
// the tag it carries, for a tagged version
|
||||
Tag string `json:"tag,omitempty"`
|
||||
// for kept: the other tags still naming its digest
|
||||
Tags []string `json:"tags,omitempty"`
|
||||
// the transfer it ends, when it ended one
|
||||
Transfer string `json:"transfer,omitempty"`
|
||||
// the version the builder gave it (design 51 *The versions*); empty for a bundle or archive published by its digest alone
|
||||
Version string `json:"version,omitempty"`
|
||||
}
|
||||
|
||||
// The values of Outcome's kind.
|
||||
const (
|
||||
OutcomeKindPackage = "package"
|
||||
OutcomeKindImage = "image"
|
||||
OutcomeKindBundle = "bundle"
|
||||
OutcomeKindArchive = "archive"
|
||||
OutcomeKindUpstream = "upstream"
|
||||
)
|
||||
|
||||
// Part is one part of a transfer.
|
||||
type Part struct {
|
||||
// the part's digest, sha256:<hex>: an image's layer or config, a package's file, a bundle's blob
|
||||
Digest string `json:"digest"`
|
||||
// its size in bytes
|
||||
Size int64 `json:"size"`
|
||||
}
|
||||
|
||||
// Repository is a type of the contract.
|
||||
type Repository struct {
|
||||
// each manifest it holds, by digest
|
||||
Manifests []Listed `json:"manifests"`
|
||||
// the repository
|
||||
Name string `json:"name"`
|
||||
}
|
||||
|
||||
// PublishInput is what publish takes.
|
||||
type PublishInput struct {
|
||||
// the source commit it was built from
|
||||
Commit string `json:"commit,omitempty"`
|
||||
// its digest, sha256:<hex> (a Go package's h1: hash beside it in the version's record)
|
||||
Digest string `json:"digest"`
|
||||
// the build's fingerprint
|
||||
Fingerprint string `json:"fingerprint,omitempty"`
|
||||
// what it is
|
||||
Kind string `json:"kind"`
|
||||
// the package's, image's, bundle's or archive's name; a package's is its name in its own language (design 32): a Go package's the go.mod module path (git.novox.be/novox/mesh-sdk/go), an npm package's package.json's name (@novox/mesh-sdk)
|
||||
Name string `json:"name"`
|
||||
// every part's digest and size; none when the digest is already held
|
||||
Parts []Part `json:"parts,omitempty"`
|
||||
// the tag it carries, for a tagged version
|
||||
Tag string `json:"tag,omitempty"`
|
||||
// the transfer's name, the caller's own; the same name opens the same transfer again
|
||||
Transfer string `json:"transfer"`
|
||||
// the version the builder gave it (design 51 *The versions*); empty for a bundle or archive published by its digest alone
|
||||
Version string `json:"version,omitempty"`
|
||||
}
|
||||
|
||||
// The values of PublishInput's kind.
|
||||
const (
|
||||
PublishInputKindPackage = "package"
|
||||
PublishInputKindImage = "image"
|
||||
PublishInputKindBundle = "bundle"
|
||||
PublishInputKindArchive = "archive"
|
||||
PublishInputKindUpstream = "upstream"
|
||||
)
|
||||
|
||||
// PublishAnswer is what publish answers.
|
||||
type PublishAnswer struct {
|
||||
// the digest is held already: nothing to send; the outcome follows as an event
|
||||
Held bool `json:"held,omitempty"`
|
||||
// the digests of the parts the holder lacks, to send by publish-part
|
||||
Lacks []string `json:"lacks,omitempty"`
|
||||
// the transfer
|
||||
Transfer string `json:"transfer"`
|
||||
}
|
||||
|
||||
// PublishPartInput is what publish-part takes.
|
||||
type PublishPartInput struct {
|
||||
// the chunk's bytes, base64 (at most 760 KiB before encoding)
|
||||
Data string `json:"data"`
|
||||
// this chunk is the part's last
|
||||
Last bool `json:"last"`
|
||||
// the part's digest
|
||||
Part string `json:"part"`
|
||||
// the chunk's place in its part, from 0
|
||||
Sequence int64 `json:"sequence"`
|
||||
// the transfer
|
||||
Transfer string `json:"transfer"`
|
||||
}
|
||||
|
||||
// PublishPartAnswer is what publish-part answers.
|
||||
type PublishPartAnswer struct {
|
||||
// the part is whole and its bytes match its digest
|
||||
Complete bool `json:"complete,omitempty"`
|
||||
// the part
|
||||
Part string `json:"part"`
|
||||
// the chunks of the part held so far
|
||||
Received int64 `json:"received,omitempty"`
|
||||
// the transfer
|
||||
Transfer string `json:"transfer"`
|
||||
}
|
||||
|
||||
// VersionsInput is what versions takes.
|
||||
type VersionsInput struct {
|
||||
// narrowed to one kind (optional)
|
||||
Kind string `json:"kind,omitempty"`
|
||||
// the name
|
||||
Name string `json:"name"`
|
||||
}
|
||||
|
||||
// VersionsAnswer is what versions answers.
|
||||
type VersionsAnswer struct {
|
||||
// the name
|
||||
Name string `json:"name"`
|
||||
// newest first
|
||||
Versions []Artifact `json:"versions"`
|
||||
}
|
||||
|
||||
// RemoveInput is what remove takes.
|
||||
type RemoveInput struct {
|
||||
// the digest (for a bundle, archive or copied base)
|
||||
Digest string `json:"digest,omitempty"`
|
||||
// the name
|
||||
Name string `json:"name"`
|
||||
// why it goes
|
||||
Reason string `json:"reason"`
|
||||
// the tag (for a tagged version)
|
||||
Tag string `json:"tag,omitempty"`
|
||||
// the version
|
||||
Version string `json:"version,omitempty"`
|
||||
}
|
||||
|
||||
// The values of RemoveInput's reason.
|
||||
const (
|
||||
RemoveInputReasonTagGone = "tag-gone"
|
||||
RemoveInputReasonRetention = "retention"
|
||||
)
|
||||
|
||||
// RemoveAnswer is what remove answers.
|
||||
type RemoveAnswer struct {
|
||||
Artifact *Artifact `json:"artifact,omitempty"`
|
||||
// it stays whole, its tag and its bytes: the store cannot remove the tag alone while another tag names its digest (reason, tags)
|
||||
Kept bool `json:"kept,omitempty"`
|
||||
// why it was kept: shared-digest, another tag names its digest
|
||||
Reason string `json:"reason,omitempty"`
|
||||
// it is gone
|
||||
Removed bool `json:"removed"`
|
||||
// the other tags still naming its digest, when kept
|
||||
Tags []string `json:"tags,omitempty"`
|
||||
}
|
||||
|
||||
// The values of RemoveAnswer's reason.
|
||||
const (
|
||||
RemoveAnswerReasonSharedDigest = "shared-digest"
|
||||
)
|
||||
|
||||
// MirrorInput is what mirror takes.
|
||||
type MirrorInput struct {
|
||||
// its pinned digest, sha256:<hex>
|
||||
Digest string `json:"digest"`
|
||||
// the name it is kept under
|
||||
Name string `json:"name"`
|
||||
// the outside image's reference
|
||||
Source string `json:"source"`
|
||||
}
|
||||
|
||||
// MirrorAnswer is what mirror answers.
|
||||
type MirrorAnswer struct {
|
||||
// the digest stored
|
||||
Digest string `json:"digest"`
|
||||
// the reference the seat answers
|
||||
Reference string `json:"reference"`
|
||||
}
|
||||
|
||||
// TagsInput is what tags takes.
|
||||
type TagsInput struct {
|
||||
// the image
|
||||
Name string `json:"name"`
|
||||
}
|
||||
|
||||
// TagsAnswerTagsItem is a type of the contract.
|
||||
type TagsAnswerTagsItem struct {
|
||||
// its digest
|
||||
Digest string `json:"digest"`
|
||||
// the tag
|
||||
Tag string `json:"tag"`
|
||||
}
|
||||
|
||||
// TagsAnswer is what tags answers.
|
||||
type TagsAnswer struct {
|
||||
// the image
|
||||
Name string `json:"name"`
|
||||
// every tag
|
||||
Tags []TagsAnswerTagsItem `json:"tags"`
|
||||
}
|
||||
|
||||
// HoldInput is what hold takes.
|
||||
type HoldInput struct {
|
||||
// the digest
|
||||
Digest string `json:"digest"`
|
||||
// the name
|
||||
Name string `json:"name"`
|
||||
// which of the sweep's rules keeps it
|
||||
Reason string `json:"reason"`
|
||||
}
|
||||
|
||||
// HoldAnswer is what hold answers.
|
||||
type HoldAnswer struct {
|
||||
// it is held
|
||||
Held bool `json:"held"`
|
||||
// when the hold began, RFC 3339
|
||||
Since string `json:"since,omitempty"`
|
||||
}
|
||||
|
||||
// ReleaseInput is what release takes.
|
||||
type ReleaseInput struct {
|
||||
// the digest
|
||||
Digest string `json:"digest"`
|
||||
// the name
|
||||
Name string `json:"name"`
|
||||
// why it is let go
|
||||
Reason string `json:"reason"`
|
||||
}
|
||||
|
||||
// ReleaseAnswer is what release answers.
|
||||
type ReleaseAnswer struct {
|
||||
// a hold was let go; false when there was none
|
||||
Released bool `json:"released"`
|
||||
}
|
||||
|
||||
// ListInput is what list takes.
|
||||
type ListInput struct {
|
||||
// the repository, <module>/<artifact>; every repository of the seat when not given
|
||||
Name string `json:"name,omitempty"`
|
||||
}
|
||||
|
||||
// ListAnswer is what list answers.
|
||||
type ListAnswer struct {
|
||||
// each repository named, in order of its name
|
||||
Repositories []Repository `json:"repositories"`
|
||||
}
|
||||
|
||||
// PublishedEvent is the body of the event published.
|
||||
type PublishedEvent = Outcome
|
||||
|
||||
// RefusedEvent is the body of the event refused.
|
||||
type RefusedEvent = Outcome
|
||||
|
||||
// RemovedEvent is the body of the event removed.
|
||||
type RemovedEvent = Outcome
|
||||
|
||||
// KeptEvent is the body of the event kept.
|
||||
type KeptEvent = Outcome
|
||||
|
||||
// HeldEvent is the body of the event held.
|
||||
type HeldEvent = Hold
|
||||
|
||||
// ReleasedEvent is the body of the event released.
|
||||
type ReleasedEvent = Hold
|
||||
|
||||
// HoldsBySeatNameDigestValue is one value of the state holds under the key <seat>.<name>.<digest>.
|
||||
type HoldsBySeatNameDigestValue struct {
|
||||
By string `json:"by"`
|
||||
Digest string `json:"digest"`
|
||||
Kind string `json:"kind"`
|
||||
Name string `json:"name"`
|
||||
Seat string `json:"seat"`
|
||||
// when (RFC 3339 time)
|
||||
Since string `json:"since"`
|
||||
Why string `json:"why,omitempty"`
|
||||
}
|
||||
|
||||
// WindowsByCallerValueDroppedItem is a type of the contract.
|
||||
type WindowsByCallerValueDroppedItem struct {
|
||||
// when (RFC 3339 time)
|
||||
At string `json:"at,omitempty"`
|
||||
By string `json:"by,omitempty"`
|
||||
Node string `json:"node"`
|
||||
Why string `json:"why,omitempty"`
|
||||
}
|
||||
|
||||
// WindowsByCallerValue is one value of the state windows under the key <caller>.
|
||||
type WindowsByCallerValue struct {
|
||||
// when it last closed (RFC 3339 time)
|
||||
Closed string `json:"closed,omitempty"`
|
||||
// retired nodes dropped by hand
|
||||
Dropped []WindowsByCallerValueDroppedItem `json:"dropped,omitempty"`
|
||||
// the nodes holding it open
|
||||
Holding []string `json:"holding,omitempty"`
|
||||
LastHolding []string `json:"last_holding,omitempty"`
|
||||
// the caller whose window it is
|
||||
Name string `json:"name"`
|
||||
Open bool `json:"open,omitempty"`
|
||||
// node to when it began to hold
|
||||
Since map[string]string `json:"since,omitempty"`
|
||||
// of each node holding the window, why it holds it
|
||||
Why map[string]string `json:"why,omitempty"`
|
||||
}
|
||||
@@ -0,0 +1,82 @@
|
||||
// Code generated by seatgen from the seat definitions; DO NOT EDIT.
|
||||
|
||||
package containerimageregistry
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"reflect"
|
||||
"regexp"
|
||||
"testing"
|
||||
)
|
||||
|
||||
// TestFixtures reads every fixture of seats/container-image-registry/fixtures: each decodes into its type, a field the type does not
|
||||
// know is dropped (ADR 0608 §6), and it reads back as its wire says; each subject named is the contract's.
|
||||
func TestFixtures(t *testing.T) {
|
||||
cases := []struct {
|
||||
name, subject, given, wire string
|
||||
decode func([]byte) (any, error)
|
||||
subjectIs *regexp.Regexp
|
||||
}{
|
||||
{name: "published on the seat's subject", subject: "mesh.seat.container-image-registry.event.published", given: "{\"commit\":\"cccccccccccccccccccccccccccccccccccccccc\",\"digest\":\"sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\",\"fingerprint\":\"f1\",\"holder\":\"distribution\",\"kind\":\"image\",\"name\":\"mesh-sdk\",\"reference\":\"container-image-registry/mesh-sdk/lib@sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\",\"transfer\":\"t1\",\"version\":\"0.1.19\"}", wire: "{\"commit\":\"cccccccccccccccccccccccccccccccccccccccc\",\"digest\":\"sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\",\"fingerprint\":\"f1\",\"holder\":\"distribution\",\"kind\":\"image\",\"name\":\"mesh-sdk\",\"reference\":\"container-image-registry/mesh-sdk/lib@sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\",\"transfer\":\"t1\",\"version\":\"0.1.19\"}",
|
||||
decode: func(b []byte) (any, error) { var v PublishedEvent; err := json.Unmarshal(b, &v); return v, err }, subjectIs: regexp.MustCompile("^mesh\\.seat\\.container-image-registry\\.event\\.published$")},
|
||||
{name: "refused on the seat's subject", subject: "mesh.seat.container-image-registry.event.refused", given: "{\"commit\":\"cccccccccccccccccccccccccccccccccccccccc\",\"digest\":\"sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\",\"fingerprint\":\"f1\",\"holder\":\"distribution\",\"kind\":\"image\",\"name\":\"mesh-sdk\",\"reason\":\"retention\",\"reference\":\"container-image-registry/mesh-sdk/lib@sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\",\"transfer\":\"t1\",\"version\":\"0.1.19\"}", wire: "{\"commit\":\"cccccccccccccccccccccccccccccccccccccccc\",\"digest\":\"sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\",\"fingerprint\":\"f1\",\"holder\":\"distribution\",\"kind\":\"image\",\"name\":\"mesh-sdk\",\"reason\":\"retention\",\"reference\":\"container-image-registry/mesh-sdk/lib@sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\",\"transfer\":\"t1\",\"version\":\"0.1.19\"}",
|
||||
decode: func(b []byte) (any, error) { var v RefusedEvent; err := json.Unmarshal(b, &v); return v, err }, subjectIs: regexp.MustCompile("^mesh\\.seat\\.container-image-registry\\.event\\.refused$")},
|
||||
{name: "removed on the seat's subject", subject: "mesh.seat.container-image-registry.event.removed", given: "{\"commit\":\"cccccccccccccccccccccccccccccccccccccccc\",\"digest\":\"sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\",\"fingerprint\":\"f1\",\"holder\":\"distribution\",\"kind\":\"image\",\"name\":\"mesh-sdk\",\"reason\":\"retention\",\"reference\":\"container-image-registry/mesh-sdk/lib@sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\",\"transfer\":\"t1\",\"version\":\"0.1.19\"}", wire: "{\"commit\":\"cccccccccccccccccccccccccccccccccccccccc\",\"digest\":\"sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\",\"fingerprint\":\"f1\",\"holder\":\"distribution\",\"kind\":\"image\",\"name\":\"mesh-sdk\",\"reason\":\"retention\",\"reference\":\"container-image-registry/mesh-sdk/lib@sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\",\"transfer\":\"t1\",\"version\":\"0.1.19\"}",
|
||||
decode: func(b []byte) (any, error) { var v RemovedEvent; err := json.Unmarshal(b, &v); return v, err }, subjectIs: regexp.MustCompile("^mesh\\.seat\\.container-image-registry\\.event\\.removed$")},
|
||||
{name: "kept on the seat's subject: a tagged version sharing its digest stays, naming the tags on it", subject: "mesh.seat.container-image-registry.event.kept", given: "{\"commit\":\"cccccccccccccccccccccccccccccccccccccccc\",\"digest\":\"sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\",\"fingerprint\":\"f1\",\"holder\":\"distribution\",\"kind\":\"image\",\"name\":\"mesh-sdk\",\"reason\":\"shared-digest\",\"reference\":\"container-image-registry/mesh-sdk/lib@sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\",\"tag\":\"0.1.19-feat-x.2\",\"tags\":[\"0.1.19\"],\"version\":\"0.1.19-feat-x.2\"}", wire: "{\"commit\":\"cccccccccccccccccccccccccccccccccccccccc\",\"digest\":\"sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\",\"fingerprint\":\"f1\",\"holder\":\"distribution\",\"kind\":\"image\",\"name\":\"mesh-sdk\",\"reason\":\"shared-digest\",\"reference\":\"container-image-registry/mesh-sdk/lib@sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\",\"tag\":\"0.1.19-feat-x.2\",\"tags\":[\"0.1.19\"],\"version\":\"0.1.19-feat-x.2\"}",
|
||||
decode: func(b []byte) (any, error) { var v KeptEvent; err := json.Unmarshal(b, &v); return v, err }, subjectIs: regexp.MustCompile("^mesh\\.seat\\.container-image-registry\\.event\\.kept$")},
|
||||
{name: "held on the seat's subject", subject: "mesh.seat.container-image-registry.event.held", given: "{\"at\":\"2026-11-04T12:00:00Z\",\"by\":\"mesh-artifact-sweep\",\"digest\":\"sha256:bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb\",\"holder\":\"distribution\",\"name\":\"mesh-sdk\",\"reason\":\"kept by the sweep's rule\"}", wire: "{\"at\":\"2026-11-04T12:00:00Z\",\"by\":\"mesh-artifact-sweep\",\"digest\":\"sha256:bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb\",\"holder\":\"distribution\",\"name\":\"mesh-sdk\",\"reason\":\"kept by the sweep's rule\"}",
|
||||
decode: func(b []byte) (any, error) { var v HeldEvent; err := json.Unmarshal(b, &v); return v, err }, subjectIs: regexp.MustCompile("^mesh\\.seat\\.container-image-registry\\.event\\.held$")},
|
||||
{name: "released on the seat's subject", subject: "mesh.seat.container-image-registry.event.released", given: "{\"at\":\"2026-11-04T12:00:00Z\",\"by\":\"mesh-artifact-sweep\",\"digest\":\"sha256:bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb\",\"holder\":\"distribution\",\"name\":\"mesh-sdk\",\"reason\":\"kept by the sweep's rule\"}", wire: "{\"at\":\"2026-11-04T12:00:00Z\",\"by\":\"mesh-artifact-sweep\",\"digest\":\"sha256:bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb\",\"holder\":\"distribution\",\"name\":\"mesh-sdk\",\"reason\":\"kept by the sweep's rule\"}",
|
||||
decode: func(b []byte) (any, error) { var v ReleasedEvent; err := json.Unmarshal(b, &v); return v, err }, subjectIs: regexp.MustCompile("^mesh\\.seat\\.container-image-registry\\.event\\.released$")},
|
||||
{name: "a hold", subject: "", given: "{\"by\":\"operator@shanks\",\"digest\":\"sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\",\"kind\":\"base\",\"name\":\"base\",\"seat\":\"container-image-registry\",\"since\":\"2026-11-05T10:00:00Z\"}", wire: "{\"by\":\"operator@shanks\",\"digest\":\"sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\",\"kind\":\"base\",\"name\":\"base\",\"seat\":\"container-image-registry\",\"since\":\"2026-11-05T10:00:00Z\"}",
|
||||
decode: func(b []byte) (any, error) {
|
||||
var v HoldsBySeatNameDigestValue
|
||||
err := json.Unmarshal(b, &v)
|
||||
return v, err
|
||||
}, subjectIs: nil},
|
||||
{name: "an open window", subject: "", given: "{\"holding\":[\"ace\"],\"name\":\"mesh-delivery-builder\",\"open\":true,\"since\":{\"ace\":\"2026-11-05T10:00:00Z\"}}", wire: "{\"holding\":[\"ace\"],\"name\":\"mesh-delivery-builder\",\"open\":true,\"since\":{\"ace\":\"2026-11-05T10:00:00Z\"}}",
|
||||
decode: func(b []byte) (any, error) { var v WindowsByCallerValue; err := json.Unmarshal(b, &v); return v, err }, subjectIs: nil},
|
||||
{name: "publish opens a transfer", subject: "", given: "{\"commit\":\"cccccccccccccccccccccccccccccccccccccccc\",\"digest\":\"sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\",\"fingerprint\":\"f1\",\"kind\":\"image\",\"name\":\"mesh-sdk\",\"parts\":[{\"digest\":\"sha256:bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb\",\"size\":1048576}],\"transfer\":\"t1\",\"version\":\"0.1.19\"}", wire: "{\"commit\":\"cccccccccccccccccccccccccccccccccccccccc\",\"digest\":\"sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\",\"fingerprint\":\"f1\",\"kind\":\"image\",\"name\":\"mesh-sdk\",\"parts\":[{\"digest\":\"sha256:bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb\",\"size\":1048576}],\"transfer\":\"t1\",\"version\":\"0.1.19\"}",
|
||||
decode: func(b []byte) (any, error) { var v PublishInput; err := json.Unmarshal(b, &v); return v, err }, subjectIs: nil},
|
||||
{name: "a field the reader does not know is dropped", subject: "", given: "{\"commit\":\"cccccccccccccccccccccccccccccccccccccccc\",\"digest\":\"sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\",\"fingerprint\":\"f1\",\"kind\":\"image\",\"later\":{\"x\":1},\"name\":\"mesh-sdk\",\"parts\":[{\"digest\":\"sha256:bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb\",\"size\":1048576}],\"transfer\":\"t1\",\"version\":\"0.1.19\"}", wire: "{\"commit\":\"cccccccccccccccccccccccccccccccccccccccc\",\"digest\":\"sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\",\"fingerprint\":\"f1\",\"kind\":\"image\",\"name\":\"mesh-sdk\",\"parts\":[{\"digest\":\"sha256:bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb\",\"size\":1048576}],\"transfer\":\"t1\",\"version\":\"0.1.19\"}",
|
||||
decode: func(b []byte) (any, error) { var v PublishInput; err := json.Unmarshal(b, &v); return v, err }, subjectIs: nil},
|
||||
{name: "publish answers what it lacks", subject: "", given: "{\"lacks\":[\"sha256:bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb\"],\"transfer\":\"t1\"}", wire: "{\"lacks\":[\"sha256:bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb\"],\"transfer\":\"t1\"}",
|
||||
decode: func(b []byte) (any, error) { var v PublishAnswer; err := json.Unmarshal(b, &v); return v, err }, subjectIs: nil},
|
||||
{name: "a chunk of a part", subject: "", given: "{\"data\":\"aGVsbG8=\",\"last\":true,\"part\":\"sha256:bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb\",\"sequence\":0,\"transfer\":\"t1\"}", wire: "{\"data\":\"aGVsbG8=\",\"last\":true,\"part\":\"sha256:bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb\",\"sequence\":0,\"transfer\":\"t1\"}",
|
||||
decode: func(b []byte) (any, error) { var v PublishPartInput; err := json.Unmarshal(b, &v); return v, err }, subjectIs: nil},
|
||||
{name: "the versions held", subject: "", given: "{\"name\":\"mesh-sdk\",\"versions\":[{\"commit\":\"cccccccccccccccccccccccccccccccccccccccc\",\"digest\":\"sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\",\"fingerprint\":\"f1\",\"kind\":\"package\",\"name\":\"mesh-sdk\",\"version\":\"0.1.19\"}]}", wire: "{\"name\":\"mesh-sdk\",\"versions\":[{\"commit\":\"cccccccccccccccccccccccccccccccccccccccc\",\"digest\":\"sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\",\"fingerprint\":\"f1\",\"kind\":\"package\",\"name\":\"mesh-sdk\",\"version\":\"0.1.19\"}]}",
|
||||
decode: func(b []byte) (any, error) { var v VersionsAnswer; err := json.Unmarshal(b, &v); return v, err }, subjectIs: nil},
|
||||
{name: "a release of what is not held answers released false", subject: "", given: "{\"released\":false}", wire: "{\"released\":false}",
|
||||
decode: func(b []byte) (any, error) { var v ReleaseAnswer; err := json.Unmarshal(b, &v); return v, err }, subjectIs: nil},
|
||||
{name: "a tagged version sharing its digest with another tag is kept, not removed", subject: "", given: "{\"kept\":true,\"reason\":\"shared-digest\",\"removed\":false,\"tags\":[\"0.1.19\"]}", wire: "{\"kept\":true,\"reason\":\"shared-digest\",\"removed\":false,\"tags\":[\"0.1.19\"]}",
|
||||
decode: func(b []byte) (any, error) { var v RemoveAnswer; err := json.Unmarshal(b, &v); return v, err }, subjectIs: nil},
|
||||
{name: "list answers an index with its platforms, its tag and its hold", subject: "", given: "{\"repositories\":[{\"manifests\":[{\"digest\":\"sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\",\"held\":false,\"index\":true,\"platforms\":[\"sha256:bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb\"],\"size\":2048,\"tags\":[\"3\"]},{\"digest\":\"sha256:bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb\",\"held\":false,\"index\":false,\"size\":1024}],\"name\":\"mail/server\"}]}", wire: "{\"repositories\":[{\"manifests\":[{\"digest\":\"sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\",\"held\":false,\"index\":true,\"platforms\":[\"sha256:bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb\"],\"size\":2048,\"tags\":[\"3\"]},{\"digest\":\"sha256:bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb\",\"held\":false,\"index\":false,\"size\":1024}],\"name\":\"mail/server\"}]}",
|
||||
decode: func(b []byte) (any, error) { var v ListAnswer; err := json.Unmarshal(b, &v); return v, err }, subjectIs: nil},
|
||||
}
|
||||
for _, c := range cases {
|
||||
v, err := c.decode([]byte(c.given))
|
||||
if err != nil {
|
||||
t.Errorf("%s: does not decode: %v", c.name, err)
|
||||
continue
|
||||
}
|
||||
back, err := json.Marshal(v)
|
||||
if err != nil {
|
||||
t.Errorf("%s: does not encode: %v", c.name, err)
|
||||
continue
|
||||
}
|
||||
var got, want any
|
||||
if err := json.Unmarshal(back, &got); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if err := json.Unmarshal([]byte(c.wire), &want); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if !reflect.DeepEqual(got, want) {
|
||||
t.Errorf("%s: reads back as %s, want %s", c.name, back, c.wire)
|
||||
}
|
||||
if c.subjectIs != nil && !c.subjectIs.MatchString(c.subject) {
|
||||
t.Errorf("%s: subject %q is not the contract's %s", c.name, c.subject, c.subjectIs)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,3 @@
|
||||
module git.novox.be/novox/mesh-delivery/contracts/go/seats/containerimageregistry
|
||||
|
||||
go 1.26.0
|
||||
@@ -0,0 +1,262 @@
|
||||
// Code generated by seatgen from the seat definitions; DO NOT EDIT.
|
||||
|
||||
// Package gopackageregistry is the contract of the seat "go-package-registry", defined whole (seats/go-package-registry/seat.json): The mesh's Go modules: Go's module proxy protocol's read side, and publishing over the bus.
|
||||
package gopackageregistry
|
||||
|
||||
// Seat is the seat's name.
|
||||
const Seat = "go-package-registry"
|
||||
|
||||
const (
|
||||
VerbPublish = "publish"
|
||||
VerbPublishPart = "publish-part"
|
||||
VerbVersions = "versions"
|
||||
VerbRemove = "remove"
|
||||
)
|
||||
|
||||
const (
|
||||
EventPublished = "published"
|
||||
EventRefused = "refused"
|
||||
EventRemoved = "removed"
|
||||
EventKept = "kept"
|
||||
)
|
||||
|
||||
// Verbs is every verb the seat serves, in its definition's order: a new copy each call.
|
||||
func Verbs() []string {
|
||||
return []string{VerbPublish, VerbPublishPart, VerbVersions, VerbRemove}
|
||||
}
|
||||
|
||||
// Events is every event published on the seat's subject: a new copy each call.
|
||||
func Events() []string {
|
||||
return []string{EventPublished, EventRefused, EventRemoved, EventKept}
|
||||
}
|
||||
|
||||
// Family is where the seat's events are kept: the shared events stream, filtered to the seat's subjects.
|
||||
const (
|
||||
Family = "go-package-registry.events"
|
||||
FamilyMaxAgeDays = 7
|
||||
)
|
||||
|
||||
// SubjectPublished is where published is published.
|
||||
const SubjectPublished = "mesh.seat.go-package-registry.event.published"
|
||||
|
||||
// SubjectRefused is where refused is published.
|
||||
const SubjectRefused = "mesh.seat.go-package-registry.event.refused"
|
||||
|
||||
// SubjectRemoved is where removed is published.
|
||||
const SubjectRemoved = "mesh.seat.go-package-registry.event.removed"
|
||||
|
||||
// SubjectKept is where kept is published.
|
||||
const SubjectKept = "mesh.seat.go-package-registry.event.kept"
|
||||
|
||||
// Artifact is one published thing, as every registry event and answer names it.
|
||||
type Artifact struct {
|
||||
// the source commit it was built from
|
||||
Commit string `json:"commit,omitempty"`
|
||||
// its digest, sha256:<hex> (a Go package's h1: hash beside it in the version's record)
|
||||
Digest string `json:"digest"`
|
||||
// the build's fingerprint
|
||||
Fingerprint string `json:"fingerprint,omitempty"`
|
||||
// what it is
|
||||
Kind string `json:"kind"`
|
||||
// the package's, image's, bundle's or archive's name; a package's is its name in its own language (design 32): a Go package's the go.mod module path (git.novox.be/novox/mesh-sdk/go), an npm package's package.json's name (@novox/mesh-sdk)
|
||||
Name string `json:"name"`
|
||||
// the tag it carries, for a tagged version
|
||||
Tag string `json:"tag,omitempty"`
|
||||
// the version the builder gave it (design 51 *The versions*); empty for a bundle or archive published by its digest alone
|
||||
Version string `json:"version,omitempty"`
|
||||
}
|
||||
|
||||
// The values of Artifact's kind.
|
||||
const (
|
||||
ArtifactKindPackage = "package"
|
||||
ArtifactKindImage = "image"
|
||||
ArtifactKindBundle = "bundle"
|
||||
ArtifactKindArchive = "archive"
|
||||
ArtifactKindUpstream = "upstream"
|
||||
)
|
||||
|
||||
// Outcome is the body of published, refused and removed.
|
||||
type Outcome struct {
|
||||
// the source commit it was built from
|
||||
Commit string `json:"commit,omitempty"`
|
||||
// its digest, sha256:<hex> (a Go package's h1: hash beside it in the version's record)
|
||||
Digest string `json:"digest"`
|
||||
// the build's fingerprint
|
||||
Fingerprint string `json:"fingerprint,omitempty"`
|
||||
// the module holding the seat that said it
|
||||
Holder string `json:"holder"`
|
||||
// what it is
|
||||
Kind string `json:"kind"`
|
||||
// the Go module path a Go package is fetched under, equal to name for a Go package (go-package-registry's published); empty for every other kind of artifact
|
||||
Module string `json:"module,omitempty"`
|
||||
// the package's, image's, bundle's or archive's name; a package's is its name in its own language (design 32): a Go package's the go.mod module path (git.novox.be/novox/mesh-sdk/go), an npm package's package.json's name (@novox/mesh-sdk)
|
||||
Name string `json:"name"`
|
||||
// why: a refusal's cause, a removal's (retention, tag-gone, by-hand), or why a removal was kept (shared-digest)
|
||||
Reason string `json:"reason,omitempty"`
|
||||
// the reference the seat answers: <registry>/<module>/<artifact>@sha256:<digest>, or <name>@<version> for a package (ADR 0605 §9)
|
||||
Reference string `json:"reference,omitempty"`
|
||||
// the tag it carries, for a tagged version
|
||||
Tag string `json:"tag,omitempty"`
|
||||
// for kept: the other tags still naming its digest
|
||||
Tags []string `json:"tags,omitempty"`
|
||||
// the transfer it ends, when it ended one
|
||||
Transfer string `json:"transfer,omitempty"`
|
||||
// the version the builder gave it (design 51 *The versions*); empty for a bundle or archive published by its digest alone
|
||||
Version string `json:"version,omitempty"`
|
||||
}
|
||||
|
||||
// The values of Outcome's kind.
|
||||
const (
|
||||
OutcomeKindPackage = "package"
|
||||
OutcomeKindImage = "image"
|
||||
OutcomeKindBundle = "bundle"
|
||||
OutcomeKindArchive = "archive"
|
||||
OutcomeKindUpstream = "upstream"
|
||||
)
|
||||
|
||||
// Part is one part of a transfer.
|
||||
type Part struct {
|
||||
// the part's digest, sha256:<hex>: an image's layer or config, a package's file, a bundle's blob
|
||||
Digest string `json:"digest"`
|
||||
// its size in bytes
|
||||
Size int64 `json:"size"`
|
||||
}
|
||||
|
||||
// PublishInput is what publish takes.
|
||||
type PublishInput struct {
|
||||
// the source commit it was built from
|
||||
Commit string `json:"commit,omitempty"`
|
||||
// its digest, sha256:<hex> (a Go package's h1: hash beside it in the version's record)
|
||||
Digest string `json:"digest"`
|
||||
// the build's fingerprint
|
||||
Fingerprint string `json:"fingerprint,omitempty"`
|
||||
// what it is
|
||||
Kind string `json:"kind"`
|
||||
// the package's, image's, bundle's or archive's name; a package's is its name in its own language (design 32): a Go package's the go.mod module path (git.novox.be/novox/mesh-sdk/go), an npm package's package.json's name (@novox/mesh-sdk)
|
||||
Name string `json:"name"`
|
||||
// every part's digest and size; none when the digest is already held
|
||||
Parts []Part `json:"parts,omitempty"`
|
||||
// the tag it carries, for a tagged version
|
||||
Tag string `json:"tag,omitempty"`
|
||||
// the transfer's name, the caller's own; the same name opens the same transfer again
|
||||
Transfer string `json:"transfer"`
|
||||
// the version the builder gave it (design 51 *The versions*); empty for a bundle or archive published by its digest alone
|
||||
Version string `json:"version,omitempty"`
|
||||
}
|
||||
|
||||
// The values of PublishInput's kind.
|
||||
const (
|
||||
PublishInputKindPackage = "package"
|
||||
PublishInputKindImage = "image"
|
||||
PublishInputKindBundle = "bundle"
|
||||
PublishInputKindArchive = "archive"
|
||||
PublishInputKindUpstream = "upstream"
|
||||
)
|
||||
|
||||
// PublishAnswer is what publish answers.
|
||||
type PublishAnswer struct {
|
||||
// the digest is held already: nothing to send; the outcome follows as an event
|
||||
Held bool `json:"held,omitempty"`
|
||||
// the digests of the parts the holder lacks, to send by publish-part
|
||||
Lacks []string `json:"lacks,omitempty"`
|
||||
// the transfer
|
||||
Transfer string `json:"transfer"`
|
||||
}
|
||||
|
||||
// PublishPartInput is what publish-part takes.
|
||||
type PublishPartInput struct {
|
||||
// the chunk's bytes, base64 (at most 760 KiB before encoding)
|
||||
Data string `json:"data"`
|
||||
// this chunk is the part's last
|
||||
Last bool `json:"last"`
|
||||
// the part's digest
|
||||
Part string `json:"part"`
|
||||
// the chunk's place in its part, from 0
|
||||
Sequence int64 `json:"sequence"`
|
||||
// the transfer
|
||||
Transfer string `json:"transfer"`
|
||||
}
|
||||
|
||||
// PublishPartAnswer is what publish-part answers.
|
||||
type PublishPartAnswer struct {
|
||||
// the part is whole and its bytes match its digest
|
||||
Complete bool `json:"complete,omitempty"`
|
||||
// the part
|
||||
Part string `json:"part"`
|
||||
// the chunks of the part held so far
|
||||
Received int64 `json:"received,omitempty"`
|
||||
// the transfer
|
||||
Transfer string `json:"transfer"`
|
||||
}
|
||||
|
||||
// VersionsInput is what versions takes.
|
||||
type VersionsInput struct {
|
||||
// narrowed to one kind (optional)
|
||||
Kind string `json:"kind,omitempty"`
|
||||
// the name
|
||||
Name string `json:"name"`
|
||||
}
|
||||
|
||||
// VersionsAnswer is what versions answers.
|
||||
type VersionsAnswer struct {
|
||||
// the name
|
||||
Name string `json:"name"`
|
||||
// newest first
|
||||
Versions []Artifact `json:"versions"`
|
||||
}
|
||||
|
||||
// RemoveInput is what remove takes.
|
||||
type RemoveInput struct {
|
||||
// the digest (for a bundle, archive or copied base)
|
||||
Digest string `json:"digest,omitempty"`
|
||||
// the name
|
||||
Name string `json:"name"`
|
||||
// why it goes
|
||||
Reason string `json:"reason"`
|
||||
// the tag (for a tagged version)
|
||||
Tag string `json:"tag,omitempty"`
|
||||
// the version
|
||||
Version string `json:"version,omitempty"`
|
||||
}
|
||||
|
||||
// The values of RemoveInput's reason.
|
||||
const (
|
||||
RemoveInputReasonTagGone = "tag-gone"
|
||||
RemoveInputReasonRetention = "retention"
|
||||
)
|
||||
|
||||
// RemoveAnswer is what remove answers.
|
||||
type RemoveAnswer struct {
|
||||
Artifact *Artifact `json:"artifact,omitempty"`
|
||||
// it stays whole, its tag and its bytes: the store cannot remove the tag alone while another tag names its digest (reason, tags)
|
||||
Kept bool `json:"kept,omitempty"`
|
||||
// why it was kept: shared-digest, another tag names its digest
|
||||
Reason string `json:"reason,omitempty"`
|
||||
// it is gone
|
||||
Removed bool `json:"removed"`
|
||||
// the other tags still naming its digest, when kept
|
||||
Tags []string `json:"tags,omitempty"`
|
||||
}
|
||||
|
||||
// The values of RemoveAnswer's reason.
|
||||
const (
|
||||
RemoveAnswerReasonSharedDigest = "shared-digest"
|
||||
)
|
||||
|
||||
// PublishedEvent is the body of the event published.
|
||||
type PublishedEvent = Outcome
|
||||
|
||||
// RefusedEvent is the body of the event refused.
|
||||
type RefusedEvent = Outcome
|
||||
|
||||
// RemovedEvent is the body of the event removed.
|
||||
type RemovedEvent = Outcome
|
||||
|
||||
// KeptEvent is the body of the event kept.
|
||||
type KeptEvent = Outcome
|
||||
|
||||
// GoPackageRegistryForm is the form of the provision go-package-registry.
|
||||
type GoPackageRegistryForm struct {
|
||||
// the read side's address
|
||||
URL string `json:"url"`
|
||||
}
|
||||
@@ -0,0 +1,66 @@
|
||||
// Code generated by seatgen from the seat definitions; DO NOT EDIT.
|
||||
|
||||
package gopackageregistry
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"reflect"
|
||||
"regexp"
|
||||
"testing"
|
||||
)
|
||||
|
||||
// TestFixtures reads every fixture of seats/go-package-registry/fixtures: each decodes into its type, a field the type does not
|
||||
// know is dropped (ADR 0608 §6), and it reads back as its wire says; each subject named is the contract's.
|
||||
func TestFixtures(t *testing.T) {
|
||||
cases := []struct {
|
||||
name, subject, given, wire string
|
||||
decode func([]byte) (any, error)
|
||||
subjectIs *regexp.Regexp
|
||||
}{
|
||||
{name: "published on the seat's subject", subject: "mesh.seat.go-package-registry.event.published", given: "{\"commit\":\"cccccccccccccccccccccccccccccccccccccccc\",\"digest\":\"sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\",\"fingerprint\":\"f1\",\"holder\":\"gitea\",\"kind\":\"package\",\"module\":\"git.novox.be/novox/mesh-sdk/go\",\"name\":\"git.novox.be/novox/mesh-sdk/go\",\"reference\":\"git.novox.be/novox/mesh-sdk/go@0.1.19\",\"transfer\":\"t1\",\"version\":\"0.1.19\"}", wire: "{\"commit\":\"cccccccccccccccccccccccccccccccccccccccc\",\"digest\":\"sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\",\"fingerprint\":\"f1\",\"holder\":\"gitea\",\"kind\":\"package\",\"module\":\"git.novox.be/novox/mesh-sdk/go\",\"name\":\"git.novox.be/novox/mesh-sdk/go\",\"reference\":\"git.novox.be/novox/mesh-sdk/go@0.1.19\",\"transfer\":\"t1\",\"version\":\"0.1.19\"}",
|
||||
decode: func(b []byte) (any, error) { var v PublishedEvent; err := json.Unmarshal(b, &v); return v, err }, subjectIs: regexp.MustCompile("^mesh\\.seat\\.go-package-registry\\.event\\.published$")},
|
||||
{name: "refused on the seat's subject", subject: "mesh.seat.go-package-registry.event.refused", given: "{\"commit\":\"cccccccccccccccccccccccccccccccccccccccc\",\"digest\":\"sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\",\"fingerprint\":\"f1\",\"holder\":\"gitea\",\"kind\":\"package\",\"name\":\"git.novox.be/novox/mesh-sdk/go\",\"reason\":\"retention\",\"reference\":\"git.novox.be/novox/mesh-sdk/go@0.1.19\",\"transfer\":\"t1\",\"version\":\"0.1.19\"}", wire: "{\"commit\":\"cccccccccccccccccccccccccccccccccccccccc\",\"digest\":\"sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\",\"fingerprint\":\"f1\",\"holder\":\"gitea\",\"kind\":\"package\",\"name\":\"git.novox.be/novox/mesh-sdk/go\",\"reason\":\"retention\",\"reference\":\"git.novox.be/novox/mesh-sdk/go@0.1.19\",\"transfer\":\"t1\",\"version\":\"0.1.19\"}",
|
||||
decode: func(b []byte) (any, error) { var v RefusedEvent; err := json.Unmarshal(b, &v); return v, err }, subjectIs: regexp.MustCompile("^mesh\\.seat\\.go-package-registry\\.event\\.refused$")},
|
||||
{name: "removed on the seat's subject", subject: "mesh.seat.go-package-registry.event.removed", given: "{\"commit\":\"cccccccccccccccccccccccccccccccccccccccc\",\"digest\":\"sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\",\"fingerprint\":\"f1\",\"holder\":\"gitea\",\"kind\":\"package\",\"name\":\"git.novox.be/novox/mesh-sdk/go\",\"reason\":\"retention\",\"reference\":\"git.novox.be/novox/mesh-sdk/go@0.1.19\",\"transfer\":\"t1\",\"version\":\"0.1.19\"}", wire: "{\"commit\":\"cccccccccccccccccccccccccccccccccccccccc\",\"digest\":\"sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\",\"fingerprint\":\"f1\",\"holder\":\"gitea\",\"kind\":\"package\",\"name\":\"git.novox.be/novox/mesh-sdk/go\",\"reason\":\"retention\",\"reference\":\"git.novox.be/novox/mesh-sdk/go@0.1.19\",\"transfer\":\"t1\",\"version\":\"0.1.19\"}",
|
||||
decode: func(b []byte) (any, error) { var v RemovedEvent; err := json.Unmarshal(b, &v); return v, err }, subjectIs: regexp.MustCompile("^mesh\\.seat\\.go-package-registry\\.event\\.removed$")},
|
||||
{name: "kept on the seat's subject: a tagged version sharing its digest stays, naming the tags on it", subject: "mesh.seat.go-package-registry.event.kept", given: "{\"commit\":\"cccccccccccccccccccccccccccccccccccccccc\",\"digest\":\"sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\",\"fingerprint\":\"f1\",\"holder\":\"gitea\",\"kind\":\"package\",\"name\":\"git.novox.be/novox/mesh-sdk/go\",\"reason\":\"shared-digest\",\"reference\":\"git.novox.be/novox/mesh-sdk/go@0.1.19\",\"tag\":\"0.1.19-feat-x.2\",\"tags\":[\"0.1.19\"],\"version\":\"0.1.19-feat-x.2\"}", wire: "{\"commit\":\"cccccccccccccccccccccccccccccccccccccccc\",\"digest\":\"sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\",\"fingerprint\":\"f1\",\"holder\":\"gitea\",\"kind\":\"package\",\"name\":\"git.novox.be/novox/mesh-sdk/go\",\"reason\":\"shared-digest\",\"reference\":\"git.novox.be/novox/mesh-sdk/go@0.1.19\",\"tag\":\"0.1.19-feat-x.2\",\"tags\":[\"0.1.19\"],\"version\":\"0.1.19-feat-x.2\"}",
|
||||
decode: func(b []byte) (any, error) { var v KeptEvent; err := json.Unmarshal(b, &v); return v, err }, subjectIs: regexp.MustCompile("^mesh\\.seat\\.go-package-registry\\.event\\.kept$")},
|
||||
{name: "publish opens a transfer", subject: "", given: "{\"commit\":\"cccccccccccccccccccccccccccccccccccccccc\",\"digest\":\"sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\",\"fingerprint\":\"f1\",\"kind\":\"package\",\"name\":\"git.novox.be/novox/mesh-sdk/go\",\"parts\":[{\"digest\":\"sha256:bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb\",\"size\":1048576}],\"transfer\":\"t1\",\"version\":\"0.1.19\"}", wire: "{\"commit\":\"cccccccccccccccccccccccccccccccccccccccc\",\"digest\":\"sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\",\"fingerprint\":\"f1\",\"kind\":\"package\",\"name\":\"git.novox.be/novox/mesh-sdk/go\",\"parts\":[{\"digest\":\"sha256:bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb\",\"size\":1048576}],\"transfer\":\"t1\",\"version\":\"0.1.19\"}",
|
||||
decode: func(b []byte) (any, error) { var v PublishInput; err := json.Unmarshal(b, &v); return v, err }, subjectIs: nil},
|
||||
{name: "a field the reader does not know is dropped", subject: "", given: "{\"commit\":\"cccccccccccccccccccccccccccccccccccccccc\",\"digest\":\"sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\",\"fingerprint\":\"f1\",\"kind\":\"package\",\"later\":{\"x\":1},\"name\":\"git.novox.be/novox/mesh-sdk/go\",\"parts\":[{\"digest\":\"sha256:bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb\",\"size\":1048576}],\"transfer\":\"t1\",\"version\":\"0.1.19\"}", wire: "{\"commit\":\"cccccccccccccccccccccccccccccccccccccccc\",\"digest\":\"sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\",\"fingerprint\":\"f1\",\"kind\":\"package\",\"name\":\"git.novox.be/novox/mesh-sdk/go\",\"parts\":[{\"digest\":\"sha256:bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb\",\"size\":1048576}],\"transfer\":\"t1\",\"version\":\"0.1.19\"}",
|
||||
decode: func(b []byte) (any, error) { var v PublishInput; err := json.Unmarshal(b, &v); return v, err }, subjectIs: nil},
|
||||
{name: "publish answers what it lacks", subject: "", given: "{\"lacks\":[\"sha256:bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb\"],\"transfer\":\"t1\"}", wire: "{\"lacks\":[\"sha256:bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb\"],\"transfer\":\"t1\"}",
|
||||
decode: func(b []byte) (any, error) { var v PublishAnswer; err := json.Unmarshal(b, &v); return v, err }, subjectIs: nil},
|
||||
{name: "a chunk of a part", subject: "", given: "{\"data\":\"aGVsbG8=\",\"last\":true,\"part\":\"sha256:bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb\",\"sequence\":0,\"transfer\":\"t1\"}", wire: "{\"data\":\"aGVsbG8=\",\"last\":true,\"part\":\"sha256:bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb\",\"sequence\":0,\"transfer\":\"t1\"}",
|
||||
decode: func(b []byte) (any, error) { var v PublishPartInput; err := json.Unmarshal(b, &v); return v, err }, subjectIs: nil},
|
||||
{name: "the versions held", subject: "", given: "{\"name\":\"git.novox.be/novox/mesh-sdk/go\",\"versions\":[{\"commit\":\"cccccccccccccccccccccccccccccccccccccccc\",\"digest\":\"sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\",\"fingerprint\":\"f1\",\"kind\":\"package\",\"name\":\"git.novox.be/novox/mesh-sdk/go\",\"version\":\"0.1.19\"}]}", wire: "{\"name\":\"git.novox.be/novox/mesh-sdk/go\",\"versions\":[{\"commit\":\"cccccccccccccccccccccccccccccccccccccccc\",\"digest\":\"sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\",\"fingerprint\":\"f1\",\"kind\":\"package\",\"name\":\"git.novox.be/novox/mesh-sdk/go\",\"version\":\"0.1.19\"}]}",
|
||||
decode: func(b []byte) (any, error) { var v VersionsAnswer; err := json.Unmarshal(b, &v); return v, err }, subjectIs: nil},
|
||||
{name: "a tagged version sharing its digest with another tag is kept, not removed", subject: "", given: "{\"kept\":true,\"reason\":\"shared-digest\",\"removed\":false,\"tags\":[\"0.1.19\"]}", wire: "{\"kept\":true,\"reason\":\"shared-digest\",\"removed\":false,\"tags\":[\"0.1.19\"]}",
|
||||
decode: func(b []byte) (any, error) { var v RemoveAnswer; err := json.Unmarshal(b, &v); return v, err }, subjectIs: nil},
|
||||
}
|
||||
for _, c := range cases {
|
||||
v, err := c.decode([]byte(c.given))
|
||||
if err != nil {
|
||||
t.Errorf("%s: does not decode: %v", c.name, err)
|
||||
continue
|
||||
}
|
||||
back, err := json.Marshal(v)
|
||||
if err != nil {
|
||||
t.Errorf("%s: does not encode: %v", c.name, err)
|
||||
continue
|
||||
}
|
||||
var got, want any
|
||||
if err := json.Unmarshal(back, &got); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if err := json.Unmarshal([]byte(c.wire), &want); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if !reflect.DeepEqual(got, want) {
|
||||
t.Errorf("%s: reads back as %s, want %s", c.name, back, c.wire)
|
||||
}
|
||||
if c.subjectIs != nil && !c.subjectIs.MatchString(c.subject) {
|
||||
t.Errorf("%s: subject %q is not the contract's %s", c.name, c.subject, c.subjectIs)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,3 @@
|
||||
module git.novox.be/novox/mesh-delivery/contracts/go/seats/gopackageregistry
|
||||
|
||||
go 1.26.0
|
||||
@@ -0,0 +1,594 @@
|
||||
// Code generated by seatgen from the seat definitions; DO NOT EDIT.
|
||||
|
||||
// Package meshcatalog is the contract of the seat "mesh-catalog", defined whole (seats/mesh-catalog/seat.json): The catalog of the mesh: every module the trunk holds with its manifest, fingerprint and verdict, the packages published, and the seat list (ADR 0636 §1, §4).
|
||||
package meshcatalog
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"strings"
|
||||
)
|
||||
|
||||
// Seat is the seat's name.
|
||||
const Seat = "mesh-catalog"
|
||||
|
||||
const (
|
||||
VerbModules = "modules"
|
||||
VerbModule = "module"
|
||||
VerbProvides = "provides"
|
||||
VerbDependents = "dependents"
|
||||
)
|
||||
|
||||
const (
|
||||
EventSeatDefinitionChanged = "seat-definition-changed"
|
||||
EventSeatDefinitionRemoved = "seat-definition-removed"
|
||||
)
|
||||
|
||||
// Verbs is every verb the seat serves, in its definition's order: a new copy each call.
|
||||
func Verbs() []string {
|
||||
return []string{VerbModules, VerbModule, VerbProvides, VerbDependents}
|
||||
}
|
||||
|
||||
// Events is every event published on the seat's subject: a new copy each call.
|
||||
func Events() []string {
|
||||
return []string{EventSeatDefinitionChanged, EventSeatDefinitionRemoved}
|
||||
}
|
||||
|
||||
// Family is where the seat's events are kept: the shared events stream, filtered to the seat's subjects.
|
||||
const (
|
||||
Family = "mesh-catalog.events"
|
||||
FamilyMaxAgeDays = 7
|
||||
)
|
||||
|
||||
// SubjectSeatDefinitionChanged is where seat-definition-changed is published.
|
||||
const SubjectSeatDefinitionChanged = "mesh.seat.mesh-catalog.event.seat-definition-changed"
|
||||
|
||||
// SubjectSeatDefinitionRemoved is where seat-definition-removed is published.
|
||||
const SubjectSeatDefinitionRemoved = "mesh.seat.mesh-catalog.event.seat-definition-removed"
|
||||
|
||||
const (
|
||||
StateManifests = "manifests"
|
||||
StateModules = "modules"
|
||||
StateCandidates = "candidates"
|
||||
StateVerdicts = "verdicts"
|
||||
StatePredefined = "predefined"
|
||||
StateConfigCandidates = "config-candidates"
|
||||
StatePackages = "packages"
|
||||
)
|
||||
|
||||
// ManifestsByDigestKey is the key of one value of the state manifests under the form "<digest>".
|
||||
func ManifestsByDigestKey(digest string) string { return digest }
|
||||
|
||||
// ParseManifestsByDigestKey reads the parts of a key of the form "<digest>", in the order of the form; the last result is false for a key of another form.
|
||||
func ParseManifestsByDigestKey(key string) (string, bool) {
|
||||
rest := key
|
||||
if rest == "" {
|
||||
return "", false
|
||||
}
|
||||
v0 := rest
|
||||
rest = ""
|
||||
if rest != "" {
|
||||
return "", false
|
||||
}
|
||||
return v0, true
|
||||
}
|
||||
|
||||
// DecodeManifestsByDigestValue reads one value of the state manifests under the form "<digest>", refusing a field ManifestsByDigestValue does not know and any text after the value.
|
||||
func DecodeManifestsByDigestValue(raw []byte) (ManifestsByDigestValue, error) {
|
||||
var v ManifestsByDigestValue
|
||||
dec := json.NewDecoder(bytes.NewReader(raw))
|
||||
dec.DisallowUnknownFields()
|
||||
if err := dec.Decode(&v); err != nil {
|
||||
return v, err
|
||||
}
|
||||
if dec.More() {
|
||||
return v, errors.New("text after the value")
|
||||
}
|
||||
return v, nil
|
||||
}
|
||||
|
||||
// ModulesByModuleKey is the key of one value of the state modules under the form "<module>".
|
||||
func ModulesByModuleKey(module string) string { return module }
|
||||
|
||||
// ParseModulesByModuleKey reads the parts of a key of the form "<module>", in the order of the form; the last result is false for a key of another form.
|
||||
func ParseModulesByModuleKey(key string) (string, bool) {
|
||||
rest := key
|
||||
if rest == "" {
|
||||
return "", false
|
||||
}
|
||||
v0 := rest
|
||||
rest = ""
|
||||
if rest != "" {
|
||||
return "", false
|
||||
}
|
||||
return v0, true
|
||||
}
|
||||
|
||||
// DecodeModulesByModuleValue reads one value of the state modules under the form "<module>", refusing a field ModulesByModuleValue does not know and any text after the value.
|
||||
func DecodeModulesByModuleValue(raw []byte) (ModulesByModuleValue, error) {
|
||||
var v ModulesByModuleValue
|
||||
dec := json.NewDecoder(bytes.NewReader(raw))
|
||||
dec.DisallowUnknownFields()
|
||||
if err := dec.Decode(&v); err != nil {
|
||||
return v, err
|
||||
}
|
||||
if dec.More() {
|
||||
return v, errors.New("text after the value")
|
||||
}
|
||||
return v, nil
|
||||
}
|
||||
|
||||
// CandidatesByGroupKey is the key of one value of the state candidates under the form "<group>".
|
||||
func CandidatesByGroupKey(group string) string { return group }
|
||||
|
||||
// ParseCandidatesByGroupKey reads the parts of a key of the form "<group>", in the order of the form; the last result is false for a key of another form.
|
||||
func ParseCandidatesByGroupKey(key string) (string, bool) {
|
||||
rest := key
|
||||
if rest == "" {
|
||||
return "", false
|
||||
}
|
||||
v0 := rest
|
||||
rest = ""
|
||||
if rest != "" {
|
||||
return "", false
|
||||
}
|
||||
return v0, true
|
||||
}
|
||||
|
||||
// DecodeCandidatesByGroupValue reads one value of the state candidates under the form "<group>", refusing a field CandidatesByGroupValue does not know and any text after the value.
|
||||
func DecodeCandidatesByGroupValue(raw []byte) (CandidatesByGroupValue, error) {
|
||||
var v CandidatesByGroupValue
|
||||
dec := json.NewDecoder(bytes.NewReader(raw))
|
||||
dec.DisallowUnknownFields()
|
||||
if err := dec.Decode(&v); err != nil {
|
||||
return v, err
|
||||
}
|
||||
if dec.More() {
|
||||
return v, errors.New("text after the value")
|
||||
}
|
||||
return v, nil
|
||||
}
|
||||
|
||||
// VerdictsByDigestKey is the key of one value of the state verdicts under the form "<digest>".
|
||||
func VerdictsByDigestKey(digest string) string { return digest }
|
||||
|
||||
// ParseVerdictsByDigestKey reads the parts of a key of the form "<digest>", in the order of the form; the last result is false for a key of another form.
|
||||
func ParseVerdictsByDigestKey(key string) (string, bool) {
|
||||
rest := key
|
||||
if rest == "" {
|
||||
return "", false
|
||||
}
|
||||
v0 := rest
|
||||
rest = ""
|
||||
if rest != "" {
|
||||
return "", false
|
||||
}
|
||||
return v0, true
|
||||
}
|
||||
|
||||
// DecodeVerdictsByDigestValue reads one value of the state verdicts under the form "<digest>", refusing a field VerdictsByDigestValue does not know and any text after the value.
|
||||
func DecodeVerdictsByDigestValue(raw []byte) (VerdictsByDigestValue, error) {
|
||||
var v VerdictsByDigestValue
|
||||
dec := json.NewDecoder(bytes.NewReader(raw))
|
||||
dec.DisallowUnknownFields()
|
||||
if err := dec.Decode(&v); err != nil {
|
||||
return v, err
|
||||
}
|
||||
if dec.More() {
|
||||
return v, errors.New("text after the value")
|
||||
}
|
||||
return v, nil
|
||||
}
|
||||
|
||||
// PredefinedByListKey is the key of one value of the state predefined under the form "list".
|
||||
func PredefinedByListKey() string { return "list" }
|
||||
|
||||
// ParsePredefinedByListKey reads the parts of a key of the form "list", in the order of the form; the last result is false for a key of another form.
|
||||
func ParsePredefinedByListKey(key string) bool {
|
||||
rest := key
|
||||
if !strings.HasPrefix(rest, "list") {
|
||||
return false
|
||||
}
|
||||
rest = rest[4:]
|
||||
if rest != "" {
|
||||
return false
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
// DecodePredefinedByListValue reads one value of the state predefined under the form "list", refusing a field PredefinedByListValue does not know and any text after the value.
|
||||
func DecodePredefinedByListValue(raw []byte) (PredefinedByListValue, error) {
|
||||
var v PredefinedByListValue
|
||||
dec := json.NewDecoder(bytes.NewReader(raw))
|
||||
dec.DisallowUnknownFields()
|
||||
if err := dec.Decode(&v); err != nil {
|
||||
return v, err
|
||||
}
|
||||
if dec.More() {
|
||||
return v, errors.New("text after the value")
|
||||
}
|
||||
return v, nil
|
||||
}
|
||||
|
||||
// ConfigCandidatesByPullKey is the key of one value of the state config-candidates under the form "<pull>".
|
||||
func ConfigCandidatesByPullKey(pull string) string { return pull }
|
||||
|
||||
// ParseConfigCandidatesByPullKey reads the parts of a key of the form "<pull>", in the order of the form; the last result is false for a key of another form.
|
||||
func ParseConfigCandidatesByPullKey(key string) (string, bool) {
|
||||
rest := key
|
||||
if rest == "" {
|
||||
return "", false
|
||||
}
|
||||
v0 := rest
|
||||
rest = ""
|
||||
if rest != "" {
|
||||
return "", false
|
||||
}
|
||||
return v0, true
|
||||
}
|
||||
|
||||
// DecodeConfigCandidatesByPullValue reads one value of the state config-candidates under the form "<pull>", refusing a field ConfigCandidatesByPullValue does not know and any text after the value.
|
||||
func DecodeConfigCandidatesByPullValue(raw []byte) (ConfigCandidatesByPullValue, error) {
|
||||
var v ConfigCandidatesByPullValue
|
||||
dec := json.NewDecoder(bytes.NewReader(raw))
|
||||
dec.DisallowUnknownFields()
|
||||
if err := dec.Decode(&v); err != nil {
|
||||
return v, err
|
||||
}
|
||||
if dec.More() {
|
||||
return v, errors.New("text after the value")
|
||||
}
|
||||
return v, nil
|
||||
}
|
||||
|
||||
// PackagesByModuleKey is the key of one value of the state packages under the form "<module>".
|
||||
func PackagesByModuleKey(module string) string { return module }
|
||||
|
||||
// ParsePackagesByModuleKey reads the parts of a key of the form "<module>", in the order of the form; the last result is false for a key of another form.
|
||||
func ParsePackagesByModuleKey(key string) (string, bool) {
|
||||
rest := key
|
||||
if rest == "" {
|
||||
return "", false
|
||||
}
|
||||
v0 := rest
|
||||
rest = ""
|
||||
if rest != "" {
|
||||
return "", false
|
||||
}
|
||||
return v0, true
|
||||
}
|
||||
|
||||
// DecodePackagesByModuleValue reads one value of the state packages under the form "<module>", refusing a field PackagesByModuleValue does not know and any text after the value.
|
||||
func DecodePackagesByModuleValue(raw []byte) (PackagesByModuleValue, error) {
|
||||
var v PackagesByModuleValue
|
||||
dec := json.NewDecoder(bytes.NewReader(raw))
|
||||
dec.DisallowUnknownFields()
|
||||
if err := dec.Decode(&v); err != nil {
|
||||
return v, err
|
||||
}
|
||||
if dec.More() {
|
||||
return v, errors.New("text after the value")
|
||||
}
|
||||
return v, nil
|
||||
}
|
||||
|
||||
// PackagesByPackageSeriesKey is the key of one value of the state packages under the form "<package>.<series>".
|
||||
func PackagesByPackageSeriesKey(packagePart, series string) string { return packagePart + "." + series }
|
||||
|
||||
// ParsePackagesByPackageSeriesKey reads the parts of a key of the form "<package>.<series>", in the order of the form; the last result is false for a key of another form.
|
||||
func ParsePackagesByPackageSeriesKey(key string) (string, string, bool) {
|
||||
rest := key
|
||||
v0, after0, found0 := strings.Cut(rest, ".")
|
||||
if !found0 || v0 == "" {
|
||||
return "", "", false
|
||||
}
|
||||
rest = after0
|
||||
if rest == "" {
|
||||
return "", "", false
|
||||
}
|
||||
v2 := rest
|
||||
rest = ""
|
||||
if rest != "" {
|
||||
return "", "", false
|
||||
}
|
||||
return v0, v2, true
|
||||
}
|
||||
|
||||
// DecodePackagesByPackageSeriesValue reads one value of the state packages under the form "<package>.<series>", refusing a field PackagesByPackageSeriesValue does not know and any text after the value.
|
||||
func DecodePackagesByPackageSeriesValue(raw []byte) (PackagesByPackageSeriesValue, error) {
|
||||
var v PackagesByPackageSeriesValue
|
||||
dec := json.NewDecoder(bytes.NewReader(raw))
|
||||
dec.DisallowUnknownFields()
|
||||
if err := dec.Decode(&v); err != nil {
|
||||
return v, err
|
||||
}
|
||||
if dec.More() {
|
||||
return v, errors.New("text after the value")
|
||||
}
|
||||
return v, nil
|
||||
}
|
||||
|
||||
// ModulesInput is what modules takes.
|
||||
type ModulesInput struct {
|
||||
}
|
||||
|
||||
// ModulesAnswer is what modules answers.
|
||||
type ModulesAnswer = map[string]any
|
||||
|
||||
// ModuleInput is what module takes.
|
||||
type ModuleInput struct {
|
||||
// the module's name
|
||||
Module string `json:"module"`
|
||||
}
|
||||
|
||||
// ModuleAnswer is what module answers.
|
||||
type ModuleAnswer = map[string]any
|
||||
|
||||
// ProvidesInput is what provides takes.
|
||||
type ProvidesInput struct {
|
||||
// the provision, such as postgres-database
|
||||
Provision string `json:"provision"`
|
||||
}
|
||||
|
||||
// ProvidesAnswer is what provides answers.
|
||||
type ProvidesAnswer = map[string]any
|
||||
|
||||
// DependentsInput is what dependents takes.
|
||||
type DependentsInput struct {
|
||||
// the module's name
|
||||
Module string `json:"module"`
|
||||
}
|
||||
|
||||
// DependentsAnswer is what dependents answers.
|
||||
type DependentsAnswer = map[string]any
|
||||
|
||||
// SeatDefinitionChangedEvent is the body of the event seat-definition-changed.
|
||||
type SeatDefinitionChangedEvent struct {
|
||||
Digest string `json:"digest"`
|
||||
Seat string `json:"seat"`
|
||||
// trunk
|
||||
Source string `json:"source"`
|
||||
}
|
||||
|
||||
// SeatDefinitionRemovedEvent is the body of the event seat-definition-removed.
|
||||
type SeatDefinitionRemovedEvent struct {
|
||||
Seat string `json:"seat"`
|
||||
}
|
||||
|
||||
// ManifestsByDigestValue is one value of the state manifests under the key <digest>.
|
||||
type ManifestsByDigestValue struct {
|
||||
// the manifest, UTF-8 JSON text
|
||||
Manifest string `json:"manifest"`
|
||||
Module string `json:"module"`
|
||||
// the layout version
|
||||
V int64 `json:"v,omitempty"`
|
||||
}
|
||||
|
||||
// ModulesByModuleValueInputs2 is a type of the contract.
|
||||
type ModulesByModuleValueInputs2 struct {
|
||||
Bases []string `json:"bases,omitempty"`
|
||||
Builder string `json:"builder,omitempty"`
|
||||
Contexts map[string]string `json:"contexts,omitempty"`
|
||||
ImportForm string `json:"import-form,omitempty"`
|
||||
Imports []string `json:"imports,omitempty"`
|
||||
Locks map[string]string `json:"locks,omitempty"`
|
||||
Packages []string `json:"packages,omitempty"`
|
||||
Paths map[string]string `json:"paths,omitempty"`
|
||||
Tree string `json:"tree"`
|
||||
}
|
||||
|
||||
// ModulesByModuleValue is one value of the state modules under the key <module>.
|
||||
type ModulesByModuleValue struct {
|
||||
Commit string `json:"commit"`
|
||||
// the old-form fingerprint
|
||||
Fp string `json:"fp,omitempty"`
|
||||
FpSrc int64 `json:"fp-src,omitempty"`
|
||||
// src2: fingerprint
|
||||
Fp2 string `json:"fp2,omitempty"`
|
||||
Inputs2 *ModulesByModuleValueInputs2 `json:"inputs2,omitempty"`
|
||||
// manifest digest
|
||||
Manifest string `json:"manifest,omitempty"`
|
||||
Needs []string `json:"needs,omitempty"`
|
||||
None string `json:"none,omitempty"`
|
||||
// why it has none
|
||||
None2 string `json:"none2,omitempty"`
|
||||
Packages map[string]string `json:"packages,omitempty"`
|
||||
Path string `json:"path,omitempty"`
|
||||
Paths map[string][]string `json:"paths,omitempty"`
|
||||
Problems []string `json:"problems,omitempty"`
|
||||
Repository string `json:"repository"`
|
||||
// the source revision
|
||||
Src int64 `json:"src,omitempty"`
|
||||
Toolchains map[string]any `json:"toolchains,omitempty"`
|
||||
Unknown []string `json:"unknown,omitempty"`
|
||||
// the layout version
|
||||
V int64 `json:"v,omitempty"`
|
||||
Verdict string `json:"verdict,omitempty"`
|
||||
Warnings []string `json:"warnings,omitempty"`
|
||||
}
|
||||
|
||||
// CandidatesByGroupValuePullsItemFpsValue is a type of the contract.
|
||||
type CandidatesByGroupValuePullsItemFpsValue struct {
|
||||
Fp2 string `json:"fp2,omitempty"`
|
||||
Manifest string `json:"manifest,omitempty"`
|
||||
None2 string `json:"none2,omitempty"`
|
||||
Packages map[string]string `json:"packages,omitempty"`
|
||||
Paths map[string]string `json:"paths,omitempty"`
|
||||
}
|
||||
|
||||
// CandidatesByGroupValuePullsItem is a type of the contract.
|
||||
type CandidatesByGroupValuePullsItem struct {
|
||||
Fps map[string]CandidatesByGroupValuePullsItemFpsValue `json:"fps,omitempty"`
|
||||
Head string `json:"head"`
|
||||
Modules []string `json:"modules"`
|
||||
Number int64 `json:"number"`
|
||||
Repository string `json:"repository"`
|
||||
}
|
||||
|
||||
// CandidatesByGroupValue is one value of the state candidates under the key <group>.
|
||||
type CandidatesByGroupValue struct {
|
||||
// the source revision it is read on
|
||||
Base int64 `json:"base"`
|
||||
Fpset string `json:"fpset,omitempty"`
|
||||
Group string `json:"group"`
|
||||
Heads string `json:"heads"`
|
||||
Pulls []CandidatesByGroupValuePullsItem `json:"pulls"`
|
||||
// the layout version
|
||||
V int64 `json:"v,omitempty"`
|
||||
}
|
||||
|
||||
// VerdictsByDigestValue is one value of the state verdicts under the key <digest>.
|
||||
type VerdictsByDigestValue struct {
|
||||
// when (RFC 3339 time)
|
||||
At string `json:"at,omitempty"`
|
||||
Judge string `json:"judge,omitempty"`
|
||||
Module string `json:"module"`
|
||||
Problems []string `json:"problems,omitempty"`
|
||||
State string `json:"state"`
|
||||
Unknown []string `json:"unknown,omitempty"`
|
||||
// the layout version
|
||||
V int64 `json:"v,omitempty"`
|
||||
Warnings []string `json:"warnings,omitempty"`
|
||||
}
|
||||
|
||||
// PredefinedByListValue is one value of the state predefined under the key list.
|
||||
type PredefinedByListValue struct {
|
||||
// when (RFC 3339 time)
|
||||
At string `json:"at,omitempty"`
|
||||
Commit string `json:"commit"`
|
||||
// the foundation's predefined list as kept
|
||||
List any `json:"list"`
|
||||
// the layout version
|
||||
V int64 `json:"v,omitempty"`
|
||||
}
|
||||
|
||||
// ConfigCandidatesByPullValueDeclaresValueValue is a type of the contract.
|
||||
type ConfigCandidatesByPullValueDeclaresValueValue struct {
|
||||
// the default
|
||||
Default any `json:"default"`
|
||||
Description string `json:"description,omitempty"`
|
||||
// the allowed values
|
||||
Enum any `json:"enum,omitempty"`
|
||||
Kind string `json:"kind"`
|
||||
Maximum float64 `json:"maximum,omitempty"`
|
||||
Minimum float64 `json:"minimum,omitempty"`
|
||||
Pattern string `json:"pattern,omitempty"`
|
||||
Predefined string `json:"predefined,omitempty"`
|
||||
Required bool `json:"required,omitempty"`
|
||||
Runtime bool `json:"runtime,omitempty"`
|
||||
Trusted bool `json:"trusted,omitempty"`
|
||||
Type string `json:"type,omitempty"`
|
||||
Why string `json:"why"`
|
||||
}
|
||||
|
||||
// ConfigCandidatesByPullValue is one value of the state config-candidates under the key <pull>.
|
||||
type ConfigCandidatesByPullValue struct {
|
||||
// when (RFC 3339 time)
|
||||
At string `json:"at,omitempty"`
|
||||
// module to key to declaration
|
||||
Declares map[string]map[string]ConfigCandidatesByPullValueDeclaresValueValue `json:"declares"`
|
||||
Head string `json:"head"`
|
||||
Pull string `json:"pull"`
|
||||
// the layout version
|
||||
V int64 `json:"v,omitempty"`
|
||||
}
|
||||
|
||||
// PackagesByModuleValuePackagesItem is a type of the contract.
|
||||
type PackagesByModuleValuePackagesItem struct {
|
||||
Digest string `json:"digest"`
|
||||
Package string `json:"package"`
|
||||
}
|
||||
|
||||
// PackagesByModuleValue is one value of the state packages under the key <module>.
|
||||
type PackagesByModuleValue struct {
|
||||
// when (RFC 3339 time)
|
||||
At string `json:"at,omitempty"`
|
||||
Commit string `json:"commit"`
|
||||
Dir string `json:"dir,omitempty"`
|
||||
// the Go module path
|
||||
Module string `json:"module"`
|
||||
Packages []PackagesByModuleValuePackagesItem `json:"packages"`
|
||||
Repository string `json:"repository"`
|
||||
// digest of go.mod and go.sum
|
||||
Requires string `json:"requires,omitempty"`
|
||||
// the layout version
|
||||
V int64 `json:"v,omitempty"`
|
||||
}
|
||||
|
||||
// PackagesByPackageSeriesValueNewestContentsPackagesItem is a type of the contract.
|
||||
type PackagesByPackageSeriesValueNewestContentsPackagesItem struct {
|
||||
Digest string `json:"digest"`
|
||||
Package string `json:"package"`
|
||||
}
|
||||
|
||||
// PackagesByPackageSeriesValueNewestContents is a type of the contract.
|
||||
type PackagesByPackageSeriesValueNewestContents struct {
|
||||
// when (RFC 3339 time)
|
||||
At string `json:"at,omitempty"`
|
||||
Commit string `json:"commit"`
|
||||
Dir string `json:"dir,omitempty"`
|
||||
// the Go module path
|
||||
Module string `json:"module"`
|
||||
Packages []PackagesByPackageSeriesValueNewestContentsPackagesItem `json:"packages"`
|
||||
Repository string `json:"repository"`
|
||||
// digest of go.mod and go.sum
|
||||
Requires string `json:"requires,omitempty"`
|
||||
// the layout version
|
||||
V int64 `json:"v,omitempty"`
|
||||
}
|
||||
|
||||
// PackagesByPackageSeriesValueNewest is a type of the contract.
|
||||
type PackagesByPackageSeriesValueNewest struct {
|
||||
Commit string `json:"commit,omitempty"`
|
||||
Contents *PackagesByPackageSeriesValueNewestContents `json:"contents,omitempty"`
|
||||
Digest string `json:"digest,omitempty"`
|
||||
Fingerprint string `json:"fingerprint,omitempty"`
|
||||
Version string `json:"version"`
|
||||
}
|
||||
|
||||
// PackagesByPackageSeriesValueNumberedContentsPackagesItem is a type of the contract.
|
||||
type PackagesByPackageSeriesValueNumberedContentsPackagesItem struct {
|
||||
Digest string `json:"digest"`
|
||||
Package string `json:"package"`
|
||||
}
|
||||
|
||||
// PackagesByPackageSeriesValueNumberedContents is a type of the contract.
|
||||
type PackagesByPackageSeriesValueNumberedContents struct {
|
||||
// when (RFC 3339 time)
|
||||
At string `json:"at,omitempty"`
|
||||
Commit string `json:"commit"`
|
||||
Dir string `json:"dir,omitempty"`
|
||||
// the Go module path
|
||||
Module string `json:"module"`
|
||||
Packages []PackagesByPackageSeriesValueNumberedContentsPackagesItem `json:"packages"`
|
||||
Repository string `json:"repository"`
|
||||
// digest of go.mod and go.sum
|
||||
Requires string `json:"requires,omitempty"`
|
||||
// the layout version
|
||||
V int64 `json:"v,omitempty"`
|
||||
}
|
||||
|
||||
// PackagesByPackageSeriesValueNumbered is a type of the contract.
|
||||
type PackagesByPackageSeriesValueNumbered struct {
|
||||
Commit string `json:"commit,omitempty"`
|
||||
Contents *PackagesByPackageSeriesValueNumberedContents `json:"contents,omitempty"`
|
||||
Digest string `json:"digest,omitempty"`
|
||||
Fingerprint string `json:"fingerprint,omitempty"`
|
||||
Version string `json:"version"`
|
||||
}
|
||||
|
||||
// PackagesByPackageSeriesValue is one value of the state packages under the key <package>.<series>.
|
||||
type PackagesByPackageSeriesValue struct {
|
||||
// when (RFC 3339 time)
|
||||
At string `json:"at,omitempty"`
|
||||
GoModule string `json:"go-module,omitempty"`
|
||||
Name string `json:"name"`
|
||||
Newest *PackagesByPackageSeriesValueNewest `json:"newest,omitempty"`
|
||||
Numbered *PackagesByPackageSeriesValueNumbered `json:"numbered,omitempty"`
|
||||
Package string `json:"package,omitempty"`
|
||||
Seat string `json:"seat,omitempty"`
|
||||
Series string `json:"series"`
|
||||
// the layout version
|
||||
V int64 `json:"v,omitempty"`
|
||||
}
|
||||
@@ -0,0 +1,70 @@
|
||||
// Code generated by seatgen from the seat definitions; DO NOT EDIT.
|
||||
|
||||
package meshcatalog
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"reflect"
|
||||
"regexp"
|
||||
"testing"
|
||||
)
|
||||
|
||||
// TestFixtures reads every fixture of seats/mesh-catalog/fixtures: each decodes into its type, a field the type does not
|
||||
// know is dropped (ADR 0608 §6), and it reads back as its wire says; each subject named is the contract's.
|
||||
func TestFixtures(t *testing.T) {
|
||||
cases := []struct {
|
||||
name, subject, given, wire string
|
||||
decode func([]byte) (any, error)
|
||||
subjectIs *regexp.Regexp
|
||||
}{
|
||||
{name: "a manifest", subject: "", given: "{\"manifest\":\"{\\\"module\\\":\\\"nats\\\"}\",\"module\":\"nats\",\"v\":1}", wire: "{\"manifest\":\"{\\\"module\\\":\\\"nats\\\"}\",\"module\":\"nats\",\"v\":1}",
|
||||
decode: func(b []byte) (any, error) { var v ManifestsByDigestValue; err := json.Unmarshal(b, &v); return v, err }, subjectIs: nil},
|
||||
{name: "a module", subject: "", given: "{\"commit\":\"cccccccccccccccccccccccccccccccccccccccc\",\"fp2\":\"src2:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\",\"repository\":\"novox/mesh-catalog\",\"v\":1}", wire: "{\"commit\":\"cccccccccccccccccccccccccccccccccccccccc\",\"fp2\":\"src2:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\",\"repository\":\"novox/mesh-catalog\",\"v\":1}",
|
||||
decode: func(b []byte) (any, error) { var v ModulesByModuleValue; err := json.Unmarshal(b, &v); return v, err }, subjectIs: nil},
|
||||
{name: "a candidate", subject: "", given: "{\"base\":3,\"group\":\"feat/registry\",\"heads\":\"0123456789ab\",\"pulls\":[{\"head\":\"cccccccccccccccccccccccccccccccccccccccc\",\"modules\":[\"nats\"],\"number\":1,\"repository\":\"novox/mesh-sdk\"}],\"v\":1}", wire: "{\"base\":3,\"group\":\"feat/registry\",\"heads\":\"0123456789ab\",\"pulls\":[{\"head\":\"cccccccccccccccccccccccccccccccccccccccc\",\"modules\":[\"nats\"],\"number\":1,\"repository\":\"novox/mesh-sdk\"}],\"v\":1}",
|
||||
decode: func(b []byte) (any, error) { var v CandidatesByGroupValue; err := json.Unmarshal(b, &v); return v, err }, subjectIs: nil},
|
||||
{name: "a verdict", subject: "", given: "{\"judge\":\"trunk\",\"module\":\"nats\",\"state\":\"accepted\",\"v\":1}", wire: "{\"judge\":\"trunk\",\"module\":\"nats\",\"state\":\"accepted\",\"v\":1}",
|
||||
decode: func(b []byte) (any, error) { var v VerdictsByDigestValue; err := json.Unmarshal(b, &v); return v, err }, subjectIs: nil},
|
||||
{name: "the predefined list", subject: "", given: "{\"commit\":\"cccccccccccccccccccccccccccccccccccccccc\",\"list\":{\"streams\":[]},\"v\":1}", wire: "{\"commit\":\"cccccccccccccccccccccccccccccccccccccccc\",\"list\":{\"streams\":[]},\"v\":1}",
|
||||
decode: func(b []byte) (any, error) { var v PredefinedByListValue; err := json.Unmarshal(b, &v); return v, err }, subjectIs: nil},
|
||||
{name: "a pull request's configuration", subject: "", given: "{\"declares\":{\"nats\":{\"limit\":{\"default\":1,\"kind\":\"setting\",\"why\":\"a bound\"}}},\"head\":\"cccccccccccccccccccccccccccccccccccccccc\",\"pull\":\"novox/mesh-catalog#1\",\"v\":1}", wire: "{\"declares\":{\"nats\":{\"limit\":{\"default\":1,\"kind\":\"setting\",\"why\":\"a bound\"}}},\"head\":\"cccccccccccccccccccccccccccccccccccccccc\",\"pull\":\"novox/mesh-catalog#1\",\"v\":1}",
|
||||
decode: func(b []byte) (any, error) {
|
||||
var v ConfigCandidatesByPullValue
|
||||
err := json.Unmarshal(b, &v)
|
||||
return v, err
|
||||
}, subjectIs: nil},
|
||||
{name: "a published Go module", subject: "", given: "{\"commit\":\"cccccccccccccccccccccccccccccccccccccccc\",\"module\":\"git.novox.be/novox/mesh-sdk/go\",\"packages\":[{\"digest\":\"ab\",\"package\":\"git.novox.be/novox/mesh-sdk/go/keys\"}],\"repository\":\"novox/mesh-sdk\",\"v\":1}", wire: "{\"commit\":\"cccccccccccccccccccccccccccccccccccccccc\",\"module\":\"git.novox.be/novox/mesh-sdk/go\",\"packages\":[{\"digest\":\"ab\",\"package\":\"git.novox.be/novox/mesh-sdk/go/keys\"}],\"repository\":\"novox/mesh-sdk\",\"v\":1}",
|
||||
decode: func(b []byte) (any, error) { var v PackagesByModuleValue; err := json.Unmarshal(b, &v); return v, err }, subjectIs: nil},
|
||||
{name: "a package series", subject: "", given: "{\"name\":\"go-keys\",\"series\":\"0.1\",\"v\":1}", wire: "{\"name\":\"go-keys\",\"series\":\"0.1\",\"v\":1}",
|
||||
decode: func(b []byte) (any, error) {
|
||||
var v PackagesByPackageSeriesValue
|
||||
err := json.Unmarshal(b, &v)
|
||||
return v, err
|
||||
}, subjectIs: nil},
|
||||
}
|
||||
for _, c := range cases {
|
||||
v, err := c.decode([]byte(c.given))
|
||||
if err != nil {
|
||||
t.Errorf("%s: does not decode: %v", c.name, err)
|
||||
continue
|
||||
}
|
||||
back, err := json.Marshal(v)
|
||||
if err != nil {
|
||||
t.Errorf("%s: does not encode: %v", c.name, err)
|
||||
continue
|
||||
}
|
||||
var got, want any
|
||||
if err := json.Unmarshal(back, &got); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if err := json.Unmarshal([]byte(c.wire), &want); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if !reflect.DeepEqual(got, want) {
|
||||
t.Errorf("%s: reads back as %s, want %s", c.name, back, c.wire)
|
||||
}
|
||||
if c.subjectIs != nil && !c.subjectIs.MatchString(c.subject) {
|
||||
t.Errorf("%s: subject %q is not the contract's %s", c.name, c.subject, c.subjectIs)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,3 @@
|
||||
module git.novox.be/novox/mesh-delivery/contracts/go/seats/meshcatalog
|
||||
|
||||
go 1.26.0
|
||||
@@ -0,0 +1,454 @@
|
||||
// Code generated by seatgen from the seat definitions; DO NOT EDIT.
|
||||
|
||||
// Package meshdelivery is the contract of the seat "mesh-delivery", defined whole (seats/mesh-delivery/seat.json): The delivery gate: reads and steers deliveries, merges and the checks that decide them; the merge is made on the operator's Approve once both checks passed.
|
||||
package meshdelivery
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"strings"
|
||||
)
|
||||
|
||||
// Seat is the seat's name.
|
||||
const Seat = "mesh-delivery"
|
||||
|
||||
const (
|
||||
VerbDeliveries = "deliveries"
|
||||
VerbTimes = "times"
|
||||
VerbShow = "show"
|
||||
VerbGroups = "groups"
|
||||
VerbWhatIf = "what-if"
|
||||
VerbChecks = "checks"
|
||||
VerbTable = "table"
|
||||
VerbStalled = "stalled"
|
||||
VerbRecheck = "recheck"
|
||||
VerbRelease = "release"
|
||||
VerbStop = "stop"
|
||||
VerbClose = "close"
|
||||
VerbRetireHistory = "retire-history"
|
||||
VerbSettle = "settle"
|
||||
)
|
||||
|
||||
// Verbs is every verb the seat serves, in its definition's order: a new copy each call.
|
||||
func Verbs() []string {
|
||||
return []string{VerbDeliveries, VerbTimes, VerbShow, VerbGroups, VerbWhatIf, VerbChecks, VerbTable, VerbStalled, VerbRecheck, VerbRelease, VerbStop, VerbClose, VerbRetireHistory, VerbSettle}
|
||||
}
|
||||
|
||||
const (
|
||||
StateSource = "source"
|
||||
StateMerges = "merges"
|
||||
StateAsks = "asks"
|
||||
StateOwed = "owed"
|
||||
StateTurns = "turns"
|
||||
)
|
||||
|
||||
// SourceByMainKey is the key of one value of the state source under the form "main".
|
||||
func SourceByMainKey() string { return "main" }
|
||||
|
||||
// ParseSourceByMainKey reads the parts of a key of the form "main", in the order of the form; the last result is false for a key of another form.
|
||||
func ParseSourceByMainKey(key string) bool {
|
||||
rest := key
|
||||
if !strings.HasPrefix(rest, "main") {
|
||||
return false
|
||||
}
|
||||
rest = rest[4:]
|
||||
if rest != "" {
|
||||
return false
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
// DecodeSourceByMainValue reads one value of the state source under the form "main", refusing a field SourceByMainValue does not know and any text after the value.
|
||||
func DecodeSourceByMainValue(raw []byte) (SourceByMainValue, error) {
|
||||
var v SourceByMainValue
|
||||
dec := json.NewDecoder(bytes.NewReader(raw))
|
||||
dec.DisallowUnknownFields()
|
||||
if err := dec.Decode(&v); err != nil {
|
||||
return v, err
|
||||
}
|
||||
if dec.More() {
|
||||
return v, errors.New("text after the value")
|
||||
}
|
||||
return v, nil
|
||||
}
|
||||
|
||||
// MergesByGroupKey is the key of one value of the state merges under the form "<group>".
|
||||
func MergesByGroupKey(group string) string { return group }
|
||||
|
||||
// ParseMergesByGroupKey reads the parts of a key of the form "<group>", in the order of the form; the last result is false for a key of another form.
|
||||
func ParseMergesByGroupKey(key string) (string, bool) {
|
||||
rest := key
|
||||
if rest == "" {
|
||||
return "", false
|
||||
}
|
||||
v0 := rest
|
||||
rest = ""
|
||||
if rest != "" {
|
||||
return "", false
|
||||
}
|
||||
return v0, true
|
||||
}
|
||||
|
||||
// DecodeMergesByGroupValue reads one value of the state merges under the form "<group>", refusing a field MergesByGroupValue does not know and any text after the value.
|
||||
func DecodeMergesByGroupValue(raw []byte) (MergesByGroupValue, error) {
|
||||
var v MergesByGroupValue
|
||||
dec := json.NewDecoder(bytes.NewReader(raw))
|
||||
dec.DisallowUnknownFields()
|
||||
if err := dec.Decode(&v); err != nil {
|
||||
return v, err
|
||||
}
|
||||
if dec.More() {
|
||||
return v, errors.New("text after the value")
|
||||
}
|
||||
return v, nil
|
||||
}
|
||||
|
||||
// AsksByMergeGroupHeadsKey is the key of one value of the state asks under the form "merge.<group>.<heads>".
|
||||
func AsksByMergeGroupHeadsKey(group, heads string) string { return "merge." + group + "." + heads }
|
||||
|
||||
// ParseAsksByMergeGroupHeadsKey reads the parts of a key of the form "merge.<group>.<heads>", in the order of the form; the last result is false for a key of another form.
|
||||
func ParseAsksByMergeGroupHeadsKey(key string) (string, string, bool) {
|
||||
rest := key
|
||||
if !strings.HasPrefix(rest, "merge.") {
|
||||
return "", "", false
|
||||
}
|
||||
rest = rest[6:]
|
||||
v1, after1, found1 := strings.Cut(rest, ".")
|
||||
if !found1 || v1 == "" {
|
||||
return "", "", false
|
||||
}
|
||||
rest = after1
|
||||
if rest == "" {
|
||||
return "", "", false
|
||||
}
|
||||
v3 := rest
|
||||
rest = ""
|
||||
if rest != "" {
|
||||
return "", "", false
|
||||
}
|
||||
return v1, v3, true
|
||||
}
|
||||
|
||||
// DecodeAsksByMergeGroupHeadsValue reads one value of the state asks under the form "merge.<group>.<heads>", refusing a field AsksByMergeGroupHeadsValue does not know and any text after the value.
|
||||
func DecodeAsksByMergeGroupHeadsValue(raw []byte) (AsksByMergeGroupHeadsValue, error) {
|
||||
var v AsksByMergeGroupHeadsValue
|
||||
dec := json.NewDecoder(bytes.NewReader(raw))
|
||||
dec.DisallowUnknownFields()
|
||||
if err := dec.Decode(&v); err != nil {
|
||||
return v, err
|
||||
}
|
||||
if dec.More() {
|
||||
return v, errors.New("text after the value")
|
||||
}
|
||||
return v, nil
|
||||
}
|
||||
|
||||
// OwedByRepositoryNumberHeadActKey is the key of one value of the state owed under the form "<repository>.<number>.<head>.<act>".
|
||||
func OwedByRepositoryNumberHeadActKey(repository, number, head, act string) string {
|
||||
return repository + "." + number + "." + head + "." + act
|
||||
}
|
||||
|
||||
// ParseOwedByRepositoryNumberHeadActKey reads the parts of a key of the form "<repository>.<number>.<head>.<act>", in the order of the form; the last result is false for a key of another form.
|
||||
func ParseOwedByRepositoryNumberHeadActKey(key string) (string, string, string, string, bool) {
|
||||
rest := key
|
||||
v0, after0, found0 := strings.Cut(rest, ".")
|
||||
if !found0 || v0 == "" {
|
||||
return "", "", "", "", false
|
||||
}
|
||||
rest = after0
|
||||
v2, after2, found2 := strings.Cut(rest, ".")
|
||||
if !found2 || v2 == "" {
|
||||
return "", "", "", "", false
|
||||
}
|
||||
rest = after2
|
||||
v4, after4, found4 := strings.Cut(rest, ".")
|
||||
if !found4 || v4 == "" {
|
||||
return "", "", "", "", false
|
||||
}
|
||||
rest = after4
|
||||
if rest == "" {
|
||||
return "", "", "", "", false
|
||||
}
|
||||
v6 := rest
|
||||
rest = ""
|
||||
if rest != "" {
|
||||
return "", "", "", "", false
|
||||
}
|
||||
return v0, v2, v4, v6, true
|
||||
}
|
||||
|
||||
// DecodeOwedByRepositoryNumberHeadActValue reads one value of the state owed under the form "<repository>.<number>.<head>.<act>", refusing a field OwedByRepositoryNumberHeadActValue does not know and any text after the value.
|
||||
func DecodeOwedByRepositoryNumberHeadActValue(raw []byte) (OwedByRepositoryNumberHeadActValue, error) {
|
||||
var v OwedByRepositoryNumberHeadActValue
|
||||
dec := json.NewDecoder(bytes.NewReader(raw))
|
||||
dec.DisallowUnknownFields()
|
||||
if err := dec.Decode(&v); err != nil {
|
||||
return v, err
|
||||
}
|
||||
if dec.More() {
|
||||
return v, errors.New("text after the value")
|
||||
}
|
||||
return v, nil
|
||||
}
|
||||
|
||||
// TurnsByRepositoryKey is the key of one value of the state turns under the form "<repository>".
|
||||
func TurnsByRepositoryKey(repository string) string { return repository }
|
||||
|
||||
// ParseTurnsByRepositoryKey reads the parts of a key of the form "<repository>", in the order of the form; the last result is false for a key of another form.
|
||||
func ParseTurnsByRepositoryKey(key string) (string, bool) {
|
||||
rest := key
|
||||
if rest == "" {
|
||||
return "", false
|
||||
}
|
||||
v0 := rest
|
||||
rest = ""
|
||||
if rest != "" {
|
||||
return "", false
|
||||
}
|
||||
return v0, true
|
||||
}
|
||||
|
||||
// DecodeTurnsByRepositoryValue reads one value of the state turns under the form "<repository>", refusing a field TurnsByRepositoryValue does not know and any text after the value.
|
||||
func DecodeTurnsByRepositoryValue(raw []byte) (TurnsByRepositoryValue, error) {
|
||||
var v TurnsByRepositoryValue
|
||||
dec := json.NewDecoder(bytes.NewReader(raw))
|
||||
dec.DisallowUnknownFields()
|
||||
if err := dec.Decode(&v); err != nil {
|
||||
return v, err
|
||||
}
|
||||
if dec.More() {
|
||||
return v, errors.New("text after the value")
|
||||
}
|
||||
return v, nil
|
||||
}
|
||||
|
||||
// DeliveriesInput is what deliveries takes.
|
||||
type DeliveriesInput struct {
|
||||
// "true": the final ones of the last thirty days too
|
||||
All string `json:"all,omitempty"`
|
||||
// a group's id (its branch name)
|
||||
Group string `json:"group,omitempty"`
|
||||
// owner/repository
|
||||
Repository string `json:"repository,omitempty"`
|
||||
// one state, e.g. delivering or held
|
||||
State string `json:"state,omitempty"`
|
||||
}
|
||||
|
||||
// The values of DeliveriesInput's all.
|
||||
const (
|
||||
DeliveriesInputAllTrue = "true"
|
||||
DeliveriesInputAllFalse = "false"
|
||||
)
|
||||
|
||||
// DeliveriesAnswer is what deliveries answers.
|
||||
type DeliveriesAnswer = map[string]any
|
||||
|
||||
// TimesInput is what times takes.
|
||||
type TimesInput struct {
|
||||
}
|
||||
|
||||
// TimesAnswer is what times answers.
|
||||
type TimesAnswer = map[string]any
|
||||
|
||||
// ShowInput is what show takes.
|
||||
type ShowInput struct {
|
||||
// a delivery's id, or a group's
|
||||
ID string `json:"id"`
|
||||
}
|
||||
|
||||
// ShowAnswer is what show answers.
|
||||
type ShowAnswer = map[string]any
|
||||
|
||||
// GroupsInput is what groups takes.
|
||||
type GroupsInput struct {
|
||||
// "true": the groups that ended too
|
||||
All string `json:"all,omitempty"`
|
||||
}
|
||||
|
||||
// The values of GroupsInput's all.
|
||||
const (
|
||||
GroupsInputAllTrue = "true"
|
||||
GroupsInputAllFalse = "false"
|
||||
)
|
||||
|
||||
// GroupsAnswer is what groups answers.
|
||||
type GroupsAnswer = map[string]any
|
||||
|
||||
// WhatIfInput is what what-if takes.
|
||||
type WhatIfInput struct {
|
||||
}
|
||||
|
||||
// WhatIfAnswer is what what-if answers.
|
||||
type WhatIfAnswer = map[string]any
|
||||
|
||||
// ChecksInput is what checks takes.
|
||||
type ChecksInput struct {
|
||||
// a commit's sha, or the start of one, instead of a pull request
|
||||
Commit string `json:"commit,omitempty"`
|
||||
// a pull request's number: its head is read
|
||||
Number string `json:"number,omitempty"`
|
||||
// owner/repository
|
||||
Repository string `json:"repository"`
|
||||
}
|
||||
|
||||
// ChecksAnswer is what checks answers.
|
||||
type ChecksAnswer = map[string]any
|
||||
|
||||
// TableInput is what table takes.
|
||||
type TableInput struct {
|
||||
}
|
||||
|
||||
// TableAnswer is what table answers.
|
||||
type TableAnswer = map[string]any
|
||||
|
||||
// StalledInput is what stalled takes.
|
||||
type StalledInput struct {
|
||||
}
|
||||
|
||||
// StalledAnswer is what stalled answers.
|
||||
type StalledAnswer = map[string]any
|
||||
|
||||
// RecheckInput is what recheck takes.
|
||||
type RecheckInput struct {
|
||||
// the delivery's id
|
||||
ID string `json:"id"`
|
||||
// why, kept with the transition
|
||||
Why string `json:"why"`
|
||||
}
|
||||
|
||||
// RecheckAnswer is what recheck answers.
|
||||
type RecheckAnswer = map[string]any
|
||||
|
||||
// ReleaseInput is what release takes.
|
||||
type ReleaseInput struct {
|
||||
}
|
||||
|
||||
// ReleaseAnswer is what release answers.
|
||||
type ReleaseAnswer = map[string]any
|
||||
|
||||
// StopInput is what stop takes.
|
||||
type StopInput struct {
|
||||
}
|
||||
|
||||
// StopAnswer is what stop answers.
|
||||
type StopAnswer = map[string]any
|
||||
|
||||
// CloseInput is what close takes.
|
||||
type CloseInput struct {
|
||||
// the delivery's id
|
||||
ID string `json:"id"`
|
||||
// the condition it answers
|
||||
Why string `json:"why"`
|
||||
}
|
||||
|
||||
// CloseAnswer is what close answers.
|
||||
type CloseAnswer = map[string]any
|
||||
|
||||
// RetireHistoryInput is what retire-history takes.
|
||||
type RetireHistoryInput struct {
|
||||
}
|
||||
|
||||
// RetireHistoryAnswer is what retire-history answers.
|
||||
type RetireHistoryAnswer = map[string]any
|
||||
|
||||
// SettleInput is what settle takes.
|
||||
type SettleInput struct {
|
||||
// the held deliveries' ids (owner/repository@commit)
|
||||
Ids []string `json:"ids"`
|
||||
// why they may be settled, shown to the operator
|
||||
Why string `json:"why"`
|
||||
}
|
||||
|
||||
// SettleAnswer is what settle answers.
|
||||
type SettleAnswer = map[string]any
|
||||
|
||||
// SourceByMainValue is one value of the state source under the key main.
|
||||
type SourceByMainValue struct {
|
||||
// the group that moved it
|
||||
Group string `json:"group"`
|
||||
// repository to the merged head, a full commit
|
||||
Heads map[string]string `json:"heads"`
|
||||
// the layout version
|
||||
V int64 `json:"v,omitempty"`
|
||||
}
|
||||
|
||||
// MergesByGroupValuePullsItem is a type of the contract.
|
||||
type MergesByGroupValuePullsItem struct {
|
||||
Commit string `json:"commit,omitempty"`
|
||||
Head string `json:"head"`
|
||||
Number int64 `json:"number"`
|
||||
Repository string `json:"repository"`
|
||||
// merging, merged or refused
|
||||
State string `json:"state"`
|
||||
Words string `json:"words,omitempty"`
|
||||
}
|
||||
|
||||
// MergesByGroupValue is one value of the state merges under the key <group>.
|
||||
type MergesByGroupValue struct {
|
||||
// <repository>@<number> to the approved head
|
||||
Approved map[string]string `json:"approved,omitempty"`
|
||||
// the ask key
|
||||
Ask string `json:"ask"`
|
||||
Group string `json:"group"`
|
||||
Pulls []MergesByGroupValuePullsItem `json:"pulls"`
|
||||
// the layout version
|
||||
V int64 `json:"v,omitempty"`
|
||||
}
|
||||
|
||||
// AsksByMergeGroupHeadsValueHeadsItem is a type of the contract.
|
||||
type AsksByMergeGroupHeadsValueHeadsItem struct {
|
||||
// the full commit
|
||||
Head string `json:"head"`
|
||||
Number int64 `json:"number"`
|
||||
// <owner>/<name>
|
||||
Repository string `json:"repository"`
|
||||
}
|
||||
|
||||
// AsksByMergeGroupHeadsValue is one value of the state asks under the key merge.<group>.<heads>.
|
||||
type AsksByMergeGroupHeadsValue struct {
|
||||
// the operator-channel ask as raised
|
||||
Ask any `json:"ask"`
|
||||
// the number of times it was asked
|
||||
Asking int64 `json:"asking,omitempty"`
|
||||
Group string `json:"group"`
|
||||
Heads []AsksByMergeGroupHeadsValueHeadsItem `json:"heads"`
|
||||
// the layout version
|
||||
V int64 `json:"v,omitempty"`
|
||||
}
|
||||
|
||||
// OwedByRepositoryNumberHeadActValue is one value of the state owed under the key <repository>.<number>.<head>.<act>.
|
||||
type OwedByRepositoryNumberHeadActValue struct {
|
||||
// status=<context>, view, note or comment.<seq>
|
||||
Act string `json:"act"`
|
||||
Context string `json:"context,omitempty"`
|
||||
Ref string `json:"ref,omitempty"`
|
||||
State string `json:"state,omitempty"`
|
||||
Target string `json:"target,omitempty"`
|
||||
Text string `json:"text,omitempty"`
|
||||
// the layout version
|
||||
V int64 `json:"v,omitempty"`
|
||||
}
|
||||
|
||||
// TurnsByRepositoryValueWaitingItem is a type of the contract.
|
||||
type TurnsByRepositoryValueWaitingItem struct {
|
||||
// when (RFC 3339 time)
|
||||
Approved string `json:"approved"`
|
||||
Ask string `json:"ask,omitempty"`
|
||||
Group string `json:"group"`
|
||||
Heads string `json:"heads"`
|
||||
// when (RFC 3339 time)
|
||||
Since string `json:"since"`
|
||||
Step string `json:"step,omitempty"`
|
||||
WaitsFor string `json:"waits-for,omitempty"`
|
||||
// when (RFC 3339 time)
|
||||
WaitsSince string `json:"waits-since,omitempty"`
|
||||
}
|
||||
|
||||
// TurnsByRepositoryValue is one value of the state turns under the key <repository>.
|
||||
type TurnsByRepositoryValue struct {
|
||||
Repository string `json:"repository"`
|
||||
// the layout version
|
||||
V int64 `json:"v,omitempty"`
|
||||
Waiting []TurnsByRepositoryValueWaitingItem `json:"waiting"`
|
||||
}
|
||||
@@ -0,0 +1,64 @@
|
||||
// Code generated by seatgen from the seat definitions; DO NOT EDIT.
|
||||
|
||||
package meshdelivery
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"reflect"
|
||||
"regexp"
|
||||
"testing"
|
||||
)
|
||||
|
||||
// TestFixtures reads every fixture of seats/mesh-delivery/fixtures: each decodes into its type, a field the type does not
|
||||
// know is dropped (ADR 0608 §6), and it reads back as its wire says; each subject named is the contract's.
|
||||
func TestFixtures(t *testing.T) {
|
||||
cases := []struct {
|
||||
name, subject, given, wire string
|
||||
decode func([]byte) (any, error)
|
||||
subjectIs *regexp.Regexp
|
||||
}{
|
||||
{name: "the source revision", subject: "", given: "{\"group\":\"feat/registry\",\"heads\":{\"novox/mesh-sdk\":\"cccccccccccccccccccccccccccccccccccccccc\"},\"v\":1}", wire: "{\"group\":\"feat/registry\",\"heads\":{\"novox/mesh-sdk\":\"cccccccccccccccccccccccccccccccccccccccc\"},\"v\":1}",
|
||||
decode: func(b []byte) (any, error) { var v SourceByMainValue; err := json.Unmarshal(b, &v); return v, err }, subjectIs: nil},
|
||||
{name: "a merge", subject: "", given: "{\"ask\":\"merge.feat=registry.0123456789ab\",\"group\":\"feat/registry\",\"pulls\":[{\"head\":\"cccccccccccccccccccccccccccccccccccccccc\",\"number\":1,\"repository\":\"novox/mesh-sdk\",\"state\":\"merging\"}],\"v\":1}", wire: "{\"ask\":\"merge.feat=registry.0123456789ab\",\"group\":\"feat/registry\",\"pulls\":[{\"head\":\"cccccccccccccccccccccccccccccccccccccccc\",\"number\":1,\"repository\":\"novox/mesh-sdk\",\"state\":\"merging\"}],\"v\":1}",
|
||||
decode: func(b []byte) (any, error) { var v MergesByGroupValue; err := json.Unmarshal(b, &v); return v, err }, subjectIs: nil},
|
||||
{name: "an ask", subject: "", given: "{\"ask\":{\"headline\":\"merge\"},\"group\":\"feat/registry\",\"heads\":[{\"head\":\"cccccccccccccccccccccccccccccccccccccccc\",\"number\":1,\"repository\":\"novox/mesh-sdk\"}],\"v\":1}", wire: "{\"ask\":{\"headline\":\"merge\"},\"group\":\"feat/registry\",\"heads\":[{\"head\":\"cccccccccccccccccccccccccccccccccccccccc\",\"number\":1,\"repository\":\"novox/mesh-sdk\"}],\"v\":1}",
|
||||
decode: func(b []byte) (any, error) {
|
||||
var v AsksByMergeGroupHeadsValue
|
||||
err := json.Unmarshal(b, &v)
|
||||
return v, err
|
||||
}, subjectIs: nil},
|
||||
{name: "an act owed", subject: "", given: "{\"act\":\"view\",\"v\":1}", wire: "{\"act\":\"view\",\"v\":1}",
|
||||
decode: func(b []byte) (any, error) {
|
||||
var v OwedByRepositoryNumberHeadActValue
|
||||
err := json.Unmarshal(b, &v)
|
||||
return v, err
|
||||
}, subjectIs: nil},
|
||||
{name: "a turn", subject: "", given: "{\"repository\":\"novox/mesh-sdk\",\"v\":1,\"waiting\":[{\"approved\":\"2026-11-05T10:00:00Z\",\"group\":\"feat/registry\",\"heads\":\"0123456789ab\",\"since\":\"2026-11-05T10:01:00Z\"}]}", wire: "{\"repository\":\"novox/mesh-sdk\",\"v\":1,\"waiting\":[{\"approved\":\"2026-11-05T10:00:00Z\",\"group\":\"feat/registry\",\"heads\":\"0123456789ab\",\"since\":\"2026-11-05T10:01:00Z\"}]}",
|
||||
decode: func(b []byte) (any, error) { var v TurnsByRepositoryValue; err := json.Unmarshal(b, &v); return v, err }, subjectIs: nil},
|
||||
}
|
||||
for _, c := range cases {
|
||||
v, err := c.decode([]byte(c.given))
|
||||
if err != nil {
|
||||
t.Errorf("%s: does not decode: %v", c.name, err)
|
||||
continue
|
||||
}
|
||||
back, err := json.Marshal(v)
|
||||
if err != nil {
|
||||
t.Errorf("%s: does not encode: %v", c.name, err)
|
||||
continue
|
||||
}
|
||||
var got, want any
|
||||
if err := json.Unmarshal(back, &got); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if err := json.Unmarshal([]byte(c.wire), &want); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if !reflect.DeepEqual(got, want) {
|
||||
t.Errorf("%s: reads back as %s, want %s", c.name, back, c.wire)
|
||||
}
|
||||
if c.subjectIs != nil && !c.subjectIs.MatchString(c.subject) {
|
||||
t.Errorf("%s: subject %q is not the contract's %s", c.name, c.subject, c.subjectIs)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,3 @@
|
||||
module git.novox.be/novox/mesh-delivery/contracts/go/seats/meshdelivery
|
||||
|
||||
go 1.26.0
|
||||
@@ -0,0 +1,438 @@
|
||||
// Code generated by seatgen from the seat definitions; DO NOT EDIT.
|
||||
|
||||
// Package meshdeliverychecker is the contract of the seat "mesh-delivery-checker", defined whole (seats/mesh-delivery-checker/seat.json): The merge check's tries: start one, read one, end one. The checker's own events stay under its own subjects (ADR 0620 §2).
|
||||
package meshdeliverychecker
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"strings"
|
||||
)
|
||||
|
||||
// Seat is the seat's name.
|
||||
const Seat = "mesh-delivery-checker"
|
||||
|
||||
const (
|
||||
VerbTry = "try"
|
||||
VerbTryRead = "try-read"
|
||||
VerbTryEnd = "try-end"
|
||||
)
|
||||
|
||||
// Verbs is every verb the seat serves, in its definition's order: a new copy each call.
|
||||
func Verbs() []string {
|
||||
return []string{VerbTry, VerbTryRead, VerbTryEnd}
|
||||
}
|
||||
|
||||
// The holder's own events, listed beside the seat and published under the holder's subjects (ADR 0620 §2).
|
||||
const (
|
||||
HolderEventVerdict = "verdict"
|
||||
HolderEventTagWanted = "tag-wanted"
|
||||
HolderEventPreviewWritten = "preview.written"
|
||||
)
|
||||
|
||||
// HolderSubjectVerdict is where the holder publishes verdict, kept in checker.asks.
|
||||
func HolderSubjectVerdict(holder string) string { return "mesh.stream." + holder + ".event.verdict" }
|
||||
|
||||
// HolderSubjectTagWanted is where the holder publishes tag-wanted, kept in checker.asks.
|
||||
func HolderSubjectTagWanted(holder string) string {
|
||||
return "mesh.stream." + holder + ".event.tag-wanted"
|
||||
}
|
||||
|
||||
// HolderSubjectPreviewWritten is where the holder publishes preview.written, kept in checks.
|
||||
func HolderSubjectPreviewWritten(holder string) string {
|
||||
return "mesh.stream." + holder + ".event.preview.written"
|
||||
}
|
||||
|
||||
const (
|
||||
StateVerdicts = "verdicts"
|
||||
StateKept = "kept"
|
||||
StateOwed = "owed"
|
||||
StatePreviews = "previews"
|
||||
)
|
||||
|
||||
// VerdictsByGroupHeadsKey is the key of one value of the state verdicts under the form "<group>.<heads>".
|
||||
func VerdictsByGroupHeadsKey(group, heads string) string { return group + "." + heads }
|
||||
|
||||
// ParseVerdictsByGroupHeadsKey reads the parts of a key of the form "<group>.<heads>", in the order of the form; the last result is false for a key of another form.
|
||||
func ParseVerdictsByGroupHeadsKey(key string) (string, string, bool) {
|
||||
rest := key
|
||||
v0, after0, found0 := strings.Cut(rest, ".")
|
||||
if !found0 || v0 == "" {
|
||||
return "", "", false
|
||||
}
|
||||
rest = after0
|
||||
if rest == "" {
|
||||
return "", "", false
|
||||
}
|
||||
v2 := rest
|
||||
rest = ""
|
||||
if rest != "" {
|
||||
return "", "", false
|
||||
}
|
||||
return v0, v2, true
|
||||
}
|
||||
|
||||
// DecodeVerdictsByGroupHeadsValue reads one value of the state verdicts under the form "<group>.<heads>", refusing a field VerdictsByGroupHeadsValue does not know and any text after the value.
|
||||
func DecodeVerdictsByGroupHeadsValue(raw []byte) (VerdictsByGroupHeadsValue, error) {
|
||||
var v VerdictsByGroupHeadsValue
|
||||
dec := json.NewDecoder(bytes.NewReader(raw))
|
||||
dec.DisallowUnknownFields()
|
||||
if err := dec.Decode(&v); err != nil {
|
||||
return v, err
|
||||
}
|
||||
if dec.More() {
|
||||
return v, errors.New("text after the value")
|
||||
}
|
||||
return v, nil
|
||||
}
|
||||
|
||||
// KeptByLevelDigestKey is the key of one value of the state kept under the form "<level>.<digest>".
|
||||
func KeptByLevelDigestKey(level, digest string) string { return level + "." + digest }
|
||||
|
||||
// ParseKeptByLevelDigestKey reads the parts of a key of the form "<level>.<digest>", in the order of the form; the last result is false for a key of another form.
|
||||
func ParseKeptByLevelDigestKey(key string) (string, string, bool) {
|
||||
rest := key
|
||||
v0, after0, found0 := strings.Cut(rest, ".")
|
||||
if !found0 || v0 == "" {
|
||||
return "", "", false
|
||||
}
|
||||
rest = after0
|
||||
if rest == "" {
|
||||
return "", "", false
|
||||
}
|
||||
v2 := rest
|
||||
rest = ""
|
||||
if rest != "" {
|
||||
return "", "", false
|
||||
}
|
||||
return v0, v2, true
|
||||
}
|
||||
|
||||
// DecodeKeptByLevelDigestValue reads one value of the state kept under the form "<level>.<digest>", refusing a field KeptByLevelDigestValue does not know and any text after the value.
|
||||
func DecodeKeptByLevelDigestValue(raw []byte) (KeptByLevelDigestValue, error) {
|
||||
var v KeptByLevelDigestValue
|
||||
dec := json.NewDecoder(bytes.NewReader(raw))
|
||||
dec.DisallowUnknownFields()
|
||||
if err := dec.Decode(&v); err != nil {
|
||||
return v, err
|
||||
}
|
||||
if dec.More() {
|
||||
return v, errors.New("text after the value")
|
||||
}
|
||||
return v, nil
|
||||
}
|
||||
|
||||
// OwedByRepositoryNumberHeadActKey is the key of one value of the state owed under the form "<repository>.<number>.<head>.<act>".
|
||||
func OwedByRepositoryNumberHeadActKey(repository, number, head, act string) string {
|
||||
return repository + "." + number + "." + head + "." + act
|
||||
}
|
||||
|
||||
// ParseOwedByRepositoryNumberHeadActKey reads the parts of a key of the form "<repository>.<number>.<head>.<act>", in the order of the form; the last result is false for a key of another form.
|
||||
func ParseOwedByRepositoryNumberHeadActKey(key string) (string, string, string, string, bool) {
|
||||
rest := key
|
||||
v0, after0, found0 := strings.Cut(rest, ".")
|
||||
if !found0 || v0 == "" {
|
||||
return "", "", "", "", false
|
||||
}
|
||||
rest = after0
|
||||
v2, after2, found2 := strings.Cut(rest, ".")
|
||||
if !found2 || v2 == "" {
|
||||
return "", "", "", "", false
|
||||
}
|
||||
rest = after2
|
||||
v4, after4, found4 := strings.Cut(rest, ".")
|
||||
if !found4 || v4 == "" {
|
||||
return "", "", "", "", false
|
||||
}
|
||||
rest = after4
|
||||
if rest == "" {
|
||||
return "", "", "", "", false
|
||||
}
|
||||
v6 := rest
|
||||
rest = ""
|
||||
if rest != "" {
|
||||
return "", "", "", "", false
|
||||
}
|
||||
return v0, v2, v4, v6, true
|
||||
}
|
||||
|
||||
// DecodeOwedByRepositoryNumberHeadActValue reads one value of the state owed under the form "<repository>.<number>.<head>.<act>", refusing a field OwedByRepositoryNumberHeadActValue does not know and any text after the value.
|
||||
func DecodeOwedByRepositoryNumberHeadActValue(raw []byte) (OwedByRepositoryNumberHeadActValue, error) {
|
||||
var v OwedByRepositoryNumberHeadActValue
|
||||
dec := json.NewDecoder(bytes.NewReader(raw))
|
||||
dec.DisallowUnknownFields()
|
||||
if err := dec.Decode(&v); err != nil {
|
||||
return v, err
|
||||
}
|
||||
if dec.More() {
|
||||
return v, errors.New("text after the value")
|
||||
}
|
||||
return v, nil
|
||||
}
|
||||
|
||||
// PreviewsByRepositoryHeadTrunkKey is the key of one value of the state previews under the form "<repository>.<head>.<trunk>".
|
||||
func PreviewsByRepositoryHeadTrunkKey(repository, head, trunk string) string {
|
||||
return repository + "." + head + "." + trunk
|
||||
}
|
||||
|
||||
// ParsePreviewsByRepositoryHeadTrunkKey reads the parts of a key of the form "<repository>.<head>.<trunk>", in the order of the form; the last result is false for a key of another form.
|
||||
func ParsePreviewsByRepositoryHeadTrunkKey(key string) (string, string, string, bool) {
|
||||
rest := key
|
||||
v0, after0, found0 := strings.Cut(rest, ".")
|
||||
if !found0 || v0 == "" {
|
||||
return "", "", "", false
|
||||
}
|
||||
rest = after0
|
||||
v2, after2, found2 := strings.Cut(rest, ".")
|
||||
if !found2 || v2 == "" {
|
||||
return "", "", "", false
|
||||
}
|
||||
rest = after2
|
||||
if rest == "" {
|
||||
return "", "", "", false
|
||||
}
|
||||
v4 := rest
|
||||
rest = ""
|
||||
if rest != "" {
|
||||
return "", "", "", false
|
||||
}
|
||||
return v0, v2, v4, true
|
||||
}
|
||||
|
||||
// DecodePreviewsByRepositoryHeadTrunkValue reads one value of the state previews under the form "<repository>.<head>.<trunk>", refusing a field PreviewsByRepositoryHeadTrunkValue does not know and any text after the value.
|
||||
func DecodePreviewsByRepositoryHeadTrunkValue(raw []byte) (PreviewsByRepositoryHeadTrunkValue, error) {
|
||||
var v PreviewsByRepositoryHeadTrunkValue
|
||||
dec := json.NewDecoder(bytes.NewReader(raw))
|
||||
dec.DisallowUnknownFields()
|
||||
if err := dec.Decode(&v); err != nil {
|
||||
return v, err
|
||||
}
|
||||
if dec.More() {
|
||||
return v, errors.New("text after the value")
|
||||
}
|
||||
return v, nil
|
||||
}
|
||||
|
||||
// TryInput is what try takes.
|
||||
type TryInput struct {
|
||||
// the full commit to try, 40 or 64 hex digits
|
||||
Head string `json:"head"`
|
||||
// unit or integration: only this level; both when absent
|
||||
Level string `json:"level,omitempty"`
|
||||
// <owner>/<name>
|
||||
Repository string `json:"repository"`
|
||||
// a go test -run pattern: only those tests run, and nothing is kept
|
||||
Tests string `json:"tests,omitempty"`
|
||||
// the trunk commit to merge it onto, or the trunk's branch for its head now; the trunk's head when absent
|
||||
Trunk string `json:"trunk,omitempty"`
|
||||
}
|
||||
|
||||
// TryAnswer is what try answers.
|
||||
type TryAnswer struct {
|
||||
// the try's id
|
||||
ID string `json:"id"`
|
||||
// its state
|
||||
State string `json:"state,omitempty"`
|
||||
}
|
||||
|
||||
// TryReadInput is what try-read takes.
|
||||
type TryReadInput struct {
|
||||
// the try's id, try-<digits>
|
||||
ID string `json:"id"`
|
||||
}
|
||||
|
||||
// TryReadAnswer is what try-read answers.
|
||||
type TryReadAnswer = map[string]any
|
||||
|
||||
// TryEndInput is what try-end takes.
|
||||
type TryEndInput struct {
|
||||
// the try's id, try-<digits>
|
||||
ID string `json:"id"`
|
||||
// why it is ended, in words
|
||||
Why string `json:"why"`
|
||||
}
|
||||
|
||||
// TryEndAnswer is what try-end answers.
|
||||
type TryEndAnswer struct {
|
||||
// it is ended
|
||||
Ended bool `json:"ended"`
|
||||
// the try
|
||||
ID string `json:"id"`
|
||||
}
|
||||
|
||||
// VerdictHolderEventSeriesItem is a type of the contract.
|
||||
type VerdictHolderEventSeriesItem struct {
|
||||
// its artifact, or the package's ecosystem name as the module's packages name it
|
||||
Artifact string `json:"artifact"`
|
||||
// the series on trunk
|
||||
From string `json:"from"`
|
||||
// the module whose artifact's series changes
|
||||
Module string `json:"module"`
|
||||
// the series the checked head gives it
|
||||
To string `json:"to"`
|
||||
}
|
||||
|
||||
// VerdictHolderEvent is the body of the holder's event verdict.
|
||||
type VerdictHolderEvent struct {
|
||||
// the commit checked
|
||||
Head string `json:"head"`
|
||||
// the generated files out of step with their seat definitions, each path (design 26)
|
||||
OutOfStep []string `json:"out-of-step,omitempty"`
|
||||
// the pull request's number, for a pull request's check
|
||||
Pull int64 `json:"pull,omitempty"`
|
||||
// owner/repo
|
||||
Repository string `json:"repository"`
|
||||
// each change of an artifact's series the checked head makes against trunk's manifests, once each (design 32); absent when it changes none
|
||||
Series []VerdictHolderEventSeriesItem `json:"series,omitempty"`
|
||||
// pass or fail
|
||||
Verdict string `json:"verdict"`
|
||||
}
|
||||
|
||||
// TagWantedHolderEvent is the body of the holder's event tag-wanted.
|
||||
type TagWantedHolderEvent struct {
|
||||
// the pull request's branch: its tag is removed when the branch ends
|
||||
Branch string `json:"branch"`
|
||||
// its head
|
||||
Head string `json:"head"`
|
||||
// the group member
|
||||
Module string `json:"module"`
|
||||
// the package it declares
|
||||
Package string `json:"package"`
|
||||
// the pull request
|
||||
Pull int64 `json:"pull,omitempty"`
|
||||
// owner/repo
|
||||
Repository string `json:"repository"`
|
||||
// the tag wanted
|
||||
Tag string `json:"tag,omitempty"`
|
||||
}
|
||||
|
||||
// VerdictsByGroupHeadsValueComposition is a type of the contract.
|
||||
type VerdictsByGroupHeadsValueComposition struct {
|
||||
Fpset string `json:"fpset,omitempty"`
|
||||
Machines []string `json:"machines,omitempty"`
|
||||
Sdk string `json:"sdk,omitempty"`
|
||||
// not-run, pass or fail
|
||||
State string `json:"state"`
|
||||
Words string `json:"words,omitempty"`
|
||||
}
|
||||
|
||||
// VerdictsByGroupHeadsValueFailed is a type of the contract.
|
||||
type VerdictsByGroupHeadsValueFailed struct {
|
||||
Machine string `json:"machine,omitempty"`
|
||||
Test string `json:"test,omitempty"`
|
||||
Words string `json:"words,omitempty"`
|
||||
}
|
||||
|
||||
// VerdictsByGroupHeadsValueFailuresItem is a type of the contract.
|
||||
type VerdictsByGroupHeadsValueFailuresItem struct {
|
||||
Machine string `json:"machine,omitempty"`
|
||||
Test string `json:"test,omitempty"`
|
||||
Words string `json:"words,omitempty"`
|
||||
}
|
||||
|
||||
// VerdictsByGroupHeadsValueLevelsValue is a type of the contract.
|
||||
type VerdictsByGroupHeadsValueLevelsValue struct {
|
||||
Kept int64 `json:"kept"`
|
||||
Run int64 `json:"run"`
|
||||
Skipped int64 `json:"skipped,omitempty"`
|
||||
}
|
||||
|
||||
// VerdictsByGroupHeadsValueSeriesItem is a type of the contract.
|
||||
type VerdictsByGroupHeadsValueSeriesItem struct {
|
||||
Artifact string `json:"artifact"`
|
||||
From string `json:"from"`
|
||||
Module string `json:"module"`
|
||||
To string `json:"to"`
|
||||
}
|
||||
|
||||
// VerdictsByGroupHeadsValueTook is milliseconds.
|
||||
type VerdictsByGroupHeadsValueTook struct {
|
||||
Clone int64 `json:"clone,omitempty"`
|
||||
Composition int64 `json:"composition,omitempty"`
|
||||
IntegrationRun int64 `json:"integration-run,omitempty"`
|
||||
IntegrationSetup int64 `json:"integration-setup,omitempty"`
|
||||
Lint int64 `json:"lint,omitempty"`
|
||||
Queue int64 `json:"queue,omitempty"`
|
||||
Unit int64 `json:"unit,omitempty"`
|
||||
}
|
||||
|
||||
// VerdictsByGroupHeadsValue is one value of the state verdicts under the key <group>.<heads>.
|
||||
type VerdictsByGroupHeadsValue struct {
|
||||
Beside map[string]string `json:"beside,omitempty"`
|
||||
CarriedFrom map[string]string `json:"carried-from,omitempty"`
|
||||
Composition *VerdictsByGroupHeadsValueComposition `json:"composition,omitempty"`
|
||||
Failed *VerdictsByGroupHeadsValueFailed `json:"failed,omitempty"`
|
||||
Failures []VerdictsByGroupHeadsValueFailuresItem `json:"failures,omitempty"`
|
||||
Group string `json:"group"`
|
||||
// 12 hex digits
|
||||
Heads string `json:"heads"`
|
||||
// by level, unit or integration
|
||||
Levels map[string]VerdictsByGroupHeadsValueLevelsValue `json:"levels,omitempty"`
|
||||
Machines []string `json:"machines,omitempty"`
|
||||
OverBound []string `json:"over-bound,omitempty"`
|
||||
Sdk string `json:"sdk,omitempty"`
|
||||
Series []VerdictsByGroupHeadsValueSeriesItem `json:"series,omitempty"`
|
||||
// running, pass or fail
|
||||
State string `json:"state"`
|
||||
// milliseconds
|
||||
Took *VerdictsByGroupHeadsValueTook `json:"took,omitempty"`
|
||||
// repository to the trunk head merged onto
|
||||
Trunk map[string]string `json:"trunk,omitempty"`
|
||||
// the layout version
|
||||
V int64 `json:"v,omitempty"`
|
||||
}
|
||||
|
||||
// KeptByLevelDigestValue is one value of the state kept under the key <level>.<digest>.
|
||||
type KeptByLevelDigestValue struct {
|
||||
// when (RFC 3339 time)
|
||||
At string `json:"at,omitempty"`
|
||||
Level string `json:"level"`
|
||||
Package string `json:"package"`
|
||||
Repository string `json:"repository"`
|
||||
// pass: a failure is never kept
|
||||
State string `json:"state"`
|
||||
Tests int64 `json:"tests,omitempty"`
|
||||
Took int64 `json:"took,omitempty"`
|
||||
Toolchain string `json:"toolchain,omitempty"`
|
||||
// the layout version
|
||||
V int64 `json:"v,omitempty"`
|
||||
}
|
||||
|
||||
// OwedByRepositoryNumberHeadActValue is one value of the state owed under the key <repository>.<number>.<head>.<act>.
|
||||
type OwedByRepositoryNumberHeadActValue struct {
|
||||
// status=<context>, view, note or comment.<seq>
|
||||
Act string `json:"act"`
|
||||
Context string `json:"context,omitempty"`
|
||||
Ref string `json:"ref,omitempty"`
|
||||
State string `json:"state,omitempty"`
|
||||
Target string `json:"target,omitempty"`
|
||||
Text string `json:"text,omitempty"`
|
||||
// the layout version
|
||||
V int64 `json:"v,omitempty"`
|
||||
}
|
||||
|
||||
// PreviewsByRepositoryHeadTrunkValueModulesValue is a type of the contract.
|
||||
type PreviewsByRepositoryHeadTrunkValueModulesValue struct {
|
||||
Locks map[string]string `json:"locks,omitempty"`
|
||||
// manifest digest
|
||||
Manifest string `json:"manifest"`
|
||||
Packages []string `json:"packages,omitempty"`
|
||||
Paths map[string]string `json:"paths,omitempty"`
|
||||
Tree string `json:"tree"`
|
||||
}
|
||||
|
||||
// PreviewsByRepositoryHeadTrunkValue is one value of the state previews under the key <repository>.<head>.<trunk>.
|
||||
type PreviewsByRepositoryHeadTrunkValue struct {
|
||||
// files in conflict
|
||||
Conflicts []string `json:"conflicts,omitempty"`
|
||||
Head string `json:"head"`
|
||||
// the merge commit
|
||||
Merge string `json:"merge,omitempty"`
|
||||
// by directory
|
||||
Modules map[string]PreviewsByRepositoryHeadTrunkValueModulesValue `json:"modules,omitempty"`
|
||||
Repository string `json:"repository"`
|
||||
Trunk string `json:"trunk"`
|
||||
// the layout version
|
||||
V int64 `json:"v,omitempty"`
|
||||
}
|
||||
@@ -0,0 +1,72 @@
|
||||
// Code generated by seatgen from the seat definitions; DO NOT EDIT.
|
||||
|
||||
package meshdeliverychecker
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"reflect"
|
||||
"regexp"
|
||||
"testing"
|
||||
)
|
||||
|
||||
// TestFixtures reads every fixture of seats/mesh-delivery-checker/fixtures: each decodes into its type, a field the type does not
|
||||
// know is dropped (ADR 0608 §6), and it reads back as its wire says; each subject named is the contract's.
|
||||
func TestFixtures(t *testing.T) {
|
||||
cases := []struct {
|
||||
name, subject, given, wire string
|
||||
decode func([]byte) (any, error)
|
||||
subjectIs *regexp.Regexp
|
||||
}{
|
||||
{name: "verdict on the checker's own family", subject: "mesh.stream.mesh-delivery-checker.event.verdict", given: "{\"head\":\"cccccccccccccccccccccccccccccccccccccccc\",\"out-of-step\":[\"go/seats/meshbroker/contract.go\"],\"pull\":3,\"repository\":\"novox/mesh-contracts\",\"series\":[{\"artifact\":\"go\",\"from\":\"0.1\",\"module\":\"mesh-contracts\",\"to\":\"0.2\"}],\"verdict\":\"fail\"}", wire: "{\"head\":\"cccccccccccccccccccccccccccccccccccccccc\",\"out-of-step\":[\"go/seats/meshbroker/contract.go\"],\"pull\":3,\"repository\":\"novox/mesh-contracts\",\"series\":[{\"artifact\":\"go\",\"from\":\"0.1\",\"module\":\"mesh-contracts\",\"to\":\"0.2\"}],\"verdict\":\"fail\"}",
|
||||
decode: func(b []byte) (any, error) { var v VerdictHolderEvent; err := json.Unmarshal(b, &v); return v, err }, subjectIs: regexp.MustCompile("^mesh\\.stream\\.[a-z0-9-]+\\.event\\.verdict$")},
|
||||
{name: "tag-wanted on the checker's own family", subject: "mesh.stream.mesh-delivery-checker.event.tag-wanted", given: "{\"branch\":\"feat/registry\",\"head\":\"cccccccccccccccccccccccccccccccccccccccc\",\"module\":\"mesh-sdk\",\"package\":\"go\",\"pull\":170,\"repository\":\"novox/mesh-sdk\",\"tag\":\"registry-1966\"}", wire: "{\"branch\":\"feat/registry\",\"head\":\"cccccccccccccccccccccccccccccccccccccccc\",\"module\":\"mesh-sdk\",\"package\":\"go\",\"pull\":170,\"repository\":\"novox/mesh-sdk\",\"tag\":\"registry-1966\"}",
|
||||
decode: func(b []byte) (any, error) { var v TagWantedHolderEvent; err := json.Unmarshal(b, &v); return v, err }, subjectIs: regexp.MustCompile("^mesh\\.stream\\.[a-z0-9-]+\\.event\\.tag-wanted$")},
|
||||
{name: "preview.written on the checker's stream", subject: "mesh.stream.mesh-delivery-checker.event.preview.written", given: "{}", wire: "{}",
|
||||
decode: func(b []byte) (any, error) { var v map[string]any; err := json.Unmarshal(b, &v); return v, err }, subjectIs: regexp.MustCompile("^mesh\\.stream\\.[a-z0-9-]+\\.event\\.preview\\.written$")},
|
||||
{name: "a running verdict", subject: "", given: "{\"group\":\"feat/registry\",\"heads\":\"0123456789ab\",\"state\":\"running\",\"v\":1}", wire: "{\"group\":\"feat/registry\",\"heads\":\"0123456789ab\",\"state\":\"running\",\"v\":1}",
|
||||
decode: func(b []byte) (any, error) {
|
||||
var v VerdictsByGroupHeadsValue
|
||||
err := json.Unmarshal(b, &v)
|
||||
return v, err
|
||||
}, subjectIs: nil},
|
||||
{name: "a kept pass", subject: "", given: "{\"at\":\"2026-11-05T10:00:00Z\",\"level\":\"unit\",\"package\":\"go/keys\",\"repository\":\"novox/mesh-sdk\",\"state\":\"pass\",\"tests\":4,\"took\":120,\"toolchain\":\"go1.26\",\"v\":1}", wire: "{\"at\":\"2026-11-05T10:00:00Z\",\"level\":\"unit\",\"package\":\"go/keys\",\"repository\":\"novox/mesh-sdk\",\"state\":\"pass\",\"tests\":4,\"took\":120,\"toolchain\":\"go1.26\",\"v\":1}",
|
||||
decode: func(b []byte) (any, error) { var v KeptByLevelDigestValue; err := json.Unmarshal(b, &v); return v, err }, subjectIs: nil},
|
||||
{name: "the status owed for a head", subject: "", given: "{\"act\":\"status=mesh/check\",\"context\":\"mesh/check\",\"state\":\"success\",\"v\":1}", wire: "{\"act\":\"status=mesh/check\",\"context\":\"mesh/check\",\"state\":\"success\",\"v\":1}",
|
||||
decode: func(b []byte) (any, error) {
|
||||
var v OwedByRepositoryNumberHeadActValue
|
||||
err := json.Unmarshal(b, &v)
|
||||
return v, err
|
||||
}, subjectIs: nil},
|
||||
{name: "a preview", subject: "", given: "{\"head\":\"cccccccccccccccccccccccccccccccccccccccc\",\"merge\":\"eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee\",\"repository\":\"novox/mesh-sdk\",\"trunk\":\"dddddddddddddddddddddddddddddddddddddddd\",\"v\":1}", wire: "{\"head\":\"cccccccccccccccccccccccccccccccccccccccc\",\"merge\":\"eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee\",\"repository\":\"novox/mesh-sdk\",\"trunk\":\"dddddddddddddddddddddddddddddddddddddddd\",\"v\":1}",
|
||||
decode: func(b []byte) (any, error) {
|
||||
var v PreviewsByRepositoryHeadTrunkValue
|
||||
err := json.Unmarshal(b, &v)
|
||||
return v, err
|
||||
}, subjectIs: nil},
|
||||
}
|
||||
for _, c := range cases {
|
||||
v, err := c.decode([]byte(c.given))
|
||||
if err != nil {
|
||||
t.Errorf("%s: does not decode: %v", c.name, err)
|
||||
continue
|
||||
}
|
||||
back, err := json.Marshal(v)
|
||||
if err != nil {
|
||||
t.Errorf("%s: does not encode: %v", c.name, err)
|
||||
continue
|
||||
}
|
||||
var got, want any
|
||||
if err := json.Unmarshal(back, &got); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if err := json.Unmarshal([]byte(c.wire), &want); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if !reflect.DeepEqual(got, want) {
|
||||
t.Errorf("%s: reads back as %s, want %s", c.name, back, c.wire)
|
||||
}
|
||||
if c.subjectIs != nil && !c.subjectIs.MatchString(c.subject) {
|
||||
t.Errorf("%s: subject %q is not the contract's %s", c.name, c.subject, c.subjectIs)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,3 @@
|
||||
module git.novox.be/novox/mesh-delivery/contracts/go/seats/meshdeliverychecker
|
||||
|
||||
go 1.26.0
|
||||
@@ -0,0 +1,447 @@
|
||||
// Code generated by seatgen from the seat definitions; DO NOT EDIT.
|
||||
|
||||
// Package nodebuildgo is the contract of the seat "node-build-go", defined whole (seats/node-build-go/seat.json): The Go build module on a node: it takes the asks of the builder and the checker, runs them as steps and answers.
|
||||
package nodebuildgo
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"strings"
|
||||
)
|
||||
|
||||
// Seat is the seat's name.
|
||||
const Seat = "node-build-go"
|
||||
|
||||
const (
|
||||
VerbCurrent = "current"
|
||||
VerbPause = "pause"
|
||||
VerbResume = "resume"
|
||||
)
|
||||
|
||||
const (
|
||||
AcceptDelivery = "delivery"
|
||||
AcceptCheck = "check"
|
||||
AcceptRecheck = "recheck"
|
||||
)
|
||||
|
||||
// Verbs is every verb the seat serves, in its definition's order: a new copy each call.
|
||||
func Verbs() []string {
|
||||
return []string{VerbCurrent, VerbPause, VerbResume}
|
||||
}
|
||||
|
||||
// AcceptSubjectDelivery is where delivery is sent.
|
||||
const AcceptSubjectDelivery = "mesh.seat.node-build-go.accept.delivery"
|
||||
|
||||
// AcceptSubjectCheck is where check is sent.
|
||||
const AcceptSubjectCheck = "mesh.seat.node-build-go.accept.check"
|
||||
|
||||
// AcceptSubjectRecheck is where recheck is sent.
|
||||
const AcceptSubjectRecheck = "mesh.seat.node-build-go.accept.recheck"
|
||||
|
||||
const (
|
||||
StateSteps = "steps"
|
||||
StatePauses = "pauses"
|
||||
StateToolchains = "toolchains"
|
||||
)
|
||||
|
||||
// StepsByNodeIDKey is the key of one value of the state steps under the form "<node>.<id>".
|
||||
func StepsByNodeIDKey(node, id string) string { return node + "." + id }
|
||||
|
||||
// ParseStepsByNodeIDKey reads the parts of a key of the form "<node>.<id>", in the order of the form; the last result is false for a key of another form.
|
||||
func ParseStepsByNodeIDKey(key string) (string, string, bool) {
|
||||
rest := key
|
||||
v0, after0, found0 := strings.Cut(rest, ".")
|
||||
if !found0 || v0 == "" {
|
||||
return "", "", false
|
||||
}
|
||||
rest = after0
|
||||
if rest == "" {
|
||||
return "", "", false
|
||||
}
|
||||
v2 := rest
|
||||
rest = ""
|
||||
if rest != "" {
|
||||
return "", "", false
|
||||
}
|
||||
return v0, v2, true
|
||||
}
|
||||
|
||||
// DecodeStepsByNodeIDValue reads one value of the state steps under the form "<node>.<id>", refusing a field StepsByNodeIDValue does not know and any text after the value.
|
||||
func DecodeStepsByNodeIDValue(raw []byte) (StepsByNodeIDValue, error) {
|
||||
var v StepsByNodeIDValue
|
||||
dec := json.NewDecoder(bytes.NewReader(raw))
|
||||
dec.DisallowUnknownFields()
|
||||
if err := dec.Decode(&v); err != nil {
|
||||
return v, err
|
||||
}
|
||||
if dec.More() {
|
||||
return v, errors.New("text after the value")
|
||||
}
|
||||
return v, nil
|
||||
}
|
||||
|
||||
// PausesByPausedNodeKey is the key of one value of the state pauses under the form "paused.<node>".
|
||||
func PausesByPausedNodeKey(node string) string { return "paused." + node }
|
||||
|
||||
// ParsePausesByPausedNodeKey reads the parts of a key of the form "paused.<node>", in the order of the form; the last result is false for a key of another form.
|
||||
func ParsePausesByPausedNodeKey(key string) (string, bool) {
|
||||
rest := key
|
||||
if !strings.HasPrefix(rest, "paused.") {
|
||||
return "", false
|
||||
}
|
||||
rest = rest[7:]
|
||||
if rest == "" {
|
||||
return "", false
|
||||
}
|
||||
v1 := rest
|
||||
rest = ""
|
||||
if rest != "" {
|
||||
return "", false
|
||||
}
|
||||
return v1, true
|
||||
}
|
||||
|
||||
// DecodePausesByPausedNodeValue reads one value of the state pauses under the form "paused.<node>", refusing a field PausesByPausedNodeValue does not know and any text after the value.
|
||||
func DecodePausesByPausedNodeValue(raw []byte) (PausesByPausedNodeValue, error) {
|
||||
var v PausesByPausedNodeValue
|
||||
dec := json.NewDecoder(bytes.NewReader(raw))
|
||||
dec.DisallowUnknownFields()
|
||||
if err := dec.Decode(&v); err != nil {
|
||||
return v, err
|
||||
}
|
||||
if dec.More() {
|
||||
return v, errors.New("text after the value")
|
||||
}
|
||||
return v, nil
|
||||
}
|
||||
|
||||
// PausesByCapacityNodeKey is the key of one value of the state pauses under the form "capacity.<node>".
|
||||
func PausesByCapacityNodeKey(node string) string { return "capacity." + node }
|
||||
|
||||
// ParsePausesByCapacityNodeKey reads the parts of a key of the form "capacity.<node>", in the order of the form; the last result is false for a key of another form.
|
||||
func ParsePausesByCapacityNodeKey(key string) (string, bool) {
|
||||
rest := key
|
||||
if !strings.HasPrefix(rest, "capacity.") {
|
||||
return "", false
|
||||
}
|
||||
rest = rest[9:]
|
||||
if rest == "" {
|
||||
return "", false
|
||||
}
|
||||
v1 := rest
|
||||
rest = ""
|
||||
if rest != "" {
|
||||
return "", false
|
||||
}
|
||||
return v1, true
|
||||
}
|
||||
|
||||
// DecodePausesByCapacityNodeValue reads one value of the state pauses under the form "capacity.<node>", refusing a field PausesByCapacityNodeValue does not know and any text after the value.
|
||||
func DecodePausesByCapacityNodeValue(raw []byte) (PausesByCapacityNodeValue, error) {
|
||||
var v PausesByCapacityNodeValue
|
||||
dec := json.NewDecoder(bytes.NewReader(raw))
|
||||
dec.DisallowUnknownFields()
|
||||
if err := dec.Decode(&v); err != nil {
|
||||
return v, err
|
||||
}
|
||||
if dec.More() {
|
||||
return v, errors.New("text after the value")
|
||||
}
|
||||
return v, nil
|
||||
}
|
||||
|
||||
// ToolchainsByNodeLanguageKey is the key of one value of the state toolchains under the form "<node>.<language>".
|
||||
func ToolchainsByNodeLanguageKey(node, language string) string { return node + "." + language }
|
||||
|
||||
// ParseToolchainsByNodeLanguageKey reads the parts of a key of the form "<node>.<language>", in the order of the form; the last result is false for a key of another form.
|
||||
func ParseToolchainsByNodeLanguageKey(key string) (string, string, bool) {
|
||||
rest := key
|
||||
v0, after0, found0 := strings.Cut(rest, ".")
|
||||
if !found0 || v0 == "" {
|
||||
return "", "", false
|
||||
}
|
||||
rest = after0
|
||||
if rest == "" {
|
||||
return "", "", false
|
||||
}
|
||||
v2 := rest
|
||||
rest = ""
|
||||
if rest != "" {
|
||||
return "", "", false
|
||||
}
|
||||
return v0, v2, true
|
||||
}
|
||||
|
||||
// DecodeToolchainsByNodeLanguageValue reads one value of the state toolchains under the form "<node>.<language>", refusing a field ToolchainsByNodeLanguageValue does not know and any text after the value.
|
||||
func DecodeToolchainsByNodeLanguageValue(raw []byte) (ToolchainsByNodeLanguageValue, error) {
|
||||
var v ToolchainsByNodeLanguageValue
|
||||
dec := json.NewDecoder(bytes.NewReader(raw))
|
||||
dec.DisallowUnknownFields()
|
||||
if err := dec.Decode(&v); err != nil {
|
||||
return v, err
|
||||
}
|
||||
if dec.More() {
|
||||
return v, errors.New("text after the value")
|
||||
}
|
||||
return v, nil
|
||||
}
|
||||
|
||||
// BuildAnswerArtifactsItem is a type of the contract.
|
||||
type BuildAnswerArtifactsItem struct {
|
||||
Artifact string `json:"artifact"`
|
||||
Digest string `json:"digest"`
|
||||
Reference string `json:"reference,omitempty"`
|
||||
}
|
||||
|
||||
// BuildAnswerToolchain is a type of the contract.
|
||||
type BuildAnswerToolchain struct {
|
||||
Language string `json:"language"`
|
||||
Recipe string `json:"recipe"`
|
||||
Version string `json:"version"`
|
||||
}
|
||||
|
||||
// BuildAnswer is a type of the contract.
|
||||
type BuildAnswer struct {
|
||||
Artifacts []BuildAnswerArtifactsItem `json:"artifacts"`
|
||||
Imports []string `json:"imports,omitempty"`
|
||||
OutOfStep []string `json:"out-of-step,omitempty"`
|
||||
Toolchain *BuildAnswerToolchain `json:"toolchain,omitempty"`
|
||||
}
|
||||
|
||||
// Capacity is how many steps the node runs at once.
|
||||
type Capacity struct {
|
||||
// when (RFC 3339 time)
|
||||
At string `json:"at,omitempty"`
|
||||
BuildHours string `json:"build-hours,omitempty"`
|
||||
BuildHoursFault string `json:"build-hours-fault,omitempty"`
|
||||
ByCpu int64 `json:"by-cpu,omitempty"`
|
||||
Configured int64 `json:"configured"`
|
||||
ConfiguredFrom string `json:"configured-from,omitempty"`
|
||||
CoresPerStep float64 `json:"cores-per-step,omitempty"`
|
||||
Effective int64 `json:"effective"`
|
||||
Ineligible string `json:"ineligible,omitempty"`
|
||||
OutsideHours bool `json:"outside-hours,omitempty"`
|
||||
QuotaCores float64 `json:"quota-cores,omitempty"`
|
||||
QuotaFrom string `json:"quota-from,omitempty"`
|
||||
// the layout version
|
||||
V int64 `json:"v,omitempty"`
|
||||
Why string `json:"why"`
|
||||
}
|
||||
|
||||
// CheckAnswerLevelsItemDigestNeedsItem is a type of the contract.
|
||||
type CheckAnswerLevelsItemDigestNeedsItem struct {
|
||||
Needs []string `json:"needs,omitempty"`
|
||||
Package string `json:"package,omitempty"`
|
||||
}
|
||||
|
||||
// CheckAnswerLevelsItemDigestPackagesItem is a type of the contract.
|
||||
type CheckAnswerLevelsItemDigestPackagesItem struct {
|
||||
Digest string `json:"digest"`
|
||||
Package string `json:"package"`
|
||||
}
|
||||
|
||||
// CheckAnswerLevelsItemDigest is a type of the contract.
|
||||
type CheckAnswerLevelsItemDigest struct {
|
||||
Beside []string `json:"beside,omitempty"`
|
||||
JudgedBy string `json:"judged_by,omitempty"`
|
||||
Level string `json:"level,omitempty"`
|
||||
Needs []CheckAnswerLevelsItemDigestNeedsItem `json:"needs,omitempty"`
|
||||
Packages []CheckAnswerLevelsItemDigestPackagesItem `json:"packages,omitempty"`
|
||||
WithoutTests []string `json:"without_tests,omitempty"`
|
||||
}
|
||||
|
||||
// CheckAnswerLevelsItemKeptItem is a type of the contract.
|
||||
type CheckAnswerLevelsItemKeptItem struct {
|
||||
Digest string `json:"digest,omitempty"`
|
||||
Package string `json:"package,omitempty"`
|
||||
Tests int64 `json:"tests,omitempty"`
|
||||
}
|
||||
|
||||
// CheckAnswerLevelsItemUnmetItem is a type of the contract.
|
||||
type CheckAnswerLevelsItemUnmetItem struct {
|
||||
Needs []string `json:"needs,omitempty"`
|
||||
Package string `json:"package,omitempty"`
|
||||
}
|
||||
|
||||
// CheckAnswerLevelsItem is a type of the contract.
|
||||
type CheckAnswerLevelsItem struct {
|
||||
Digest *CheckAnswerLevelsItemDigest `json:"digest,omitempty"`
|
||||
Judged string `json:"judged,omitempty"`
|
||||
Kept []CheckAnswerLevelsItemKeptItem `json:"kept,omitempty"`
|
||||
Level string `json:"level"`
|
||||
Tests []any `json:"tests,omitempty"`
|
||||
Unmet []CheckAnswerLevelsItemUnmetItem `json:"unmet,omitempty"`
|
||||
}
|
||||
|
||||
// CheckAnswer is a type of the contract.
|
||||
type CheckAnswer struct {
|
||||
// what the build module fetches: the repository, commit and directory, as the indexer states it
|
||||
Fetch any `json:"fetch"`
|
||||
Levels []CheckAnswerLevelsItem `json:"levels,omitempty"`
|
||||
}
|
||||
|
||||
// ComposeAnswer is a type of the contract.
|
||||
type ComposeAnswer struct {
|
||||
Built bool `json:"built"`
|
||||
Key string `json:"key,omitempty"`
|
||||
// the compose-candidate verdict
|
||||
Verdict any `json:"verdict,omitempty"`
|
||||
}
|
||||
|
||||
// Pause is whether the node takes no new ask.
|
||||
type Pause struct {
|
||||
// when (RFC 3339 time)
|
||||
At string `json:"at,omitempty"`
|
||||
By string `json:"by,omitempty"`
|
||||
Paused bool `json:"paused"`
|
||||
// the layout version
|
||||
V int64 `json:"v,omitempty"`
|
||||
}
|
||||
|
||||
// Step is one step a build module runs on a node.
|
||||
type Step struct {
|
||||
// when (RFC 3339 time)
|
||||
Ended string `json:"ended,omitempty"`
|
||||
ID string `json:"id"`
|
||||
Node string `json:"node"`
|
||||
Owner string `json:"owner,omitempty"`
|
||||
Phase string `json:"phase,omitempty"`
|
||||
// when (RFC 3339 time)
|
||||
PhaseAt string `json:"phase-at,omitempty"`
|
||||
Reason string `json:"reason,omitempty"`
|
||||
// what the step answered
|
||||
Result any `json:"result,omitempty"`
|
||||
// when (RFC 3339 time)
|
||||
Started string `json:"started"`
|
||||
// running, passed, failed or refused
|
||||
State string `json:"state"`
|
||||
Tool string `json:"tool"`
|
||||
// the layout version
|
||||
V int64 `json:"v,omitempty"`
|
||||
What string `json:"what,omitempty"`
|
||||
}
|
||||
|
||||
// StepAskBuildArtifactsItem is a type of the contract.
|
||||
type StepAskBuildArtifactsItem struct {
|
||||
Artifact string `json:"artifact"`
|
||||
From string `json:"from,omitempty"`
|
||||
Seat string `json:"seat,omitempty"`
|
||||
Version string `json:"version,omitempty"`
|
||||
}
|
||||
|
||||
// StepAskBuildToolchain is a type of the contract.
|
||||
type StepAskBuildToolchain struct {
|
||||
Language string `json:"language"`
|
||||
Recipe string `json:"recipe"`
|
||||
Version string `json:"version"`
|
||||
}
|
||||
|
||||
// StepAskBuild is a type of the contract.
|
||||
type StepAskBuild struct {
|
||||
Artifacts []StepAskBuildArtifactsItem `json:"artifacts"`
|
||||
// what the build module fetches: the repository, commit and directory, as the indexer states it
|
||||
Fetch any `json:"fetch,omitempty"`
|
||||
Fp string `json:"fp,omitempty"`
|
||||
Module string `json:"module"`
|
||||
Path string `json:"path,omitempty"`
|
||||
Publish bool `json:"publish,omitempty"`
|
||||
Toolchain *StepAskBuildToolchain `json:"toolchain,omitempty"`
|
||||
}
|
||||
|
||||
// StepAskCheck is a type of the contract.
|
||||
type StepAskCheck struct {
|
||||
// what the build module fetches: the repository, commit and directory, as the indexer states it
|
||||
Fetch any `json:"fetch"`
|
||||
Kept bool `json:"kept,omitempty"`
|
||||
Levels []string `json:"levels"`
|
||||
Packages []string `json:"packages,omitempty"`
|
||||
Run string `json:"run,omitempty"`
|
||||
Runner string `json:"runner,omitempty"`
|
||||
RunnerWhen string `json:"runner_when,omitempty"`
|
||||
}
|
||||
|
||||
// StepAskCompose is a type of the contract.
|
||||
type StepAskCompose struct {
|
||||
Dir string `json:"dir"`
|
||||
// what the build module fetches: the repository, commit and directory, as the indexer states it
|
||||
Fetch any `json:"fetch"`
|
||||
Package string `json:"package"`
|
||||
Reader string `json:"reader,omitempty"`
|
||||
// the compose-candidate request
|
||||
Request any `json:"request"`
|
||||
}
|
||||
|
||||
// StepAsk is one ask of a build module.
|
||||
type StepAsk struct {
|
||||
Asker string `json:"asker"`
|
||||
// when (RFC 3339 time)
|
||||
At string `json:"at,omitempty"`
|
||||
Build *StepAskBuild `json:"build,omitempty"`
|
||||
Check *StepAskCheck `json:"check,omitempty"`
|
||||
Compose *StepAskCompose `json:"compose,omitempty"`
|
||||
ID string `json:"id"`
|
||||
// build, check or compose
|
||||
Kind string `json:"kind"`
|
||||
// go, typescript or image
|
||||
Language string `json:"language"`
|
||||
Needs []string `json:"needs,omitempty"`
|
||||
// package to the tagged version a build pins
|
||||
Packages map[string]string `json:"packages,omitempty"`
|
||||
// the layout version
|
||||
V int64 `json:"v,omitempty"`
|
||||
}
|
||||
|
||||
// Toolchain is the toolchain a node reports.
|
||||
type Toolchain struct {
|
||||
// when (RFC 3339 time)
|
||||
At string `json:"at,omitempty"`
|
||||
// go or typescript
|
||||
Language string `json:"language"`
|
||||
Recipe string `json:"recipe"`
|
||||
// the layout version
|
||||
V int64 `json:"v,omitempty"`
|
||||
Version string `json:"version"`
|
||||
}
|
||||
|
||||
// CurrentInput is what current takes.
|
||||
type CurrentInput struct {
|
||||
}
|
||||
|
||||
// CurrentAnswer is what current answers.
|
||||
type CurrentAnswer = map[string]any
|
||||
|
||||
// PauseInput is what pause takes.
|
||||
type PauseInput struct {
|
||||
}
|
||||
|
||||
// PauseAnswer is what pause answers.
|
||||
type PauseAnswer = map[string]any
|
||||
|
||||
// ResumeInput is what resume takes.
|
||||
type ResumeInput struct {
|
||||
}
|
||||
|
||||
// ResumeAnswer is what resume answers.
|
||||
type ResumeAnswer = map[string]any
|
||||
|
||||
// DeliveryAccept is the body of delivery.
|
||||
type DeliveryAccept = StepAsk
|
||||
|
||||
// CheckAccept is the body of check.
|
||||
type CheckAccept = StepAsk
|
||||
|
||||
// RecheckAccept is the body of recheck.
|
||||
type RecheckAccept = StepAsk
|
||||
|
||||
// StepsByNodeIDValue is one value of the state steps under the key <node>.<id>.
|
||||
type StepsByNodeIDValue = Step
|
||||
|
||||
// PausesByPausedNodeValue is one value of the state pauses under the key paused.<node>.
|
||||
type PausesByPausedNodeValue = Pause
|
||||
|
||||
// PausesByCapacityNodeValue is one value of the state pauses under the key capacity.<node>.
|
||||
type PausesByCapacityNodeValue = Capacity
|
||||
|
||||
// ToolchainsByNodeLanguageValue is one value of the state toolchains under the key <node>.<language>.
|
||||
type ToolchainsByNodeLanguageValue = Toolchain
|
||||
@@ -0,0 +1,68 @@
|
||||
// Code generated by seatgen from the seat definitions; DO NOT EDIT.
|
||||
|
||||
package nodebuildgo
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"reflect"
|
||||
"regexp"
|
||||
"testing"
|
||||
)
|
||||
|
||||
// TestFixtures reads every fixture of seats/node-build-go/fixtures: each decodes into its type, a field the type does not
|
||||
// know is dropped (ADR 0608 §6), and it reads back as its wire says; each subject named is the contract's.
|
||||
func TestFixtures(t *testing.T) {
|
||||
cases := []struct {
|
||||
name, subject, given, wire string
|
||||
decode func([]byte) (any, error)
|
||||
subjectIs *regexp.Regexp
|
||||
}{
|
||||
{name: "a running step", subject: "", given: "{\"id\":\"a1b2\",\"node\":\"ace\",\"started\":\"2026-11-05T10:00:00Z\",\"state\":\"running\",\"tool\":\"build\",\"v\":1}", wire: "{\"id\":\"a1b2\",\"node\":\"ace\",\"started\":\"2026-11-05T10:00:00Z\",\"state\":\"running\",\"tool\":\"build\",\"v\":1}",
|
||||
decode: func(b []byte) (any, error) { var v StepsByNodeIDValue; err := json.Unmarshal(b, &v); return v, err }, subjectIs: nil},
|
||||
{name: "a paused node", subject: "", given: "{\"at\":\"2026-11-05T10:00:00Z\",\"by\":\"operator@shanks\",\"paused\":true,\"v\":1}", wire: "{\"at\":\"2026-11-05T10:00:00Z\",\"by\":\"operator@shanks\",\"paused\":true,\"v\":1}",
|
||||
decode: func(b []byte) (any, error) {
|
||||
var v PausesByPausedNodeValue
|
||||
err := json.Unmarshal(b, &v)
|
||||
return v, err
|
||||
}, subjectIs: nil},
|
||||
{name: "a node's capacity", subject: "", given: "{\"configured\":2,\"effective\":2,\"v\":1,\"why\":\"the configured steps fit the cores\"}", wire: "{\"configured\":2,\"effective\":2,\"v\":1,\"why\":\"the configured steps fit the cores\"}",
|
||||
decode: func(b []byte) (any, error) {
|
||||
var v PausesByCapacityNodeValue
|
||||
err := json.Unmarshal(b, &v)
|
||||
return v, err
|
||||
}, subjectIs: nil},
|
||||
{name: "a node's toolchain", subject: "", given: "{\"language\":\"go\",\"recipe\":\"1\",\"v\":1,\"version\":\"1.26.0\"}", wire: "{\"language\":\"go\",\"recipe\":\"1\",\"v\":1,\"version\":\"1.26.0\"}",
|
||||
decode: func(b []byte) (any, error) {
|
||||
var v ToolchainsByNodeLanguageValue
|
||||
err := json.Unmarshal(b, &v)
|
||||
return v, err
|
||||
}, subjectIs: nil},
|
||||
{name: "a build ask", subject: "mesh.seat.node-build-go.accept.delivery", given: "{\"asker\":\"mesh-delivery-builder\",\"build\":{\"artifacts\":[{\"artifact\":\"go\"}],\"module\":\"nats\"},\"id\":\"a1b2\",\"kind\":\"build\",\"language\":\"go\"}", wire: "{\"asker\":\"mesh-delivery-builder\",\"build\":{\"artifacts\":[{\"artifact\":\"go\"}],\"module\":\"nats\"},\"id\":\"a1b2\",\"kind\":\"build\",\"language\":\"go\"}",
|
||||
decode: func(b []byte) (any, error) { var v DeliveryAccept; err := json.Unmarshal(b, &v); return v, err }, subjectIs: regexp.MustCompile("^mesh\\.seat\\.node-build-go\\.accept\\.delivery$")},
|
||||
}
|
||||
for _, c := range cases {
|
||||
v, err := c.decode([]byte(c.given))
|
||||
if err != nil {
|
||||
t.Errorf("%s: does not decode: %v", c.name, err)
|
||||
continue
|
||||
}
|
||||
back, err := json.Marshal(v)
|
||||
if err != nil {
|
||||
t.Errorf("%s: does not encode: %v", c.name, err)
|
||||
continue
|
||||
}
|
||||
var got, want any
|
||||
if err := json.Unmarshal(back, &got); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if err := json.Unmarshal([]byte(c.wire), &want); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if !reflect.DeepEqual(got, want) {
|
||||
t.Errorf("%s: reads back as %s, want %s", c.name, back, c.wire)
|
||||
}
|
||||
if c.subjectIs != nil && !c.subjectIs.MatchString(c.subject) {
|
||||
t.Errorf("%s: subject %q is not the contract's %s", c.name, c.subject, c.subjectIs)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,3 @@
|
||||
module git.novox.be/novox/mesh-delivery/contracts/go/seats/nodebuildgo
|
||||
|
||||
go 1.26.0
|
||||
@@ -0,0 +1,447 @@
|
||||
// Code generated by seatgen from the seat definitions; DO NOT EDIT.
|
||||
|
||||
// Package nodebuildimage is the contract of the seat "node-build-image", defined whole (seats/node-build-image/seat.json): The image build module on a node: it takes the asks of the builder and the checker, runs them as steps and answers.
|
||||
package nodebuildimage
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"strings"
|
||||
)
|
||||
|
||||
// Seat is the seat's name.
|
||||
const Seat = "node-build-image"
|
||||
|
||||
const (
|
||||
VerbCurrent = "current"
|
||||
VerbPause = "pause"
|
||||
VerbResume = "resume"
|
||||
)
|
||||
|
||||
const (
|
||||
AcceptDelivery = "delivery"
|
||||
AcceptCheck = "check"
|
||||
AcceptRecheck = "recheck"
|
||||
)
|
||||
|
||||
// Verbs is every verb the seat serves, in its definition's order: a new copy each call.
|
||||
func Verbs() []string {
|
||||
return []string{VerbCurrent, VerbPause, VerbResume}
|
||||
}
|
||||
|
||||
// AcceptSubjectDelivery is where delivery is sent.
|
||||
const AcceptSubjectDelivery = "mesh.seat.node-build-image.accept.delivery"
|
||||
|
||||
// AcceptSubjectCheck is where check is sent.
|
||||
const AcceptSubjectCheck = "mesh.seat.node-build-image.accept.check"
|
||||
|
||||
// AcceptSubjectRecheck is where recheck is sent.
|
||||
const AcceptSubjectRecheck = "mesh.seat.node-build-image.accept.recheck"
|
||||
|
||||
const (
|
||||
StateSteps = "steps"
|
||||
StatePauses = "pauses"
|
||||
StateToolchains = "toolchains"
|
||||
)
|
||||
|
||||
// StepsByNodeIDKey is the key of one value of the state steps under the form "<node>.<id>".
|
||||
func StepsByNodeIDKey(node, id string) string { return node + "." + id }
|
||||
|
||||
// ParseStepsByNodeIDKey reads the parts of a key of the form "<node>.<id>", in the order of the form; the last result is false for a key of another form.
|
||||
func ParseStepsByNodeIDKey(key string) (string, string, bool) {
|
||||
rest := key
|
||||
v0, after0, found0 := strings.Cut(rest, ".")
|
||||
if !found0 || v0 == "" {
|
||||
return "", "", false
|
||||
}
|
||||
rest = after0
|
||||
if rest == "" {
|
||||
return "", "", false
|
||||
}
|
||||
v2 := rest
|
||||
rest = ""
|
||||
if rest != "" {
|
||||
return "", "", false
|
||||
}
|
||||
return v0, v2, true
|
||||
}
|
||||
|
||||
// DecodeStepsByNodeIDValue reads one value of the state steps under the form "<node>.<id>", refusing a field StepsByNodeIDValue does not know and any text after the value.
|
||||
func DecodeStepsByNodeIDValue(raw []byte) (StepsByNodeIDValue, error) {
|
||||
var v StepsByNodeIDValue
|
||||
dec := json.NewDecoder(bytes.NewReader(raw))
|
||||
dec.DisallowUnknownFields()
|
||||
if err := dec.Decode(&v); err != nil {
|
||||
return v, err
|
||||
}
|
||||
if dec.More() {
|
||||
return v, errors.New("text after the value")
|
||||
}
|
||||
return v, nil
|
||||
}
|
||||
|
||||
// PausesByPausedNodeKey is the key of one value of the state pauses under the form "paused.<node>".
|
||||
func PausesByPausedNodeKey(node string) string { return "paused." + node }
|
||||
|
||||
// ParsePausesByPausedNodeKey reads the parts of a key of the form "paused.<node>", in the order of the form; the last result is false for a key of another form.
|
||||
func ParsePausesByPausedNodeKey(key string) (string, bool) {
|
||||
rest := key
|
||||
if !strings.HasPrefix(rest, "paused.") {
|
||||
return "", false
|
||||
}
|
||||
rest = rest[7:]
|
||||
if rest == "" {
|
||||
return "", false
|
||||
}
|
||||
v1 := rest
|
||||
rest = ""
|
||||
if rest != "" {
|
||||
return "", false
|
||||
}
|
||||
return v1, true
|
||||
}
|
||||
|
||||
// DecodePausesByPausedNodeValue reads one value of the state pauses under the form "paused.<node>", refusing a field PausesByPausedNodeValue does not know and any text after the value.
|
||||
func DecodePausesByPausedNodeValue(raw []byte) (PausesByPausedNodeValue, error) {
|
||||
var v PausesByPausedNodeValue
|
||||
dec := json.NewDecoder(bytes.NewReader(raw))
|
||||
dec.DisallowUnknownFields()
|
||||
if err := dec.Decode(&v); err != nil {
|
||||
return v, err
|
||||
}
|
||||
if dec.More() {
|
||||
return v, errors.New("text after the value")
|
||||
}
|
||||
return v, nil
|
||||
}
|
||||
|
||||
// PausesByCapacityNodeKey is the key of one value of the state pauses under the form "capacity.<node>".
|
||||
func PausesByCapacityNodeKey(node string) string { return "capacity." + node }
|
||||
|
||||
// ParsePausesByCapacityNodeKey reads the parts of a key of the form "capacity.<node>", in the order of the form; the last result is false for a key of another form.
|
||||
func ParsePausesByCapacityNodeKey(key string) (string, bool) {
|
||||
rest := key
|
||||
if !strings.HasPrefix(rest, "capacity.") {
|
||||
return "", false
|
||||
}
|
||||
rest = rest[9:]
|
||||
if rest == "" {
|
||||
return "", false
|
||||
}
|
||||
v1 := rest
|
||||
rest = ""
|
||||
if rest != "" {
|
||||
return "", false
|
||||
}
|
||||
return v1, true
|
||||
}
|
||||
|
||||
// DecodePausesByCapacityNodeValue reads one value of the state pauses under the form "capacity.<node>", refusing a field PausesByCapacityNodeValue does not know and any text after the value.
|
||||
func DecodePausesByCapacityNodeValue(raw []byte) (PausesByCapacityNodeValue, error) {
|
||||
var v PausesByCapacityNodeValue
|
||||
dec := json.NewDecoder(bytes.NewReader(raw))
|
||||
dec.DisallowUnknownFields()
|
||||
if err := dec.Decode(&v); err != nil {
|
||||
return v, err
|
||||
}
|
||||
if dec.More() {
|
||||
return v, errors.New("text after the value")
|
||||
}
|
||||
return v, nil
|
||||
}
|
||||
|
||||
// ToolchainsByNodeLanguageKey is the key of one value of the state toolchains under the form "<node>.<language>".
|
||||
func ToolchainsByNodeLanguageKey(node, language string) string { return node + "." + language }
|
||||
|
||||
// ParseToolchainsByNodeLanguageKey reads the parts of a key of the form "<node>.<language>", in the order of the form; the last result is false for a key of another form.
|
||||
func ParseToolchainsByNodeLanguageKey(key string) (string, string, bool) {
|
||||
rest := key
|
||||
v0, after0, found0 := strings.Cut(rest, ".")
|
||||
if !found0 || v0 == "" {
|
||||
return "", "", false
|
||||
}
|
||||
rest = after0
|
||||
if rest == "" {
|
||||
return "", "", false
|
||||
}
|
||||
v2 := rest
|
||||
rest = ""
|
||||
if rest != "" {
|
||||
return "", "", false
|
||||
}
|
||||
return v0, v2, true
|
||||
}
|
||||
|
||||
// DecodeToolchainsByNodeLanguageValue reads one value of the state toolchains under the form "<node>.<language>", refusing a field ToolchainsByNodeLanguageValue does not know and any text after the value.
|
||||
func DecodeToolchainsByNodeLanguageValue(raw []byte) (ToolchainsByNodeLanguageValue, error) {
|
||||
var v ToolchainsByNodeLanguageValue
|
||||
dec := json.NewDecoder(bytes.NewReader(raw))
|
||||
dec.DisallowUnknownFields()
|
||||
if err := dec.Decode(&v); err != nil {
|
||||
return v, err
|
||||
}
|
||||
if dec.More() {
|
||||
return v, errors.New("text after the value")
|
||||
}
|
||||
return v, nil
|
||||
}
|
||||
|
||||
// BuildAnswerArtifactsItem is a type of the contract.
|
||||
type BuildAnswerArtifactsItem struct {
|
||||
Artifact string `json:"artifact"`
|
||||
Digest string `json:"digest"`
|
||||
Reference string `json:"reference,omitempty"`
|
||||
}
|
||||
|
||||
// BuildAnswerToolchain is a type of the contract.
|
||||
type BuildAnswerToolchain struct {
|
||||
Language string `json:"language"`
|
||||
Recipe string `json:"recipe"`
|
||||
Version string `json:"version"`
|
||||
}
|
||||
|
||||
// BuildAnswer is a type of the contract.
|
||||
type BuildAnswer struct {
|
||||
Artifacts []BuildAnswerArtifactsItem `json:"artifacts"`
|
||||
Imports []string `json:"imports,omitempty"`
|
||||
OutOfStep []string `json:"out-of-step,omitempty"`
|
||||
Toolchain *BuildAnswerToolchain `json:"toolchain,omitempty"`
|
||||
}
|
||||
|
||||
// Capacity is how many steps the node runs at once.
|
||||
type Capacity struct {
|
||||
// when (RFC 3339 time)
|
||||
At string `json:"at,omitempty"`
|
||||
BuildHours string `json:"build-hours,omitempty"`
|
||||
BuildHoursFault string `json:"build-hours-fault,omitempty"`
|
||||
ByCpu int64 `json:"by-cpu,omitempty"`
|
||||
Configured int64 `json:"configured"`
|
||||
ConfiguredFrom string `json:"configured-from,omitempty"`
|
||||
CoresPerStep float64 `json:"cores-per-step,omitempty"`
|
||||
Effective int64 `json:"effective"`
|
||||
Ineligible string `json:"ineligible,omitempty"`
|
||||
OutsideHours bool `json:"outside-hours,omitempty"`
|
||||
QuotaCores float64 `json:"quota-cores,omitempty"`
|
||||
QuotaFrom string `json:"quota-from,omitempty"`
|
||||
// the layout version
|
||||
V int64 `json:"v,omitempty"`
|
||||
Why string `json:"why"`
|
||||
}
|
||||
|
||||
// CheckAnswerLevelsItemDigestNeedsItem is a type of the contract.
|
||||
type CheckAnswerLevelsItemDigestNeedsItem struct {
|
||||
Needs []string `json:"needs,omitempty"`
|
||||
Package string `json:"package,omitempty"`
|
||||
}
|
||||
|
||||
// CheckAnswerLevelsItemDigestPackagesItem is a type of the contract.
|
||||
type CheckAnswerLevelsItemDigestPackagesItem struct {
|
||||
Digest string `json:"digest"`
|
||||
Package string `json:"package"`
|
||||
}
|
||||
|
||||
// CheckAnswerLevelsItemDigest is a type of the contract.
|
||||
type CheckAnswerLevelsItemDigest struct {
|
||||
Beside []string `json:"beside,omitempty"`
|
||||
JudgedBy string `json:"judged_by,omitempty"`
|
||||
Level string `json:"level,omitempty"`
|
||||
Needs []CheckAnswerLevelsItemDigestNeedsItem `json:"needs,omitempty"`
|
||||
Packages []CheckAnswerLevelsItemDigestPackagesItem `json:"packages,omitempty"`
|
||||
WithoutTests []string `json:"without_tests,omitempty"`
|
||||
}
|
||||
|
||||
// CheckAnswerLevelsItemKeptItem is a type of the contract.
|
||||
type CheckAnswerLevelsItemKeptItem struct {
|
||||
Digest string `json:"digest,omitempty"`
|
||||
Package string `json:"package,omitempty"`
|
||||
Tests int64 `json:"tests,omitempty"`
|
||||
}
|
||||
|
||||
// CheckAnswerLevelsItemUnmetItem is a type of the contract.
|
||||
type CheckAnswerLevelsItemUnmetItem struct {
|
||||
Needs []string `json:"needs,omitempty"`
|
||||
Package string `json:"package,omitempty"`
|
||||
}
|
||||
|
||||
// CheckAnswerLevelsItem is a type of the contract.
|
||||
type CheckAnswerLevelsItem struct {
|
||||
Digest *CheckAnswerLevelsItemDigest `json:"digest,omitempty"`
|
||||
Judged string `json:"judged,omitempty"`
|
||||
Kept []CheckAnswerLevelsItemKeptItem `json:"kept,omitempty"`
|
||||
Level string `json:"level"`
|
||||
Tests []any `json:"tests,omitempty"`
|
||||
Unmet []CheckAnswerLevelsItemUnmetItem `json:"unmet,omitempty"`
|
||||
}
|
||||
|
||||
// CheckAnswer is a type of the contract.
|
||||
type CheckAnswer struct {
|
||||
// what the build module fetches: the repository, commit and directory, as the indexer states it
|
||||
Fetch any `json:"fetch"`
|
||||
Levels []CheckAnswerLevelsItem `json:"levels,omitempty"`
|
||||
}
|
||||
|
||||
// ComposeAnswer is a type of the contract.
|
||||
type ComposeAnswer struct {
|
||||
Built bool `json:"built"`
|
||||
Key string `json:"key,omitempty"`
|
||||
// the compose-candidate verdict
|
||||
Verdict any `json:"verdict,omitempty"`
|
||||
}
|
||||
|
||||
// Pause is whether the node takes no new ask.
|
||||
type Pause struct {
|
||||
// when (RFC 3339 time)
|
||||
At string `json:"at,omitempty"`
|
||||
By string `json:"by,omitempty"`
|
||||
Paused bool `json:"paused"`
|
||||
// the layout version
|
||||
V int64 `json:"v,omitempty"`
|
||||
}
|
||||
|
||||
// Step is one step a build module runs on a node.
|
||||
type Step struct {
|
||||
// when (RFC 3339 time)
|
||||
Ended string `json:"ended,omitempty"`
|
||||
ID string `json:"id"`
|
||||
Node string `json:"node"`
|
||||
Owner string `json:"owner,omitempty"`
|
||||
Phase string `json:"phase,omitempty"`
|
||||
// when (RFC 3339 time)
|
||||
PhaseAt string `json:"phase-at,omitempty"`
|
||||
Reason string `json:"reason,omitempty"`
|
||||
// what the step answered
|
||||
Result any `json:"result,omitempty"`
|
||||
// when (RFC 3339 time)
|
||||
Started string `json:"started"`
|
||||
// running, passed, failed or refused
|
||||
State string `json:"state"`
|
||||
Tool string `json:"tool"`
|
||||
// the layout version
|
||||
V int64 `json:"v,omitempty"`
|
||||
What string `json:"what,omitempty"`
|
||||
}
|
||||
|
||||
// StepAskBuildArtifactsItem is a type of the contract.
|
||||
type StepAskBuildArtifactsItem struct {
|
||||
Artifact string `json:"artifact"`
|
||||
From string `json:"from,omitempty"`
|
||||
Seat string `json:"seat,omitempty"`
|
||||
Version string `json:"version,omitempty"`
|
||||
}
|
||||
|
||||
// StepAskBuildToolchain is a type of the contract.
|
||||
type StepAskBuildToolchain struct {
|
||||
Language string `json:"language"`
|
||||
Recipe string `json:"recipe"`
|
||||
Version string `json:"version"`
|
||||
}
|
||||
|
||||
// StepAskBuild is a type of the contract.
|
||||
type StepAskBuild struct {
|
||||
Artifacts []StepAskBuildArtifactsItem `json:"artifacts"`
|
||||
// what the build module fetches: the repository, commit and directory, as the indexer states it
|
||||
Fetch any `json:"fetch,omitempty"`
|
||||
Fp string `json:"fp,omitempty"`
|
||||
Module string `json:"module"`
|
||||
Path string `json:"path,omitempty"`
|
||||
Publish bool `json:"publish,omitempty"`
|
||||
Toolchain *StepAskBuildToolchain `json:"toolchain,omitempty"`
|
||||
}
|
||||
|
||||
// StepAskCheck is a type of the contract.
|
||||
type StepAskCheck struct {
|
||||
// what the build module fetches: the repository, commit and directory, as the indexer states it
|
||||
Fetch any `json:"fetch"`
|
||||
Kept bool `json:"kept,omitempty"`
|
||||
Levels []string `json:"levels"`
|
||||
Packages []string `json:"packages,omitempty"`
|
||||
Run string `json:"run,omitempty"`
|
||||
Runner string `json:"runner,omitempty"`
|
||||
RunnerWhen string `json:"runner_when,omitempty"`
|
||||
}
|
||||
|
||||
// StepAskCompose is a type of the contract.
|
||||
type StepAskCompose struct {
|
||||
Dir string `json:"dir"`
|
||||
// what the build module fetches: the repository, commit and directory, as the indexer states it
|
||||
Fetch any `json:"fetch"`
|
||||
Package string `json:"package"`
|
||||
Reader string `json:"reader,omitempty"`
|
||||
// the compose-candidate request
|
||||
Request any `json:"request"`
|
||||
}
|
||||
|
||||
// StepAsk is one ask of a build module.
|
||||
type StepAsk struct {
|
||||
Asker string `json:"asker"`
|
||||
// when (RFC 3339 time)
|
||||
At string `json:"at,omitempty"`
|
||||
Build *StepAskBuild `json:"build,omitempty"`
|
||||
Check *StepAskCheck `json:"check,omitempty"`
|
||||
Compose *StepAskCompose `json:"compose,omitempty"`
|
||||
ID string `json:"id"`
|
||||
// build, check or compose
|
||||
Kind string `json:"kind"`
|
||||
// go, typescript or image
|
||||
Language string `json:"language"`
|
||||
Needs []string `json:"needs,omitempty"`
|
||||
// package to the tagged version a build pins
|
||||
Packages map[string]string `json:"packages,omitempty"`
|
||||
// the layout version
|
||||
V int64 `json:"v,omitempty"`
|
||||
}
|
||||
|
||||
// Toolchain is the toolchain a node reports.
|
||||
type Toolchain struct {
|
||||
// when (RFC 3339 time)
|
||||
At string `json:"at,omitempty"`
|
||||
// go or typescript
|
||||
Language string `json:"language"`
|
||||
Recipe string `json:"recipe"`
|
||||
// the layout version
|
||||
V int64 `json:"v,omitempty"`
|
||||
Version string `json:"version"`
|
||||
}
|
||||
|
||||
// CurrentInput is what current takes.
|
||||
type CurrentInput struct {
|
||||
}
|
||||
|
||||
// CurrentAnswer is what current answers.
|
||||
type CurrentAnswer = map[string]any
|
||||
|
||||
// PauseInput is what pause takes.
|
||||
type PauseInput struct {
|
||||
}
|
||||
|
||||
// PauseAnswer is what pause answers.
|
||||
type PauseAnswer = map[string]any
|
||||
|
||||
// ResumeInput is what resume takes.
|
||||
type ResumeInput struct {
|
||||
}
|
||||
|
||||
// ResumeAnswer is what resume answers.
|
||||
type ResumeAnswer = map[string]any
|
||||
|
||||
// DeliveryAccept is the body of delivery.
|
||||
type DeliveryAccept = StepAsk
|
||||
|
||||
// CheckAccept is the body of check.
|
||||
type CheckAccept = StepAsk
|
||||
|
||||
// RecheckAccept is the body of recheck.
|
||||
type RecheckAccept = StepAsk
|
||||
|
||||
// StepsByNodeIDValue is one value of the state steps under the key <node>.<id>.
|
||||
type StepsByNodeIDValue = Step
|
||||
|
||||
// PausesByPausedNodeValue is one value of the state pauses under the key paused.<node>.
|
||||
type PausesByPausedNodeValue = Pause
|
||||
|
||||
// PausesByCapacityNodeValue is one value of the state pauses under the key capacity.<node>.
|
||||
type PausesByCapacityNodeValue = Capacity
|
||||
|
||||
// ToolchainsByNodeLanguageValue is one value of the state toolchains under the key <node>.<language>.
|
||||
type ToolchainsByNodeLanguageValue = Toolchain
|
||||
@@ -0,0 +1,68 @@
|
||||
// Code generated by seatgen from the seat definitions; DO NOT EDIT.
|
||||
|
||||
package nodebuildimage
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"reflect"
|
||||
"regexp"
|
||||
"testing"
|
||||
)
|
||||
|
||||
// TestFixtures reads every fixture of seats/node-build-image/fixtures: each decodes into its type, a field the type does not
|
||||
// know is dropped (ADR 0608 §6), and it reads back as its wire says; each subject named is the contract's.
|
||||
func TestFixtures(t *testing.T) {
|
||||
cases := []struct {
|
||||
name, subject, given, wire string
|
||||
decode func([]byte) (any, error)
|
||||
subjectIs *regexp.Regexp
|
||||
}{
|
||||
{name: "a running step", subject: "", given: "{\"id\":\"a1b2\",\"node\":\"ace\",\"started\":\"2026-11-05T10:00:00Z\",\"state\":\"running\",\"tool\":\"build\",\"v\":1}", wire: "{\"id\":\"a1b2\",\"node\":\"ace\",\"started\":\"2026-11-05T10:00:00Z\",\"state\":\"running\",\"tool\":\"build\",\"v\":1}",
|
||||
decode: func(b []byte) (any, error) { var v StepsByNodeIDValue; err := json.Unmarshal(b, &v); return v, err }, subjectIs: nil},
|
||||
{name: "a paused node", subject: "", given: "{\"at\":\"2026-11-05T10:00:00Z\",\"by\":\"operator@shanks\",\"paused\":true,\"v\":1}", wire: "{\"at\":\"2026-11-05T10:00:00Z\",\"by\":\"operator@shanks\",\"paused\":true,\"v\":1}",
|
||||
decode: func(b []byte) (any, error) {
|
||||
var v PausesByPausedNodeValue
|
||||
err := json.Unmarshal(b, &v)
|
||||
return v, err
|
||||
}, subjectIs: nil},
|
||||
{name: "a node's capacity", subject: "", given: "{\"configured\":2,\"effective\":2,\"v\":1,\"why\":\"the configured steps fit the cores\"}", wire: "{\"configured\":2,\"effective\":2,\"v\":1,\"why\":\"the configured steps fit the cores\"}",
|
||||
decode: func(b []byte) (any, error) {
|
||||
var v PausesByCapacityNodeValue
|
||||
err := json.Unmarshal(b, &v)
|
||||
return v, err
|
||||
}, subjectIs: nil},
|
||||
{name: "a node's toolchain", subject: "", given: "{\"language\":\"go\",\"recipe\":\"1\",\"v\":1,\"version\":\"1.26.0\"}", wire: "{\"language\":\"go\",\"recipe\":\"1\",\"v\":1,\"version\":\"1.26.0\"}",
|
||||
decode: func(b []byte) (any, error) {
|
||||
var v ToolchainsByNodeLanguageValue
|
||||
err := json.Unmarshal(b, &v)
|
||||
return v, err
|
||||
}, subjectIs: nil},
|
||||
{name: "a build ask", subject: "mesh.seat.node-build-image.accept.delivery", given: "{\"asker\":\"mesh-delivery-builder\",\"build\":{\"artifacts\":[{\"artifact\":\"go\"}],\"module\":\"nats\"},\"id\":\"a1b2\",\"kind\":\"build\",\"language\":\"go\"}", wire: "{\"asker\":\"mesh-delivery-builder\",\"build\":{\"artifacts\":[{\"artifact\":\"go\"}],\"module\":\"nats\"},\"id\":\"a1b2\",\"kind\":\"build\",\"language\":\"go\"}",
|
||||
decode: func(b []byte) (any, error) { var v DeliveryAccept; err := json.Unmarshal(b, &v); return v, err }, subjectIs: regexp.MustCompile("^mesh\\.seat\\.node-build-image\\.accept\\.delivery$")},
|
||||
}
|
||||
for _, c := range cases {
|
||||
v, err := c.decode([]byte(c.given))
|
||||
if err != nil {
|
||||
t.Errorf("%s: does not decode: %v", c.name, err)
|
||||
continue
|
||||
}
|
||||
back, err := json.Marshal(v)
|
||||
if err != nil {
|
||||
t.Errorf("%s: does not encode: %v", c.name, err)
|
||||
continue
|
||||
}
|
||||
var got, want any
|
||||
if err := json.Unmarshal(back, &got); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if err := json.Unmarshal([]byte(c.wire), &want); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if !reflect.DeepEqual(got, want) {
|
||||
t.Errorf("%s: reads back as %s, want %s", c.name, back, c.wire)
|
||||
}
|
||||
if c.subjectIs != nil && !c.subjectIs.MatchString(c.subject) {
|
||||
t.Errorf("%s: subject %q is not the contract's %s", c.name, c.subject, c.subjectIs)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,3 @@
|
||||
module git.novox.be/novox/mesh-delivery/contracts/go/seats/nodebuildimage
|
||||
|
||||
go 1.26.0
|
||||
@@ -0,0 +1,447 @@
|
||||
// Code generated by seatgen from the seat definitions; DO NOT EDIT.
|
||||
|
||||
// Package nodebuildnode is the contract of the seat "node-build-node", defined whole (seats/node-build-node/seat.json): The Node build module on a node: it takes the asks of the builder and the checker, runs them as steps and answers.
|
||||
package nodebuildnode
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"strings"
|
||||
)
|
||||
|
||||
// Seat is the seat's name.
|
||||
const Seat = "node-build-node"
|
||||
|
||||
const (
|
||||
VerbCurrent = "current"
|
||||
VerbPause = "pause"
|
||||
VerbResume = "resume"
|
||||
)
|
||||
|
||||
const (
|
||||
AcceptDelivery = "delivery"
|
||||
AcceptCheck = "check"
|
||||
AcceptRecheck = "recheck"
|
||||
)
|
||||
|
||||
// Verbs is every verb the seat serves, in its definition's order: a new copy each call.
|
||||
func Verbs() []string {
|
||||
return []string{VerbCurrent, VerbPause, VerbResume}
|
||||
}
|
||||
|
||||
// AcceptSubjectDelivery is where delivery is sent.
|
||||
const AcceptSubjectDelivery = "mesh.seat.node-build-node.accept.delivery"
|
||||
|
||||
// AcceptSubjectCheck is where check is sent.
|
||||
const AcceptSubjectCheck = "mesh.seat.node-build-node.accept.check"
|
||||
|
||||
// AcceptSubjectRecheck is where recheck is sent.
|
||||
const AcceptSubjectRecheck = "mesh.seat.node-build-node.accept.recheck"
|
||||
|
||||
const (
|
||||
StateSteps = "steps"
|
||||
StatePauses = "pauses"
|
||||
StateToolchains = "toolchains"
|
||||
)
|
||||
|
||||
// StepsByNodeIDKey is the key of one value of the state steps under the form "<node>.<id>".
|
||||
func StepsByNodeIDKey(node, id string) string { return node + "." + id }
|
||||
|
||||
// ParseStepsByNodeIDKey reads the parts of a key of the form "<node>.<id>", in the order of the form; the last result is false for a key of another form.
|
||||
func ParseStepsByNodeIDKey(key string) (string, string, bool) {
|
||||
rest := key
|
||||
v0, after0, found0 := strings.Cut(rest, ".")
|
||||
if !found0 || v0 == "" {
|
||||
return "", "", false
|
||||
}
|
||||
rest = after0
|
||||
if rest == "" {
|
||||
return "", "", false
|
||||
}
|
||||
v2 := rest
|
||||
rest = ""
|
||||
if rest != "" {
|
||||
return "", "", false
|
||||
}
|
||||
return v0, v2, true
|
||||
}
|
||||
|
||||
// DecodeStepsByNodeIDValue reads one value of the state steps under the form "<node>.<id>", refusing a field StepsByNodeIDValue does not know and any text after the value.
|
||||
func DecodeStepsByNodeIDValue(raw []byte) (StepsByNodeIDValue, error) {
|
||||
var v StepsByNodeIDValue
|
||||
dec := json.NewDecoder(bytes.NewReader(raw))
|
||||
dec.DisallowUnknownFields()
|
||||
if err := dec.Decode(&v); err != nil {
|
||||
return v, err
|
||||
}
|
||||
if dec.More() {
|
||||
return v, errors.New("text after the value")
|
||||
}
|
||||
return v, nil
|
||||
}
|
||||
|
||||
// PausesByPausedNodeKey is the key of one value of the state pauses under the form "paused.<node>".
|
||||
func PausesByPausedNodeKey(node string) string { return "paused." + node }
|
||||
|
||||
// ParsePausesByPausedNodeKey reads the parts of a key of the form "paused.<node>", in the order of the form; the last result is false for a key of another form.
|
||||
func ParsePausesByPausedNodeKey(key string) (string, bool) {
|
||||
rest := key
|
||||
if !strings.HasPrefix(rest, "paused.") {
|
||||
return "", false
|
||||
}
|
||||
rest = rest[7:]
|
||||
if rest == "" {
|
||||
return "", false
|
||||
}
|
||||
v1 := rest
|
||||
rest = ""
|
||||
if rest != "" {
|
||||
return "", false
|
||||
}
|
||||
return v1, true
|
||||
}
|
||||
|
||||
// DecodePausesByPausedNodeValue reads one value of the state pauses under the form "paused.<node>", refusing a field PausesByPausedNodeValue does not know and any text after the value.
|
||||
func DecodePausesByPausedNodeValue(raw []byte) (PausesByPausedNodeValue, error) {
|
||||
var v PausesByPausedNodeValue
|
||||
dec := json.NewDecoder(bytes.NewReader(raw))
|
||||
dec.DisallowUnknownFields()
|
||||
if err := dec.Decode(&v); err != nil {
|
||||
return v, err
|
||||
}
|
||||
if dec.More() {
|
||||
return v, errors.New("text after the value")
|
||||
}
|
||||
return v, nil
|
||||
}
|
||||
|
||||
// PausesByCapacityNodeKey is the key of one value of the state pauses under the form "capacity.<node>".
|
||||
func PausesByCapacityNodeKey(node string) string { return "capacity." + node }
|
||||
|
||||
// ParsePausesByCapacityNodeKey reads the parts of a key of the form "capacity.<node>", in the order of the form; the last result is false for a key of another form.
|
||||
func ParsePausesByCapacityNodeKey(key string) (string, bool) {
|
||||
rest := key
|
||||
if !strings.HasPrefix(rest, "capacity.") {
|
||||
return "", false
|
||||
}
|
||||
rest = rest[9:]
|
||||
if rest == "" {
|
||||
return "", false
|
||||
}
|
||||
v1 := rest
|
||||
rest = ""
|
||||
if rest != "" {
|
||||
return "", false
|
||||
}
|
||||
return v1, true
|
||||
}
|
||||
|
||||
// DecodePausesByCapacityNodeValue reads one value of the state pauses under the form "capacity.<node>", refusing a field PausesByCapacityNodeValue does not know and any text after the value.
|
||||
func DecodePausesByCapacityNodeValue(raw []byte) (PausesByCapacityNodeValue, error) {
|
||||
var v PausesByCapacityNodeValue
|
||||
dec := json.NewDecoder(bytes.NewReader(raw))
|
||||
dec.DisallowUnknownFields()
|
||||
if err := dec.Decode(&v); err != nil {
|
||||
return v, err
|
||||
}
|
||||
if dec.More() {
|
||||
return v, errors.New("text after the value")
|
||||
}
|
||||
return v, nil
|
||||
}
|
||||
|
||||
// ToolchainsByNodeLanguageKey is the key of one value of the state toolchains under the form "<node>.<language>".
|
||||
func ToolchainsByNodeLanguageKey(node, language string) string { return node + "." + language }
|
||||
|
||||
// ParseToolchainsByNodeLanguageKey reads the parts of a key of the form "<node>.<language>", in the order of the form; the last result is false for a key of another form.
|
||||
func ParseToolchainsByNodeLanguageKey(key string) (string, string, bool) {
|
||||
rest := key
|
||||
v0, after0, found0 := strings.Cut(rest, ".")
|
||||
if !found0 || v0 == "" {
|
||||
return "", "", false
|
||||
}
|
||||
rest = after0
|
||||
if rest == "" {
|
||||
return "", "", false
|
||||
}
|
||||
v2 := rest
|
||||
rest = ""
|
||||
if rest != "" {
|
||||
return "", "", false
|
||||
}
|
||||
return v0, v2, true
|
||||
}
|
||||
|
||||
// DecodeToolchainsByNodeLanguageValue reads one value of the state toolchains under the form "<node>.<language>", refusing a field ToolchainsByNodeLanguageValue does not know and any text after the value.
|
||||
func DecodeToolchainsByNodeLanguageValue(raw []byte) (ToolchainsByNodeLanguageValue, error) {
|
||||
var v ToolchainsByNodeLanguageValue
|
||||
dec := json.NewDecoder(bytes.NewReader(raw))
|
||||
dec.DisallowUnknownFields()
|
||||
if err := dec.Decode(&v); err != nil {
|
||||
return v, err
|
||||
}
|
||||
if dec.More() {
|
||||
return v, errors.New("text after the value")
|
||||
}
|
||||
return v, nil
|
||||
}
|
||||
|
||||
// BuildAnswerArtifactsItem is a type of the contract.
|
||||
type BuildAnswerArtifactsItem struct {
|
||||
Artifact string `json:"artifact"`
|
||||
Digest string `json:"digest"`
|
||||
Reference string `json:"reference,omitempty"`
|
||||
}
|
||||
|
||||
// BuildAnswerToolchain is a type of the contract.
|
||||
type BuildAnswerToolchain struct {
|
||||
Language string `json:"language"`
|
||||
Recipe string `json:"recipe"`
|
||||
Version string `json:"version"`
|
||||
}
|
||||
|
||||
// BuildAnswer is a type of the contract.
|
||||
type BuildAnswer struct {
|
||||
Artifacts []BuildAnswerArtifactsItem `json:"artifacts"`
|
||||
Imports []string `json:"imports,omitempty"`
|
||||
OutOfStep []string `json:"out-of-step,omitempty"`
|
||||
Toolchain *BuildAnswerToolchain `json:"toolchain,omitempty"`
|
||||
}
|
||||
|
||||
// Capacity is how many steps the node runs at once.
|
||||
type Capacity struct {
|
||||
// when (RFC 3339 time)
|
||||
At string `json:"at,omitempty"`
|
||||
BuildHours string `json:"build-hours,omitempty"`
|
||||
BuildHoursFault string `json:"build-hours-fault,omitempty"`
|
||||
ByCpu int64 `json:"by-cpu,omitempty"`
|
||||
Configured int64 `json:"configured"`
|
||||
ConfiguredFrom string `json:"configured-from,omitempty"`
|
||||
CoresPerStep float64 `json:"cores-per-step,omitempty"`
|
||||
Effective int64 `json:"effective"`
|
||||
Ineligible string `json:"ineligible,omitempty"`
|
||||
OutsideHours bool `json:"outside-hours,omitempty"`
|
||||
QuotaCores float64 `json:"quota-cores,omitempty"`
|
||||
QuotaFrom string `json:"quota-from,omitempty"`
|
||||
// the layout version
|
||||
V int64 `json:"v,omitempty"`
|
||||
Why string `json:"why"`
|
||||
}
|
||||
|
||||
// CheckAnswerLevelsItemDigestNeedsItem is a type of the contract.
|
||||
type CheckAnswerLevelsItemDigestNeedsItem struct {
|
||||
Needs []string `json:"needs,omitempty"`
|
||||
Package string `json:"package,omitempty"`
|
||||
}
|
||||
|
||||
// CheckAnswerLevelsItemDigestPackagesItem is a type of the contract.
|
||||
type CheckAnswerLevelsItemDigestPackagesItem struct {
|
||||
Digest string `json:"digest"`
|
||||
Package string `json:"package"`
|
||||
}
|
||||
|
||||
// CheckAnswerLevelsItemDigest is a type of the contract.
|
||||
type CheckAnswerLevelsItemDigest struct {
|
||||
Beside []string `json:"beside,omitempty"`
|
||||
JudgedBy string `json:"judged_by,omitempty"`
|
||||
Level string `json:"level,omitempty"`
|
||||
Needs []CheckAnswerLevelsItemDigestNeedsItem `json:"needs,omitempty"`
|
||||
Packages []CheckAnswerLevelsItemDigestPackagesItem `json:"packages,omitempty"`
|
||||
WithoutTests []string `json:"without_tests,omitempty"`
|
||||
}
|
||||
|
||||
// CheckAnswerLevelsItemKeptItem is a type of the contract.
|
||||
type CheckAnswerLevelsItemKeptItem struct {
|
||||
Digest string `json:"digest,omitempty"`
|
||||
Package string `json:"package,omitempty"`
|
||||
Tests int64 `json:"tests,omitempty"`
|
||||
}
|
||||
|
||||
// CheckAnswerLevelsItemUnmetItem is a type of the contract.
|
||||
type CheckAnswerLevelsItemUnmetItem struct {
|
||||
Needs []string `json:"needs,omitempty"`
|
||||
Package string `json:"package,omitempty"`
|
||||
}
|
||||
|
||||
// CheckAnswerLevelsItem is a type of the contract.
|
||||
type CheckAnswerLevelsItem struct {
|
||||
Digest *CheckAnswerLevelsItemDigest `json:"digest,omitempty"`
|
||||
Judged string `json:"judged,omitempty"`
|
||||
Kept []CheckAnswerLevelsItemKeptItem `json:"kept,omitempty"`
|
||||
Level string `json:"level"`
|
||||
Tests []any `json:"tests,omitempty"`
|
||||
Unmet []CheckAnswerLevelsItemUnmetItem `json:"unmet,omitempty"`
|
||||
}
|
||||
|
||||
// CheckAnswer is a type of the contract.
|
||||
type CheckAnswer struct {
|
||||
// what the build module fetches: the repository, commit and directory, as the indexer states it
|
||||
Fetch any `json:"fetch"`
|
||||
Levels []CheckAnswerLevelsItem `json:"levels,omitempty"`
|
||||
}
|
||||
|
||||
// ComposeAnswer is a type of the contract.
|
||||
type ComposeAnswer struct {
|
||||
Built bool `json:"built"`
|
||||
Key string `json:"key,omitempty"`
|
||||
// the compose-candidate verdict
|
||||
Verdict any `json:"verdict,omitempty"`
|
||||
}
|
||||
|
||||
// Pause is whether the node takes no new ask.
|
||||
type Pause struct {
|
||||
// when (RFC 3339 time)
|
||||
At string `json:"at,omitempty"`
|
||||
By string `json:"by,omitempty"`
|
||||
Paused bool `json:"paused"`
|
||||
// the layout version
|
||||
V int64 `json:"v,omitempty"`
|
||||
}
|
||||
|
||||
// Step is one step a build module runs on a node.
|
||||
type Step struct {
|
||||
// when (RFC 3339 time)
|
||||
Ended string `json:"ended,omitempty"`
|
||||
ID string `json:"id"`
|
||||
Node string `json:"node"`
|
||||
Owner string `json:"owner,omitempty"`
|
||||
Phase string `json:"phase,omitempty"`
|
||||
// when (RFC 3339 time)
|
||||
PhaseAt string `json:"phase-at,omitempty"`
|
||||
Reason string `json:"reason,omitempty"`
|
||||
// what the step answered
|
||||
Result any `json:"result,omitempty"`
|
||||
// when (RFC 3339 time)
|
||||
Started string `json:"started"`
|
||||
// running, passed, failed or refused
|
||||
State string `json:"state"`
|
||||
Tool string `json:"tool"`
|
||||
// the layout version
|
||||
V int64 `json:"v,omitempty"`
|
||||
What string `json:"what,omitempty"`
|
||||
}
|
||||
|
||||
// StepAskBuildArtifactsItem is a type of the contract.
|
||||
type StepAskBuildArtifactsItem struct {
|
||||
Artifact string `json:"artifact"`
|
||||
From string `json:"from,omitempty"`
|
||||
Seat string `json:"seat,omitempty"`
|
||||
Version string `json:"version,omitempty"`
|
||||
}
|
||||
|
||||
// StepAskBuildToolchain is a type of the contract.
|
||||
type StepAskBuildToolchain struct {
|
||||
Language string `json:"language"`
|
||||
Recipe string `json:"recipe"`
|
||||
Version string `json:"version"`
|
||||
}
|
||||
|
||||
// StepAskBuild is a type of the contract.
|
||||
type StepAskBuild struct {
|
||||
Artifacts []StepAskBuildArtifactsItem `json:"artifacts"`
|
||||
// what the build module fetches: the repository, commit and directory, as the indexer states it
|
||||
Fetch any `json:"fetch,omitempty"`
|
||||
Fp string `json:"fp,omitempty"`
|
||||
Module string `json:"module"`
|
||||
Path string `json:"path,omitempty"`
|
||||
Publish bool `json:"publish,omitempty"`
|
||||
Toolchain *StepAskBuildToolchain `json:"toolchain,omitempty"`
|
||||
}
|
||||
|
||||
// StepAskCheck is a type of the contract.
|
||||
type StepAskCheck struct {
|
||||
// what the build module fetches: the repository, commit and directory, as the indexer states it
|
||||
Fetch any `json:"fetch"`
|
||||
Kept bool `json:"kept,omitempty"`
|
||||
Levels []string `json:"levels"`
|
||||
Packages []string `json:"packages,omitempty"`
|
||||
Run string `json:"run,omitempty"`
|
||||
Runner string `json:"runner,omitempty"`
|
||||
RunnerWhen string `json:"runner_when,omitempty"`
|
||||
}
|
||||
|
||||
// StepAskCompose is a type of the contract.
|
||||
type StepAskCompose struct {
|
||||
Dir string `json:"dir"`
|
||||
// what the build module fetches: the repository, commit and directory, as the indexer states it
|
||||
Fetch any `json:"fetch"`
|
||||
Package string `json:"package"`
|
||||
Reader string `json:"reader,omitempty"`
|
||||
// the compose-candidate request
|
||||
Request any `json:"request"`
|
||||
}
|
||||
|
||||
// StepAsk is one ask of a build module.
|
||||
type StepAsk struct {
|
||||
Asker string `json:"asker"`
|
||||
// when (RFC 3339 time)
|
||||
At string `json:"at,omitempty"`
|
||||
Build *StepAskBuild `json:"build,omitempty"`
|
||||
Check *StepAskCheck `json:"check,omitempty"`
|
||||
Compose *StepAskCompose `json:"compose,omitempty"`
|
||||
ID string `json:"id"`
|
||||
// build, check or compose
|
||||
Kind string `json:"kind"`
|
||||
// go, typescript or image
|
||||
Language string `json:"language"`
|
||||
Needs []string `json:"needs,omitempty"`
|
||||
// package to the tagged version a build pins
|
||||
Packages map[string]string `json:"packages,omitempty"`
|
||||
// the layout version
|
||||
V int64 `json:"v,omitempty"`
|
||||
}
|
||||
|
||||
// Toolchain is the toolchain a node reports.
|
||||
type Toolchain struct {
|
||||
// when (RFC 3339 time)
|
||||
At string `json:"at,omitempty"`
|
||||
// go or typescript
|
||||
Language string `json:"language"`
|
||||
Recipe string `json:"recipe"`
|
||||
// the layout version
|
||||
V int64 `json:"v,omitempty"`
|
||||
Version string `json:"version"`
|
||||
}
|
||||
|
||||
// CurrentInput is what current takes.
|
||||
type CurrentInput struct {
|
||||
}
|
||||
|
||||
// CurrentAnswer is what current answers.
|
||||
type CurrentAnswer = map[string]any
|
||||
|
||||
// PauseInput is what pause takes.
|
||||
type PauseInput struct {
|
||||
}
|
||||
|
||||
// PauseAnswer is what pause answers.
|
||||
type PauseAnswer = map[string]any
|
||||
|
||||
// ResumeInput is what resume takes.
|
||||
type ResumeInput struct {
|
||||
}
|
||||
|
||||
// ResumeAnswer is what resume answers.
|
||||
type ResumeAnswer = map[string]any
|
||||
|
||||
// DeliveryAccept is the body of delivery.
|
||||
type DeliveryAccept = StepAsk
|
||||
|
||||
// CheckAccept is the body of check.
|
||||
type CheckAccept = StepAsk
|
||||
|
||||
// RecheckAccept is the body of recheck.
|
||||
type RecheckAccept = StepAsk
|
||||
|
||||
// StepsByNodeIDValue is one value of the state steps under the key <node>.<id>.
|
||||
type StepsByNodeIDValue = Step
|
||||
|
||||
// PausesByPausedNodeValue is one value of the state pauses under the key paused.<node>.
|
||||
type PausesByPausedNodeValue = Pause
|
||||
|
||||
// PausesByCapacityNodeValue is one value of the state pauses under the key capacity.<node>.
|
||||
type PausesByCapacityNodeValue = Capacity
|
||||
|
||||
// ToolchainsByNodeLanguageValue is one value of the state toolchains under the key <node>.<language>.
|
||||
type ToolchainsByNodeLanguageValue = Toolchain
|
||||
@@ -0,0 +1,68 @@
|
||||
// Code generated by seatgen from the seat definitions; DO NOT EDIT.
|
||||
|
||||
package nodebuildnode
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"reflect"
|
||||
"regexp"
|
||||
"testing"
|
||||
)
|
||||
|
||||
// TestFixtures reads every fixture of seats/node-build-node/fixtures: each decodes into its type, a field the type does not
|
||||
// know is dropped (ADR 0608 §6), and it reads back as its wire says; each subject named is the contract's.
|
||||
func TestFixtures(t *testing.T) {
|
||||
cases := []struct {
|
||||
name, subject, given, wire string
|
||||
decode func([]byte) (any, error)
|
||||
subjectIs *regexp.Regexp
|
||||
}{
|
||||
{name: "a running step", subject: "", given: "{\"id\":\"a1b2\",\"node\":\"ace\",\"started\":\"2026-11-05T10:00:00Z\",\"state\":\"running\",\"tool\":\"build\",\"v\":1}", wire: "{\"id\":\"a1b2\",\"node\":\"ace\",\"started\":\"2026-11-05T10:00:00Z\",\"state\":\"running\",\"tool\":\"build\",\"v\":1}",
|
||||
decode: func(b []byte) (any, error) { var v StepsByNodeIDValue; err := json.Unmarshal(b, &v); return v, err }, subjectIs: nil},
|
||||
{name: "a paused node", subject: "", given: "{\"at\":\"2026-11-05T10:00:00Z\",\"by\":\"operator@shanks\",\"paused\":true,\"v\":1}", wire: "{\"at\":\"2026-11-05T10:00:00Z\",\"by\":\"operator@shanks\",\"paused\":true,\"v\":1}",
|
||||
decode: func(b []byte) (any, error) {
|
||||
var v PausesByPausedNodeValue
|
||||
err := json.Unmarshal(b, &v)
|
||||
return v, err
|
||||
}, subjectIs: nil},
|
||||
{name: "a node's capacity", subject: "", given: "{\"configured\":2,\"effective\":2,\"v\":1,\"why\":\"the configured steps fit the cores\"}", wire: "{\"configured\":2,\"effective\":2,\"v\":1,\"why\":\"the configured steps fit the cores\"}",
|
||||
decode: func(b []byte) (any, error) {
|
||||
var v PausesByCapacityNodeValue
|
||||
err := json.Unmarshal(b, &v)
|
||||
return v, err
|
||||
}, subjectIs: nil},
|
||||
{name: "a node's toolchain", subject: "", given: "{\"language\":\"go\",\"recipe\":\"1\",\"v\":1,\"version\":\"1.26.0\"}", wire: "{\"language\":\"go\",\"recipe\":\"1\",\"v\":1,\"version\":\"1.26.0\"}",
|
||||
decode: func(b []byte) (any, error) {
|
||||
var v ToolchainsByNodeLanguageValue
|
||||
err := json.Unmarshal(b, &v)
|
||||
return v, err
|
||||
}, subjectIs: nil},
|
||||
{name: "a build ask", subject: "mesh.seat.node-build-node.accept.delivery", given: "{\"asker\":\"mesh-delivery-builder\",\"build\":{\"artifacts\":[{\"artifact\":\"go\"}],\"module\":\"nats\"},\"id\":\"a1b2\",\"kind\":\"build\",\"language\":\"go\"}", wire: "{\"asker\":\"mesh-delivery-builder\",\"build\":{\"artifacts\":[{\"artifact\":\"go\"}],\"module\":\"nats\"},\"id\":\"a1b2\",\"kind\":\"build\",\"language\":\"go\"}",
|
||||
decode: func(b []byte) (any, error) { var v DeliveryAccept; err := json.Unmarshal(b, &v); return v, err }, subjectIs: regexp.MustCompile("^mesh\\.seat\\.node-build-node\\.accept\\.delivery$")},
|
||||
}
|
||||
for _, c := range cases {
|
||||
v, err := c.decode([]byte(c.given))
|
||||
if err != nil {
|
||||
t.Errorf("%s: does not decode: %v", c.name, err)
|
||||
continue
|
||||
}
|
||||
back, err := json.Marshal(v)
|
||||
if err != nil {
|
||||
t.Errorf("%s: does not encode: %v", c.name, err)
|
||||
continue
|
||||
}
|
||||
var got, want any
|
||||
if err := json.Unmarshal(back, &got); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if err := json.Unmarshal([]byte(c.wire), &want); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if !reflect.DeepEqual(got, want) {
|
||||
t.Errorf("%s: reads back as %s, want %s", c.name, back, c.wire)
|
||||
}
|
||||
if c.subjectIs != nil && !c.subjectIs.MatchString(c.subject) {
|
||||
t.Errorf("%s: subject %q is not the contract's %s", c.name, c.subject, c.subjectIs)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,3 @@
|
||||
module git.novox.be/novox/mesh-delivery/contracts/go/seats/nodebuildnode
|
||||
|
||||
go 1.26.0
|
||||
@@ -0,0 +1,262 @@
|
||||
// Code generated by seatgen from the seat definitions; DO NOT EDIT.
|
||||
|
||||
// Package npmpackageregistry is the contract of the seat "npm-package-registry", defined whole (seats/npm-package-registry/seat.json): The mesh's npm packages: the npm registry protocol's read side, and publishing over the bus.
|
||||
package npmpackageregistry
|
||||
|
||||
// Seat is the seat's name.
|
||||
const Seat = "npm-package-registry"
|
||||
|
||||
const (
|
||||
VerbPublish = "publish"
|
||||
VerbPublishPart = "publish-part"
|
||||
VerbVersions = "versions"
|
||||
VerbRemove = "remove"
|
||||
)
|
||||
|
||||
const (
|
||||
EventPublished = "published"
|
||||
EventRefused = "refused"
|
||||
EventRemoved = "removed"
|
||||
EventKept = "kept"
|
||||
)
|
||||
|
||||
// Verbs is every verb the seat serves, in its definition's order: a new copy each call.
|
||||
func Verbs() []string {
|
||||
return []string{VerbPublish, VerbPublishPart, VerbVersions, VerbRemove}
|
||||
}
|
||||
|
||||
// Events is every event published on the seat's subject: a new copy each call.
|
||||
func Events() []string {
|
||||
return []string{EventPublished, EventRefused, EventRemoved, EventKept}
|
||||
}
|
||||
|
||||
// Family is where the seat's events are kept: the shared events stream, filtered to the seat's subjects.
|
||||
const (
|
||||
Family = "npm-package-registry.events"
|
||||
FamilyMaxAgeDays = 7
|
||||
)
|
||||
|
||||
// SubjectPublished is where published is published.
|
||||
const SubjectPublished = "mesh.seat.npm-package-registry.event.published"
|
||||
|
||||
// SubjectRefused is where refused is published.
|
||||
const SubjectRefused = "mesh.seat.npm-package-registry.event.refused"
|
||||
|
||||
// SubjectRemoved is where removed is published.
|
||||
const SubjectRemoved = "mesh.seat.npm-package-registry.event.removed"
|
||||
|
||||
// SubjectKept is where kept is published.
|
||||
const SubjectKept = "mesh.seat.npm-package-registry.event.kept"
|
||||
|
||||
// Artifact is one published thing, as every registry event and answer names it.
|
||||
type Artifact struct {
|
||||
// the source commit it was built from
|
||||
Commit string `json:"commit,omitempty"`
|
||||
// its digest, sha256:<hex> (a Go package's h1: hash beside it in the version's record)
|
||||
Digest string `json:"digest"`
|
||||
// the build's fingerprint
|
||||
Fingerprint string `json:"fingerprint,omitempty"`
|
||||
// what it is
|
||||
Kind string `json:"kind"`
|
||||
// the package's, image's, bundle's or archive's name; a package's is its name in its own language (design 32): a Go package's the go.mod module path (git.novox.be/novox/mesh-sdk/go), an npm package's package.json's name (@novox/mesh-sdk)
|
||||
Name string `json:"name"`
|
||||
// the tag it carries, for a tagged version
|
||||
Tag string `json:"tag,omitempty"`
|
||||
// the version the builder gave it (design 51 *The versions*); empty for a bundle or archive published by its digest alone
|
||||
Version string `json:"version,omitempty"`
|
||||
}
|
||||
|
||||
// The values of Artifact's kind.
|
||||
const (
|
||||
ArtifactKindPackage = "package"
|
||||
ArtifactKindImage = "image"
|
||||
ArtifactKindBundle = "bundle"
|
||||
ArtifactKindArchive = "archive"
|
||||
ArtifactKindUpstream = "upstream"
|
||||
)
|
||||
|
||||
// Outcome is the body of published, refused and removed.
|
||||
type Outcome struct {
|
||||
// the source commit it was built from
|
||||
Commit string `json:"commit,omitempty"`
|
||||
// its digest, sha256:<hex> (a Go package's h1: hash beside it in the version's record)
|
||||
Digest string `json:"digest"`
|
||||
// the build's fingerprint
|
||||
Fingerprint string `json:"fingerprint,omitempty"`
|
||||
// the module holding the seat that said it
|
||||
Holder string `json:"holder"`
|
||||
// what it is
|
||||
Kind string `json:"kind"`
|
||||
// the Go module path a Go package is fetched under, equal to name for a Go package (go-package-registry's published); empty for every other kind of artifact
|
||||
Module string `json:"module,omitempty"`
|
||||
// the package's, image's, bundle's or archive's name; a package's is its name in its own language (design 32): a Go package's the go.mod module path (git.novox.be/novox/mesh-sdk/go), an npm package's package.json's name (@novox/mesh-sdk)
|
||||
Name string `json:"name"`
|
||||
// why: a refusal's cause, a removal's (retention, tag-gone, by-hand), or why a removal was kept (shared-digest)
|
||||
Reason string `json:"reason,omitempty"`
|
||||
// the reference the seat answers: <registry>/<module>/<artifact>@sha256:<digest>, or <name>@<version> for a package (ADR 0605 §9)
|
||||
Reference string `json:"reference,omitempty"`
|
||||
// the tag it carries, for a tagged version
|
||||
Tag string `json:"tag,omitempty"`
|
||||
// for kept: the other tags still naming its digest
|
||||
Tags []string `json:"tags,omitempty"`
|
||||
// the transfer it ends, when it ended one
|
||||
Transfer string `json:"transfer,omitempty"`
|
||||
// the version the builder gave it (design 51 *The versions*); empty for a bundle or archive published by its digest alone
|
||||
Version string `json:"version,omitempty"`
|
||||
}
|
||||
|
||||
// The values of Outcome's kind.
|
||||
const (
|
||||
OutcomeKindPackage = "package"
|
||||
OutcomeKindImage = "image"
|
||||
OutcomeKindBundle = "bundle"
|
||||
OutcomeKindArchive = "archive"
|
||||
OutcomeKindUpstream = "upstream"
|
||||
)
|
||||
|
||||
// Part is one part of a transfer.
|
||||
type Part struct {
|
||||
// the part's digest, sha256:<hex>: an image's layer or config, a package's file, a bundle's blob
|
||||
Digest string `json:"digest"`
|
||||
// its size in bytes
|
||||
Size int64 `json:"size"`
|
||||
}
|
||||
|
||||
// PublishInput is what publish takes.
|
||||
type PublishInput struct {
|
||||
// the source commit it was built from
|
||||
Commit string `json:"commit,omitempty"`
|
||||
// its digest, sha256:<hex> (a Go package's h1: hash beside it in the version's record)
|
||||
Digest string `json:"digest"`
|
||||
// the build's fingerprint
|
||||
Fingerprint string `json:"fingerprint,omitempty"`
|
||||
// what it is
|
||||
Kind string `json:"kind"`
|
||||
// the package's, image's, bundle's or archive's name; a package's is its name in its own language (design 32): a Go package's the go.mod module path (git.novox.be/novox/mesh-sdk/go), an npm package's package.json's name (@novox/mesh-sdk)
|
||||
Name string `json:"name"`
|
||||
// every part's digest and size; none when the digest is already held
|
||||
Parts []Part `json:"parts,omitempty"`
|
||||
// the tag it carries, for a tagged version
|
||||
Tag string `json:"tag,omitempty"`
|
||||
// the transfer's name, the caller's own; the same name opens the same transfer again
|
||||
Transfer string `json:"transfer"`
|
||||
// the version the builder gave it (design 51 *The versions*); empty for a bundle or archive published by its digest alone
|
||||
Version string `json:"version,omitempty"`
|
||||
}
|
||||
|
||||
// The values of PublishInput's kind.
|
||||
const (
|
||||
PublishInputKindPackage = "package"
|
||||
PublishInputKindImage = "image"
|
||||
PublishInputKindBundle = "bundle"
|
||||
PublishInputKindArchive = "archive"
|
||||
PublishInputKindUpstream = "upstream"
|
||||
)
|
||||
|
||||
// PublishAnswer is what publish answers.
|
||||
type PublishAnswer struct {
|
||||
// the digest is held already: nothing to send; the outcome follows as an event
|
||||
Held bool `json:"held,omitempty"`
|
||||
// the digests of the parts the holder lacks, to send by publish-part
|
||||
Lacks []string `json:"lacks,omitempty"`
|
||||
// the transfer
|
||||
Transfer string `json:"transfer"`
|
||||
}
|
||||
|
||||
// PublishPartInput is what publish-part takes.
|
||||
type PublishPartInput struct {
|
||||
// the chunk's bytes, base64 (at most 760 KiB before encoding)
|
||||
Data string `json:"data"`
|
||||
// this chunk is the part's last
|
||||
Last bool `json:"last"`
|
||||
// the part's digest
|
||||
Part string `json:"part"`
|
||||
// the chunk's place in its part, from 0
|
||||
Sequence int64 `json:"sequence"`
|
||||
// the transfer
|
||||
Transfer string `json:"transfer"`
|
||||
}
|
||||
|
||||
// PublishPartAnswer is what publish-part answers.
|
||||
type PublishPartAnswer struct {
|
||||
// the part is whole and its bytes match its digest
|
||||
Complete bool `json:"complete,omitempty"`
|
||||
// the part
|
||||
Part string `json:"part"`
|
||||
// the chunks of the part held so far
|
||||
Received int64 `json:"received,omitempty"`
|
||||
// the transfer
|
||||
Transfer string `json:"transfer"`
|
||||
}
|
||||
|
||||
// VersionsInput is what versions takes.
|
||||
type VersionsInput struct {
|
||||
// narrowed to one kind (optional)
|
||||
Kind string `json:"kind,omitempty"`
|
||||
// the name
|
||||
Name string `json:"name"`
|
||||
}
|
||||
|
||||
// VersionsAnswer is what versions answers.
|
||||
type VersionsAnswer struct {
|
||||
// the name
|
||||
Name string `json:"name"`
|
||||
// newest first
|
||||
Versions []Artifact `json:"versions"`
|
||||
}
|
||||
|
||||
// RemoveInput is what remove takes.
|
||||
type RemoveInput struct {
|
||||
// the digest (for a bundle, archive or copied base)
|
||||
Digest string `json:"digest,omitempty"`
|
||||
// the name
|
||||
Name string `json:"name"`
|
||||
// why it goes
|
||||
Reason string `json:"reason"`
|
||||
// the tag (for a tagged version)
|
||||
Tag string `json:"tag,omitempty"`
|
||||
// the version
|
||||
Version string `json:"version,omitempty"`
|
||||
}
|
||||
|
||||
// The values of RemoveInput's reason.
|
||||
const (
|
||||
RemoveInputReasonTagGone = "tag-gone"
|
||||
RemoveInputReasonRetention = "retention"
|
||||
)
|
||||
|
||||
// RemoveAnswer is what remove answers.
|
||||
type RemoveAnswer struct {
|
||||
Artifact *Artifact `json:"artifact,omitempty"`
|
||||
// it stays whole, its tag and its bytes: the store cannot remove the tag alone while another tag names its digest (reason, tags)
|
||||
Kept bool `json:"kept,omitempty"`
|
||||
// why it was kept: shared-digest, another tag names its digest
|
||||
Reason string `json:"reason,omitempty"`
|
||||
// it is gone
|
||||
Removed bool `json:"removed"`
|
||||
// the other tags still naming its digest, when kept
|
||||
Tags []string `json:"tags,omitempty"`
|
||||
}
|
||||
|
||||
// The values of RemoveAnswer's reason.
|
||||
const (
|
||||
RemoveAnswerReasonSharedDigest = "shared-digest"
|
||||
)
|
||||
|
||||
// PublishedEvent is the body of the event published.
|
||||
type PublishedEvent = Outcome
|
||||
|
||||
// RefusedEvent is the body of the event refused.
|
||||
type RefusedEvent = Outcome
|
||||
|
||||
// RemovedEvent is the body of the event removed.
|
||||
type RemovedEvent = Outcome
|
||||
|
||||
// KeptEvent is the body of the event kept.
|
||||
type KeptEvent = Outcome
|
||||
|
||||
// NpmPackageRegistryForm is the form of the provision npm-package-registry.
|
||||
type NpmPackageRegistryForm struct {
|
||||
// the read side's address
|
||||
URL string `json:"url"`
|
||||
}
|
||||
@@ -0,0 +1,66 @@
|
||||
// Code generated by seatgen from the seat definitions; DO NOT EDIT.
|
||||
|
||||
package npmpackageregistry
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"reflect"
|
||||
"regexp"
|
||||
"testing"
|
||||
)
|
||||
|
||||
// TestFixtures reads every fixture of seats/npm-package-registry/fixtures: each decodes into its type, a field the type does not
|
||||
// know is dropped (ADR 0608 §6), and it reads back as its wire says; each subject named is the contract's.
|
||||
func TestFixtures(t *testing.T) {
|
||||
cases := []struct {
|
||||
name, subject, given, wire string
|
||||
decode func([]byte) (any, error)
|
||||
subjectIs *regexp.Regexp
|
||||
}{
|
||||
{name: "published on the seat's subject", subject: "mesh.seat.npm-package-registry.event.published", given: "{\"commit\":\"cccccccccccccccccccccccccccccccccccccccc\",\"digest\":\"sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\",\"fingerprint\":\"f1\",\"holder\":\"gitea\",\"kind\":\"package\",\"name\":\"@novox/mesh-sdk\",\"reference\":\"@novox/mesh-sdk@0.1.19\",\"transfer\":\"t1\",\"version\":\"0.1.19\"}", wire: "{\"commit\":\"cccccccccccccccccccccccccccccccccccccccc\",\"digest\":\"sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\",\"fingerprint\":\"f1\",\"holder\":\"gitea\",\"kind\":\"package\",\"name\":\"@novox/mesh-sdk\",\"reference\":\"@novox/mesh-sdk@0.1.19\",\"transfer\":\"t1\",\"version\":\"0.1.19\"}",
|
||||
decode: func(b []byte) (any, error) { var v PublishedEvent; err := json.Unmarshal(b, &v); return v, err }, subjectIs: regexp.MustCompile("^mesh\\.seat\\.npm-package-registry\\.event\\.published$")},
|
||||
{name: "refused on the seat's subject", subject: "mesh.seat.npm-package-registry.event.refused", given: "{\"commit\":\"cccccccccccccccccccccccccccccccccccccccc\",\"digest\":\"sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\",\"fingerprint\":\"f1\",\"holder\":\"gitea\",\"kind\":\"package\",\"name\":\"@novox/mesh-sdk\",\"reason\":\"retention\",\"reference\":\"@novox/mesh-sdk@0.1.19\",\"transfer\":\"t1\",\"version\":\"0.1.19\"}", wire: "{\"commit\":\"cccccccccccccccccccccccccccccccccccccccc\",\"digest\":\"sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\",\"fingerprint\":\"f1\",\"holder\":\"gitea\",\"kind\":\"package\",\"name\":\"@novox/mesh-sdk\",\"reason\":\"retention\",\"reference\":\"@novox/mesh-sdk@0.1.19\",\"transfer\":\"t1\",\"version\":\"0.1.19\"}",
|
||||
decode: func(b []byte) (any, error) { var v RefusedEvent; err := json.Unmarshal(b, &v); return v, err }, subjectIs: regexp.MustCompile("^mesh\\.seat\\.npm-package-registry\\.event\\.refused$")},
|
||||
{name: "removed on the seat's subject", subject: "mesh.seat.npm-package-registry.event.removed", given: "{\"commit\":\"cccccccccccccccccccccccccccccccccccccccc\",\"digest\":\"sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\",\"fingerprint\":\"f1\",\"holder\":\"gitea\",\"kind\":\"package\",\"name\":\"@novox/mesh-sdk\",\"reason\":\"retention\",\"reference\":\"@novox/mesh-sdk@0.1.19\",\"transfer\":\"t1\",\"version\":\"0.1.19\"}", wire: "{\"commit\":\"cccccccccccccccccccccccccccccccccccccccc\",\"digest\":\"sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\",\"fingerprint\":\"f1\",\"holder\":\"gitea\",\"kind\":\"package\",\"name\":\"@novox/mesh-sdk\",\"reason\":\"retention\",\"reference\":\"@novox/mesh-sdk@0.1.19\",\"transfer\":\"t1\",\"version\":\"0.1.19\"}",
|
||||
decode: func(b []byte) (any, error) { var v RemovedEvent; err := json.Unmarshal(b, &v); return v, err }, subjectIs: regexp.MustCompile("^mesh\\.seat\\.npm-package-registry\\.event\\.removed$")},
|
||||
{name: "kept on the seat's subject: a tagged version sharing its digest stays, naming the tags on it", subject: "mesh.seat.npm-package-registry.event.kept", given: "{\"commit\":\"cccccccccccccccccccccccccccccccccccccccc\",\"digest\":\"sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\",\"fingerprint\":\"f1\",\"holder\":\"gitea\",\"kind\":\"package\",\"name\":\"@novox/mesh-sdk\",\"reason\":\"shared-digest\",\"reference\":\"@novox/mesh-sdk@0.1.19\",\"tag\":\"0.1.19-feat-x.2\",\"tags\":[\"0.1.19\"],\"version\":\"0.1.19-feat-x.2\"}", wire: "{\"commit\":\"cccccccccccccccccccccccccccccccccccccccc\",\"digest\":\"sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\",\"fingerprint\":\"f1\",\"holder\":\"gitea\",\"kind\":\"package\",\"name\":\"@novox/mesh-sdk\",\"reason\":\"shared-digest\",\"reference\":\"@novox/mesh-sdk@0.1.19\",\"tag\":\"0.1.19-feat-x.2\",\"tags\":[\"0.1.19\"],\"version\":\"0.1.19-feat-x.2\"}",
|
||||
decode: func(b []byte) (any, error) { var v KeptEvent; err := json.Unmarshal(b, &v); return v, err }, subjectIs: regexp.MustCompile("^mesh\\.seat\\.npm-package-registry\\.event\\.kept$")},
|
||||
{name: "publish opens a transfer", subject: "", given: "{\"commit\":\"cccccccccccccccccccccccccccccccccccccccc\",\"digest\":\"sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\",\"fingerprint\":\"f1\",\"kind\":\"package\",\"name\":\"@novox/mesh-sdk\",\"parts\":[{\"digest\":\"sha256:bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb\",\"size\":1048576}],\"transfer\":\"t1\",\"version\":\"0.1.19\"}", wire: "{\"commit\":\"cccccccccccccccccccccccccccccccccccccccc\",\"digest\":\"sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\",\"fingerprint\":\"f1\",\"kind\":\"package\",\"name\":\"@novox/mesh-sdk\",\"parts\":[{\"digest\":\"sha256:bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb\",\"size\":1048576}],\"transfer\":\"t1\",\"version\":\"0.1.19\"}",
|
||||
decode: func(b []byte) (any, error) { var v PublishInput; err := json.Unmarshal(b, &v); return v, err }, subjectIs: nil},
|
||||
{name: "a field the reader does not know is dropped", subject: "", given: "{\"commit\":\"cccccccccccccccccccccccccccccccccccccccc\",\"digest\":\"sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\",\"fingerprint\":\"f1\",\"kind\":\"package\",\"later\":{\"x\":1},\"name\":\"@novox/mesh-sdk\",\"parts\":[{\"digest\":\"sha256:bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb\",\"size\":1048576}],\"transfer\":\"t1\",\"version\":\"0.1.19\"}", wire: "{\"commit\":\"cccccccccccccccccccccccccccccccccccccccc\",\"digest\":\"sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\",\"fingerprint\":\"f1\",\"kind\":\"package\",\"name\":\"@novox/mesh-sdk\",\"parts\":[{\"digest\":\"sha256:bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb\",\"size\":1048576}],\"transfer\":\"t1\",\"version\":\"0.1.19\"}",
|
||||
decode: func(b []byte) (any, error) { var v PublishInput; err := json.Unmarshal(b, &v); return v, err }, subjectIs: nil},
|
||||
{name: "publish answers what it lacks", subject: "", given: "{\"lacks\":[\"sha256:bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb\"],\"transfer\":\"t1\"}", wire: "{\"lacks\":[\"sha256:bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb\"],\"transfer\":\"t1\"}",
|
||||
decode: func(b []byte) (any, error) { var v PublishAnswer; err := json.Unmarshal(b, &v); return v, err }, subjectIs: nil},
|
||||
{name: "a chunk of a part", subject: "", given: "{\"data\":\"aGVsbG8=\",\"last\":true,\"part\":\"sha256:bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb\",\"sequence\":0,\"transfer\":\"t1\"}", wire: "{\"data\":\"aGVsbG8=\",\"last\":true,\"part\":\"sha256:bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb\",\"sequence\":0,\"transfer\":\"t1\"}",
|
||||
decode: func(b []byte) (any, error) { var v PublishPartInput; err := json.Unmarshal(b, &v); return v, err }, subjectIs: nil},
|
||||
{name: "the versions held", subject: "", given: "{\"name\":\"@novox/mesh-sdk\",\"versions\":[{\"commit\":\"cccccccccccccccccccccccccccccccccccccccc\",\"digest\":\"sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\",\"fingerprint\":\"f1\",\"kind\":\"package\",\"name\":\"@novox/mesh-sdk\",\"version\":\"0.1.19\"}]}", wire: "{\"name\":\"@novox/mesh-sdk\",\"versions\":[{\"commit\":\"cccccccccccccccccccccccccccccccccccccccc\",\"digest\":\"sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\",\"fingerprint\":\"f1\",\"kind\":\"package\",\"name\":\"@novox/mesh-sdk\",\"version\":\"0.1.19\"}]}",
|
||||
decode: func(b []byte) (any, error) { var v VersionsAnswer; err := json.Unmarshal(b, &v); return v, err }, subjectIs: nil},
|
||||
{name: "a tagged version sharing its digest with another tag is kept, not removed", subject: "", given: "{\"kept\":true,\"reason\":\"shared-digest\",\"removed\":false,\"tags\":[\"0.1.19\"]}", wire: "{\"kept\":true,\"reason\":\"shared-digest\",\"removed\":false,\"tags\":[\"0.1.19\"]}",
|
||||
decode: func(b []byte) (any, error) { var v RemoveAnswer; err := json.Unmarshal(b, &v); return v, err }, subjectIs: nil},
|
||||
}
|
||||
for _, c := range cases {
|
||||
v, err := c.decode([]byte(c.given))
|
||||
if err != nil {
|
||||
t.Errorf("%s: does not decode: %v", c.name, err)
|
||||
continue
|
||||
}
|
||||
back, err := json.Marshal(v)
|
||||
if err != nil {
|
||||
t.Errorf("%s: does not encode: %v", c.name, err)
|
||||
continue
|
||||
}
|
||||
var got, want any
|
||||
if err := json.Unmarshal(back, &got); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if err := json.Unmarshal([]byte(c.wire), &want); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if !reflect.DeepEqual(got, want) {
|
||||
t.Errorf("%s: reads back as %s, want %s", c.name, back, c.wire)
|
||||
}
|
||||
if c.subjectIs != nil && !c.subjectIs.MatchString(c.subject) {
|
||||
t.Errorf("%s: subject %q is not the contract's %s", c.name, c.subject, c.subjectIs)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,3 @@
|
||||
module git.novox.be/novox/mesh-delivery/contracts/go/seats/npmpackageregistry
|
||||
|
||||
go 1.26.0
|
||||
@@ -0,0 +1,486 @@
|
||||
// Code generated by seatgen from the seat definitions; DO NOT EDIT.
|
||||
|
||||
// Package ociartifactregistry is the contract of the seat "oci-artifact-registry", defined whole (seats/oci-artifact-registry/seat.json): The mesh's bundles and archives, by digest, in the one artifact store: the OCI distribution protocol's read side.
|
||||
package ociartifactregistry
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"strings"
|
||||
)
|
||||
|
||||
// Seat is the seat's name.
|
||||
const Seat = "oci-artifact-registry"
|
||||
|
||||
const (
|
||||
VerbPublish = "publish"
|
||||
VerbPublishPart = "publish-part"
|
||||
VerbVersions = "versions"
|
||||
VerbRemove = "remove"
|
||||
VerbHold = "hold"
|
||||
VerbRelease = "release"
|
||||
VerbList = "list"
|
||||
)
|
||||
|
||||
const (
|
||||
EventPublished = "published"
|
||||
EventRefused = "refused"
|
||||
EventRemoved = "removed"
|
||||
EventKept = "kept"
|
||||
EventHeld = "held"
|
||||
EventReleased = "released"
|
||||
)
|
||||
|
||||
// Verbs is every verb the seat serves, in its definition's order: a new copy each call.
|
||||
func Verbs() []string {
|
||||
return []string{VerbPublish, VerbPublishPart, VerbVersions, VerbRemove, VerbHold, VerbRelease, VerbList}
|
||||
}
|
||||
|
||||
// Events is every event published on the seat's subject: a new copy each call.
|
||||
func Events() []string {
|
||||
return []string{EventPublished, EventRefused, EventRemoved, EventKept, EventHeld, EventReleased}
|
||||
}
|
||||
|
||||
// Family is where the seat's events are kept: the shared events stream, filtered to the seat's subjects.
|
||||
const (
|
||||
Family = "oci-artifact-registry.events"
|
||||
FamilyMaxAgeDays = 7
|
||||
)
|
||||
|
||||
// SubjectPublished is where published is published.
|
||||
const SubjectPublished = "mesh.seat.oci-artifact-registry.event.published"
|
||||
|
||||
// SubjectRefused is where refused is published.
|
||||
const SubjectRefused = "mesh.seat.oci-artifact-registry.event.refused"
|
||||
|
||||
// SubjectRemoved is where removed is published.
|
||||
const SubjectRemoved = "mesh.seat.oci-artifact-registry.event.removed"
|
||||
|
||||
// SubjectKept is where kept is published.
|
||||
const SubjectKept = "mesh.seat.oci-artifact-registry.event.kept"
|
||||
|
||||
// SubjectHeld is where held is published.
|
||||
const SubjectHeld = "mesh.seat.oci-artifact-registry.event.held"
|
||||
|
||||
// SubjectReleased is where released is published.
|
||||
const SubjectReleased = "mesh.seat.oci-artifact-registry.event.released"
|
||||
|
||||
const (
|
||||
StateHolds = "holds"
|
||||
StateWindows = "windows"
|
||||
)
|
||||
|
||||
// HoldsBySeatNameDigestKey is the key of one value of the state holds under the form "<seat>.<name>.<digest>".
|
||||
func HoldsBySeatNameDigestKey(seat, name, digest string) string {
|
||||
return seat + "." + name + "." + digest
|
||||
}
|
||||
|
||||
// ParseHoldsBySeatNameDigestKey reads the parts of a key of the form "<seat>.<name>.<digest>", in the order of the form; the last result is false for a key of another form.
|
||||
func ParseHoldsBySeatNameDigestKey(key string) (string, string, string, bool) {
|
||||
rest := key
|
||||
v0, after0, found0 := strings.Cut(rest, ".")
|
||||
if !found0 || v0 == "" {
|
||||
return "", "", "", false
|
||||
}
|
||||
rest = after0
|
||||
v2, after2, found2 := strings.Cut(rest, ".")
|
||||
if !found2 || v2 == "" {
|
||||
return "", "", "", false
|
||||
}
|
||||
rest = after2
|
||||
if rest == "" {
|
||||
return "", "", "", false
|
||||
}
|
||||
v4 := rest
|
||||
rest = ""
|
||||
if rest != "" {
|
||||
return "", "", "", false
|
||||
}
|
||||
return v0, v2, v4, true
|
||||
}
|
||||
|
||||
// DecodeHoldsBySeatNameDigestValue reads one value of the state holds under the form "<seat>.<name>.<digest>", refusing a field HoldsBySeatNameDigestValue does not know and any text after the value.
|
||||
func DecodeHoldsBySeatNameDigestValue(raw []byte) (HoldsBySeatNameDigestValue, error) {
|
||||
var v HoldsBySeatNameDigestValue
|
||||
dec := json.NewDecoder(bytes.NewReader(raw))
|
||||
dec.DisallowUnknownFields()
|
||||
if err := dec.Decode(&v); err != nil {
|
||||
return v, err
|
||||
}
|
||||
if dec.More() {
|
||||
return v, errors.New("text after the value")
|
||||
}
|
||||
return v, nil
|
||||
}
|
||||
|
||||
// WindowsByCallerKey is the key of one value of the state windows under the form "<caller>".
|
||||
func WindowsByCallerKey(caller string) string { return caller }
|
||||
|
||||
// ParseWindowsByCallerKey reads the parts of a key of the form "<caller>", in the order of the form; the last result is false for a key of another form.
|
||||
func ParseWindowsByCallerKey(key string) (string, bool) {
|
||||
rest := key
|
||||
if rest == "" {
|
||||
return "", false
|
||||
}
|
||||
v0 := rest
|
||||
rest = ""
|
||||
if rest != "" {
|
||||
return "", false
|
||||
}
|
||||
return v0, true
|
||||
}
|
||||
|
||||
// DecodeWindowsByCallerValue reads one value of the state windows under the form "<caller>", refusing a field WindowsByCallerValue does not know and any text after the value.
|
||||
func DecodeWindowsByCallerValue(raw []byte) (WindowsByCallerValue, error) {
|
||||
var v WindowsByCallerValue
|
||||
dec := json.NewDecoder(bytes.NewReader(raw))
|
||||
dec.DisallowUnknownFields()
|
||||
if err := dec.Decode(&v); err != nil {
|
||||
return v, err
|
||||
}
|
||||
if dec.More() {
|
||||
return v, errors.New("text after the value")
|
||||
}
|
||||
return v, nil
|
||||
}
|
||||
|
||||
// Artifact is one published thing, as every registry event and answer names it.
|
||||
type Artifact struct {
|
||||
// the source commit it was built from
|
||||
Commit string `json:"commit,omitempty"`
|
||||
// its digest, sha256:<hex> (a Go package's h1: hash beside it in the version's record)
|
||||
Digest string `json:"digest"`
|
||||
// the build's fingerprint
|
||||
Fingerprint string `json:"fingerprint,omitempty"`
|
||||
// what it is
|
||||
Kind string `json:"kind"`
|
||||
// the package's, image's, bundle's or archive's name; a package's is its name in its own language (design 32): a Go package's the go.mod module path (git.novox.be/novox/mesh-sdk/go), an npm package's package.json's name (@novox/mesh-sdk)
|
||||
Name string `json:"name"`
|
||||
// the tag it carries, for a tagged version
|
||||
Tag string `json:"tag,omitempty"`
|
||||
// the version the builder gave it (design 51 *The versions*); empty for a bundle or archive published by its digest alone
|
||||
Version string `json:"version,omitempty"`
|
||||
}
|
||||
|
||||
// The values of Artifact's kind.
|
||||
const (
|
||||
ArtifactKindPackage = "package"
|
||||
ArtifactKindImage = "image"
|
||||
ArtifactKindBundle = "bundle"
|
||||
ArtifactKindArchive = "archive"
|
||||
ArtifactKindUpstream = "upstream"
|
||||
)
|
||||
|
||||
// Hold is the body of held and released.
|
||||
type Hold struct {
|
||||
// when, RFC 3339
|
||||
At string `json:"at,omitempty"`
|
||||
// the caller the holder proved
|
||||
By string `json:"by,omitempty"`
|
||||
// the digest
|
||||
Digest string `json:"digest"`
|
||||
// the module holding the seat
|
||||
Holder string `json:"holder"`
|
||||
// the name
|
||||
Name string `json:"name"`
|
||||
// why
|
||||
Reason string `json:"reason,omitempty"`
|
||||
}
|
||||
|
||||
// Listed is a type of the contract.
|
||||
type Listed struct {
|
||||
// for a manifest keeping one bundle or archive, the digest of that blob: what a reference names it by
|
||||
Artifact string `json:"artifact,omitempty"`
|
||||
// the manifest's digest, sha256:<hex>
|
||||
Digest string `json:"digest"`
|
||||
// a hold keeps it from retention
|
||||
Held bool `json:"held"`
|
||||
// it is an index, naming a manifest per platform
|
||||
Index bool `json:"index"`
|
||||
// for an index, the digests of the manifests it names
|
||||
Platforms []string `json:"platforms,omitempty"`
|
||||
// its bytes: its own content and every blob it names
|
||||
Size int64 `json:"size"`
|
||||
// the tags naming it, none for an untagged one
|
||||
Tags []string `json:"tags,omitempty"`
|
||||
}
|
||||
|
||||
// Outcome is the body of published, refused and removed.
|
||||
type Outcome struct {
|
||||
// the source commit it was built from
|
||||
Commit string `json:"commit,omitempty"`
|
||||
// its digest, sha256:<hex> (a Go package's h1: hash beside it in the version's record)
|
||||
Digest string `json:"digest"`
|
||||
// the build's fingerprint
|
||||
Fingerprint string `json:"fingerprint,omitempty"`
|
||||
// the module holding the seat that said it
|
||||
Holder string `json:"holder"`
|
||||
// what it is
|
||||
Kind string `json:"kind"`
|
||||
// the Go module path a Go package is fetched under, equal to name for a Go package (go-package-registry's published); empty for every other kind of artifact
|
||||
Module string `json:"module,omitempty"`
|
||||
// the package's, image's, bundle's or archive's name; a package's is its name in its own language (design 32): a Go package's the go.mod module path (git.novox.be/novox/mesh-sdk/go), an npm package's package.json's name (@novox/mesh-sdk)
|
||||
Name string `json:"name"`
|
||||
// why: a refusal's cause, a removal's (retention, tag-gone, by-hand), or why a removal was kept (shared-digest)
|
||||
Reason string `json:"reason,omitempty"`
|
||||
// the reference the seat answers: <registry>/<module>/<artifact>@sha256:<digest>, or <name>@<version> for a package (ADR 0605 §9)
|
||||
Reference string `json:"reference,omitempty"`
|
||||
// the tag it carries, for a tagged version
|
||||
Tag string `json:"tag,omitempty"`
|
||||
// for kept: the other tags still naming its digest
|
||||
Tags []string `json:"tags,omitempty"`
|
||||
// the transfer it ends, when it ended one
|
||||
Transfer string `json:"transfer,omitempty"`
|
||||
// the version the builder gave it (design 51 *The versions*); empty for a bundle or archive published by its digest alone
|
||||
Version string `json:"version,omitempty"`
|
||||
}
|
||||
|
||||
// The values of Outcome's kind.
|
||||
const (
|
||||
OutcomeKindPackage = "package"
|
||||
OutcomeKindImage = "image"
|
||||
OutcomeKindBundle = "bundle"
|
||||
OutcomeKindArchive = "archive"
|
||||
OutcomeKindUpstream = "upstream"
|
||||
)
|
||||
|
||||
// Part is one part of a transfer.
|
||||
type Part struct {
|
||||
// the part's digest, sha256:<hex>: an image's layer or config, a package's file, a bundle's blob
|
||||
Digest string `json:"digest"`
|
||||
// its size in bytes
|
||||
Size int64 `json:"size"`
|
||||
}
|
||||
|
||||
// Repository is a type of the contract.
|
||||
type Repository struct {
|
||||
// each manifest it holds, by digest
|
||||
Manifests []Listed `json:"manifests"`
|
||||
// the repository
|
||||
Name string `json:"name"`
|
||||
}
|
||||
|
||||
// PublishInput is what publish takes.
|
||||
type PublishInput struct {
|
||||
// the source commit it was built from
|
||||
Commit string `json:"commit,omitempty"`
|
||||
// its digest, sha256:<hex> (a Go package's h1: hash beside it in the version's record)
|
||||
Digest string `json:"digest"`
|
||||
// the build's fingerprint
|
||||
Fingerprint string `json:"fingerprint,omitempty"`
|
||||
// what it is
|
||||
Kind string `json:"kind"`
|
||||
// the package's, image's, bundle's or archive's name; a package's is its name in its own language (design 32): a Go package's the go.mod module path (git.novox.be/novox/mesh-sdk/go), an npm package's package.json's name (@novox/mesh-sdk)
|
||||
Name string `json:"name"`
|
||||
// every part's digest and size; none when the digest is already held
|
||||
Parts []Part `json:"parts,omitempty"`
|
||||
// the tag it carries, for a tagged version
|
||||
Tag string `json:"tag,omitempty"`
|
||||
// the transfer's name, the caller's own; the same name opens the same transfer again
|
||||
Transfer string `json:"transfer"`
|
||||
// the version the builder gave it (design 51 *The versions*); empty for a bundle or archive published by its digest alone
|
||||
Version string `json:"version,omitempty"`
|
||||
}
|
||||
|
||||
// The values of PublishInput's kind.
|
||||
const (
|
||||
PublishInputKindPackage = "package"
|
||||
PublishInputKindImage = "image"
|
||||
PublishInputKindBundle = "bundle"
|
||||
PublishInputKindArchive = "archive"
|
||||
PublishInputKindUpstream = "upstream"
|
||||
)
|
||||
|
||||
// PublishAnswer is what publish answers.
|
||||
type PublishAnswer struct {
|
||||
// the digest is held already: nothing to send; the outcome follows as an event
|
||||
Held bool `json:"held,omitempty"`
|
||||
// the digests of the parts the holder lacks, to send by publish-part
|
||||
Lacks []string `json:"lacks,omitempty"`
|
||||
// the transfer
|
||||
Transfer string `json:"transfer"`
|
||||
}
|
||||
|
||||
// PublishPartInput is what publish-part takes.
|
||||
type PublishPartInput struct {
|
||||
// the chunk's bytes, base64 (at most 760 KiB before encoding)
|
||||
Data string `json:"data"`
|
||||
// this chunk is the part's last
|
||||
Last bool `json:"last"`
|
||||
// the part's digest
|
||||
Part string `json:"part"`
|
||||
// the chunk's place in its part, from 0
|
||||
Sequence int64 `json:"sequence"`
|
||||
// the transfer
|
||||
Transfer string `json:"transfer"`
|
||||
}
|
||||
|
||||
// PublishPartAnswer is what publish-part answers.
|
||||
type PublishPartAnswer struct {
|
||||
// the part is whole and its bytes match its digest
|
||||
Complete bool `json:"complete,omitempty"`
|
||||
// the part
|
||||
Part string `json:"part"`
|
||||
// the chunks of the part held so far
|
||||
Received int64 `json:"received,omitempty"`
|
||||
// the transfer
|
||||
Transfer string `json:"transfer"`
|
||||
}
|
||||
|
||||
// VersionsInput is what versions takes.
|
||||
type VersionsInput struct {
|
||||
// narrowed to one kind (optional)
|
||||
Kind string `json:"kind,omitempty"`
|
||||
// the name
|
||||
Name string `json:"name"`
|
||||
}
|
||||
|
||||
// VersionsAnswer is what versions answers.
|
||||
type VersionsAnswer struct {
|
||||
// the name
|
||||
Name string `json:"name"`
|
||||
// newest first
|
||||
Versions []Artifact `json:"versions"`
|
||||
}
|
||||
|
||||
// RemoveInput is what remove takes.
|
||||
type RemoveInput struct {
|
||||
// the digest (for a bundle, archive or copied base)
|
||||
Digest string `json:"digest,omitempty"`
|
||||
// the name
|
||||
Name string `json:"name"`
|
||||
// why it goes
|
||||
Reason string `json:"reason"`
|
||||
// the tag (for a tagged version)
|
||||
Tag string `json:"tag,omitempty"`
|
||||
// the version
|
||||
Version string `json:"version,omitempty"`
|
||||
}
|
||||
|
||||
// The values of RemoveInput's reason.
|
||||
const (
|
||||
RemoveInputReasonTagGone = "tag-gone"
|
||||
RemoveInputReasonRetention = "retention"
|
||||
)
|
||||
|
||||
// RemoveAnswer is what remove answers.
|
||||
type RemoveAnswer struct {
|
||||
Artifact *Artifact `json:"artifact,omitempty"`
|
||||
// it stays whole, its tag and its bytes: the store cannot remove the tag alone while another tag names its digest (reason, tags)
|
||||
Kept bool `json:"kept,omitempty"`
|
||||
// why it was kept: shared-digest, another tag names its digest
|
||||
Reason string `json:"reason,omitempty"`
|
||||
// it is gone
|
||||
Removed bool `json:"removed"`
|
||||
// the other tags still naming its digest, when kept
|
||||
Tags []string `json:"tags,omitempty"`
|
||||
}
|
||||
|
||||
// The values of RemoveAnswer's reason.
|
||||
const (
|
||||
RemoveAnswerReasonSharedDigest = "shared-digest"
|
||||
)
|
||||
|
||||
// HoldInput is what hold takes.
|
||||
type HoldInput struct {
|
||||
// the digest
|
||||
Digest string `json:"digest"`
|
||||
// the name
|
||||
Name string `json:"name"`
|
||||
// which of the sweep's rules keeps it
|
||||
Reason string `json:"reason"`
|
||||
}
|
||||
|
||||
// HoldAnswer is what hold answers.
|
||||
type HoldAnswer struct {
|
||||
// it is held
|
||||
Held bool `json:"held"`
|
||||
// when the hold began, RFC 3339
|
||||
Since string `json:"since,omitempty"`
|
||||
}
|
||||
|
||||
// ReleaseInput is what release takes.
|
||||
type ReleaseInput struct {
|
||||
// the digest
|
||||
Digest string `json:"digest"`
|
||||
// the name
|
||||
Name string `json:"name"`
|
||||
// why it is let go
|
||||
Reason string `json:"reason"`
|
||||
}
|
||||
|
||||
// ReleaseAnswer is what release answers.
|
||||
type ReleaseAnswer struct {
|
||||
// a hold was let go; false when there was none
|
||||
Released bool `json:"released"`
|
||||
}
|
||||
|
||||
// ListInput is what list takes.
|
||||
type ListInput struct {
|
||||
// the repository, <module>/<artifact>; every repository of the seat when not given
|
||||
Name string `json:"name,omitempty"`
|
||||
}
|
||||
|
||||
// ListAnswer is what list answers.
|
||||
type ListAnswer struct {
|
||||
// each repository named, in order of its name
|
||||
Repositories []Repository `json:"repositories"`
|
||||
}
|
||||
|
||||
// PublishedEvent is the body of the event published.
|
||||
type PublishedEvent = Outcome
|
||||
|
||||
// RefusedEvent is the body of the event refused.
|
||||
type RefusedEvent = Outcome
|
||||
|
||||
// RemovedEvent is the body of the event removed.
|
||||
type RemovedEvent = Outcome
|
||||
|
||||
// KeptEvent is the body of the event kept.
|
||||
type KeptEvent = Outcome
|
||||
|
||||
// HeldEvent is the body of the event held.
|
||||
type HeldEvent = Hold
|
||||
|
||||
// ReleasedEvent is the body of the event released.
|
||||
type ReleasedEvent = Hold
|
||||
|
||||
// HoldsBySeatNameDigestValue is one value of the state holds under the key <seat>.<name>.<digest>.
|
||||
type HoldsBySeatNameDigestValue struct {
|
||||
By string `json:"by"`
|
||||
Digest string `json:"digest"`
|
||||
Kind string `json:"kind"`
|
||||
Name string `json:"name"`
|
||||
Seat string `json:"seat"`
|
||||
// when (RFC 3339 time)
|
||||
Since string `json:"since"`
|
||||
Why string `json:"why,omitempty"`
|
||||
}
|
||||
|
||||
// WindowsByCallerValueDroppedItem is a type of the contract.
|
||||
type WindowsByCallerValueDroppedItem struct {
|
||||
// when (RFC 3339 time)
|
||||
At string `json:"at,omitempty"`
|
||||
By string `json:"by,omitempty"`
|
||||
Node string `json:"node"`
|
||||
Why string `json:"why,omitempty"`
|
||||
}
|
||||
|
||||
// WindowsByCallerValue is one value of the state windows under the key <caller>.
|
||||
type WindowsByCallerValue struct {
|
||||
// when it last closed (RFC 3339 time)
|
||||
Closed string `json:"closed,omitempty"`
|
||||
// retired nodes dropped by hand
|
||||
Dropped []WindowsByCallerValueDroppedItem `json:"dropped,omitempty"`
|
||||
// the nodes holding it open
|
||||
Holding []string `json:"holding,omitempty"`
|
||||
LastHolding []string `json:"last_holding,omitempty"`
|
||||
// the caller whose window it is
|
||||
Name string `json:"name"`
|
||||
Open bool `json:"open,omitempty"`
|
||||
// node to when it began to hold
|
||||
Since map[string]string `json:"since,omitempty"`
|
||||
// of each node holding the window, why it holds it
|
||||
Why map[string]string `json:"why,omitempty"`
|
||||
}
|
||||
@@ -0,0 +1,82 @@
|
||||
// Code generated by seatgen from the seat definitions; DO NOT EDIT.
|
||||
|
||||
package ociartifactregistry
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"reflect"
|
||||
"regexp"
|
||||
"testing"
|
||||
)
|
||||
|
||||
// TestFixtures reads every fixture of seats/oci-artifact-registry/fixtures: each decodes into its type, a field the type does not
|
||||
// know is dropped (ADR 0608 §6), and it reads back as its wire says; each subject named is the contract's.
|
||||
func TestFixtures(t *testing.T) {
|
||||
cases := []struct {
|
||||
name, subject, given, wire string
|
||||
decode func([]byte) (any, error)
|
||||
subjectIs *regexp.Regexp
|
||||
}{
|
||||
{name: "published on the seat's subject", subject: "mesh.seat.oci-artifact-registry.event.published", given: "{\"commit\":\"cccccccccccccccccccccccccccccccccccccccc\",\"digest\":\"sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\",\"fingerprint\":\"f1\",\"holder\":\"distribution\",\"kind\":\"bundle\",\"name\":\"mesh-sdk\",\"reference\":\"oci-artifact-registry/mesh-sdk/lib@sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\",\"transfer\":\"t1\",\"version\":\"0.1.19\"}", wire: "{\"commit\":\"cccccccccccccccccccccccccccccccccccccccc\",\"digest\":\"sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\",\"fingerprint\":\"f1\",\"holder\":\"distribution\",\"kind\":\"bundle\",\"name\":\"mesh-sdk\",\"reference\":\"oci-artifact-registry/mesh-sdk/lib@sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\",\"transfer\":\"t1\",\"version\":\"0.1.19\"}",
|
||||
decode: func(b []byte) (any, error) { var v PublishedEvent; err := json.Unmarshal(b, &v); return v, err }, subjectIs: regexp.MustCompile("^mesh\\.seat\\.oci-artifact-registry\\.event\\.published$")},
|
||||
{name: "refused on the seat's subject", subject: "mesh.seat.oci-artifact-registry.event.refused", given: "{\"commit\":\"cccccccccccccccccccccccccccccccccccccccc\",\"digest\":\"sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\",\"fingerprint\":\"f1\",\"holder\":\"distribution\",\"kind\":\"bundle\",\"name\":\"mesh-sdk\",\"reason\":\"retention\",\"reference\":\"oci-artifact-registry/mesh-sdk/lib@sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\",\"transfer\":\"t1\",\"version\":\"0.1.19\"}", wire: "{\"commit\":\"cccccccccccccccccccccccccccccccccccccccc\",\"digest\":\"sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\",\"fingerprint\":\"f1\",\"holder\":\"distribution\",\"kind\":\"bundle\",\"name\":\"mesh-sdk\",\"reason\":\"retention\",\"reference\":\"oci-artifact-registry/mesh-sdk/lib@sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\",\"transfer\":\"t1\",\"version\":\"0.1.19\"}",
|
||||
decode: func(b []byte) (any, error) { var v RefusedEvent; err := json.Unmarshal(b, &v); return v, err }, subjectIs: regexp.MustCompile("^mesh\\.seat\\.oci-artifact-registry\\.event\\.refused$")},
|
||||
{name: "removed on the seat's subject", subject: "mesh.seat.oci-artifact-registry.event.removed", given: "{\"commit\":\"cccccccccccccccccccccccccccccccccccccccc\",\"digest\":\"sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\",\"fingerprint\":\"f1\",\"holder\":\"distribution\",\"kind\":\"bundle\",\"name\":\"mesh-sdk\",\"reason\":\"retention\",\"reference\":\"oci-artifact-registry/mesh-sdk/lib@sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\",\"transfer\":\"t1\",\"version\":\"0.1.19\"}", wire: "{\"commit\":\"cccccccccccccccccccccccccccccccccccccccc\",\"digest\":\"sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\",\"fingerprint\":\"f1\",\"holder\":\"distribution\",\"kind\":\"bundle\",\"name\":\"mesh-sdk\",\"reason\":\"retention\",\"reference\":\"oci-artifact-registry/mesh-sdk/lib@sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\",\"transfer\":\"t1\",\"version\":\"0.1.19\"}",
|
||||
decode: func(b []byte) (any, error) { var v RemovedEvent; err := json.Unmarshal(b, &v); return v, err }, subjectIs: regexp.MustCompile("^mesh\\.seat\\.oci-artifact-registry\\.event\\.removed$")},
|
||||
{name: "kept on the seat's subject: a tagged version sharing its digest stays, naming the tags on it", subject: "mesh.seat.oci-artifact-registry.event.kept", given: "{\"commit\":\"cccccccccccccccccccccccccccccccccccccccc\",\"digest\":\"sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\",\"fingerprint\":\"f1\",\"holder\":\"distribution\",\"kind\":\"bundle\",\"name\":\"mesh-sdk\",\"reason\":\"shared-digest\",\"reference\":\"oci-artifact-registry/mesh-sdk/lib@sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\",\"tag\":\"0.1.19-feat-x.2\",\"tags\":[\"0.1.19\"],\"version\":\"0.1.19-feat-x.2\"}", wire: "{\"commit\":\"cccccccccccccccccccccccccccccccccccccccc\",\"digest\":\"sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\",\"fingerprint\":\"f1\",\"holder\":\"distribution\",\"kind\":\"bundle\",\"name\":\"mesh-sdk\",\"reason\":\"shared-digest\",\"reference\":\"oci-artifact-registry/mesh-sdk/lib@sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\",\"tag\":\"0.1.19-feat-x.2\",\"tags\":[\"0.1.19\"],\"version\":\"0.1.19-feat-x.2\"}",
|
||||
decode: func(b []byte) (any, error) { var v KeptEvent; err := json.Unmarshal(b, &v); return v, err }, subjectIs: regexp.MustCompile("^mesh\\.seat\\.oci-artifact-registry\\.event\\.kept$")},
|
||||
{name: "held on the seat's subject", subject: "mesh.seat.oci-artifact-registry.event.held", given: "{\"at\":\"2026-11-04T12:00:00Z\",\"by\":\"mesh-artifact-sweep\",\"digest\":\"sha256:bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb\",\"holder\":\"distribution\",\"name\":\"mesh-sdk\",\"reason\":\"kept by the sweep's rule\"}", wire: "{\"at\":\"2026-11-04T12:00:00Z\",\"by\":\"mesh-artifact-sweep\",\"digest\":\"sha256:bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb\",\"holder\":\"distribution\",\"name\":\"mesh-sdk\",\"reason\":\"kept by the sweep's rule\"}",
|
||||
decode: func(b []byte) (any, error) { var v HeldEvent; err := json.Unmarshal(b, &v); return v, err }, subjectIs: regexp.MustCompile("^mesh\\.seat\\.oci-artifact-registry\\.event\\.held$")},
|
||||
{name: "released on the seat's subject", subject: "mesh.seat.oci-artifact-registry.event.released", given: "{\"at\":\"2026-11-04T12:00:00Z\",\"by\":\"mesh-artifact-sweep\",\"digest\":\"sha256:bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb\",\"holder\":\"distribution\",\"name\":\"mesh-sdk\",\"reason\":\"kept by the sweep's rule\"}", wire: "{\"at\":\"2026-11-04T12:00:00Z\",\"by\":\"mesh-artifact-sweep\",\"digest\":\"sha256:bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb\",\"holder\":\"distribution\",\"name\":\"mesh-sdk\",\"reason\":\"kept by the sweep's rule\"}",
|
||||
decode: func(b []byte) (any, error) { var v ReleasedEvent; err := json.Unmarshal(b, &v); return v, err }, subjectIs: regexp.MustCompile("^mesh\\.seat\\.oci-artifact-registry\\.event\\.released$")},
|
||||
{name: "a hold", subject: "", given: "{\"by\":\"operator@shanks\",\"digest\":\"sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\",\"kind\":\"base\",\"name\":\"base\",\"seat\":\"oci-artifact-registry\",\"since\":\"2026-11-05T10:00:00Z\"}", wire: "{\"by\":\"operator@shanks\",\"digest\":\"sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\",\"kind\":\"base\",\"name\":\"base\",\"seat\":\"oci-artifact-registry\",\"since\":\"2026-11-05T10:00:00Z\"}",
|
||||
decode: func(b []byte) (any, error) {
|
||||
var v HoldsBySeatNameDigestValue
|
||||
err := json.Unmarshal(b, &v)
|
||||
return v, err
|
||||
}, subjectIs: nil},
|
||||
{name: "an open window", subject: "", given: "{\"holding\":[\"ace\"],\"name\":\"mesh-delivery-builder\",\"open\":true,\"since\":{\"ace\":\"2026-11-05T10:00:00Z\"}}", wire: "{\"holding\":[\"ace\"],\"name\":\"mesh-delivery-builder\",\"open\":true,\"since\":{\"ace\":\"2026-11-05T10:00:00Z\"}}",
|
||||
decode: func(b []byte) (any, error) { var v WindowsByCallerValue; err := json.Unmarshal(b, &v); return v, err }, subjectIs: nil},
|
||||
{name: "publish opens a transfer", subject: "", given: "{\"commit\":\"cccccccccccccccccccccccccccccccccccccccc\",\"digest\":\"sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\",\"fingerprint\":\"f1\",\"kind\":\"bundle\",\"name\":\"mesh-sdk\",\"parts\":[{\"digest\":\"sha256:bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb\",\"size\":1048576}],\"transfer\":\"t1\",\"version\":\"0.1.19\"}", wire: "{\"commit\":\"cccccccccccccccccccccccccccccccccccccccc\",\"digest\":\"sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\",\"fingerprint\":\"f1\",\"kind\":\"bundle\",\"name\":\"mesh-sdk\",\"parts\":[{\"digest\":\"sha256:bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb\",\"size\":1048576}],\"transfer\":\"t1\",\"version\":\"0.1.19\"}",
|
||||
decode: func(b []byte) (any, error) { var v PublishInput; err := json.Unmarshal(b, &v); return v, err }, subjectIs: nil},
|
||||
{name: "a field the reader does not know is dropped", subject: "", given: "{\"commit\":\"cccccccccccccccccccccccccccccccccccccccc\",\"digest\":\"sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\",\"fingerprint\":\"f1\",\"kind\":\"bundle\",\"later\":{\"x\":1},\"name\":\"mesh-sdk\",\"parts\":[{\"digest\":\"sha256:bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb\",\"size\":1048576}],\"transfer\":\"t1\",\"version\":\"0.1.19\"}", wire: "{\"commit\":\"cccccccccccccccccccccccccccccccccccccccc\",\"digest\":\"sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\",\"fingerprint\":\"f1\",\"kind\":\"bundle\",\"name\":\"mesh-sdk\",\"parts\":[{\"digest\":\"sha256:bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb\",\"size\":1048576}],\"transfer\":\"t1\",\"version\":\"0.1.19\"}",
|
||||
decode: func(b []byte) (any, error) { var v PublishInput; err := json.Unmarshal(b, &v); return v, err }, subjectIs: nil},
|
||||
{name: "publish answers what it lacks", subject: "", given: "{\"lacks\":[\"sha256:bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb\"],\"transfer\":\"t1\"}", wire: "{\"lacks\":[\"sha256:bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb\"],\"transfer\":\"t1\"}",
|
||||
decode: func(b []byte) (any, error) { var v PublishAnswer; err := json.Unmarshal(b, &v); return v, err }, subjectIs: nil},
|
||||
{name: "a chunk of a part", subject: "", given: "{\"data\":\"aGVsbG8=\",\"last\":true,\"part\":\"sha256:bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb\",\"sequence\":0,\"transfer\":\"t1\"}", wire: "{\"data\":\"aGVsbG8=\",\"last\":true,\"part\":\"sha256:bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb\",\"sequence\":0,\"transfer\":\"t1\"}",
|
||||
decode: func(b []byte) (any, error) { var v PublishPartInput; err := json.Unmarshal(b, &v); return v, err }, subjectIs: nil},
|
||||
{name: "the versions held", subject: "", given: "{\"name\":\"mesh-sdk\",\"versions\":[{\"commit\":\"cccccccccccccccccccccccccccccccccccccccc\",\"digest\":\"sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\",\"fingerprint\":\"f1\",\"kind\":\"package\",\"name\":\"mesh-sdk\",\"version\":\"0.1.19\"}]}", wire: "{\"name\":\"mesh-sdk\",\"versions\":[{\"commit\":\"cccccccccccccccccccccccccccccccccccccccc\",\"digest\":\"sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\",\"fingerprint\":\"f1\",\"kind\":\"package\",\"name\":\"mesh-sdk\",\"version\":\"0.1.19\"}]}",
|
||||
decode: func(b []byte) (any, error) { var v VersionsAnswer; err := json.Unmarshal(b, &v); return v, err }, subjectIs: nil},
|
||||
{name: "a release of what is not held answers released false", subject: "", given: "{\"released\":false}", wire: "{\"released\":false}",
|
||||
decode: func(b []byte) (any, error) { var v ReleaseAnswer; err := json.Unmarshal(b, &v); return v, err }, subjectIs: nil},
|
||||
{name: "a tagged version sharing its digest with another tag is kept, not removed", subject: "", given: "{\"kept\":true,\"reason\":\"shared-digest\",\"removed\":false,\"tags\":[\"0.1.19\"]}", wire: "{\"kept\":true,\"reason\":\"shared-digest\",\"removed\":false,\"tags\":[\"0.1.19\"]}",
|
||||
decode: func(b []byte) (any, error) { var v RemoveAnswer; err := json.Unmarshal(b, &v); return v, err }, subjectIs: nil},
|
||||
{name: "list answers an untagged archive by the blob it keeps", subject: "", given: "{\"repositories\":[{\"manifests\":[{\"artifact\":\"sha256:cccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc\",\"digest\":\"sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\",\"held\":true,\"index\":false,\"size\":4096}],\"name\":\"mail/files\"}]}", wire: "{\"repositories\":[{\"manifests\":[{\"artifact\":\"sha256:cccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc\",\"digest\":\"sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\",\"held\":true,\"index\":false,\"size\":4096}],\"name\":\"mail/files\"}]}",
|
||||
decode: func(b []byte) (any, error) { var v ListAnswer; err := json.Unmarshal(b, &v); return v, err }, subjectIs: nil},
|
||||
}
|
||||
for _, c := range cases {
|
||||
v, err := c.decode([]byte(c.given))
|
||||
if err != nil {
|
||||
t.Errorf("%s: does not decode: %v", c.name, err)
|
||||
continue
|
||||
}
|
||||
back, err := json.Marshal(v)
|
||||
if err != nil {
|
||||
t.Errorf("%s: does not encode: %v", c.name, err)
|
||||
continue
|
||||
}
|
||||
var got, want any
|
||||
if err := json.Unmarshal(back, &got); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if err := json.Unmarshal([]byte(c.wire), &want); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if !reflect.DeepEqual(got, want) {
|
||||
t.Errorf("%s: reads back as %s, want %s", c.name, back, c.wire)
|
||||
}
|
||||
if c.subjectIs != nil && !c.subjectIs.MatchString(c.subject) {
|
||||
t.Errorf("%s: subject %q is not the contract's %s", c.name, c.subject, c.subjectIs)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,3 @@
|
||||
module git.novox.be/novox/mesh-delivery/contracts/go/seats/ociartifactregistry
|
||||
|
||||
go 1.26.0
|
||||
@@ -0,0 +1,245 @@
|
||||
{
|
||||
"module": "mesh-delivery-contracts",
|
||||
"version": "1",
|
||||
"build": {
|
||||
"artifacts": [
|
||||
{
|
||||
"name": "go-containerimageregistry",
|
||||
"kind": "package",
|
||||
"language": "go",
|
||||
"paths": [
|
||||
"go/seats/containerimageregistry"
|
||||
],
|
||||
"series": "0.1",
|
||||
"generated-from": "."
|
||||
},
|
||||
{
|
||||
"name": "go-gopackageregistry",
|
||||
"kind": "package",
|
||||
"language": "go",
|
||||
"paths": [
|
||||
"go/seats/gopackageregistry"
|
||||
],
|
||||
"series": "0.1",
|
||||
"generated-from": "."
|
||||
},
|
||||
{
|
||||
"name": "go-meshcatalog",
|
||||
"kind": "package",
|
||||
"language": "go",
|
||||
"paths": [
|
||||
"go/seats/meshcatalog"
|
||||
],
|
||||
"series": "0.1",
|
||||
"generated-from": "."
|
||||
},
|
||||
{
|
||||
"name": "go-meshdelivery",
|
||||
"kind": "package",
|
||||
"language": "go",
|
||||
"paths": [
|
||||
"go/seats/meshdelivery"
|
||||
],
|
||||
"series": "0.1",
|
||||
"generated-from": "."
|
||||
},
|
||||
{
|
||||
"name": "go-meshdeliverychecker",
|
||||
"kind": "package",
|
||||
"language": "go",
|
||||
"paths": [
|
||||
"go/seats/meshdeliverychecker"
|
||||
],
|
||||
"series": "0.1",
|
||||
"generated-from": "."
|
||||
},
|
||||
{
|
||||
"name": "go-nodebuildgo",
|
||||
"kind": "package",
|
||||
"language": "go",
|
||||
"paths": [
|
||||
"go/seats/nodebuildgo"
|
||||
],
|
||||
"series": "0.1",
|
||||
"generated-from": "."
|
||||
},
|
||||
{
|
||||
"name": "go-nodebuildimage",
|
||||
"kind": "package",
|
||||
"language": "go",
|
||||
"paths": [
|
||||
"go/seats/nodebuildimage"
|
||||
],
|
||||
"series": "0.1",
|
||||
"generated-from": "."
|
||||
},
|
||||
{
|
||||
"name": "go-nodebuildnode",
|
||||
"kind": "package",
|
||||
"language": "go",
|
||||
"paths": [
|
||||
"go/seats/nodebuildnode"
|
||||
],
|
||||
"series": "0.1",
|
||||
"generated-from": "."
|
||||
},
|
||||
{
|
||||
"name": "go-npmpackageregistry",
|
||||
"kind": "package",
|
||||
"language": "go",
|
||||
"paths": [
|
||||
"go/seats/npmpackageregistry"
|
||||
],
|
||||
"series": "0.1",
|
||||
"generated-from": "."
|
||||
},
|
||||
{
|
||||
"name": "go-ociartifactregistry",
|
||||
"kind": "package",
|
||||
"language": "go",
|
||||
"paths": [
|
||||
"go/seats/ociartifactregistry"
|
||||
],
|
||||
"series": "0.1",
|
||||
"generated-from": "."
|
||||
},
|
||||
{
|
||||
"name": "go-containerimageregistryclient",
|
||||
"kind": "package",
|
||||
"language": "go",
|
||||
"paths": [
|
||||
"go/clients/containerimageregistryclient"
|
||||
],
|
||||
"series": "0.1"
|
||||
},
|
||||
{
|
||||
"name": "go-gopackageregistryclient",
|
||||
"kind": "package",
|
||||
"language": "go",
|
||||
"paths": [
|
||||
"go/clients/gopackageregistryclient"
|
||||
],
|
||||
"series": "0.1"
|
||||
},
|
||||
{
|
||||
"name": "go-npmpackageregistryclient",
|
||||
"kind": "package",
|
||||
"language": "go",
|
||||
"paths": [
|
||||
"go/clients/npmpackageregistryclient"
|
||||
],
|
||||
"series": "0.1"
|
||||
},
|
||||
{
|
||||
"name": "go-ociartifactregistryclient",
|
||||
"kind": "package",
|
||||
"language": "go",
|
||||
"paths": [
|
||||
"go/clients/ociartifactregistryclient"
|
||||
],
|
||||
"series": "0.1"
|
||||
},
|
||||
{
|
||||
"name": "ts-containerimageregistry",
|
||||
"kind": "package",
|
||||
"language": "typescript",
|
||||
"paths": [
|
||||
"ts/seats/containerimageregistry"
|
||||
],
|
||||
"series": "0.1",
|
||||
"generated-from": "."
|
||||
},
|
||||
{
|
||||
"name": "ts-gopackageregistry",
|
||||
"kind": "package",
|
||||
"language": "typescript",
|
||||
"paths": [
|
||||
"ts/seats/gopackageregistry"
|
||||
],
|
||||
"series": "0.1",
|
||||
"generated-from": "."
|
||||
},
|
||||
{
|
||||
"name": "ts-meshcatalog",
|
||||
"kind": "package",
|
||||
"language": "typescript",
|
||||
"paths": [
|
||||
"ts/seats/meshcatalog"
|
||||
],
|
||||
"series": "0.1",
|
||||
"generated-from": "."
|
||||
},
|
||||
{
|
||||
"name": "ts-meshdelivery",
|
||||
"kind": "package",
|
||||
"language": "typescript",
|
||||
"paths": [
|
||||
"ts/seats/meshdelivery"
|
||||
],
|
||||
"series": "0.1",
|
||||
"generated-from": "."
|
||||
},
|
||||
{
|
||||
"name": "ts-meshdeliverychecker",
|
||||
"kind": "package",
|
||||
"language": "typescript",
|
||||
"paths": [
|
||||
"ts/seats/meshdeliverychecker"
|
||||
],
|
||||
"series": "0.1",
|
||||
"generated-from": "."
|
||||
},
|
||||
{
|
||||
"name": "ts-nodebuildgo",
|
||||
"kind": "package",
|
||||
"language": "typescript",
|
||||
"paths": [
|
||||
"ts/seats/nodebuildgo"
|
||||
],
|
||||
"series": "0.1",
|
||||
"generated-from": "."
|
||||
},
|
||||
{
|
||||
"name": "ts-nodebuildimage",
|
||||
"kind": "package",
|
||||
"language": "typescript",
|
||||
"paths": [
|
||||
"ts/seats/nodebuildimage"
|
||||
],
|
||||
"series": "0.1",
|
||||
"generated-from": "."
|
||||
},
|
||||
{
|
||||
"name": "ts-nodebuildnode",
|
||||
"kind": "package",
|
||||
"language": "typescript",
|
||||
"paths": [
|
||||
"ts/seats/nodebuildnode"
|
||||
],
|
||||
"series": "0.1",
|
||||
"generated-from": "."
|
||||
},
|
||||
{
|
||||
"name": "ts-npmpackageregistry",
|
||||
"kind": "package",
|
||||
"language": "typescript",
|
||||
"paths": [
|
||||
"ts/seats/npmpackageregistry"
|
||||
],
|
||||
"series": "0.1",
|
||||
"generated-from": "."
|
||||
},
|
||||
{
|
||||
"name": "ts-ociartifactregistry",
|
||||
"kind": "package",
|
||||
"language": "typescript",
|
||||
"paths": [
|
||||
"ts/seats/ociartifactregistry"
|
||||
],
|
||||
"series": "0.1",
|
||||
"generated-from": "."
|
||||
}
|
||||
]
|
||||
},
|
||||
"resources": []
|
||||
}
|
||||
@@ -0,0 +1,588 @@
|
||||
{
|
||||
"format": 1,
|
||||
"part": "build-step",
|
||||
"description": "What the three build seats share: the verbs that read and steer a node's builds, and the layouts of the asks they accept and the steps they keep. A seat lists its states against these (a part carries no states).",
|
||||
"verbs": [
|
||||
{
|
||||
"name": "current",
|
||||
"description": "The asks this node runs now (each ask's id, kind, module and when it started), its build capacity, and whether it is paused, taking no new ask.",
|
||||
"input": {
|
||||
"type": "object",
|
||||
"properties": {}
|
||||
},
|
||||
"answer": {
|
||||
"type": "object",
|
||||
"description": "the steps running, the capacity and whether paused"
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "pause",
|
||||
"description": "Take no new ask on this node until resumed; an ask running here ends in its place. Kept across a restart of the build module. A capacity of 0 is the lasting way to keep a node from building.",
|
||||
"input": {
|
||||
"type": "object",
|
||||
"properties": {}
|
||||
},
|
||||
"answer": {
|
||||
"type": "object",
|
||||
"description": "whether the node is paused"
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "resume",
|
||||
"description": "Take asks again on this node.",
|
||||
"input": {
|
||||
"type": "object",
|
||||
"properties": {}
|
||||
},
|
||||
"answer": {
|
||||
"type": "object",
|
||||
"description": "whether the node is paused"
|
||||
}
|
||||
}
|
||||
],
|
||||
"defs": {
|
||||
"StepAsk": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"v": {
|
||||
"type": "integer",
|
||||
"description": "the layout version"
|
||||
},
|
||||
"id": {
|
||||
"type": "string"
|
||||
},
|
||||
"kind": {
|
||||
"type": "string",
|
||||
"description": "build, check or compose"
|
||||
},
|
||||
"language": {
|
||||
"type": "string",
|
||||
"description": "go, typescript or image"
|
||||
},
|
||||
"asker": {
|
||||
"type": "string"
|
||||
},
|
||||
"at": {
|
||||
"type": "string",
|
||||
"description": "when (RFC 3339 time)"
|
||||
},
|
||||
"build": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"module": {
|
||||
"type": "string"
|
||||
},
|
||||
"path": {
|
||||
"type": "string"
|
||||
},
|
||||
"fp": {
|
||||
"type": "string"
|
||||
},
|
||||
"fetch": {
|
||||
"description": "what the build module fetches: the repository, commit and directory, as the indexer states it"
|
||||
},
|
||||
"artifacts": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"artifact": {
|
||||
"type": "string"
|
||||
},
|
||||
"from": {
|
||||
"type": "string"
|
||||
},
|
||||
"seat": {
|
||||
"type": "string"
|
||||
},
|
||||
"version": {
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"artifact"
|
||||
]
|
||||
}
|
||||
},
|
||||
"publish": {
|
||||
"type": "boolean"
|
||||
},
|
||||
"toolchain": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"language": {
|
||||
"type": "string"
|
||||
},
|
||||
"version": {
|
||||
"type": "string"
|
||||
},
|
||||
"recipe": {
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"language",
|
||||
"version",
|
||||
"recipe"
|
||||
]
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"module",
|
||||
"artifacts"
|
||||
]
|
||||
},
|
||||
"check": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"fetch": {
|
||||
"description": "what the build module fetches: the repository, commit and directory, as the indexer states it"
|
||||
},
|
||||
"levels": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"type": "string",
|
||||
"description": "unit or integration"
|
||||
}
|
||||
},
|
||||
"kept": {
|
||||
"type": "boolean"
|
||||
},
|
||||
"runner": {
|
||||
"type": "string"
|
||||
},
|
||||
"runner_when": {
|
||||
"type": "string"
|
||||
},
|
||||
"run": {
|
||||
"type": "string"
|
||||
},
|
||||
"packages": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"fetch",
|
||||
"levels"
|
||||
]
|
||||
},
|
||||
"compose": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"fetch": {
|
||||
"description": "what the build module fetches: the repository, commit and directory, as the indexer states it"
|
||||
},
|
||||
"dir": {
|
||||
"type": "string"
|
||||
},
|
||||
"package": {
|
||||
"type": "string"
|
||||
},
|
||||
"request": {
|
||||
"description": "the compose-candidate request"
|
||||
},
|
||||
"reader": {
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"fetch",
|
||||
"dir",
|
||||
"package",
|
||||
"request"
|
||||
]
|
||||
},
|
||||
"needs": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"packages": {
|
||||
"type": "object",
|
||||
"additionalProperties": {
|
||||
"type": "string"
|
||||
},
|
||||
"description": "package to the tagged version a build pins"
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"id",
|
||||
"kind",
|
||||
"language",
|
||||
"asker"
|
||||
],
|
||||
"description": "one ask of a build module"
|
||||
},
|
||||
"BuildAnswer": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"artifacts": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"artifact": {
|
||||
"type": "string"
|
||||
},
|
||||
"digest": {
|
||||
"type": "string"
|
||||
},
|
||||
"reference": {
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"artifact",
|
||||
"digest"
|
||||
]
|
||||
}
|
||||
},
|
||||
"toolchain": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"language": {
|
||||
"type": "string"
|
||||
},
|
||||
"version": {
|
||||
"type": "string"
|
||||
},
|
||||
"recipe": {
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"language",
|
||||
"version",
|
||||
"recipe"
|
||||
]
|
||||
},
|
||||
"imports": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"out-of-step": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"artifacts"
|
||||
]
|
||||
},
|
||||
"CheckAnswer": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"fetch": {
|
||||
"description": "what the build module fetches: the repository, commit and directory, as the indexer states it"
|
||||
},
|
||||
"levels": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"level": {
|
||||
"type": "string"
|
||||
},
|
||||
"judged": {
|
||||
"type": "string"
|
||||
},
|
||||
"digest": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"level": {
|
||||
"type": "string"
|
||||
},
|
||||
"packages": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"package": {
|
||||
"type": "string"
|
||||
},
|
||||
"digest": {
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"package",
|
||||
"digest"
|
||||
]
|
||||
}
|
||||
},
|
||||
"beside": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"without_tests": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"judged_by": {
|
||||
"type": "string"
|
||||
},
|
||||
"needs": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"package": {
|
||||
"type": "string"
|
||||
},
|
||||
"needs": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"kept": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"package": {
|
||||
"type": "string"
|
||||
},
|
||||
"digest": {
|
||||
"type": "string"
|
||||
},
|
||||
"tests": {
|
||||
"type": "integer"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"tests": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"description": "one test's result"
|
||||
}
|
||||
},
|
||||
"unmet": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"package": {
|
||||
"type": "string"
|
||||
},
|
||||
"needs": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"level"
|
||||
]
|
||||
}
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"fetch"
|
||||
]
|
||||
},
|
||||
"ComposeAnswer": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"built": {
|
||||
"type": "boolean"
|
||||
},
|
||||
"key": {
|
||||
"type": "string"
|
||||
},
|
||||
"verdict": {
|
||||
"description": "the compose-candidate verdict"
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"built"
|
||||
]
|
||||
},
|
||||
"Step": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"v": {
|
||||
"type": "integer",
|
||||
"description": "the layout version"
|
||||
},
|
||||
"id": {
|
||||
"type": "string"
|
||||
},
|
||||
"tool": {
|
||||
"type": "string"
|
||||
},
|
||||
"node": {
|
||||
"type": "string"
|
||||
},
|
||||
"state": {
|
||||
"type": "string",
|
||||
"description": "running, passed, failed or refused"
|
||||
},
|
||||
"started": {
|
||||
"type": "string",
|
||||
"description": "when (RFC 3339 time)"
|
||||
},
|
||||
"ended": {
|
||||
"type": "string",
|
||||
"description": "when (RFC 3339 time)"
|
||||
},
|
||||
"reason": {
|
||||
"type": "string"
|
||||
},
|
||||
"result": {
|
||||
"description": "what the step answered"
|
||||
},
|
||||
"what": {
|
||||
"type": "string"
|
||||
},
|
||||
"phase": {
|
||||
"type": "string"
|
||||
},
|
||||
"phase-at": {
|
||||
"type": "string",
|
||||
"description": "when (RFC 3339 time)"
|
||||
},
|
||||
"owner": {
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"id",
|
||||
"tool",
|
||||
"node",
|
||||
"state",
|
||||
"started"
|
||||
],
|
||||
"description": "one step a build module runs on a node"
|
||||
},
|
||||
"Toolchain": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"v": {
|
||||
"type": "integer",
|
||||
"description": "the layout version"
|
||||
},
|
||||
"language": {
|
||||
"type": "string",
|
||||
"description": "go or typescript"
|
||||
},
|
||||
"version": {
|
||||
"type": "string"
|
||||
},
|
||||
"recipe": {
|
||||
"type": "string"
|
||||
},
|
||||
"at": {
|
||||
"type": "string",
|
||||
"description": "when (RFC 3339 time)"
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"language",
|
||||
"version",
|
||||
"recipe"
|
||||
],
|
||||
"description": "the toolchain a node reports"
|
||||
},
|
||||
"Pause": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"v": {
|
||||
"type": "integer",
|
||||
"description": "the layout version"
|
||||
},
|
||||
"paused": {
|
||||
"type": "boolean"
|
||||
},
|
||||
"by": {
|
||||
"type": "string"
|
||||
},
|
||||
"at": {
|
||||
"type": "string",
|
||||
"description": "when (RFC 3339 time)"
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"paused"
|
||||
],
|
||||
"description": "whether the node takes no new ask"
|
||||
},
|
||||
"Capacity": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"v": {
|
||||
"type": "integer",
|
||||
"description": "the layout version"
|
||||
},
|
||||
"effective": {
|
||||
"type": "integer"
|
||||
},
|
||||
"configured": {
|
||||
"type": "integer"
|
||||
},
|
||||
"configured-from": {
|
||||
"type": "string"
|
||||
},
|
||||
"quota-cores": {
|
||||
"type": "number"
|
||||
},
|
||||
"quota-from": {
|
||||
"type": "string"
|
||||
},
|
||||
"cores-per-step": {
|
||||
"type": "number"
|
||||
},
|
||||
"by-cpu": {
|
||||
"type": "integer"
|
||||
},
|
||||
"build-hours": {
|
||||
"type": "string"
|
||||
},
|
||||
"outside-hours": {
|
||||
"type": "boolean"
|
||||
},
|
||||
"build-hours-fault": {
|
||||
"type": "string"
|
||||
},
|
||||
"ineligible": {
|
||||
"type": "string"
|
||||
},
|
||||
"why": {
|
||||
"type": "string"
|
||||
},
|
||||
"at": {
|
||||
"type": "string",
|
||||
"description": "when (RFC 3339 time)"
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"effective",
|
||||
"configured",
|
||||
"why"
|
||||
],
|
||||
"description": "how many steps the node runs at once"
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,436 @@
|
||||
{
|
||||
"format": 1,
|
||||
"part": "registry",
|
||||
"description": "The verbs and events every registry seat shares, written once so the four cannot drift (design 51, design 26). Who may call each verb is the holder's trusted config value listing its callers (ADR 0363's form), checked against the caller the tool runner proved.",
|
||||
"verbs": [
|
||||
{
|
||||
"name": "publish",
|
||||
"description": "Open a transfer, naming the artifact and the digest of every part; answers the digests the holder lacks, which the caller then sends by publish-part. A transfer already open answers the parts held, so a cut transfer goes on from there. With no parts and a version, the held digest is published under that version and nothing is stored (the trunk's claim).",
|
||||
"callers": "the build modules",
|
||||
"input": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"name": {
|
||||
"type": "string",
|
||||
"description": "the package's, image's, bundle's or archive's name; a package's is its name in its own language (design 32): a Go package's the go.mod module path (git.novox.be/novox/mesh-sdk/go), an npm package's package.json's name (@novox/mesh-sdk)"
|
||||
},
|
||||
"version": {
|
||||
"type": "string",
|
||||
"description": "the version the builder gave it (design 51 *The versions*); empty for a bundle or archive published by its digest alone"
|
||||
},
|
||||
"kind": {
|
||||
"type": "string",
|
||||
"enum": [
|
||||
"package",
|
||||
"image",
|
||||
"bundle",
|
||||
"archive",
|
||||
"upstream"
|
||||
],
|
||||
"description": "what it is"
|
||||
},
|
||||
"tag": {
|
||||
"type": "string",
|
||||
"description": "the tag it carries, for a tagged version"
|
||||
},
|
||||
"digest": {
|
||||
"type": "string",
|
||||
"description": "its digest, sha256:<hex> (a Go package's h1: hash beside it in the version's record)"
|
||||
},
|
||||
"fingerprint": {
|
||||
"type": "string",
|
||||
"description": "the build's fingerprint"
|
||||
},
|
||||
"commit": {
|
||||
"type": "string",
|
||||
"description": "the source commit it was built from"
|
||||
},
|
||||
"transfer": {
|
||||
"type": "string",
|
||||
"description": "the transfer's name, the caller's own; the same name opens the same transfer again"
|
||||
},
|
||||
"parts": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"$ref": "#/defs/Part"
|
||||
},
|
||||
"description": "every part's digest and size; none when the digest is already held"
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"transfer",
|
||||
"name",
|
||||
"kind",
|
||||
"digest"
|
||||
]
|
||||
},
|
||||
"answer": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"transfer": {
|
||||
"type": "string",
|
||||
"description": "the transfer"
|
||||
},
|
||||
"lacks": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"type": "string"
|
||||
},
|
||||
"description": "the digests of the parts the holder lacks, to send by publish-part"
|
||||
},
|
||||
"held": {
|
||||
"type": "boolean",
|
||||
"description": "the digest is held already: nothing to send; the outcome follows as an event"
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"transfer"
|
||||
]
|
||||
},
|
||||
"optional": true
|
||||
},
|
||||
{
|
||||
"name": "publish-part",
|
||||
"description": "Send one chunk of one part of an open transfer; the last chunk of the last part ends the transfer, and the holder hashes what it received, asks builder_versions, stores it and says published or refused. A chunk's message stays under the bus's largest message (1 MiB), so at most 760 KiB of data, base64.",
|
||||
"callers": "the build modules, and only the proved caller that opened the transfer",
|
||||
"input": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"transfer": {
|
||||
"type": "string",
|
||||
"description": "the transfer"
|
||||
},
|
||||
"part": {
|
||||
"type": "string",
|
||||
"description": "the part's digest"
|
||||
},
|
||||
"sequence": {
|
||||
"type": "integer",
|
||||
"description": "the chunk's place in its part, from 0"
|
||||
},
|
||||
"last": {
|
||||
"type": "boolean",
|
||||
"description": "this chunk is the part's last"
|
||||
},
|
||||
"data": {
|
||||
"type": "string",
|
||||
"description": "the chunk's bytes, base64 (at most 760 KiB before encoding)"
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"transfer",
|
||||
"part",
|
||||
"sequence",
|
||||
"last",
|
||||
"data"
|
||||
]
|
||||
},
|
||||
"answer": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"transfer": {
|
||||
"type": "string",
|
||||
"description": "the transfer"
|
||||
},
|
||||
"part": {
|
||||
"type": "string",
|
||||
"description": "the part"
|
||||
},
|
||||
"received": {
|
||||
"type": "integer",
|
||||
"description": "the chunks of the part held so far"
|
||||
},
|
||||
"complete": {
|
||||
"type": "boolean",
|
||||
"description": "the part is whole and its bytes match its digest"
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"transfer",
|
||||
"part"
|
||||
]
|
||||
},
|
||||
"optional": true
|
||||
},
|
||||
{
|
||||
"name": "versions",
|
||||
"description": "The versions the seat holds of one name, newest first: each one's version, tag, digest, kind and when it was published.",
|
||||
"callers": "any caller whose role grants it",
|
||||
"input": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"name": {
|
||||
"type": "string",
|
||||
"description": "the name"
|
||||
},
|
||||
"kind": {
|
||||
"type": "string",
|
||||
"description": "narrowed to one kind (optional)"
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"name"
|
||||
]
|
||||
},
|
||||
"answer": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"name": {
|
||||
"type": "string",
|
||||
"description": "the name"
|
||||
},
|
||||
"versions": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"$ref": "#/defs/Artifact"
|
||||
},
|
||||
"description": "newest first"
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"name",
|
||||
"versions"
|
||||
]
|
||||
},
|
||||
"optional": true
|
||||
},
|
||||
{
|
||||
"name": "remove",
|
||||
"description": "Remove one tagged version; the holder refuses an untagged one but for the retention removal of a bundle, archive or copied base by the artifact sweep's holder. Every removal emits removed with its reason. A tagged version the store cannot remove alone, because another tag names its digest, stays whole, its tag and bytes kept: the answer says kept with the reason shared-digest and the tags still naming the digest, removed false, and kept is emitted, never removed; the caller ends there without a retry.",
|
||||
"callers": "the builder, for a tagged version; the artifact sweep's holder, for retention",
|
||||
"input": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"name": {
|
||||
"type": "string",
|
||||
"description": "the name"
|
||||
},
|
||||
"version": {
|
||||
"type": "string",
|
||||
"description": "the version"
|
||||
},
|
||||
"tag": {
|
||||
"type": "string",
|
||||
"description": "the tag (for a tagged version)"
|
||||
},
|
||||
"digest": {
|
||||
"type": "string",
|
||||
"description": "the digest (for a bundle, archive or copied base)"
|
||||
},
|
||||
"reason": {
|
||||
"type": "string",
|
||||
"enum": [
|
||||
"tag-gone",
|
||||
"retention"
|
||||
],
|
||||
"description": "why it goes"
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"name",
|
||||
"reason"
|
||||
]
|
||||
},
|
||||
"answer": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"removed": {
|
||||
"type": "boolean",
|
||||
"description": "it is gone"
|
||||
},
|
||||
"kept": {
|
||||
"type": "boolean",
|
||||
"description": "it stays whole, its tag and its bytes: the store cannot remove the tag alone while another tag names its digest (reason, tags)"
|
||||
},
|
||||
"reason": {
|
||||
"type": "string",
|
||||
"enum": [
|
||||
"shared-digest"
|
||||
],
|
||||
"description": "why it was kept: shared-digest, another tag names its digest"
|
||||
},
|
||||
"tags": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"type": "string"
|
||||
},
|
||||
"description": "the other tags still naming its digest, when kept"
|
||||
},
|
||||
"artifact": {
|
||||
"$ref": "#/defs/Artifact"
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"removed"
|
||||
]
|
||||
},
|
||||
"optional": true
|
||||
}
|
||||
],
|
||||
"events": [
|
||||
{
|
||||
"name": "published",
|
||||
"body": {
|
||||
"$ref": "#/defs/Outcome"
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "refused",
|
||||
"body": {
|
||||
"$ref": "#/defs/Outcome"
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "removed",
|
||||
"body": {
|
||||
"$ref": "#/defs/Outcome"
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "kept",
|
||||
"body": {
|
||||
"$ref": "#/defs/Outcome"
|
||||
}
|
||||
}
|
||||
],
|
||||
"defs": {
|
||||
"Artifact": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"name": {
|
||||
"type": "string",
|
||||
"description": "the package's, image's, bundle's or archive's name; a package's is its name in its own language (design 32): a Go package's the go.mod module path (git.novox.be/novox/mesh-sdk/go), an npm package's package.json's name (@novox/mesh-sdk)"
|
||||
},
|
||||
"version": {
|
||||
"type": "string",
|
||||
"description": "the version the builder gave it (design 51 *The versions*); empty for a bundle or archive published by its digest alone"
|
||||
},
|
||||
"kind": {
|
||||
"type": "string",
|
||||
"enum": [
|
||||
"package",
|
||||
"image",
|
||||
"bundle",
|
||||
"archive",
|
||||
"upstream"
|
||||
],
|
||||
"description": "what it is"
|
||||
},
|
||||
"tag": {
|
||||
"type": "string",
|
||||
"description": "the tag it carries, for a tagged version"
|
||||
},
|
||||
"digest": {
|
||||
"type": "string",
|
||||
"description": "its digest, sha256:<hex> (a Go package's h1: hash beside it in the version's record)"
|
||||
},
|
||||
"fingerprint": {
|
||||
"type": "string",
|
||||
"description": "the build's fingerprint"
|
||||
},
|
||||
"commit": {
|
||||
"type": "string",
|
||||
"description": "the source commit it was built from"
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"name",
|
||||
"kind",
|
||||
"digest"
|
||||
],
|
||||
"description": "one published thing, as every registry event and answer names it"
|
||||
},
|
||||
"Part": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"digest": {
|
||||
"type": "string",
|
||||
"description": "the part's digest, sha256:<hex>: an image's layer or config, a package's file, a bundle's blob"
|
||||
},
|
||||
"size": {
|
||||
"type": "integer",
|
||||
"description": "its size in bytes"
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"digest",
|
||||
"size"
|
||||
],
|
||||
"description": "one part of a transfer"
|
||||
},
|
||||
"Outcome": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"name": {
|
||||
"type": "string",
|
||||
"description": "the package's, image's, bundle's or archive's name; a package's is its name in its own language (design 32): a Go package's the go.mod module path (git.novox.be/novox/mesh-sdk/go), an npm package's package.json's name (@novox/mesh-sdk)"
|
||||
},
|
||||
"module": {
|
||||
"type": "string",
|
||||
"description": "the Go module path a Go package is fetched under, equal to name for a Go package (go-package-registry's published); empty for every other kind of artifact"
|
||||
},
|
||||
"version": {
|
||||
"type": "string",
|
||||
"description": "the version the builder gave it (design 51 *The versions*); empty for a bundle or archive published by its digest alone"
|
||||
},
|
||||
"kind": {
|
||||
"type": "string",
|
||||
"enum": [
|
||||
"package",
|
||||
"image",
|
||||
"bundle",
|
||||
"archive",
|
||||
"upstream"
|
||||
],
|
||||
"description": "what it is"
|
||||
},
|
||||
"tag": {
|
||||
"type": "string",
|
||||
"description": "the tag it carries, for a tagged version"
|
||||
},
|
||||
"digest": {
|
||||
"type": "string",
|
||||
"description": "its digest, sha256:<hex> (a Go package's h1: hash beside it in the version's record)"
|
||||
},
|
||||
"fingerprint": {
|
||||
"type": "string",
|
||||
"description": "the build's fingerprint"
|
||||
},
|
||||
"commit": {
|
||||
"type": "string",
|
||||
"description": "the source commit it was built from"
|
||||
},
|
||||
"holder": {
|
||||
"type": "string",
|
||||
"description": "the module holding the seat that said it"
|
||||
},
|
||||
"transfer": {
|
||||
"type": "string",
|
||||
"description": "the transfer it ends, when it ended one"
|
||||
},
|
||||
"reference": {
|
||||
"type": "string",
|
||||
"description": "the reference the seat answers: <registry>/<module>/<artifact>@sha256:<digest>, or <name>@<version> for a package (ADR 0605 §9)"
|
||||
},
|
||||
"reason": {
|
||||
"type": "string",
|
||||
"description": "why: a refusal's cause, a removal's (retention, tag-gone, by-hand), or why a removal was kept (shared-digest)"
|
||||
},
|
||||
"tags": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"type": "string"
|
||||
},
|
||||
"description": "for kept: the other tags still naming its digest"
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"name",
|
||||
"kind",
|
||||
"digest",
|
||||
"holder"
|
||||
],
|
||||
"description": "the body of published, refused and removed"
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,98 @@
|
||||
[
|
||||
{
|
||||
"name": "published on the seat's subject",
|
||||
"of": "event:published",
|
||||
"subject": "mesh.seat.container-image-registry.event.published",
|
||||
"given": {
|
||||
"name": "mesh-sdk",
|
||||
"version": "0.1.19",
|
||||
"kind": "image",
|
||||
"digest": "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa",
|
||||
"fingerprint": "f1",
|
||||
"commit": "cccccccccccccccccccccccccccccccccccccccc",
|
||||
"holder": "distribution",
|
||||
"transfer": "t1",
|
||||
"reference": "container-image-registry/mesh-sdk/lib@sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "refused on the seat's subject",
|
||||
"of": "event:refused",
|
||||
"subject": "mesh.seat.container-image-registry.event.refused",
|
||||
"given": {
|
||||
"name": "mesh-sdk",
|
||||
"version": "0.1.19",
|
||||
"kind": "image",
|
||||
"digest": "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa",
|
||||
"fingerprint": "f1",
|
||||
"commit": "cccccccccccccccccccccccccccccccccccccccc",
|
||||
"holder": "distribution",
|
||||
"transfer": "t1",
|
||||
"reference": "container-image-registry/mesh-sdk/lib@sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa",
|
||||
"reason": "retention"
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "removed on the seat's subject",
|
||||
"of": "event:removed",
|
||||
"subject": "mesh.seat.container-image-registry.event.removed",
|
||||
"given": {
|
||||
"name": "mesh-sdk",
|
||||
"version": "0.1.19",
|
||||
"kind": "image",
|
||||
"digest": "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa",
|
||||
"fingerprint": "f1",
|
||||
"commit": "cccccccccccccccccccccccccccccccccccccccc",
|
||||
"holder": "distribution",
|
||||
"transfer": "t1",
|
||||
"reference": "container-image-registry/mesh-sdk/lib@sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa",
|
||||
"reason": "retention"
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "kept on the seat's subject: a tagged version sharing its digest stays, naming the tags on it",
|
||||
"of": "event:kept",
|
||||
"subject": "mesh.seat.container-image-registry.event.kept",
|
||||
"given": {
|
||||
"name": "mesh-sdk",
|
||||
"version": "0.1.19-feat-x.2",
|
||||
"kind": "image",
|
||||
"digest": "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa",
|
||||
"fingerprint": "f1",
|
||||
"commit": "cccccccccccccccccccccccccccccccccccccccc",
|
||||
"holder": "distribution",
|
||||
"reference": "container-image-registry/mesh-sdk/lib@sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa",
|
||||
"reason": "shared-digest",
|
||||
"tag": "0.1.19-feat-x.2",
|
||||
"tags": [
|
||||
"0.1.19"
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "held on the seat's subject",
|
||||
"of": "event:held",
|
||||
"subject": "mesh.seat.container-image-registry.event.held",
|
||||
"given": {
|
||||
"name": "mesh-sdk",
|
||||
"digest": "sha256:bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb",
|
||||
"reason": "kept by the sweep's rule",
|
||||
"holder": "distribution",
|
||||
"by": "mesh-artifact-sweep",
|
||||
"at": "2026-11-04T12:00:00Z"
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "released on the seat's subject",
|
||||
"of": "event:released",
|
||||
"subject": "mesh.seat.container-image-registry.event.released",
|
||||
"given": {
|
||||
"name": "mesh-sdk",
|
||||
"digest": "sha256:bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb",
|
||||
"reason": "kept by the sweep's rule",
|
||||
"holder": "distribution",
|
||||
"by": "mesh-artifact-sweep",
|
||||
"at": "2026-11-04T12:00:00Z"
|
||||
}
|
||||
}
|
||||
]
|
||||
@@ -0,0 +1,28 @@
|
||||
[
|
||||
{
|
||||
"name": "a hold",
|
||||
"of": "state:holds/<seat>.<name>.<digest>",
|
||||
"given": {
|
||||
"seat": "container-image-registry",
|
||||
"name": "base",
|
||||
"kind": "base",
|
||||
"digest": "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa",
|
||||
"by": "operator@shanks",
|
||||
"since": "2026-11-05T10:00:00Z"
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "an open window",
|
||||
"of": "state:windows/<caller>",
|
||||
"given": {
|
||||
"name": "mesh-delivery-builder",
|
||||
"open": true,
|
||||
"holding": [
|
||||
"ace"
|
||||
],
|
||||
"since": {
|
||||
"ace": "2026-11-05T10:00:00Z"
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
@@ -0,0 +1,149 @@
|
||||
[
|
||||
{
|
||||
"name": "publish opens a transfer",
|
||||
"of": "verb:publish.input",
|
||||
"given": {
|
||||
"name": "mesh-sdk",
|
||||
"version": "0.1.19",
|
||||
"kind": "image",
|
||||
"digest": "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa",
|
||||
"fingerprint": "f1",
|
||||
"commit": "cccccccccccccccccccccccccccccccccccccccc",
|
||||
"transfer": "t1",
|
||||
"parts": [
|
||||
{
|
||||
"digest": "sha256:bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb",
|
||||
"size": 1048576
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "a field the reader does not know is dropped",
|
||||
"of": "verb:publish.input",
|
||||
"why": "ADR 0608 §6: a reader ignores a field it does not know",
|
||||
"given": {
|
||||
"name": "mesh-sdk",
|
||||
"version": "0.1.19",
|
||||
"kind": "image",
|
||||
"digest": "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa",
|
||||
"fingerprint": "f1",
|
||||
"commit": "cccccccccccccccccccccccccccccccccccccccc",
|
||||
"transfer": "t1",
|
||||
"parts": [
|
||||
{
|
||||
"digest": "sha256:bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb",
|
||||
"size": 1048576
|
||||
}
|
||||
],
|
||||
"later": {
|
||||
"x": 1
|
||||
}
|
||||
},
|
||||
"wire": {
|
||||
"name": "mesh-sdk",
|
||||
"version": "0.1.19",
|
||||
"kind": "image",
|
||||
"digest": "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa",
|
||||
"fingerprint": "f1",
|
||||
"commit": "cccccccccccccccccccccccccccccccccccccccc",
|
||||
"transfer": "t1",
|
||||
"parts": [
|
||||
{
|
||||
"digest": "sha256:bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb",
|
||||
"size": 1048576
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "publish answers what it lacks",
|
||||
"of": "verb:publish.answer",
|
||||
"given": {
|
||||
"transfer": "t1",
|
||||
"lacks": [
|
||||
"sha256:bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb"
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "a chunk of a part",
|
||||
"of": "verb:publish-part.input",
|
||||
"given": {
|
||||
"transfer": "t1",
|
||||
"part": "sha256:bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb",
|
||||
"sequence": 0,
|
||||
"last": true,
|
||||
"data": "aGVsbG8="
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "the versions held",
|
||||
"of": "verb:versions.answer",
|
||||
"given": {
|
||||
"name": "mesh-sdk",
|
||||
"versions": [
|
||||
{
|
||||
"name": "mesh-sdk",
|
||||
"version": "0.1.19",
|
||||
"kind": "package",
|
||||
"digest": "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa",
|
||||
"fingerprint": "f1",
|
||||
"commit": "cccccccccccccccccccccccccccccccccccccccc"
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "a release of what is not held answers released false",
|
||||
"of": "verb:release.answer",
|
||||
"why": "A3t: the outcome asked holds already, so it is no error",
|
||||
"given": {
|
||||
"released": false
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "a tagged version sharing its digest with another tag is kept, not removed",
|
||||
"of": "verb:remove.answer",
|
||||
"why": "G-sdk-contracts 3: the store cannot remove the tag alone, so the answer says what holds",
|
||||
"given": {
|
||||
"removed": false,
|
||||
"kept": true,
|
||||
"reason": "shared-digest",
|
||||
"tags": [
|
||||
"0.1.19"
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "list answers an index with its platforms, its tag and its hold",
|
||||
"of": "verb:list.answer",
|
||||
"given": {
|
||||
"repositories": [
|
||||
{
|
||||
"name": "mail/server",
|
||||
"manifests": [
|
||||
{
|
||||
"digest": "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa",
|
||||
"index": true,
|
||||
"platforms": [
|
||||
"sha256:bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb"
|
||||
],
|
||||
"tags": [
|
||||
"3"
|
||||
],
|
||||
"size": 2048,
|
||||
"held": false
|
||||
},
|
||||
{
|
||||
"digest": "sha256:bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb",
|
||||
"index": false,
|
||||
"size": 1024,
|
||||
"held": false
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
]
|
||||
@@ -0,0 +1,479 @@
|
||||
{
|
||||
"format": 1,
|
||||
"seat": "container-image-registry",
|
||||
"scope": "mesh",
|
||||
"grain": "whole",
|
||||
"decision": "novox/hq ADR 0605",
|
||||
"description": "The mesh's container images, in the one artifact store: the OCI distribution protocol's read side, an image's tags and mirroring an outside image.",
|
||||
"includes": [
|
||||
"registry"
|
||||
],
|
||||
"verbs": [
|
||||
{
|
||||
"name": "mirror",
|
||||
"description": "Copy an outside image into the store by its pinned digest; emits published or refused.",
|
||||
"callers": "mesh-build-image's holder of the image build seat",
|
||||
"input": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"source": {
|
||||
"type": "string",
|
||||
"description": "the outside image's reference"
|
||||
},
|
||||
"digest": {
|
||||
"type": "string",
|
||||
"description": "its pinned digest, sha256:<hex>"
|
||||
},
|
||||
"name": {
|
||||
"type": "string",
|
||||
"description": "the name it is kept under"
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"source",
|
||||
"digest",
|
||||
"name"
|
||||
]
|
||||
},
|
||||
"answer": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"reference": {
|
||||
"type": "string",
|
||||
"description": "the reference the seat answers"
|
||||
},
|
||||
"digest": {
|
||||
"type": "string",
|
||||
"description": "the digest stored"
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"reference",
|
||||
"digest"
|
||||
]
|
||||
},
|
||||
"optional": true
|
||||
},
|
||||
{
|
||||
"name": "tags",
|
||||
"description": "The tags of one image and the digest each names.",
|
||||
"callers": "any caller whose role grants it",
|
||||
"input": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"name": {
|
||||
"type": "string",
|
||||
"description": "the image"
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"name"
|
||||
]
|
||||
},
|
||||
"answer": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"name": {
|
||||
"type": "string",
|
||||
"description": "the image"
|
||||
},
|
||||
"tags": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"tag": {
|
||||
"type": "string",
|
||||
"description": "the tag"
|
||||
},
|
||||
"digest": {
|
||||
"type": "string",
|
||||
"description": "its digest"
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"tag",
|
||||
"digest"
|
||||
]
|
||||
},
|
||||
"description": "every tag"
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"name",
|
||||
"tags"
|
||||
]
|
||||
},
|
||||
"optional": true
|
||||
},
|
||||
{
|
||||
"name": "hold",
|
||||
"description": "Keep one untagged bundle, archive or copied base from removal by retention, as the artifact sweep's rules keep it (ADR 0617 §2); never an image of the holder's own. Emits held.",
|
||||
"callers": "the module that runs the artifact sweep alone",
|
||||
"input": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"name": {
|
||||
"type": "string",
|
||||
"description": "the name"
|
||||
},
|
||||
"digest": {
|
||||
"type": "string",
|
||||
"description": "the digest"
|
||||
},
|
||||
"reason": {
|
||||
"type": "string",
|
||||
"description": "which of the sweep's rules keeps it"
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"name",
|
||||
"digest",
|
||||
"reason"
|
||||
]
|
||||
},
|
||||
"answer": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"held": {
|
||||
"type": "boolean",
|
||||
"description": "it is held"
|
||||
},
|
||||
"since": {
|
||||
"type": "string",
|
||||
"description": "when the hold began, RFC 3339"
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"held"
|
||||
]
|
||||
},
|
||||
"optional": true
|
||||
},
|
||||
{
|
||||
"name": "release",
|
||||
"description": "Let a hold go; emits released. A release of what is not held answers released false and emits nothing, never an error: the outcome asked holds already. By hand only through the holder's own tool, the operator's session as the proved caller, recorded as a hand act.",
|
||||
"callers": "the module that runs the artifact sweep; the operator's session through the holder's tool",
|
||||
"input": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"name": {
|
||||
"type": "string",
|
||||
"description": "the name"
|
||||
},
|
||||
"digest": {
|
||||
"type": "string",
|
||||
"description": "the digest"
|
||||
},
|
||||
"reason": {
|
||||
"type": "string",
|
||||
"description": "why it is let go"
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"name",
|
||||
"digest",
|
||||
"reason"
|
||||
]
|
||||
},
|
||||
"answer": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"released": {
|
||||
"type": "boolean",
|
||||
"description": "a hold was let go; false when there was none"
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"released"
|
||||
]
|
||||
},
|
||||
"optional": true
|
||||
},
|
||||
{
|
||||
"name": "list",
|
||||
"description": "What the store holds of one repository, or of every repository the seat holds when none is named: every image manifest and index, each with its digest, whether it is an index (and the manifests an index names), the tags naming it, its size and whether it is held. Read from the store itself, so a manifest no tag names is listed too; it reads only (ADR 0632 §3).",
|
||||
"callers": "any caller whose role grants it",
|
||||
"input": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"name": {
|
||||
"type": "string",
|
||||
"description": "the repository, <module>/<artifact>; every repository of the seat when not given"
|
||||
}
|
||||
}
|
||||
},
|
||||
"answer": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"repositories": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"$ref": "#/defs/Repository"
|
||||
},
|
||||
"description": "each repository named, in order of its name"
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"repositories"
|
||||
]
|
||||
},
|
||||
"optional": true
|
||||
}
|
||||
],
|
||||
"events": [
|
||||
{
|
||||
"name": "held",
|
||||
"body": {
|
||||
"$ref": "#/defs/Hold"
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "released",
|
||||
"body": {
|
||||
"$ref": "#/defs/Hold"
|
||||
}
|
||||
}
|
||||
],
|
||||
"family": {
|
||||
"name": "container-image-registry.events",
|
||||
"max-age-days": 7
|
||||
},
|
||||
"defs": {
|
||||
"Hold": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"name": {
|
||||
"type": "string",
|
||||
"description": "the name"
|
||||
},
|
||||
"digest": {
|
||||
"type": "string",
|
||||
"description": "the digest"
|
||||
},
|
||||
"reason": {
|
||||
"type": "string",
|
||||
"description": "why"
|
||||
},
|
||||
"holder": {
|
||||
"type": "string",
|
||||
"description": "the module holding the seat"
|
||||
},
|
||||
"by": {
|
||||
"type": "string",
|
||||
"description": "the caller the holder proved"
|
||||
},
|
||||
"at": {
|
||||
"type": "string",
|
||||
"description": "when, RFC 3339"
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"name",
|
||||
"digest",
|
||||
"holder"
|
||||
],
|
||||
"description": "the body of held and released"
|
||||
},
|
||||
"Repository": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"name": {
|
||||
"type": "string",
|
||||
"description": "the repository"
|
||||
},
|
||||
"manifests": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"$ref": "#/defs/Listed"
|
||||
},
|
||||
"description": "each manifest it holds, by digest"
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"name",
|
||||
"manifests"
|
||||
]
|
||||
},
|
||||
"Listed": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"digest": {
|
||||
"type": "string",
|
||||
"description": "the manifest's digest, sha256:<hex>"
|
||||
},
|
||||
"index": {
|
||||
"type": "boolean",
|
||||
"description": "it is an index, naming a manifest per platform"
|
||||
},
|
||||
"platforms": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"type": "string"
|
||||
},
|
||||
"description": "for an index, the digests of the manifests it names"
|
||||
},
|
||||
"artifact": {
|
||||
"type": "string",
|
||||
"description": "for a manifest keeping one bundle or archive, the digest of that blob: what a reference names it by"
|
||||
},
|
||||
"tags": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"type": "string"
|
||||
},
|
||||
"description": "the tags naming it, none for an untagged one"
|
||||
},
|
||||
"size": {
|
||||
"type": "integer",
|
||||
"description": "its bytes: its own content and every blob it names"
|
||||
},
|
||||
"held": {
|
||||
"type": "boolean",
|
||||
"description": "a hold keeps it from retention"
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"digest",
|
||||
"index",
|
||||
"size",
|
||||
"held"
|
||||
]
|
||||
}
|
||||
},
|
||||
"states": [
|
||||
{
|
||||
"name": "holds",
|
||||
"template": "state",
|
||||
"history": 1,
|
||||
"description": "the bundles, archives and copied bases kept from retention's removal",
|
||||
"key-forms": [
|
||||
{
|
||||
"key": "<seat>.<name>.<digest>",
|
||||
"value": {
|
||||
"type": "object",
|
||||
"description": "one hold: an untagged bundle, archive or copied base kept from retention's removal",
|
||||
"properties": {
|
||||
"seat": {
|
||||
"type": "string"
|
||||
},
|
||||
"name": {
|
||||
"type": "string"
|
||||
},
|
||||
"kind": {
|
||||
"type": "string"
|
||||
},
|
||||
"digest": {
|
||||
"type": "string"
|
||||
},
|
||||
"by": {
|
||||
"type": "string"
|
||||
},
|
||||
"why": {
|
||||
"type": "string"
|
||||
},
|
||||
"since": {
|
||||
"type": "string",
|
||||
"description": "when (RFC 3339 time)"
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"seat",
|
||||
"name",
|
||||
"kind",
|
||||
"digest",
|
||||
"by",
|
||||
"since"
|
||||
]
|
||||
},
|
||||
"description": "one hold"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "windows",
|
||||
"template": "state",
|
||||
"history": 1,
|
||||
"description": "the direct-write windows, one per caller",
|
||||
"key-forms": [
|
||||
{
|
||||
"key": "<caller>",
|
||||
"value": {
|
||||
"type": "object",
|
||||
"description": "the direct-write window of one caller: open while a node holds it open",
|
||||
"properties": {
|
||||
"name": {
|
||||
"type": "string",
|
||||
"description": "the caller whose window it is"
|
||||
},
|
||||
"open": {
|
||||
"type": "boolean"
|
||||
},
|
||||
"holding": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"type": "string"
|
||||
},
|
||||
"description": "the nodes holding it open"
|
||||
},
|
||||
"since": {
|
||||
"type": "object",
|
||||
"additionalProperties": {
|
||||
"type": "string",
|
||||
"description": "when (RFC 3339 time)"
|
||||
},
|
||||
"description": "node to when it began to hold"
|
||||
},
|
||||
"closed": {
|
||||
"type": "string",
|
||||
"description": "when it last closed (RFC 3339 time)"
|
||||
},
|
||||
"last_holding": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"dropped": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"node": {
|
||||
"type": "string"
|
||||
},
|
||||
"by": {
|
||||
"type": "string"
|
||||
},
|
||||
"why": {
|
||||
"type": "string"
|
||||
},
|
||||
"at": {
|
||||
"type": "string",
|
||||
"description": "when (RFC 3339 time)"
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"node"
|
||||
]
|
||||
},
|
||||
"description": "retired nodes dropped by hand"
|
||||
},
|
||||
"why": {
|
||||
"type": "object",
|
||||
"additionalProperties": {
|
||||
"type": "string"
|
||||
},
|
||||
"description": "of each node holding the window, why it holds it"
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"name"
|
||||
]
|
||||
},
|
||||
"description": "one caller's window"
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -0,0 +1,8 @@
|
||||
{
|
||||
"format": 1,
|
||||
"seat": "git",
|
||||
"scope": "mesh",
|
||||
"grain": "seat-list",
|
||||
"decision": "novox/hq ADR 0111",
|
||||
"delivers": "git"
|
||||
}
|
||||
@@ -0,0 +1,73 @@
|
||||
[
|
||||
{
|
||||
"name": "published on the seat's subject",
|
||||
"of": "event:published",
|
||||
"subject": "mesh.seat.go-package-registry.event.published",
|
||||
"given": {
|
||||
"name": "git.novox.be/novox/mesh-sdk/go",
|
||||
"module": "git.novox.be/novox/mesh-sdk/go",
|
||||
"version": "0.1.19",
|
||||
"kind": "package",
|
||||
"digest": "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa",
|
||||
"fingerprint": "f1",
|
||||
"commit": "cccccccccccccccccccccccccccccccccccccccc",
|
||||
"holder": "gitea",
|
||||
"transfer": "t1",
|
||||
"reference": "git.novox.be/novox/mesh-sdk/go@0.1.19"
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "refused on the seat's subject",
|
||||
"of": "event:refused",
|
||||
"subject": "mesh.seat.go-package-registry.event.refused",
|
||||
"given": {
|
||||
"name": "git.novox.be/novox/mesh-sdk/go",
|
||||
"version": "0.1.19",
|
||||
"kind": "package",
|
||||
"digest": "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa",
|
||||
"fingerprint": "f1",
|
||||
"commit": "cccccccccccccccccccccccccccccccccccccccc",
|
||||
"holder": "gitea",
|
||||
"transfer": "t1",
|
||||
"reference": "git.novox.be/novox/mesh-sdk/go@0.1.19",
|
||||
"reason": "retention"
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "removed on the seat's subject",
|
||||
"of": "event:removed",
|
||||
"subject": "mesh.seat.go-package-registry.event.removed",
|
||||
"given": {
|
||||
"name": "git.novox.be/novox/mesh-sdk/go",
|
||||
"version": "0.1.19",
|
||||
"kind": "package",
|
||||
"digest": "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa",
|
||||
"fingerprint": "f1",
|
||||
"commit": "cccccccccccccccccccccccccccccccccccccccc",
|
||||
"holder": "gitea",
|
||||
"transfer": "t1",
|
||||
"reference": "git.novox.be/novox/mesh-sdk/go@0.1.19",
|
||||
"reason": "retention"
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "kept on the seat's subject: a tagged version sharing its digest stays, naming the tags on it",
|
||||
"of": "event:kept",
|
||||
"subject": "mesh.seat.go-package-registry.event.kept",
|
||||
"given": {
|
||||
"name": "git.novox.be/novox/mesh-sdk/go",
|
||||
"version": "0.1.19-feat-x.2",
|
||||
"kind": "package",
|
||||
"digest": "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa",
|
||||
"fingerprint": "f1",
|
||||
"commit": "cccccccccccccccccccccccccccccccccccccccc",
|
||||
"holder": "gitea",
|
||||
"reference": "git.novox.be/novox/mesh-sdk/go@0.1.19",
|
||||
"reason": "shared-digest",
|
||||
"tag": "0.1.19-feat-x.2",
|
||||
"tags": [
|
||||
"0.1.19"
|
||||
]
|
||||
}
|
||||
}
|
||||
]
|
||||
@@ -0,0 +1,110 @@
|
||||
[
|
||||
{
|
||||
"name": "publish opens a transfer",
|
||||
"of": "verb:publish.input",
|
||||
"given": {
|
||||
"name": "git.novox.be/novox/mesh-sdk/go",
|
||||
"version": "0.1.19",
|
||||
"kind": "package",
|
||||
"digest": "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa",
|
||||
"fingerprint": "f1",
|
||||
"commit": "cccccccccccccccccccccccccccccccccccccccc",
|
||||
"transfer": "t1",
|
||||
"parts": [
|
||||
{
|
||||
"digest": "sha256:bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb",
|
||||
"size": 1048576
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "a field the reader does not know is dropped",
|
||||
"of": "verb:publish.input",
|
||||
"why": "ADR 0608 §6: a reader ignores a field it does not know",
|
||||
"given": {
|
||||
"name": "git.novox.be/novox/mesh-sdk/go",
|
||||
"version": "0.1.19",
|
||||
"kind": "package",
|
||||
"digest": "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa",
|
||||
"fingerprint": "f1",
|
||||
"commit": "cccccccccccccccccccccccccccccccccccccccc",
|
||||
"transfer": "t1",
|
||||
"parts": [
|
||||
{
|
||||
"digest": "sha256:bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb",
|
||||
"size": 1048576
|
||||
}
|
||||
],
|
||||
"later": {
|
||||
"x": 1
|
||||
}
|
||||
},
|
||||
"wire": {
|
||||
"name": "git.novox.be/novox/mesh-sdk/go",
|
||||
"version": "0.1.19",
|
||||
"kind": "package",
|
||||
"digest": "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa",
|
||||
"fingerprint": "f1",
|
||||
"commit": "cccccccccccccccccccccccccccccccccccccccc",
|
||||
"transfer": "t1",
|
||||
"parts": [
|
||||
{
|
||||
"digest": "sha256:bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb",
|
||||
"size": 1048576
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "publish answers what it lacks",
|
||||
"of": "verb:publish.answer",
|
||||
"given": {
|
||||
"transfer": "t1",
|
||||
"lacks": [
|
||||
"sha256:bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb"
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "a chunk of a part",
|
||||
"of": "verb:publish-part.input",
|
||||
"given": {
|
||||
"transfer": "t1",
|
||||
"part": "sha256:bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb",
|
||||
"sequence": 0,
|
||||
"last": true,
|
||||
"data": "aGVsbG8="
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "the versions held",
|
||||
"of": "verb:versions.answer",
|
||||
"given": {
|
||||
"name": "git.novox.be/novox/mesh-sdk/go",
|
||||
"versions": [
|
||||
{
|
||||
"name": "git.novox.be/novox/mesh-sdk/go",
|
||||
"version": "0.1.19",
|
||||
"kind": "package",
|
||||
"digest": "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa",
|
||||
"fingerprint": "f1",
|
||||
"commit": "cccccccccccccccccccccccccccccccccccccccc"
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "a tagged version sharing its digest with another tag is kept, not removed",
|
||||
"of": "verb:remove.answer",
|
||||
"why": "G-sdk-contracts 3: the store cannot remove the tag alone, so the answer says what holds",
|
||||
"given": {
|
||||
"removed": false,
|
||||
"kept": true,
|
||||
"reason": "shared-digest",
|
||||
"tags": [
|
||||
"0.1.19"
|
||||
]
|
||||
}
|
||||
}
|
||||
]
|
||||
@@ -0,0 +1,34 @@
|
||||
{
|
||||
"format": 1,
|
||||
"seat": "go-package-registry",
|
||||
"scope": "mesh",
|
||||
"grain": "whole",
|
||||
"decision": "novox/hq ADR 0605",
|
||||
"description": "The mesh's Go modules: Go's module proxy protocol's read side, and publishing over the bus.",
|
||||
"delivers": "go-package-registry",
|
||||
"includes": [
|
||||
"registry"
|
||||
],
|
||||
"family": {
|
||||
"name": "go-package-registry.events",
|
||||
"max-age-days": 7
|
||||
},
|
||||
"provisions": [
|
||||
{
|
||||
"name": "go-package-registry",
|
||||
"form": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"url": {
|
||||
"type": "string",
|
||||
"description": "the read side's address"
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"url"
|
||||
],
|
||||
"description": "form none: the Go module proxy's read side, answered anonymously, with no credential (design 51)"
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -0,0 +1,7 @@
|
||||
{
|
||||
"format": 1,
|
||||
"seat": "mesh-artifact-store",
|
||||
"scope": "mesh",
|
||||
"grain": "seat-list",
|
||||
"decision": "novox/hq ADR 0075"
|
||||
}
|
||||
@@ -0,0 +1,103 @@
|
||||
{
|
||||
"format": 1,
|
||||
"seat": "mesh-artifact-sweep",
|
||||
"scope": "mesh",
|
||||
"grain": "seat-list",
|
||||
"decision": "novox/hq ADR 0571",
|
||||
"verbs": [
|
||||
{
|
||||
"name": "artifacts",
|
||||
"description": "Every artifact the mesh made that lives in its artifact store, each with its repository, digest, kind (image or archive) and state: kept, and why (a want names it, or one of the five newest builds of its module in the builder's log), eligible (made, kept for no reason, not yet let go), or unrecorded (in the store and named by nothing the mesh made: never removed). Reads only.",
|
||||
"input": {
|
||||
"properties": {
|
||||
"collected": {
|
||||
"description": "\"true\": list the collected references too (counted always)",
|
||||
"enum": [
|
||||
"true",
|
||||
"false"
|
||||
],
|
||||
"type": "string"
|
||||
},
|
||||
"repository": {
|
||||
"description": "only this repository's references, as \u003cmodule\u003e/\u003cartifact\u003e (the counts stay whole)",
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"type": "object"
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "held",
|
||||
"description": "Whether the artifact store holds each reference given, asked with one HEAD each: held, gone (the store answered 404) or unknown with why. The builder asks it before it reuses a done key. Reads only.",
|
||||
"input": {
|
||||
"properties": {
|
||||
"references": {
|
||||
"description": "the artifacts' references, artifact-store://\u003cmodule\u003e/\u003cartifact\u003e/blobs/sha256:\u003chex\u003e: a list, or one text separated by commas",
|
||||
"items": {
|
||||
"type": "string"
|
||||
},
|
||||
"type": "array"
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"references"
|
||||
],
|
||||
"type": "object"
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "collect",
|
||||
"description": "The artifact store's sweep, now: what is eligible is let go of. Without confirm a dry run: what would be let go, nothing held or deleted. With confirm and why: every kept archive held first, then each eligible artifact let go of, oldest first, an index with the platform manifests no kept index names, each deletion recorded in the hand-act log. Bounded by most (500 by default, at most 5000) and 45 seconds; what is left is said. Never touches a digest the mesh did not make.",
|
||||
"input": {
|
||||
"properties": {
|
||||
"confirm": {
|
||||
"description": "\"true\": let go of what is eligible (needs why); without it nothing is done",
|
||||
"enum": [
|
||||
"true",
|
||||
"false"
|
||||
],
|
||||
"type": "string"
|
||||
},
|
||||
"most": {
|
||||
"description": "let go of at most this many (500 by default, at most 5000)",
|
||||
"type": "string"
|
||||
},
|
||||
"why": {
|
||||
"description": "with confirm: why, required and recorded",
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"type": "object"
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "mirrors",
|
||||
"description": "The bases the mesh copied into its artifact store, each kept (a kept build stood on it), eligible (only builds no longer kept stood on it) or unrecorded (no build the builder's log holds stood on it: never removed), by what the builder's log says each build stood on. Records nothing by hand.",
|
||||
"input": {
|
||||
"properties": {},
|
||||
"type": "object"
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "images",
|
||||
"description": "Every container image one machine's want and have name, each with the resources naming it: what the machine keeps when it prunes its images. Reads only.",
|
||||
"input": {
|
||||
"properties": {
|
||||
"node": {
|
||||
"description": "the machine's name",
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"node"
|
||||
],
|
||||
"type": "object"
|
||||
}
|
||||
}
|
||||
],
|
||||
"events": [
|
||||
{
|
||||
"name": "artifacts-gone"
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -0,0 +1,105 @@
|
||||
[
|
||||
{
|
||||
"name": "a manifest",
|
||||
"of": "state:manifests/<digest>",
|
||||
"given": {
|
||||
"v": 1,
|
||||
"module": "nats",
|
||||
"manifest": "{\"module\":\"nats\"}"
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "a module",
|
||||
"of": "state:modules/<module>",
|
||||
"given": {
|
||||
"v": 1,
|
||||
"repository": "novox/mesh-catalog",
|
||||
"commit": "cccccccccccccccccccccccccccccccccccccccc",
|
||||
"fp2": "src2:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "a candidate",
|
||||
"of": "state:candidates/<group>",
|
||||
"given": {
|
||||
"v": 1,
|
||||
"group": "feat/registry",
|
||||
"base": 3,
|
||||
"heads": "0123456789ab",
|
||||
"pulls": [
|
||||
{
|
||||
"repository": "novox/mesh-sdk",
|
||||
"number": 1,
|
||||
"head": "cccccccccccccccccccccccccccccccccccccccc",
|
||||
"modules": [
|
||||
"nats"
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "a verdict",
|
||||
"of": "state:verdicts/<digest>",
|
||||
"given": {
|
||||
"v": 1,
|
||||
"module": "nats",
|
||||
"state": "accepted",
|
||||
"judge": "trunk"
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "the predefined list",
|
||||
"of": "state:predefined/list",
|
||||
"given": {
|
||||
"v": 1,
|
||||
"commit": "cccccccccccccccccccccccccccccccccccccccc",
|
||||
"list": {
|
||||
"streams": []
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "a pull request's configuration",
|
||||
"of": "state:config-candidates/<pull>",
|
||||
"given": {
|
||||
"v": 1,
|
||||
"pull": "novox/mesh-catalog#1",
|
||||
"head": "cccccccccccccccccccccccccccccccccccccccc",
|
||||
"declares": {
|
||||
"nats": {
|
||||
"limit": {
|
||||
"kind": "setting",
|
||||
"default": 1,
|
||||
"why": "a bound"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "a published Go module",
|
||||
"of": "state:packages/<module>",
|
||||
"given": {
|
||||
"v": 1,
|
||||
"module": "git.novox.be/novox/mesh-sdk/go",
|
||||
"repository": "novox/mesh-sdk",
|
||||
"commit": "cccccccccccccccccccccccccccccccccccccccc",
|
||||
"packages": [
|
||||
{
|
||||
"package": "git.novox.be/novox/mesh-sdk/go/keys",
|
||||
"digest": "ab"
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "a package series",
|
||||
"of": "state:packages/<package>.<series>",
|
||||
"given": {
|
||||
"v": 1,
|
||||
"name": "go-keys",
|
||||
"series": "0.1"
|
||||
}
|
||||
}
|
||||
]
|
||||
@@ -0,0 +1,858 @@
|
||||
{
|
||||
"format": 1,
|
||||
"seat": "mesh-catalog",
|
||||
"scope": "mesh",
|
||||
"grain": "whole",
|
||||
"decision": "novox/hq ADR 0604",
|
||||
"verbs": [
|
||||
{
|
||||
"name": "modules",
|
||||
"description": "Every module the trunk holds, each with its repository, its directory there, the merge commit it was last changed at and its fingerprint (or why it has none): the catalog of the mesh, read from its holder's state. Reads only.",
|
||||
"optional": true,
|
||||
"input": {
|
||||
"properties": {},
|
||||
"type": "object"
|
||||
},
|
||||
"answer": {
|
||||
"type": "object",
|
||||
"description": "what modules answers, as its description says"
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "module",
|
||||
"description": "One module whole: its repository, directory, commit and fingerprint, and its manifest as the trunk holds it. Older versions are in its repository's history. Reads only.",
|
||||
"optional": true,
|
||||
"input": {
|
||||
"properties": {
|
||||
"module": {
|
||||
"description": "the module's name",
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"module"
|
||||
],
|
||||
"type": "object"
|
||||
},
|
||||
"answer": {
|
||||
"type": "object",
|
||||
"description": "what module answers, as its description says"
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "provides",
|
||||
"description": "Which modules provide a provision (a module provides its own name too) and which require it, from the module graph's provision edges. Reads only.",
|
||||
"optional": true,
|
||||
"input": {
|
||||
"properties": {
|
||||
"provision": {
|
||||
"description": "the provision, such as postgres-database",
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"provision"
|
||||
],
|
||||
"type": "object"
|
||||
},
|
||||
"answer": {
|
||||
"type": "object",
|
||||
"description": "what provides answers, as its description says"
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "dependents",
|
||||
"description": "The modules whose build edges reach a module, directly or through others (a base, a mesh package it publishes, the build module that builds them): what a change of it builds again, from the module graph. Reads only.",
|
||||
"optional": true,
|
||||
"input": {
|
||||
"properties": {
|
||||
"module": {
|
||||
"description": "the module's name",
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"module"
|
||||
],
|
||||
"type": "object"
|
||||
},
|
||||
"answer": {
|
||||
"type": "object",
|
||||
"description": "what dependents answers, as its description says"
|
||||
}
|
||||
}
|
||||
],
|
||||
"description": "The catalog of the mesh: every module the trunk holds with its manifest, fingerprint and verdict, the packages published, and the seat list (ADR 0636 §1, §4).",
|
||||
"family": {
|
||||
"name": "mesh-catalog.events",
|
||||
"max-age-days": 7
|
||||
},
|
||||
"events": [
|
||||
{
|
||||
"name": "seat-definition-changed",
|
||||
"body": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"seat": {
|
||||
"type": "string"
|
||||
},
|
||||
"source": {
|
||||
"type": "string",
|
||||
"description": "trunk"
|
||||
},
|
||||
"digest": {
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"seat",
|
||||
"source",
|
||||
"digest"
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "seat-definition-removed",
|
||||
"body": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"seat": {
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"seat"
|
||||
]
|
||||
}
|
||||
}
|
||||
],
|
||||
"states": [
|
||||
{
|
||||
"name": "manifests",
|
||||
"template": "state",
|
||||
"key-forms": [
|
||||
{
|
||||
"key": "<digest>",
|
||||
"value": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"v": {
|
||||
"type": "integer",
|
||||
"description": "the layout version"
|
||||
},
|
||||
"module": {
|
||||
"type": "string"
|
||||
},
|
||||
"manifest": {
|
||||
"type": "string",
|
||||
"description": "the manifest, UTF-8 JSON text"
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"module",
|
||||
"manifest"
|
||||
]
|
||||
},
|
||||
"description": "a manifest by its digest, written once"
|
||||
}
|
||||
],
|
||||
"description": "every manifest the indexer judged",
|
||||
"history": 1
|
||||
},
|
||||
{
|
||||
"name": "modules",
|
||||
"template": "state",
|
||||
"key-forms": [
|
||||
{
|
||||
"key": "<module>",
|
||||
"value": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"v": {
|
||||
"type": "integer",
|
||||
"description": "the layout version"
|
||||
},
|
||||
"fp": {
|
||||
"type": "string",
|
||||
"description": "the old-form fingerprint"
|
||||
},
|
||||
"none": {
|
||||
"type": "string"
|
||||
},
|
||||
"fp2": {
|
||||
"type": "string",
|
||||
"description": "src2: fingerprint"
|
||||
},
|
||||
"none2": {
|
||||
"type": "string",
|
||||
"description": "why it has none"
|
||||
},
|
||||
"manifest": {
|
||||
"type": "string",
|
||||
"description": "manifest digest"
|
||||
},
|
||||
"inputs2": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"tree": {
|
||||
"type": "string"
|
||||
},
|
||||
"bases": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"builder": {
|
||||
"type": "string"
|
||||
},
|
||||
"contexts": {
|
||||
"type": "object",
|
||||
"additionalProperties": {
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"locks": {
|
||||
"type": "object",
|
||||
"additionalProperties": {
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"paths": {
|
||||
"type": "object",
|
||||
"additionalProperties": {
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"packages": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"imports": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"import-form": {
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"tree"
|
||||
]
|
||||
},
|
||||
"src": {
|
||||
"type": "integer",
|
||||
"description": "the source revision"
|
||||
},
|
||||
"fp-src": {
|
||||
"type": "integer"
|
||||
},
|
||||
"repository": {
|
||||
"type": "string"
|
||||
},
|
||||
"commit": {
|
||||
"type": "string"
|
||||
},
|
||||
"path": {
|
||||
"type": "string"
|
||||
},
|
||||
"toolchains": {
|
||||
"type": "object",
|
||||
"additionalProperties": {
|
||||
"description": "toolchain used"
|
||||
}
|
||||
},
|
||||
"paths": {
|
||||
"type": "object",
|
||||
"additionalProperties": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
},
|
||||
"packages": {
|
||||
"type": "object",
|
||||
"additionalProperties": {
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"verdict": {
|
||||
"type": "string"
|
||||
},
|
||||
"problems": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"unknown": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"warnings": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"needs": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"repository",
|
||||
"commit"
|
||||
],
|
||||
"description": "one module as the trunk holds it"
|
||||
},
|
||||
"description": "a module the trunk holds"
|
||||
}
|
||||
],
|
||||
"description": "the module catalog",
|
||||
"history": 20
|
||||
},
|
||||
{
|
||||
"name": "candidates",
|
||||
"template": "state",
|
||||
"key-forms": [
|
||||
{
|
||||
"key": "<group>",
|
||||
"value": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"v": {
|
||||
"type": "integer",
|
||||
"description": "the layout version"
|
||||
},
|
||||
"group": {
|
||||
"type": "string"
|
||||
},
|
||||
"base": {
|
||||
"type": "integer",
|
||||
"description": "the source revision it is read on"
|
||||
},
|
||||
"heads": {
|
||||
"type": "string"
|
||||
},
|
||||
"pulls": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"repository": {
|
||||
"type": "string"
|
||||
},
|
||||
"number": {
|
||||
"type": "integer"
|
||||
},
|
||||
"head": {
|
||||
"type": "string"
|
||||
},
|
||||
"modules": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"fps": {
|
||||
"type": "object",
|
||||
"additionalProperties": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"fp2": {
|
||||
"type": "string"
|
||||
},
|
||||
"none2": {
|
||||
"type": "string"
|
||||
},
|
||||
"manifest": {
|
||||
"type": "string"
|
||||
},
|
||||
"paths": {
|
||||
"type": "object",
|
||||
"additionalProperties": {
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"packages": {
|
||||
"type": "object",
|
||||
"additionalProperties": {
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"repository",
|
||||
"number",
|
||||
"head",
|
||||
"modules"
|
||||
]
|
||||
}
|
||||
},
|
||||
"fpset": {
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"group",
|
||||
"base",
|
||||
"heads",
|
||||
"pulls"
|
||||
],
|
||||
"description": "the modules a group of open pull requests would change"
|
||||
},
|
||||
"description": "a group's candidate"
|
||||
}
|
||||
],
|
||||
"description": "the candidates of open groups",
|
||||
"history": 10
|
||||
},
|
||||
{
|
||||
"name": "verdicts",
|
||||
"template": "state",
|
||||
"key-forms": [
|
||||
{
|
||||
"key": "<digest>",
|
||||
"value": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"v": {
|
||||
"type": "integer",
|
||||
"description": "the layout version"
|
||||
},
|
||||
"module": {
|
||||
"type": "string"
|
||||
},
|
||||
"state": {
|
||||
"type": "string"
|
||||
},
|
||||
"problems": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"unknown": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"warnings": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"judge": {
|
||||
"type": "string"
|
||||
},
|
||||
"at": {
|
||||
"type": "string",
|
||||
"description": "when (RFC 3339 time)"
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"module",
|
||||
"state"
|
||||
],
|
||||
"description": "a manifest's verdict, by the judge named"
|
||||
},
|
||||
"description": "a manifest's verdict by its digest"
|
||||
}
|
||||
],
|
||||
"description": "the manifest verdicts",
|
||||
"history": 5
|
||||
},
|
||||
{
|
||||
"name": "predefined",
|
||||
"template": "state",
|
||||
"key-forms": [
|
||||
{
|
||||
"key": "list",
|
||||
"value": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"v": {
|
||||
"type": "integer",
|
||||
"description": "the layout version"
|
||||
},
|
||||
"commit": {
|
||||
"type": "string"
|
||||
},
|
||||
"at": {
|
||||
"type": "string",
|
||||
"description": "when (RFC 3339 time)"
|
||||
},
|
||||
"list": {
|
||||
"description": "the foundation's predefined list as kept"
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"commit",
|
||||
"list"
|
||||
]
|
||||
},
|
||||
"description": "the one key"
|
||||
}
|
||||
],
|
||||
"description": "the kept predefined list",
|
||||
"history": 5
|
||||
},
|
||||
{
|
||||
"name": "config-candidates",
|
||||
"template": "state",
|
||||
"key-forms": [
|
||||
{
|
||||
"key": "<pull>",
|
||||
"value": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"v": {
|
||||
"type": "integer",
|
||||
"description": "the layout version"
|
||||
},
|
||||
"pull": {
|
||||
"type": "string"
|
||||
},
|
||||
"head": {
|
||||
"type": "string"
|
||||
},
|
||||
"declares": {
|
||||
"type": "object",
|
||||
"additionalProperties": {
|
||||
"type": "object",
|
||||
"additionalProperties": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"kind": {
|
||||
"type": "string"
|
||||
},
|
||||
"default": {
|
||||
"description": "the default"
|
||||
},
|
||||
"why": {
|
||||
"type": "string"
|
||||
},
|
||||
"type": {
|
||||
"type": "string"
|
||||
},
|
||||
"description": {
|
||||
"type": "string"
|
||||
},
|
||||
"enum": {
|
||||
"description": "the allowed values"
|
||||
},
|
||||
"minimum": {
|
||||
"type": "number"
|
||||
},
|
||||
"maximum": {
|
||||
"type": "number"
|
||||
},
|
||||
"pattern": {
|
||||
"type": "string"
|
||||
},
|
||||
"trusted": {
|
||||
"type": "boolean"
|
||||
},
|
||||
"predefined": {
|
||||
"type": "string"
|
||||
},
|
||||
"required": {
|
||||
"type": "boolean"
|
||||
},
|
||||
"runtime": {
|
||||
"type": "boolean"
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"kind",
|
||||
"default",
|
||||
"why"
|
||||
]
|
||||
}
|
||||
},
|
||||
"description": "module to key to declaration"
|
||||
},
|
||||
"at": {
|
||||
"type": "string",
|
||||
"description": "when (RFC 3339 time)"
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"pull",
|
||||
"head",
|
||||
"declares"
|
||||
]
|
||||
},
|
||||
"description": "the configuration a pull request declares"
|
||||
}
|
||||
],
|
||||
"description": "the configuration declared by open pull requests",
|
||||
"history": 5
|
||||
},
|
||||
{
|
||||
"name": "packages",
|
||||
"template": "state",
|
||||
"key-forms": [
|
||||
{
|
||||
"key": "<module>",
|
||||
"value": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"v": {
|
||||
"type": "integer",
|
||||
"description": "the layout version"
|
||||
},
|
||||
"module": {
|
||||
"type": "string",
|
||||
"description": "the Go module path"
|
||||
},
|
||||
"repository": {
|
||||
"type": "string"
|
||||
},
|
||||
"dir": {
|
||||
"type": "string"
|
||||
},
|
||||
"commit": {
|
||||
"type": "string"
|
||||
},
|
||||
"requires": {
|
||||
"type": "string",
|
||||
"description": "digest of go.mod and go.sum"
|
||||
},
|
||||
"packages": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"package": {
|
||||
"type": "string"
|
||||
},
|
||||
"digest": {
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"package",
|
||||
"digest"
|
||||
]
|
||||
}
|
||||
},
|
||||
"at": {
|
||||
"type": "string",
|
||||
"description": "when (RFC 3339 time)"
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"module",
|
||||
"repository",
|
||||
"commit",
|
||||
"packages"
|
||||
]
|
||||
},
|
||||
"description": "a mesh Go module as its latest trunk merge published it"
|
||||
},
|
||||
{
|
||||
"key": "<package>.<series>",
|
||||
"value": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"v": {
|
||||
"type": "integer",
|
||||
"description": "the layout version"
|
||||
},
|
||||
"name": {
|
||||
"type": "string"
|
||||
},
|
||||
"package": {
|
||||
"type": "string"
|
||||
},
|
||||
"series": {
|
||||
"type": "string"
|
||||
},
|
||||
"seat": {
|
||||
"type": "string"
|
||||
},
|
||||
"go-module": {
|
||||
"type": "string"
|
||||
},
|
||||
"newest": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"version": {
|
||||
"type": "string"
|
||||
},
|
||||
"commit": {
|
||||
"type": "string"
|
||||
},
|
||||
"digest": {
|
||||
"type": "string"
|
||||
},
|
||||
"fingerprint": {
|
||||
"type": "string"
|
||||
},
|
||||
"contents": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"v": {
|
||||
"type": "integer",
|
||||
"description": "the layout version"
|
||||
},
|
||||
"module": {
|
||||
"type": "string",
|
||||
"description": "the Go module path"
|
||||
},
|
||||
"repository": {
|
||||
"type": "string"
|
||||
},
|
||||
"dir": {
|
||||
"type": "string"
|
||||
},
|
||||
"commit": {
|
||||
"type": "string"
|
||||
},
|
||||
"requires": {
|
||||
"type": "string",
|
||||
"description": "digest of go.mod and go.sum"
|
||||
},
|
||||
"packages": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"package": {
|
||||
"type": "string"
|
||||
},
|
||||
"digest": {
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"package",
|
||||
"digest"
|
||||
]
|
||||
}
|
||||
},
|
||||
"at": {
|
||||
"type": "string",
|
||||
"description": "when (RFC 3339 time)"
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"module",
|
||||
"repository",
|
||||
"commit",
|
||||
"packages"
|
||||
]
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"version"
|
||||
]
|
||||
},
|
||||
"numbered": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"version": {
|
||||
"type": "string"
|
||||
},
|
||||
"commit": {
|
||||
"type": "string"
|
||||
},
|
||||
"digest": {
|
||||
"type": "string"
|
||||
},
|
||||
"fingerprint": {
|
||||
"type": "string"
|
||||
},
|
||||
"contents": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"v": {
|
||||
"type": "integer",
|
||||
"description": "the layout version"
|
||||
},
|
||||
"module": {
|
||||
"type": "string",
|
||||
"description": "the Go module path"
|
||||
},
|
||||
"repository": {
|
||||
"type": "string"
|
||||
},
|
||||
"dir": {
|
||||
"type": "string"
|
||||
},
|
||||
"commit": {
|
||||
"type": "string"
|
||||
},
|
||||
"requires": {
|
||||
"type": "string",
|
||||
"description": "digest of go.mod and go.sum"
|
||||
},
|
||||
"packages": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"package": {
|
||||
"type": "string"
|
||||
},
|
||||
"digest": {
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"package",
|
||||
"digest"
|
||||
]
|
||||
}
|
||||
},
|
||||
"at": {
|
||||
"type": "string",
|
||||
"description": "when (RFC 3339 time)"
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"module",
|
||||
"repository",
|
||||
"commit",
|
||||
"packages"
|
||||
]
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"version"
|
||||
]
|
||||
},
|
||||
"at": {
|
||||
"type": "string",
|
||||
"description": "when (RFC 3339 time)"
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"name",
|
||||
"series"
|
||||
]
|
||||
},
|
||||
"description": "a published package series, numbered and newest"
|
||||
}
|
||||
],
|
||||
"description": "the published packages",
|
||||
"history": 5
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -0,0 +1,44 @@
|
||||
[
|
||||
{
|
||||
"name": "verdict on the checker's own family",
|
||||
"of": "holder-event:verdict",
|
||||
"subject": "mesh.stream.mesh-delivery-checker.event.verdict",
|
||||
"given": {
|
||||
"repository": "novox/mesh-contracts",
|
||||
"pull": 3,
|
||||
"head": "cccccccccccccccccccccccccccccccccccccccc",
|
||||
"verdict": "fail",
|
||||
"out-of-step": [
|
||||
"go/seats/meshbroker/contract.go"
|
||||
],
|
||||
"series": [
|
||||
{
|
||||
"module": "mesh-contracts",
|
||||
"artifact": "go",
|
||||
"from": "0.1",
|
||||
"to": "0.2"
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "tag-wanted on the checker's own family",
|
||||
"of": "holder-event:tag-wanted",
|
||||
"subject": "mesh.stream.mesh-delivery-checker.event.tag-wanted",
|
||||
"given": {
|
||||
"repository": "novox/mesh-sdk",
|
||||
"pull": 170,
|
||||
"head": "cccccccccccccccccccccccccccccccccccccccc",
|
||||
"branch": "feat/registry",
|
||||
"module": "mesh-sdk",
|
||||
"package": "go",
|
||||
"tag": "registry-1966"
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "preview.written on the checker's stream",
|
||||
"of": "holder-event:preview.written",
|
||||
"subject": "mesh.stream.mesh-delivery-checker.event.preview.written",
|
||||
"given": {}
|
||||
}
|
||||
]
|
||||
@@ -0,0 +1,48 @@
|
||||
[
|
||||
{
|
||||
"name": "a running verdict",
|
||||
"of": "state:verdicts/<group>.<heads>",
|
||||
"given": {
|
||||
"v": 1,
|
||||
"group": "feat/registry",
|
||||
"heads": "0123456789ab",
|
||||
"state": "running"
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "a kept pass",
|
||||
"of": "state:kept/<level>.<digest>",
|
||||
"given": {
|
||||
"v": 1,
|
||||
"state": "pass",
|
||||
"repository": "novox/mesh-sdk",
|
||||
"package": "go/keys",
|
||||
"level": "unit",
|
||||
"tests": 4,
|
||||
"took": 120,
|
||||
"toolchain": "go1.26",
|
||||
"at": "2026-11-05T10:00:00Z"
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "the status owed for a head",
|
||||
"of": "state:owed/<repository>.<number>.<head>.<act>",
|
||||
"given": {
|
||||
"v": 1,
|
||||
"act": "status=mesh/check",
|
||||
"context": "mesh/check",
|
||||
"state": "success"
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "a preview",
|
||||
"of": "state:previews/<repository>.<head>.<trunk>",
|
||||
"given": {
|
||||
"v": 1,
|
||||
"repository": "novox/mesh-sdk",
|
||||
"head": "cccccccccccccccccccccccccccccccccccccccc",
|
||||
"trunk": "dddddddddddddddddddddddddddddddddddddddd",
|
||||
"merge": "eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee"
|
||||
}
|
||||
}
|
||||
]
|
||||
@@ -0,0 +1,635 @@
|
||||
{
|
||||
"format": 1,
|
||||
"seat": "mesh-delivery-checker",
|
||||
"scope": "mesh",
|
||||
"grain": "whole",
|
||||
"decision": "novox/hq ADR 0547",
|
||||
"verbs": [
|
||||
{
|
||||
"name": "try",
|
||||
"description": "Try a pushed head before its pull request is opened: the checker judges it as it judges a pull request's head, merged onto its trunk's head, its unit and integration levels through the build modules, and owes the forge nothing. Answers the try's id at once; try-read reads where it stands. A try of the same head that waits or runs is the answer to a second ask; at most 4 wait or run at once, mesh-wide.",
|
||||
"optional": true,
|
||||
"replaces": [
|
||||
"sh merge-check.sh",
|
||||
"docker run postgres"
|
||||
],
|
||||
"input": {
|
||||
"properties": {
|
||||
"head": {
|
||||
"description": "the full commit to try, 40 or 64 hex digits",
|
||||
"type": "string"
|
||||
},
|
||||
"level": {
|
||||
"description": "unit or integration: only this level; both when absent",
|
||||
"type": "string"
|
||||
},
|
||||
"repository": {
|
||||
"description": "<owner>/<name>",
|
||||
"type": "string"
|
||||
},
|
||||
"tests": {
|
||||
"description": "a go test -run pattern: only those tests run, and nothing is kept",
|
||||
"type": "string"
|
||||
},
|
||||
"trunk": {
|
||||
"description": "the trunk commit to merge it onto, or the trunk's branch for its head now; the trunk's head when absent",
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"repository",
|
||||
"head"
|
||||
],
|
||||
"type": "object"
|
||||
},
|
||||
"answer": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"id": {
|
||||
"type": "string",
|
||||
"description": "the try's id"
|
||||
},
|
||||
"state": {
|
||||
"type": "string",
|
||||
"description": "its state"
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"id"
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "try-read",
|
||||
"description": "Where a try stands, as stored: queued, running, or its end (pass, fail, or error when it could not run), with what was said of it, the head and the trunk commit it was merged onto, the time it took and what failed first. Read-only.",
|
||||
"optional": true,
|
||||
"input": {
|
||||
"properties": {
|
||||
"id": {
|
||||
"description": "the try's id, try-<digits>",
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"id"
|
||||
],
|
||||
"type": "object"
|
||||
},
|
||||
"answer": {
|
||||
"type": "object",
|
||||
"description": "the try: its state, its steps and its verdict when it has one"
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "try-end",
|
||||
"description": "End a try that waits or runs, as error, by hand: say why (kept in what it says). Its place among the tries is free at once and the next try of its head asks anew with a new id. A try that ended already is refused.",
|
||||
"optional": true,
|
||||
"input": {
|
||||
"properties": {
|
||||
"id": {
|
||||
"description": "the try's id, try-<digits>",
|
||||
"type": "string"
|
||||
},
|
||||
"why": {
|
||||
"description": "why it is ended, in words",
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"id",
|
||||
"why"
|
||||
],
|
||||
"type": "object"
|
||||
},
|
||||
"answer": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"id": {
|
||||
"type": "string",
|
||||
"description": "the try"
|
||||
},
|
||||
"ended": {
|
||||
"type": "boolean",
|
||||
"description": "it is ended"
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"id",
|
||||
"ended"
|
||||
]
|
||||
}
|
||||
}
|
||||
],
|
||||
"description": "The merge check's tries: start one, read one, end one. The checker's own events stay under its own subjects (ADR 0620 §2).",
|
||||
"holder-events": [
|
||||
{
|
||||
"name": "verdict",
|
||||
"subject": "mesh.stream.<holder>.event.verdict",
|
||||
"family": "checker.asks",
|
||||
"body": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"repository": {
|
||||
"type": "string",
|
||||
"description": "owner/repo"
|
||||
},
|
||||
"pull": {
|
||||
"type": "integer",
|
||||
"description": "the pull request's number, for a pull request's check"
|
||||
},
|
||||
"head": {
|
||||
"type": "string",
|
||||
"description": "the commit checked"
|
||||
},
|
||||
"verdict": {
|
||||
"type": "string",
|
||||
"description": "pass or fail"
|
||||
},
|
||||
"out-of-step": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"type": "string"
|
||||
},
|
||||
"description": "the generated files out of step with their seat definitions, each path (design 26)"
|
||||
},
|
||||
"series": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"module": {
|
||||
"type": "string",
|
||||
"description": "the module whose artifact's series changes"
|
||||
},
|
||||
"artifact": {
|
||||
"type": "string",
|
||||
"description": "its artifact, or the package's ecosystem name as the module's packages name it"
|
||||
},
|
||||
"from": {
|
||||
"type": "string",
|
||||
"description": "the series on trunk"
|
||||
},
|
||||
"to": {
|
||||
"type": "string",
|
||||
"description": "the series the checked head gives it"
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"module",
|
||||
"artifact",
|
||||
"from",
|
||||
"to"
|
||||
]
|
||||
},
|
||||
"description": "each change of an artifact's series the checked head makes against trunk's manifests, once each (design 32); absent when it changes none"
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"repository",
|
||||
"head",
|
||||
"verdict"
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "tag-wanted",
|
||||
"subject": "mesh.stream.<holder>.event.tag-wanted",
|
||||
"family": "checker.asks",
|
||||
"body": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"repository": {
|
||||
"type": "string",
|
||||
"description": "owner/repo"
|
||||
},
|
||||
"pull": {
|
||||
"type": "integer",
|
||||
"description": "the pull request"
|
||||
},
|
||||
"head": {
|
||||
"type": "string",
|
||||
"description": "its head"
|
||||
},
|
||||
"branch": {
|
||||
"type": "string",
|
||||
"description": "the pull request's branch: its tag is removed when the branch ends"
|
||||
},
|
||||
"module": {
|
||||
"type": "string",
|
||||
"description": "the group member"
|
||||
},
|
||||
"package": {
|
||||
"type": "string",
|
||||
"description": "the package it declares"
|
||||
},
|
||||
"tag": {
|
||||
"type": "string",
|
||||
"description": "the tag wanted"
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"repository",
|
||||
"head",
|
||||
"branch",
|
||||
"module",
|
||||
"package"
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "preview.written",
|
||||
"subject": "mesh.stream.<holder>.event.preview.written",
|
||||
"family": "checks"
|
||||
}
|
||||
],
|
||||
"states": [
|
||||
{
|
||||
"name": "verdicts",
|
||||
"template": "state",
|
||||
"key-forms": [
|
||||
{
|
||||
"key": "<group>.<heads>",
|
||||
"value": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"v": {
|
||||
"type": "integer",
|
||||
"description": "the layout version"
|
||||
},
|
||||
"group": {
|
||||
"type": "string"
|
||||
},
|
||||
"heads": {
|
||||
"type": "string",
|
||||
"description": "12 hex digits"
|
||||
},
|
||||
"trunk": {
|
||||
"type": "object",
|
||||
"additionalProperties": {
|
||||
"type": "string"
|
||||
},
|
||||
"description": "repository to the trunk head merged onto"
|
||||
},
|
||||
"state": {
|
||||
"type": "string",
|
||||
"description": "running, pass or fail"
|
||||
},
|
||||
"failed": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"test": {
|
||||
"type": "string"
|
||||
},
|
||||
"machine": {
|
||||
"type": "string"
|
||||
},
|
||||
"words": {
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
},
|
||||
"failures": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"test": {
|
||||
"type": "string"
|
||||
},
|
||||
"machine": {
|
||||
"type": "string"
|
||||
},
|
||||
"words": {
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"levels": {
|
||||
"type": "object",
|
||||
"additionalProperties": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"run": {
|
||||
"type": "integer"
|
||||
},
|
||||
"kept": {
|
||||
"type": "integer"
|
||||
},
|
||||
"skipped": {
|
||||
"type": "integer"
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"run",
|
||||
"kept"
|
||||
]
|
||||
},
|
||||
"description": "by level, unit or integration"
|
||||
},
|
||||
"took": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"queue": {
|
||||
"type": "integer"
|
||||
},
|
||||
"clone": {
|
||||
"type": "integer"
|
||||
},
|
||||
"unit": {
|
||||
"type": "integer"
|
||||
},
|
||||
"lint": {
|
||||
"type": "integer"
|
||||
},
|
||||
"integration-setup": {
|
||||
"type": "integer"
|
||||
},
|
||||
"integration-run": {
|
||||
"type": "integer"
|
||||
},
|
||||
"composition": {
|
||||
"type": "integer"
|
||||
}
|
||||
},
|
||||
"description": "milliseconds"
|
||||
},
|
||||
"machines": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"sdk": {
|
||||
"type": "string"
|
||||
},
|
||||
"composition": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"state": {
|
||||
"type": "string",
|
||||
"description": "not-run, pass or fail"
|
||||
},
|
||||
"words": {
|
||||
"type": "string"
|
||||
},
|
||||
"sdk": {
|
||||
"type": "string"
|
||||
},
|
||||
"machines": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"fpset": {
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"state"
|
||||
]
|
||||
},
|
||||
"over-bound": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"beside": {
|
||||
"type": "object",
|
||||
"additionalProperties": {
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"carried-from": {
|
||||
"type": "object",
|
||||
"additionalProperties": {
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"series": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"module": {
|
||||
"type": "string"
|
||||
},
|
||||
"artifact": {
|
||||
"type": "string"
|
||||
},
|
||||
"from": {
|
||||
"type": "string"
|
||||
},
|
||||
"to": {
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"module",
|
||||
"artifact",
|
||||
"from",
|
||||
"to"
|
||||
]
|
||||
}
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"group",
|
||||
"heads",
|
||||
"state"
|
||||
],
|
||||
"description": "one check of a group at one set of heads"
|
||||
},
|
||||
"description": "one check of a group; the group is an escaped branch name, heads 12 hex digits"
|
||||
}
|
||||
],
|
||||
"description": "the checker's verdict of each group at each set of heads",
|
||||
"history": 5
|
||||
},
|
||||
{
|
||||
"name": "kept",
|
||||
"template": "state",
|
||||
"key-forms": [
|
||||
{
|
||||
"key": "<level>.<digest>",
|
||||
"value": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"v": {
|
||||
"type": "integer",
|
||||
"description": "the layout version"
|
||||
},
|
||||
"state": {
|
||||
"type": "string",
|
||||
"description": "pass: a failure is never kept"
|
||||
},
|
||||
"repository": {
|
||||
"type": "string"
|
||||
},
|
||||
"package": {
|
||||
"type": "string"
|
||||
},
|
||||
"level": {
|
||||
"type": "string"
|
||||
},
|
||||
"tests": {
|
||||
"type": "integer"
|
||||
},
|
||||
"took": {
|
||||
"type": "integer"
|
||||
},
|
||||
"toolchain": {
|
||||
"type": "string"
|
||||
},
|
||||
"at": {
|
||||
"type": "string",
|
||||
"description": "when (RFC 3339 time)"
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"state",
|
||||
"repository",
|
||||
"package",
|
||||
"level"
|
||||
],
|
||||
"description": "one passing level of one package, kept so the next check does not run it again"
|
||||
},
|
||||
"description": "a passing level of one package by its content digest"
|
||||
}
|
||||
],
|
||||
"description": "the kept passing levels",
|
||||
"history": 1
|
||||
},
|
||||
{
|
||||
"name": "owed",
|
||||
"template": "state",
|
||||
"key-forms": [
|
||||
{
|
||||
"key": "<repository>.<number>.<head>.<act>",
|
||||
"value": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"v": {
|
||||
"type": "integer",
|
||||
"description": "the layout version"
|
||||
},
|
||||
"act": {
|
||||
"type": "string",
|
||||
"description": "status=<context>, view, note or comment.<seq>"
|
||||
},
|
||||
"context": {
|
||||
"type": "string"
|
||||
},
|
||||
"target": {
|
||||
"type": "string"
|
||||
},
|
||||
"state": {
|
||||
"type": "string"
|
||||
},
|
||||
"text": {
|
||||
"type": "string"
|
||||
},
|
||||
"ref": {
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"act"
|
||||
],
|
||||
"description": "one act owed to the forge, written by its owner and done by the forge bridge"
|
||||
},
|
||||
"description": "the status mesh/check the checker owes the forge for a head (its act is status=mesh/check)"
|
||||
}
|
||||
],
|
||||
"description": "the status the checker owes the forge",
|
||||
"history": 5
|
||||
},
|
||||
{
|
||||
"name": "previews",
|
||||
"template": "state",
|
||||
"key-forms": [
|
||||
{
|
||||
"key": "<repository>.<head>.<trunk>",
|
||||
"value": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"v": {
|
||||
"type": "integer",
|
||||
"description": "the layout version"
|
||||
},
|
||||
"repository": {
|
||||
"type": "string"
|
||||
},
|
||||
"head": {
|
||||
"type": "string"
|
||||
},
|
||||
"trunk": {
|
||||
"type": "string"
|
||||
},
|
||||
"merge": {
|
||||
"type": "string",
|
||||
"description": "the merge commit"
|
||||
},
|
||||
"conflicts": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"type": "string"
|
||||
},
|
||||
"description": "files in conflict"
|
||||
},
|
||||
"modules": {
|
||||
"type": "object",
|
||||
"additionalProperties": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"tree": {
|
||||
"type": "string"
|
||||
},
|
||||
"manifest": {
|
||||
"type": "string",
|
||||
"description": "manifest digest"
|
||||
},
|
||||
"locks": {
|
||||
"type": "object",
|
||||
"additionalProperties": {
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"paths": {
|
||||
"type": "object",
|
||||
"additionalProperties": {
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"packages": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"tree",
|
||||
"manifest"
|
||||
]
|
||||
},
|
||||
"description": "by directory"
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"repository",
|
||||
"head",
|
||||
"trunk"
|
||||
],
|
||||
"description": "the merge of a head onto a trunk commit, read before a pull request is checked"
|
||||
},
|
||||
"description": "the merge of a head onto a trunk commit"
|
||||
}
|
||||
],
|
||||
"description": "the previews of a head merged onto a trunk",
|
||||
"history": 5
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -0,0 +1,72 @@
|
||||
[
|
||||
{
|
||||
"name": "the source revision",
|
||||
"of": "state:source/main",
|
||||
"given": {
|
||||
"v": 1,
|
||||
"group": "feat/registry",
|
||||
"heads": {
|
||||
"novox/mesh-sdk": "cccccccccccccccccccccccccccccccccccccccc"
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "a merge",
|
||||
"of": "state:merges/<group>",
|
||||
"given": {
|
||||
"v": 1,
|
||||
"group": "feat/registry",
|
||||
"ask": "merge.feat=registry.0123456789ab",
|
||||
"pulls": [
|
||||
{
|
||||
"repository": "novox/mesh-sdk",
|
||||
"number": 1,
|
||||
"head": "cccccccccccccccccccccccccccccccccccccccc",
|
||||
"state": "merging"
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "an ask",
|
||||
"of": "state:asks/merge.<group>.<heads>",
|
||||
"given": {
|
||||
"v": 1,
|
||||
"group": "feat/registry",
|
||||
"heads": [
|
||||
{
|
||||
"repository": "novox/mesh-sdk",
|
||||
"number": 1,
|
||||
"head": "cccccccccccccccccccccccccccccccccccccccc"
|
||||
}
|
||||
],
|
||||
"ask": {
|
||||
"headline": "merge"
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "an act owed",
|
||||
"of": "state:owed/<repository>.<number>.<head>.<act>",
|
||||
"given": {
|
||||
"v": 1,
|
||||
"act": "view"
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "a turn",
|
||||
"of": "state:turns/<repository>",
|
||||
"given": {
|
||||
"v": 1,
|
||||
"repository": "novox/mesh-sdk",
|
||||
"waiting": [
|
||||
{
|
||||
"group": "feat/registry",
|
||||
"heads": "0123456789ab",
|
||||
"approved": "2026-11-05T10:00:00Z",
|
||||
"since": "2026-11-05T10:01:00Z"
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
]
|
||||
@@ -0,0 +1,570 @@
|
||||
{
|
||||
"format": 1,
|
||||
"seat": "mesh-delivery",
|
||||
"scope": "mesh",
|
||||
"grain": "whole",
|
||||
"decision": "novox/hq ADR 0239",
|
||||
"verbs": [
|
||||
{
|
||||
"name": "deliveries",
|
||||
"description": "Every delivery not final, and those that ended in the last day, one line each: its id (owner/repository@commit), its state, its group, what it waits for and since when. Narrowed by state, repository or group.",
|
||||
"input": {
|
||||
"properties": {
|
||||
"all": {
|
||||
"description": "\"true\": the final ones of the last thirty days too",
|
||||
"enum": [
|
||||
"true",
|
||||
"false"
|
||||
],
|
||||
"type": "string"
|
||||
},
|
||||
"group": {
|
||||
"description": "a group's id (its branch name)",
|
||||
"type": "string"
|
||||
},
|
||||
"repository": {
|
||||
"description": "owner/repository",
|
||||
"type": "string"
|
||||
},
|
||||
"state": {
|
||||
"description": "one state, e.g. delivering or held",
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"type": "object"
|
||||
},
|
||||
"answer": {
|
||||
"type": "object",
|
||||
"description": "what deliveries answers, as its description says"
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "times",
|
||||
"description": "Removed in R6 (ADR 0539): refused by name, naming its replacement: the delivery view (nox-delivery): each machine's want and have, and since when",
|
||||
"optional": true,
|
||||
"input": {
|
||||
"properties": {},
|
||||
"type": "object"
|
||||
},
|
||||
"answer": {
|
||||
"type": "object",
|
||||
"description": "what times answers, as its description says"
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "show",
|
||||
"description": "One delivery or group whole: its delivery plan (what it builds, what each machine receives, what is not an ordinary send), every transition with when and why, the machine steps of its walk, its group and its order.",
|
||||
"input": {
|
||||
"properties": {
|
||||
"id": {
|
||||
"description": "a delivery's id, or a group's",
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"id"
|
||||
],
|
||||
"type": "object"
|
||||
},
|
||||
"answer": {
|
||||
"type": "object",
|
||||
"description": "what show answers, as its description says"
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "groups",
|
||||
"description": "Every delivery group: its members in order, why each pair is ordered (declared, built by, version skew, engine before controller, by name), its composed check and its state, derived from its members.",
|
||||
"input": {
|
||||
"properties": {
|
||||
"all": {
|
||||
"description": "\"true\": the groups that ended too",
|
||||
"enum": [
|
||||
"true",
|
||||
"false"
|
||||
],
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"type": "object"
|
||||
},
|
||||
"answer": {
|
||||
"type": "object",
|
||||
"description": "what groups answers, as its description says"
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "what-if",
|
||||
"description": "Removed in R6 (ADR 0539): refused by name, naming its replacement: the merge check's composition, which composes every machine a change touches",
|
||||
"optional": true,
|
||||
"input": {
|
||||
"properties": {},
|
||||
"type": "object"
|
||||
},
|
||||
"answer": {
|
||||
"type": "object",
|
||||
"description": "what what-if answers, as its description says"
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "checks",
|
||||
"description": "What the mesh's checks said of a pull request's head or of one commit: each of the commit's mesh statuses (mesh/check, the one a main requires from R4e, hq's mesh/repo-check, mesh/delivery, …) with its state, description and when it was set; the merge check's full verdict as the controller said it — each layer's summary, the machine that ran it, when, its build id and its report; and whether the branch's protection would let it merge, every required status being success.",
|
||||
"optional": true,
|
||||
"input": {
|
||||
"properties": {
|
||||
"commit": {
|
||||
"description": "a commit's sha, or the start of one, instead of a pull request",
|
||||
"type": "string"
|
||||
},
|
||||
"number": {
|
||||
"description": "a pull request's number: its head is read",
|
||||
"type": "string"
|
||||
},
|
||||
"repository": {
|
||||
"description": "owner/repository",
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"repository"
|
||||
],
|
||||
"type": "object"
|
||||
},
|
||||
"answer": {
|
||||
"type": "object",
|
||||
"description": "what checks answers, as its description says"
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "table",
|
||||
"description": "The state table every delivery runs by: each transition with its guard, each state's bound and what healer H2 may do once it has passed; and the machine steps' table.",
|
||||
"input": {
|
||||
"properties": {},
|
||||
"type": "object"
|
||||
},
|
||||
"answer": {
|
||||
"type": "object",
|
||||
"description": "what table answers, as its description says"
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "stalled",
|
||||
"description": "Every delivery held past its state's bound, with the transition the table lets healer H2 take for it, or none.",
|
||||
"input": {
|
||||
"properties": {},
|
||||
"type": "object"
|
||||
},
|
||||
"answer": {
|
||||
"type": "object",
|
||||
"description": "what stalled answers, as its description says"
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "recheck",
|
||||
"description": "Check a rejected or ready delivery again: it goes back to proposed and the controller is asked for its check. With why.",
|
||||
"input": {
|
||||
"properties": {
|
||||
"id": {
|
||||
"description": "the delivery's id",
|
||||
"type": "string"
|
||||
},
|
||||
"why": {
|
||||
"description": "why, kept with the transition",
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"id",
|
||||
"why"
|
||||
],
|
||||
"type": "object"
|
||||
},
|
||||
"answer": {
|
||||
"type": "object",
|
||||
"description": "what recheck answers, as its description says"
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "release",
|
||||
"description": "Removed in R6 (ADR 0539): refused by name, naming its replacement: the assigner's asks (hold, restore, backup, record), answered on the phone or in the delivery view",
|
||||
"optional": true,
|
||||
"input": {
|
||||
"properties": {},
|
||||
"type": "object"
|
||||
},
|
||||
"answer": {
|
||||
"type": "object",
|
||||
"description": "what release answers, as its description says"
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "stop",
|
||||
"description": "Removed in R6 (ADR 0539): refused by name, naming its replacement: a bad fingerprint is held and its canary set back by the assigner (ADR 0429 §6, ADR 0539 clause 18)",
|
||||
"optional": true,
|
||||
"input": {
|
||||
"properties": {},
|
||||
"type": "object"
|
||||
},
|
||||
"answer": {
|
||||
"type": "object",
|
||||
"description": "what stop answers, as its description says"
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "close",
|
||||
"description": "Healer H2's verb: take the one transition the table names for a delivery held past its bound. Refused for any other.",
|
||||
"input": {
|
||||
"properties": {
|
||||
"id": {
|
||||
"description": "the delivery's id",
|
||||
"type": "string"
|
||||
},
|
||||
"why": {
|
||||
"description": "the condition it answers",
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"id",
|
||||
"why"
|
||||
],
|
||||
"type": "object"
|
||||
},
|
||||
"answer": {
|
||||
"type": "object",
|
||||
"description": "what close answers, as its description says"
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "retire-history",
|
||||
"description": "Removed in R6 (ADR 0539): refused by name, naming its replacement: nothing to retire: the gate keeps no delivery after its merge",
|
||||
"optional": true,
|
||||
"input": {
|
||||
"properties": {},
|
||||
"type": "object"
|
||||
},
|
||||
"answer": {
|
||||
"type": "object",
|
||||
"description": "what retire-history answers, as its description says"
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "settle",
|
||||
"description": "Ask the operator to settle deliveries held because their merge reached the trunk without a passing check: one ask on the operator's channel for all of them, as an Approve is asked. The verb moves nothing; on the operator's answer each goes to merged, and whether it is on the machines is derived as for any merge (novox/hq ADR 0556). Refused for a delivery not held, or held for another reason, naming why.",
|
||||
"optional": true,
|
||||
"input": {
|
||||
"additionalProperties": false,
|
||||
"properties": {
|
||||
"ids": {
|
||||
"description": "the held deliveries' ids (owner/repository@commit)",
|
||||
"items": {
|
||||
"type": "string"
|
||||
},
|
||||
"minItems": 1,
|
||||
"type": "array"
|
||||
},
|
||||
"why": {
|
||||
"description": "why they may be settled, shown to the operator",
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"ids",
|
||||
"why"
|
||||
],
|
||||
"type": "object"
|
||||
},
|
||||
"answer": {
|
||||
"type": "object",
|
||||
"description": "what settle answers, as its description says"
|
||||
}
|
||||
}
|
||||
],
|
||||
"description": "The delivery gate: reads and steers deliveries, merges and the checks that decide them; the merge is made on the operator's Approve once both checks passed.",
|
||||
"states": [
|
||||
{
|
||||
"name": "source",
|
||||
"template": "state",
|
||||
"key-forms": [
|
||||
{
|
||||
"key": "main",
|
||||
"value": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"v": {
|
||||
"type": "integer",
|
||||
"description": "the layout version"
|
||||
},
|
||||
"group": {
|
||||
"type": "string",
|
||||
"description": "the group that moved it"
|
||||
},
|
||||
"heads": {
|
||||
"type": "object",
|
||||
"additionalProperties": {
|
||||
"type": "string"
|
||||
},
|
||||
"description": "repository to the merged head, a full commit"
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"group",
|
||||
"heads"
|
||||
],
|
||||
"description": "every repository's merged head; its revision is the mesh's source revision"
|
||||
},
|
||||
"description": "the one key, written by the holder alone by compare-and-set"
|
||||
}
|
||||
],
|
||||
"description": "the mesh's source revision",
|
||||
"history": 64
|
||||
},
|
||||
{
|
||||
"name": "merges",
|
||||
"template": "state",
|
||||
"key-forms": [
|
||||
{
|
||||
"key": "<group>",
|
||||
"value": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"v": {
|
||||
"type": "integer",
|
||||
"description": "the layout version"
|
||||
},
|
||||
"group": {
|
||||
"type": "string"
|
||||
},
|
||||
"ask": {
|
||||
"type": "string",
|
||||
"description": "the ask key"
|
||||
},
|
||||
"approved": {
|
||||
"type": "object",
|
||||
"additionalProperties": {
|
||||
"type": "string"
|
||||
},
|
||||
"description": "<repository>@<number> to the approved head"
|
||||
},
|
||||
"pulls": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"repository": {
|
||||
"type": "string"
|
||||
},
|
||||
"number": {
|
||||
"type": "integer"
|
||||
},
|
||||
"head": {
|
||||
"type": "string"
|
||||
},
|
||||
"state": {
|
||||
"type": "string",
|
||||
"description": "merging, merged or refused"
|
||||
},
|
||||
"commit": {
|
||||
"type": "string"
|
||||
},
|
||||
"words": {
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"repository",
|
||||
"number",
|
||||
"head",
|
||||
"state"
|
||||
]
|
||||
}
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"group",
|
||||
"ask",
|
||||
"pulls"
|
||||
],
|
||||
"description": "one group's merge and how far each pull request is"
|
||||
},
|
||||
"description": "a group's merge"
|
||||
}
|
||||
],
|
||||
"description": "the merges",
|
||||
"history": 10
|
||||
},
|
||||
{
|
||||
"name": "asks",
|
||||
"template": "state",
|
||||
"key-forms": [
|
||||
{
|
||||
"key": "merge.<group>.<heads>",
|
||||
"value": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"v": {
|
||||
"type": "integer",
|
||||
"description": "the layout version"
|
||||
},
|
||||
"group": {
|
||||
"type": "string"
|
||||
},
|
||||
"heads": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"repository": {
|
||||
"type": "string",
|
||||
"description": "<owner>/<name>"
|
||||
},
|
||||
"number": {
|
||||
"type": "integer"
|
||||
},
|
||||
"head": {
|
||||
"type": "string",
|
||||
"description": "the full commit"
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"repository",
|
||||
"number",
|
||||
"head"
|
||||
]
|
||||
}
|
||||
},
|
||||
"ask": {
|
||||
"description": "the operator-channel ask as raised"
|
||||
},
|
||||
"asking": {
|
||||
"type": "integer",
|
||||
"description": "the number of times it was asked"
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"group",
|
||||
"heads",
|
||||
"ask"
|
||||
]
|
||||
},
|
||||
"description": "the ask of the operator for one group at one set of heads"
|
||||
}
|
||||
],
|
||||
"description": "the asks of the operator for a merge",
|
||||
"history": 5
|
||||
},
|
||||
{
|
||||
"name": "owed",
|
||||
"template": "state",
|
||||
"key-forms": [
|
||||
{
|
||||
"key": "<repository>.<number>.<head>.<act>",
|
||||
"value": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"v": {
|
||||
"type": "integer",
|
||||
"description": "the layout version"
|
||||
},
|
||||
"act": {
|
||||
"type": "string",
|
||||
"description": "status=<context>, view, note or comment.<seq>"
|
||||
},
|
||||
"context": {
|
||||
"type": "string"
|
||||
},
|
||||
"target": {
|
||||
"type": "string"
|
||||
},
|
||||
"state": {
|
||||
"type": "string"
|
||||
},
|
||||
"text": {
|
||||
"type": "string"
|
||||
},
|
||||
"ref": {
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"act"
|
||||
],
|
||||
"description": "one act owed to the forge, written by its owner and done by the forge bridge"
|
||||
},
|
||||
"description": "one act owed to the forge"
|
||||
}
|
||||
],
|
||||
"description": "the acts the gate owes the forge",
|
||||
"history": 5
|
||||
},
|
||||
{
|
||||
"name": "turns",
|
||||
"template": "state",
|
||||
"key-forms": [
|
||||
{
|
||||
"key": "<repository>",
|
||||
"value": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"v": {
|
||||
"type": "integer",
|
||||
"description": "the layout version"
|
||||
},
|
||||
"repository": {
|
||||
"type": "string"
|
||||
},
|
||||
"waiting": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"group": {
|
||||
"type": "string"
|
||||
},
|
||||
"heads": {
|
||||
"type": "string"
|
||||
},
|
||||
"ask": {
|
||||
"type": "string"
|
||||
},
|
||||
"approved": {
|
||||
"type": "string",
|
||||
"description": "when (RFC 3339 time)"
|
||||
},
|
||||
"since": {
|
||||
"type": "string",
|
||||
"description": "when (RFC 3339 time)"
|
||||
},
|
||||
"waits-for": {
|
||||
"type": "string"
|
||||
},
|
||||
"waits-since": {
|
||||
"type": "string",
|
||||
"description": "when (RFC 3339 time)"
|
||||
},
|
||||
"step": {
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"group",
|
||||
"heads",
|
||||
"approved",
|
||||
"since"
|
||||
]
|
||||
}
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"repository",
|
||||
"waiting"
|
||||
],
|
||||
"description": "the merges of a repository in the order they wait for their turn"
|
||||
},
|
||||
"description": "the merges waiting on a repository"
|
||||
}
|
||||
],
|
||||
"description": "which merge waits for which",
|
||||
"history": 5
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -0,0 +1,63 @@
|
||||
[
|
||||
{
|
||||
"name": "a running step",
|
||||
"of": "state:steps/<node>.<id>",
|
||||
"given": {
|
||||
"v": 1,
|
||||
"id": "a1b2",
|
||||
"tool": "build",
|
||||
"node": "ace",
|
||||
"state": "running",
|
||||
"started": "2026-11-05T10:00:00Z"
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "a paused node",
|
||||
"of": "state:pauses/paused.<node>",
|
||||
"given": {
|
||||
"v": 1,
|
||||
"paused": true,
|
||||
"by": "operator@shanks",
|
||||
"at": "2026-11-05T10:00:00Z"
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "a node's capacity",
|
||||
"of": "state:pauses/capacity.<node>",
|
||||
"given": {
|
||||
"v": 1,
|
||||
"effective": 2,
|
||||
"configured": 2,
|
||||
"why": "the configured steps fit the cores"
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "a node's toolchain",
|
||||
"of": "state:toolchains/<node>.<language>",
|
||||
"given": {
|
||||
"v": 1,
|
||||
"language": "go",
|
||||
"version": "1.26.0",
|
||||
"recipe": "1"
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "a build ask",
|
||||
"of": "accept:delivery",
|
||||
"subject": "mesh.seat.node-build-go.accept.delivery",
|
||||
"given": {
|
||||
"id": "a1b2",
|
||||
"kind": "build",
|
||||
"language": "go",
|
||||
"asker": "mesh-delivery-builder",
|
||||
"build": {
|
||||
"module": "nats",
|
||||
"artifacts": [
|
||||
{
|
||||
"artifact": "go"
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
@@ -0,0 +1,87 @@
|
||||
{
|
||||
"format": 1,
|
||||
"seat": "node-build-go",
|
||||
"scope": "node",
|
||||
"grain": "whole",
|
||||
"decision": "novox/hq ADR 0525, ADR 0536, ADR 0553",
|
||||
"pulled-by-kind": true,
|
||||
"yields": true,
|
||||
"accepts": [
|
||||
{
|
||||
"name": "delivery",
|
||||
"body": {
|
||||
"$ref": "#/defs/StepAsk"
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "check",
|
||||
"body": {
|
||||
"$ref": "#/defs/StepAsk"
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "recheck",
|
||||
"body": {
|
||||
"$ref": "#/defs/StepAsk"
|
||||
}
|
||||
}
|
||||
],
|
||||
"description": "The Go build module on a node: it takes the asks of the builder and the checker, runs them as steps and answers.",
|
||||
"includes": [
|
||||
"build-step"
|
||||
],
|
||||
"states": [
|
||||
{
|
||||
"name": "steps",
|
||||
"template": "state",
|
||||
"key-forms": [
|
||||
{
|
||||
"key": "<node>.<id>",
|
||||
"value": {
|
||||
"$ref": "#/defs/Step"
|
||||
},
|
||||
"description": "a step on a node"
|
||||
}
|
||||
],
|
||||
"description": "the steps this module runs",
|
||||
"history": 5
|
||||
},
|
||||
{
|
||||
"name": "pauses",
|
||||
"template": "state",
|
||||
"key-forms": [
|
||||
{
|
||||
"key": "paused.<node>",
|
||||
"value": {
|
||||
"$ref": "#/defs/Pause"
|
||||
},
|
||||
"description": "whether a node is paused"
|
||||
},
|
||||
{
|
||||
"key": "capacity.<node>",
|
||||
"value": {
|
||||
"$ref": "#/defs/Capacity"
|
||||
},
|
||||
"description": "a node's capacity"
|
||||
}
|
||||
],
|
||||
"description": "the pause and the capacity of each node",
|
||||
"history": 1
|
||||
},
|
||||
{
|
||||
"name": "toolchains",
|
||||
"template": "state",
|
||||
"key-forms": [
|
||||
{
|
||||
"key": "<node>.<language>",
|
||||
"value": {
|
||||
"$ref": "#/defs/Toolchain"
|
||||
},
|
||||
"description": "the toolchain a node reports"
|
||||
}
|
||||
],
|
||||
"description": "the toolchains the nodes report",
|
||||
"history": 1
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -0,0 +1,63 @@
|
||||
[
|
||||
{
|
||||
"name": "a running step",
|
||||
"of": "state:steps/<node>.<id>",
|
||||
"given": {
|
||||
"v": 1,
|
||||
"id": "a1b2",
|
||||
"tool": "build",
|
||||
"node": "ace",
|
||||
"state": "running",
|
||||
"started": "2026-11-05T10:00:00Z"
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "a paused node",
|
||||
"of": "state:pauses/paused.<node>",
|
||||
"given": {
|
||||
"v": 1,
|
||||
"paused": true,
|
||||
"by": "operator@shanks",
|
||||
"at": "2026-11-05T10:00:00Z"
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "a node's capacity",
|
||||
"of": "state:pauses/capacity.<node>",
|
||||
"given": {
|
||||
"v": 1,
|
||||
"effective": 2,
|
||||
"configured": 2,
|
||||
"why": "the configured steps fit the cores"
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "a node's toolchain",
|
||||
"of": "state:toolchains/<node>.<language>",
|
||||
"given": {
|
||||
"v": 1,
|
||||
"language": "go",
|
||||
"version": "1.26.0",
|
||||
"recipe": "1"
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "a build ask",
|
||||
"of": "accept:delivery",
|
||||
"subject": "mesh.seat.node-build-image.accept.delivery",
|
||||
"given": {
|
||||
"id": "a1b2",
|
||||
"kind": "build",
|
||||
"language": "go",
|
||||
"asker": "mesh-delivery-builder",
|
||||
"build": {
|
||||
"module": "nats",
|
||||
"artifacts": [
|
||||
{
|
||||
"artifact": "go"
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
@@ -0,0 +1,87 @@
|
||||
{
|
||||
"format": 1,
|
||||
"seat": "node-build-image",
|
||||
"scope": "node",
|
||||
"grain": "whole",
|
||||
"decision": "novox/hq ADR 0525, ADR 0536, ADR 0553",
|
||||
"pulled-by-kind": true,
|
||||
"yields": true,
|
||||
"accepts": [
|
||||
{
|
||||
"name": "delivery",
|
||||
"body": {
|
||||
"$ref": "#/defs/StepAsk"
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "check",
|
||||
"body": {
|
||||
"$ref": "#/defs/StepAsk"
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "recheck",
|
||||
"body": {
|
||||
"$ref": "#/defs/StepAsk"
|
||||
}
|
||||
}
|
||||
],
|
||||
"description": "The image build module on a node: it takes the asks of the builder and the checker, runs them as steps and answers.",
|
||||
"includes": [
|
||||
"build-step"
|
||||
],
|
||||
"states": [
|
||||
{
|
||||
"name": "steps",
|
||||
"template": "state",
|
||||
"key-forms": [
|
||||
{
|
||||
"key": "<node>.<id>",
|
||||
"value": {
|
||||
"$ref": "#/defs/Step"
|
||||
},
|
||||
"description": "a step on a node"
|
||||
}
|
||||
],
|
||||
"description": "the steps this module runs",
|
||||
"history": 5
|
||||
},
|
||||
{
|
||||
"name": "pauses",
|
||||
"template": "state",
|
||||
"key-forms": [
|
||||
{
|
||||
"key": "paused.<node>",
|
||||
"value": {
|
||||
"$ref": "#/defs/Pause"
|
||||
},
|
||||
"description": "whether a node is paused"
|
||||
},
|
||||
{
|
||||
"key": "capacity.<node>",
|
||||
"value": {
|
||||
"$ref": "#/defs/Capacity"
|
||||
},
|
||||
"description": "a node's capacity"
|
||||
}
|
||||
],
|
||||
"description": "the pause and the capacity of each node",
|
||||
"history": 1
|
||||
},
|
||||
{
|
||||
"name": "toolchains",
|
||||
"template": "state",
|
||||
"key-forms": [
|
||||
{
|
||||
"key": "<node>.<language>",
|
||||
"value": {
|
||||
"$ref": "#/defs/Toolchain"
|
||||
},
|
||||
"description": "the toolchain a node reports"
|
||||
}
|
||||
],
|
||||
"description": "the toolchains the nodes report",
|
||||
"history": 1
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -0,0 +1,63 @@
|
||||
[
|
||||
{
|
||||
"name": "a running step",
|
||||
"of": "state:steps/<node>.<id>",
|
||||
"given": {
|
||||
"v": 1,
|
||||
"id": "a1b2",
|
||||
"tool": "build",
|
||||
"node": "ace",
|
||||
"state": "running",
|
||||
"started": "2026-11-05T10:00:00Z"
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "a paused node",
|
||||
"of": "state:pauses/paused.<node>",
|
||||
"given": {
|
||||
"v": 1,
|
||||
"paused": true,
|
||||
"by": "operator@shanks",
|
||||
"at": "2026-11-05T10:00:00Z"
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "a node's capacity",
|
||||
"of": "state:pauses/capacity.<node>",
|
||||
"given": {
|
||||
"v": 1,
|
||||
"effective": 2,
|
||||
"configured": 2,
|
||||
"why": "the configured steps fit the cores"
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "a node's toolchain",
|
||||
"of": "state:toolchains/<node>.<language>",
|
||||
"given": {
|
||||
"v": 1,
|
||||
"language": "go",
|
||||
"version": "1.26.0",
|
||||
"recipe": "1"
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "a build ask",
|
||||
"of": "accept:delivery",
|
||||
"subject": "mesh.seat.node-build-node.accept.delivery",
|
||||
"given": {
|
||||
"id": "a1b2",
|
||||
"kind": "build",
|
||||
"language": "go",
|
||||
"asker": "mesh-delivery-builder",
|
||||
"build": {
|
||||
"module": "nats",
|
||||
"artifacts": [
|
||||
{
|
||||
"artifact": "go"
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
@@ -0,0 +1,87 @@
|
||||
{
|
||||
"format": 1,
|
||||
"seat": "node-build-node",
|
||||
"scope": "node",
|
||||
"grain": "whole",
|
||||
"decision": "novox/hq ADR 0525, ADR 0536, ADR 0553",
|
||||
"pulled-by-kind": true,
|
||||
"yields": true,
|
||||
"accepts": [
|
||||
{
|
||||
"name": "delivery",
|
||||
"body": {
|
||||
"$ref": "#/defs/StepAsk"
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "check",
|
||||
"body": {
|
||||
"$ref": "#/defs/StepAsk"
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "recheck",
|
||||
"body": {
|
||||
"$ref": "#/defs/StepAsk"
|
||||
}
|
||||
}
|
||||
],
|
||||
"description": "The Node build module on a node: it takes the asks of the builder and the checker, runs them as steps and answers.",
|
||||
"includes": [
|
||||
"build-step"
|
||||
],
|
||||
"states": [
|
||||
{
|
||||
"name": "steps",
|
||||
"template": "state",
|
||||
"key-forms": [
|
||||
{
|
||||
"key": "<node>.<id>",
|
||||
"value": {
|
||||
"$ref": "#/defs/Step"
|
||||
},
|
||||
"description": "a step on a node"
|
||||
}
|
||||
],
|
||||
"description": "the steps this module runs",
|
||||
"history": 5
|
||||
},
|
||||
{
|
||||
"name": "pauses",
|
||||
"template": "state",
|
||||
"key-forms": [
|
||||
{
|
||||
"key": "paused.<node>",
|
||||
"value": {
|
||||
"$ref": "#/defs/Pause"
|
||||
},
|
||||
"description": "whether a node is paused"
|
||||
},
|
||||
{
|
||||
"key": "capacity.<node>",
|
||||
"value": {
|
||||
"$ref": "#/defs/Capacity"
|
||||
},
|
||||
"description": "a node's capacity"
|
||||
}
|
||||
],
|
||||
"description": "the pause and the capacity of each node",
|
||||
"history": 1
|
||||
},
|
||||
{
|
||||
"name": "toolchains",
|
||||
"template": "state",
|
||||
"key-forms": [
|
||||
{
|
||||
"key": "<node>.<language>",
|
||||
"value": {
|
||||
"$ref": "#/defs/Toolchain"
|
||||
},
|
||||
"description": "the toolchain a node reports"
|
||||
}
|
||||
],
|
||||
"description": "the toolchains the nodes report",
|
||||
"history": 1
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -0,0 +1,72 @@
|
||||
[
|
||||
{
|
||||
"name": "published on the seat's subject",
|
||||
"of": "event:published",
|
||||
"subject": "mesh.seat.npm-package-registry.event.published",
|
||||
"given": {
|
||||
"name": "@novox/mesh-sdk",
|
||||
"version": "0.1.19",
|
||||
"kind": "package",
|
||||
"digest": "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa",
|
||||
"fingerprint": "f1",
|
||||
"commit": "cccccccccccccccccccccccccccccccccccccccc",
|
||||
"holder": "gitea",
|
||||
"transfer": "t1",
|
||||
"reference": "@novox/mesh-sdk@0.1.19"
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "refused on the seat's subject",
|
||||
"of": "event:refused",
|
||||
"subject": "mesh.seat.npm-package-registry.event.refused",
|
||||
"given": {
|
||||
"name": "@novox/mesh-sdk",
|
||||
"version": "0.1.19",
|
||||
"kind": "package",
|
||||
"digest": "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa",
|
||||
"fingerprint": "f1",
|
||||
"commit": "cccccccccccccccccccccccccccccccccccccccc",
|
||||
"holder": "gitea",
|
||||
"transfer": "t1",
|
||||
"reference": "@novox/mesh-sdk@0.1.19",
|
||||
"reason": "retention"
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "removed on the seat's subject",
|
||||
"of": "event:removed",
|
||||
"subject": "mesh.seat.npm-package-registry.event.removed",
|
||||
"given": {
|
||||
"name": "@novox/mesh-sdk",
|
||||
"version": "0.1.19",
|
||||
"kind": "package",
|
||||
"digest": "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa",
|
||||
"fingerprint": "f1",
|
||||
"commit": "cccccccccccccccccccccccccccccccccccccccc",
|
||||
"holder": "gitea",
|
||||
"transfer": "t1",
|
||||
"reference": "@novox/mesh-sdk@0.1.19",
|
||||
"reason": "retention"
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "kept on the seat's subject: a tagged version sharing its digest stays, naming the tags on it",
|
||||
"of": "event:kept",
|
||||
"subject": "mesh.seat.npm-package-registry.event.kept",
|
||||
"given": {
|
||||
"name": "@novox/mesh-sdk",
|
||||
"version": "0.1.19-feat-x.2",
|
||||
"kind": "package",
|
||||
"digest": "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa",
|
||||
"fingerprint": "f1",
|
||||
"commit": "cccccccccccccccccccccccccccccccccccccccc",
|
||||
"holder": "gitea",
|
||||
"reference": "@novox/mesh-sdk@0.1.19",
|
||||
"reason": "shared-digest",
|
||||
"tag": "0.1.19-feat-x.2",
|
||||
"tags": [
|
||||
"0.1.19"
|
||||
]
|
||||
}
|
||||
}
|
||||
]
|
||||
@@ -0,0 +1,110 @@
|
||||
[
|
||||
{
|
||||
"name": "publish opens a transfer",
|
||||
"of": "verb:publish.input",
|
||||
"given": {
|
||||
"name": "@novox/mesh-sdk",
|
||||
"version": "0.1.19",
|
||||
"kind": "package",
|
||||
"digest": "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa",
|
||||
"fingerprint": "f1",
|
||||
"commit": "cccccccccccccccccccccccccccccccccccccccc",
|
||||
"transfer": "t1",
|
||||
"parts": [
|
||||
{
|
||||
"digest": "sha256:bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb",
|
||||
"size": 1048576
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "a field the reader does not know is dropped",
|
||||
"of": "verb:publish.input",
|
||||
"why": "ADR 0608 §6: a reader ignores a field it does not know",
|
||||
"given": {
|
||||
"name": "@novox/mesh-sdk",
|
||||
"version": "0.1.19",
|
||||
"kind": "package",
|
||||
"digest": "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa",
|
||||
"fingerprint": "f1",
|
||||
"commit": "cccccccccccccccccccccccccccccccccccccccc",
|
||||
"transfer": "t1",
|
||||
"parts": [
|
||||
{
|
||||
"digest": "sha256:bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb",
|
||||
"size": 1048576
|
||||
}
|
||||
],
|
||||
"later": {
|
||||
"x": 1
|
||||
}
|
||||
},
|
||||
"wire": {
|
||||
"name": "@novox/mesh-sdk",
|
||||
"version": "0.1.19",
|
||||
"kind": "package",
|
||||
"digest": "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa",
|
||||
"fingerprint": "f1",
|
||||
"commit": "cccccccccccccccccccccccccccccccccccccccc",
|
||||
"transfer": "t1",
|
||||
"parts": [
|
||||
{
|
||||
"digest": "sha256:bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb",
|
||||
"size": 1048576
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "publish answers what it lacks",
|
||||
"of": "verb:publish.answer",
|
||||
"given": {
|
||||
"transfer": "t1",
|
||||
"lacks": [
|
||||
"sha256:bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb"
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "a chunk of a part",
|
||||
"of": "verb:publish-part.input",
|
||||
"given": {
|
||||
"transfer": "t1",
|
||||
"part": "sha256:bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb",
|
||||
"sequence": 0,
|
||||
"last": true,
|
||||
"data": "aGVsbG8="
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "the versions held",
|
||||
"of": "verb:versions.answer",
|
||||
"given": {
|
||||
"name": "@novox/mesh-sdk",
|
||||
"versions": [
|
||||
{
|
||||
"name": "@novox/mesh-sdk",
|
||||
"version": "0.1.19",
|
||||
"kind": "package",
|
||||
"digest": "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa",
|
||||
"fingerprint": "f1",
|
||||
"commit": "cccccccccccccccccccccccccccccccccccccccc"
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "a tagged version sharing its digest with another tag is kept, not removed",
|
||||
"of": "verb:remove.answer",
|
||||
"why": "G-sdk-contracts 3: the store cannot remove the tag alone, so the answer says what holds",
|
||||
"given": {
|
||||
"removed": false,
|
||||
"kept": true,
|
||||
"reason": "shared-digest",
|
||||
"tags": [
|
||||
"0.1.19"
|
||||
]
|
||||
}
|
||||
}
|
||||
]
|
||||
@@ -0,0 +1,34 @@
|
||||
{
|
||||
"format": 1,
|
||||
"seat": "npm-package-registry",
|
||||
"scope": "mesh",
|
||||
"grain": "whole",
|
||||
"decision": "novox/hq ADR 0109",
|
||||
"description": "The mesh's npm packages: the npm registry protocol's read side, and publishing over the bus.",
|
||||
"delivers": "npm-package-registry",
|
||||
"includes": [
|
||||
"registry"
|
||||
],
|
||||
"family": {
|
||||
"name": "npm-package-registry.events",
|
||||
"max-age-days": 7
|
||||
},
|
||||
"provisions": [
|
||||
{
|
||||
"name": "npm-package-registry",
|
||||
"form": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"url": {
|
||||
"type": "string",
|
||||
"description": "the read side's address"
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"url"
|
||||
],
|
||||
"description": "form none: the npm registry's read side, answered anonymously, with no credential (design 51)"
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -0,0 +1,98 @@
|
||||
[
|
||||
{
|
||||
"name": "published on the seat's subject",
|
||||
"of": "event:published",
|
||||
"subject": "mesh.seat.oci-artifact-registry.event.published",
|
||||
"given": {
|
||||
"name": "mesh-sdk",
|
||||
"version": "0.1.19",
|
||||
"kind": "bundle",
|
||||
"digest": "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa",
|
||||
"fingerprint": "f1",
|
||||
"commit": "cccccccccccccccccccccccccccccccccccccccc",
|
||||
"holder": "distribution",
|
||||
"transfer": "t1",
|
||||
"reference": "oci-artifact-registry/mesh-sdk/lib@sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "refused on the seat's subject",
|
||||
"of": "event:refused",
|
||||
"subject": "mesh.seat.oci-artifact-registry.event.refused",
|
||||
"given": {
|
||||
"name": "mesh-sdk",
|
||||
"version": "0.1.19",
|
||||
"kind": "bundle",
|
||||
"digest": "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa",
|
||||
"fingerprint": "f1",
|
||||
"commit": "cccccccccccccccccccccccccccccccccccccccc",
|
||||
"holder": "distribution",
|
||||
"transfer": "t1",
|
||||
"reference": "oci-artifact-registry/mesh-sdk/lib@sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa",
|
||||
"reason": "retention"
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "removed on the seat's subject",
|
||||
"of": "event:removed",
|
||||
"subject": "mesh.seat.oci-artifact-registry.event.removed",
|
||||
"given": {
|
||||
"name": "mesh-sdk",
|
||||
"version": "0.1.19",
|
||||
"kind": "bundle",
|
||||
"digest": "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa",
|
||||
"fingerprint": "f1",
|
||||
"commit": "cccccccccccccccccccccccccccccccccccccccc",
|
||||
"holder": "distribution",
|
||||
"transfer": "t1",
|
||||
"reference": "oci-artifact-registry/mesh-sdk/lib@sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa",
|
||||
"reason": "retention"
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "kept on the seat's subject: a tagged version sharing its digest stays, naming the tags on it",
|
||||
"of": "event:kept",
|
||||
"subject": "mesh.seat.oci-artifact-registry.event.kept",
|
||||
"given": {
|
||||
"name": "mesh-sdk",
|
||||
"version": "0.1.19-feat-x.2",
|
||||
"kind": "bundle",
|
||||
"digest": "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa",
|
||||
"fingerprint": "f1",
|
||||
"commit": "cccccccccccccccccccccccccccccccccccccccc",
|
||||
"holder": "distribution",
|
||||
"reference": "oci-artifact-registry/mesh-sdk/lib@sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa",
|
||||
"reason": "shared-digest",
|
||||
"tag": "0.1.19-feat-x.2",
|
||||
"tags": [
|
||||
"0.1.19"
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "held on the seat's subject",
|
||||
"of": "event:held",
|
||||
"subject": "mesh.seat.oci-artifact-registry.event.held",
|
||||
"given": {
|
||||
"name": "mesh-sdk",
|
||||
"digest": "sha256:bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb",
|
||||
"reason": "kept by the sweep's rule",
|
||||
"holder": "distribution",
|
||||
"by": "mesh-artifact-sweep",
|
||||
"at": "2026-11-04T12:00:00Z"
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "released on the seat's subject",
|
||||
"of": "event:released",
|
||||
"subject": "mesh.seat.oci-artifact-registry.event.released",
|
||||
"given": {
|
||||
"name": "mesh-sdk",
|
||||
"digest": "sha256:bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb",
|
||||
"reason": "kept by the sweep's rule",
|
||||
"holder": "distribution",
|
||||
"by": "mesh-artifact-sweep",
|
||||
"at": "2026-11-04T12:00:00Z"
|
||||
}
|
||||
}
|
||||
]
|
||||
@@ -0,0 +1,28 @@
|
||||
[
|
||||
{
|
||||
"name": "a hold",
|
||||
"of": "state:holds/<seat>.<name>.<digest>",
|
||||
"given": {
|
||||
"seat": "oci-artifact-registry",
|
||||
"name": "base",
|
||||
"kind": "base",
|
||||
"digest": "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa",
|
||||
"by": "operator@shanks",
|
||||
"since": "2026-11-05T10:00:00Z"
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "an open window",
|
||||
"of": "state:windows/<caller>",
|
||||
"given": {
|
||||
"name": "mesh-delivery-builder",
|
||||
"open": true,
|
||||
"holding": [
|
||||
"ace"
|
||||
],
|
||||
"since": {
|
||||
"ace": "2026-11-05T10:00:00Z"
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
@@ -0,0 +1,138 @@
|
||||
[
|
||||
{
|
||||
"name": "publish opens a transfer",
|
||||
"of": "verb:publish.input",
|
||||
"given": {
|
||||
"name": "mesh-sdk",
|
||||
"version": "0.1.19",
|
||||
"kind": "bundle",
|
||||
"digest": "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa",
|
||||
"fingerprint": "f1",
|
||||
"commit": "cccccccccccccccccccccccccccccccccccccccc",
|
||||
"transfer": "t1",
|
||||
"parts": [
|
||||
{
|
||||
"digest": "sha256:bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb",
|
||||
"size": 1048576
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "a field the reader does not know is dropped",
|
||||
"of": "verb:publish.input",
|
||||
"why": "ADR 0608 §6: a reader ignores a field it does not know",
|
||||
"given": {
|
||||
"name": "mesh-sdk",
|
||||
"version": "0.1.19",
|
||||
"kind": "bundle",
|
||||
"digest": "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa",
|
||||
"fingerprint": "f1",
|
||||
"commit": "cccccccccccccccccccccccccccccccccccccccc",
|
||||
"transfer": "t1",
|
||||
"parts": [
|
||||
{
|
||||
"digest": "sha256:bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb",
|
||||
"size": 1048576
|
||||
}
|
||||
],
|
||||
"later": {
|
||||
"x": 1
|
||||
}
|
||||
},
|
||||
"wire": {
|
||||
"name": "mesh-sdk",
|
||||
"version": "0.1.19",
|
||||
"kind": "bundle",
|
||||
"digest": "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa",
|
||||
"fingerprint": "f1",
|
||||
"commit": "cccccccccccccccccccccccccccccccccccccccc",
|
||||
"transfer": "t1",
|
||||
"parts": [
|
||||
{
|
||||
"digest": "sha256:bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb",
|
||||
"size": 1048576
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "publish answers what it lacks",
|
||||
"of": "verb:publish.answer",
|
||||
"given": {
|
||||
"transfer": "t1",
|
||||
"lacks": [
|
||||
"sha256:bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb"
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "a chunk of a part",
|
||||
"of": "verb:publish-part.input",
|
||||
"given": {
|
||||
"transfer": "t1",
|
||||
"part": "sha256:bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb",
|
||||
"sequence": 0,
|
||||
"last": true,
|
||||
"data": "aGVsbG8="
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "the versions held",
|
||||
"of": "verb:versions.answer",
|
||||
"given": {
|
||||
"name": "mesh-sdk",
|
||||
"versions": [
|
||||
{
|
||||
"name": "mesh-sdk",
|
||||
"version": "0.1.19",
|
||||
"kind": "package",
|
||||
"digest": "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa",
|
||||
"fingerprint": "f1",
|
||||
"commit": "cccccccccccccccccccccccccccccccccccccccc"
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "a release of what is not held answers released false",
|
||||
"of": "verb:release.answer",
|
||||
"why": "A3t: the outcome asked holds already, so it is no error",
|
||||
"given": {
|
||||
"released": false
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "a tagged version sharing its digest with another tag is kept, not removed",
|
||||
"of": "verb:remove.answer",
|
||||
"why": "G-sdk-contracts 3: the store cannot remove the tag alone, so the answer says what holds",
|
||||
"given": {
|
||||
"removed": false,
|
||||
"kept": true,
|
||||
"reason": "shared-digest",
|
||||
"tags": [
|
||||
"0.1.19"
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "list answers an untagged archive by the blob it keeps",
|
||||
"of": "verb:list.answer",
|
||||
"given": {
|
||||
"repositories": [
|
||||
{
|
||||
"name": "mail/files",
|
||||
"manifests": [
|
||||
{
|
||||
"digest": "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa",
|
||||
"index": false,
|
||||
"artifact": "sha256:cccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc",
|
||||
"size": 4096,
|
||||
"held": true
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
]
|
||||
@@ -0,0 +1,382 @@
|
||||
{
|
||||
"format": 1,
|
||||
"seat": "oci-artifact-registry",
|
||||
"scope": "mesh",
|
||||
"grain": "whole",
|
||||
"decision": "novox/hq ADR 0605",
|
||||
"description": "The mesh's bundles and archives, by digest, in the one artifact store: the OCI distribution protocol's read side.",
|
||||
"includes": [
|
||||
"registry"
|
||||
],
|
||||
"verbs": [
|
||||
{
|
||||
"name": "hold",
|
||||
"description": "Keep one untagged bundle, archive or copied base from removal by retention, as the artifact sweep's rules keep it (ADR 0617 §2); never an image of the holder's own. Emits held.",
|
||||
"callers": "the module that runs the artifact sweep alone",
|
||||
"input": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"name": {
|
||||
"type": "string",
|
||||
"description": "the name"
|
||||
},
|
||||
"digest": {
|
||||
"type": "string",
|
||||
"description": "the digest"
|
||||
},
|
||||
"reason": {
|
||||
"type": "string",
|
||||
"description": "which of the sweep's rules keeps it"
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"name",
|
||||
"digest",
|
||||
"reason"
|
||||
]
|
||||
},
|
||||
"answer": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"held": {
|
||||
"type": "boolean",
|
||||
"description": "it is held"
|
||||
},
|
||||
"since": {
|
||||
"type": "string",
|
||||
"description": "when the hold began, RFC 3339"
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"held"
|
||||
]
|
||||
},
|
||||
"optional": true
|
||||
},
|
||||
{
|
||||
"name": "release",
|
||||
"description": "Let a hold go; emits released. A release of what is not held answers released false and emits nothing, never an error: the outcome asked holds already. By hand only through the holder's own tool, the operator's session as the proved caller, recorded as a hand act.",
|
||||
"callers": "the module that runs the artifact sweep; the operator's session through the holder's tool",
|
||||
"input": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"name": {
|
||||
"type": "string",
|
||||
"description": "the name"
|
||||
},
|
||||
"digest": {
|
||||
"type": "string",
|
||||
"description": "the digest"
|
||||
},
|
||||
"reason": {
|
||||
"type": "string",
|
||||
"description": "why it is let go"
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"name",
|
||||
"digest",
|
||||
"reason"
|
||||
]
|
||||
},
|
||||
"answer": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"released": {
|
||||
"type": "boolean",
|
||||
"description": "a hold was let go; false when there was none"
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"released"
|
||||
]
|
||||
},
|
||||
"optional": true
|
||||
},
|
||||
{
|
||||
"name": "list",
|
||||
"description": "What the store holds of one repository, or of every repository the seat holds when none is named: every manifest keeping one bundle or archive, each with its digest, whether it is an index (and the manifests an index names), the tags naming it, its size and whether it is held. Read from the store itself, so a manifest no tag names is listed too; it reads only (ADR 0632 §3).",
|
||||
"callers": "any caller whose role grants it",
|
||||
"input": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"name": {
|
||||
"type": "string",
|
||||
"description": "the repository, <module>/<artifact>; every repository of the seat when not given"
|
||||
}
|
||||
}
|
||||
},
|
||||
"answer": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"repositories": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"$ref": "#/defs/Repository"
|
||||
},
|
||||
"description": "each repository named, in order of its name"
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"repositories"
|
||||
]
|
||||
},
|
||||
"optional": true
|
||||
}
|
||||
],
|
||||
"events": [
|
||||
{
|
||||
"name": "held",
|
||||
"body": {
|
||||
"$ref": "#/defs/Hold"
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "released",
|
||||
"body": {
|
||||
"$ref": "#/defs/Hold"
|
||||
}
|
||||
}
|
||||
],
|
||||
"family": {
|
||||
"name": "oci-artifact-registry.events",
|
||||
"max-age-days": 7
|
||||
},
|
||||
"defs": {
|
||||
"Hold": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"name": {
|
||||
"type": "string",
|
||||
"description": "the name"
|
||||
},
|
||||
"digest": {
|
||||
"type": "string",
|
||||
"description": "the digest"
|
||||
},
|
||||
"reason": {
|
||||
"type": "string",
|
||||
"description": "why"
|
||||
},
|
||||
"holder": {
|
||||
"type": "string",
|
||||
"description": "the module holding the seat"
|
||||
},
|
||||
"by": {
|
||||
"type": "string",
|
||||
"description": "the caller the holder proved"
|
||||
},
|
||||
"at": {
|
||||
"type": "string",
|
||||
"description": "when, RFC 3339"
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"name",
|
||||
"digest",
|
||||
"holder"
|
||||
],
|
||||
"description": "the body of held and released"
|
||||
},
|
||||
"Repository": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"name": {
|
||||
"type": "string",
|
||||
"description": "the repository"
|
||||
},
|
||||
"manifests": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"$ref": "#/defs/Listed"
|
||||
},
|
||||
"description": "each manifest it holds, by digest"
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"name",
|
||||
"manifests"
|
||||
]
|
||||
},
|
||||
"Listed": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"digest": {
|
||||
"type": "string",
|
||||
"description": "the manifest's digest, sha256:<hex>"
|
||||
},
|
||||
"index": {
|
||||
"type": "boolean",
|
||||
"description": "it is an index, naming a manifest per platform"
|
||||
},
|
||||
"platforms": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"type": "string"
|
||||
},
|
||||
"description": "for an index, the digests of the manifests it names"
|
||||
},
|
||||
"artifact": {
|
||||
"type": "string",
|
||||
"description": "for a manifest keeping one bundle or archive, the digest of that blob: what a reference names it by"
|
||||
},
|
||||
"tags": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"type": "string"
|
||||
},
|
||||
"description": "the tags naming it, none for an untagged one"
|
||||
},
|
||||
"size": {
|
||||
"type": "integer",
|
||||
"description": "its bytes: its own content and every blob it names"
|
||||
},
|
||||
"held": {
|
||||
"type": "boolean",
|
||||
"description": "a hold keeps it from retention"
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"digest",
|
||||
"index",
|
||||
"size",
|
||||
"held"
|
||||
]
|
||||
}
|
||||
},
|
||||
"states": [
|
||||
{
|
||||
"name": "holds",
|
||||
"template": "state",
|
||||
"history": 1,
|
||||
"description": "the bundles, archives and copied bases kept from retention's removal",
|
||||
"key-forms": [
|
||||
{
|
||||
"key": "<seat>.<name>.<digest>",
|
||||
"value": {
|
||||
"type": "object",
|
||||
"description": "one hold: an untagged bundle, archive or copied base kept from retention's removal",
|
||||
"properties": {
|
||||
"seat": {
|
||||
"type": "string"
|
||||
},
|
||||
"name": {
|
||||
"type": "string"
|
||||
},
|
||||
"kind": {
|
||||
"type": "string"
|
||||
},
|
||||
"digest": {
|
||||
"type": "string"
|
||||
},
|
||||
"by": {
|
||||
"type": "string"
|
||||
},
|
||||
"why": {
|
||||
"type": "string"
|
||||
},
|
||||
"since": {
|
||||
"type": "string",
|
||||
"description": "when (RFC 3339 time)"
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"seat",
|
||||
"name",
|
||||
"kind",
|
||||
"digest",
|
||||
"by",
|
||||
"since"
|
||||
]
|
||||
},
|
||||
"description": "one hold"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "windows",
|
||||
"template": "state",
|
||||
"history": 1,
|
||||
"description": "the direct-write windows, one per caller",
|
||||
"key-forms": [
|
||||
{
|
||||
"key": "<caller>",
|
||||
"value": {
|
||||
"type": "object",
|
||||
"description": "the direct-write window of one caller: open while a node holds it open",
|
||||
"properties": {
|
||||
"name": {
|
||||
"type": "string",
|
||||
"description": "the caller whose window it is"
|
||||
},
|
||||
"open": {
|
||||
"type": "boolean"
|
||||
},
|
||||
"holding": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"type": "string"
|
||||
},
|
||||
"description": "the nodes holding it open"
|
||||
},
|
||||
"since": {
|
||||
"type": "object",
|
||||
"additionalProperties": {
|
||||
"type": "string",
|
||||
"description": "when (RFC 3339 time)"
|
||||
},
|
||||
"description": "node to when it began to hold"
|
||||
},
|
||||
"closed": {
|
||||
"type": "string",
|
||||
"description": "when it last closed (RFC 3339 time)"
|
||||
},
|
||||
"last_holding": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"dropped": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"node": {
|
||||
"type": "string"
|
||||
},
|
||||
"by": {
|
||||
"type": "string"
|
||||
},
|
||||
"why": {
|
||||
"type": "string"
|
||||
},
|
||||
"at": {
|
||||
"type": "string",
|
||||
"description": "when (RFC 3339 time)"
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"node"
|
||||
]
|
||||
},
|
||||
"description": "retired nodes dropped by hand"
|
||||
},
|
||||
"why": {
|
||||
"type": "object",
|
||||
"additionalProperties": {
|
||||
"type": "string"
|
||||
},
|
||||
"description": "of each node holding the window, why it holds it"
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"name"
|
||||
]
|
||||
},
|
||||
"description": "one caller's window"
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
{
|
||||
"lockfileVersion": 3,
|
||||
"name": "@novox/mesh-delivery-contracts-container-image-registry",
|
||||
"packages": {
|
||||
"": {
|
||||
"name": "@novox/mesh-delivery-contracts-container-image-registry",
|
||||
"version": "0.1.0"
|
||||
}
|
||||
},
|
||||
"requires": true,
|
||||
"version": "0.1.0"
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
{
|
||||
"description": "The contract of the seat container-image-registry, generated from its definition.",
|
||||
"exports": {
|
||||
".": "./src/index.ts"
|
||||
},
|
||||
"files": [
|
||||
"src"
|
||||
],
|
||||
"name": "@novox/mesh-delivery-contracts-container-image-registry",
|
||||
"type": "module",
|
||||
"types": "./src/index.ts",
|
||||
"version": "0.1.0"
|
||||
}
|
||||
@@ -0,0 +1,271 @@
|
||||
// Code generated by seatgen from the seat definitions; DO NOT EDIT.
|
||||
|
||||
// The contract of the seat "container-image-registry", defined whole (seats/container-image-registry/seat.json): The mesh's container images, in the one artifact store: the OCI distribution protocol's read side, an image's tags and mirroring an outside image.
|
||||
|
||||
import type * as registry from "./parts/registry.js";
|
||||
|
||||
export const seat = "container-image-registry";
|
||||
|
||||
export const verbs = {
|
||||
publish: "publish",
|
||||
publishPart: "publish-part",
|
||||
versions: "versions",
|
||||
remove: "remove",
|
||||
mirror: "mirror",
|
||||
tags: "tags",
|
||||
hold: "hold",
|
||||
release: "release",
|
||||
list: "list",
|
||||
} as const;
|
||||
|
||||
export const events = {
|
||||
published: "published",
|
||||
refused: "refused",
|
||||
removed: "removed",
|
||||
kept: "kept",
|
||||
held: "held",
|
||||
released: "released",
|
||||
} as const;
|
||||
|
||||
export const family = { name: "container-image-registry.events", maxAgeDays: 7 } as const;
|
||||
|
||||
export const publishedSubject = "mesh.seat.container-image-registry.event.published";
|
||||
|
||||
export const refusedSubject = "mesh.seat.container-image-registry.event.refused";
|
||||
|
||||
export const removedSubject = "mesh.seat.container-image-registry.event.removed";
|
||||
|
||||
export const keptSubject = "mesh.seat.container-image-registry.event.kept";
|
||||
|
||||
export const heldSubject = "mesh.seat.container-image-registry.event.held";
|
||||
|
||||
export const releasedSubject = "mesh.seat.container-image-registry.event.released";
|
||||
|
||||
export const states = {
|
||||
holds: "holds",
|
||||
windows: "windows",
|
||||
} as const;
|
||||
|
||||
/** The key of one value of the state holds under the form "<seat>.<name>.<digest>". */
|
||||
export function holdsBySeatNameDigestKey(seat: string, name: string, digest: string): string {
|
||||
return seat + "." + name + "." + digest;
|
||||
}
|
||||
|
||||
/** The parts of a key of the form "<seat>.<name>.<digest>", or undefined for a key of another form. */
|
||||
export function parseHoldsBySeatNameDigestKey(key: string): { seat: string; name: string; digest: string } | undefined {
|
||||
let rest = key;
|
||||
const at0 = rest.indexOf(".");
|
||||
if (at0 <= 0) return undefined;
|
||||
const seat = rest.slice(0, at0);
|
||||
rest = rest.slice(at0 + 1);
|
||||
const at2 = rest.indexOf(".");
|
||||
if (at2 <= 0) return undefined;
|
||||
const name = rest.slice(0, at2);
|
||||
rest = rest.slice(at2 + 1);
|
||||
if (rest === "") return undefined;
|
||||
const digest = rest;
|
||||
rest = "";
|
||||
if (rest !== "") return undefined;
|
||||
return { seat, name, digest };
|
||||
}
|
||||
|
||||
/** The key of one value of the state windows under the form "<caller>". */
|
||||
export function windowsByCallerKey(caller: string): string {
|
||||
return caller;
|
||||
}
|
||||
|
||||
/** The parts of a key of the form "<caller>", or undefined for a key of another form. */
|
||||
export function parseWindowsByCallerKey(key: string): { caller: string } | undefined {
|
||||
let rest = key;
|
||||
if (rest === "") return undefined;
|
||||
const caller = rest;
|
||||
rest = "";
|
||||
if (rest !== "") return undefined;
|
||||
return { caller };
|
||||
}
|
||||
|
||||
export type Artifact = registry.Artifact;
|
||||
export type KeptEvent = registry.KeptEvent;
|
||||
export type Outcome = registry.Outcome;
|
||||
export type Part = registry.Part;
|
||||
export type PublishAnswer = registry.PublishAnswer;
|
||||
export type PublishInput = registry.PublishInput;
|
||||
export type PublishPartAnswer = registry.PublishPartAnswer;
|
||||
export type PublishPartInput = registry.PublishPartInput;
|
||||
export type PublishedEvent = registry.PublishedEvent;
|
||||
export type RefusedEvent = registry.RefusedEvent;
|
||||
export type RemoveAnswer = registry.RemoveAnswer;
|
||||
export type RemoveInput = registry.RemoveInput;
|
||||
export type RemovedEvent = registry.RemovedEvent;
|
||||
export type VersionsAnswer = registry.VersionsAnswer;
|
||||
export type VersionsInput = registry.VersionsInput;
|
||||
|
||||
/** the body of held and released */
|
||||
export interface Hold {
|
||||
/** when, RFC 3339 */
|
||||
"at"?: string;
|
||||
/** the caller the holder proved */
|
||||
"by"?: string;
|
||||
/** the digest */
|
||||
"digest": string;
|
||||
/** the module holding the seat */
|
||||
"holder": string;
|
||||
/** the name */
|
||||
"name": string;
|
||||
/** why */
|
||||
"reason"?: string;
|
||||
}
|
||||
|
||||
export interface Listed {
|
||||
/** for a manifest keeping one bundle or archive, the digest of that blob: what a reference names it by */
|
||||
"artifact"?: string;
|
||||
/** the manifest's digest, sha256:<hex> */
|
||||
"digest": string;
|
||||
/** a hold keeps it from retention */
|
||||
"held": boolean;
|
||||
/** it is an index, naming a manifest per platform */
|
||||
"index": boolean;
|
||||
/** for an index, the digests of the manifests it names */
|
||||
"platforms"?: string[];
|
||||
/** its bytes: its own content and every blob it names */
|
||||
"size": number;
|
||||
/** the tags naming it, none for an untagged one */
|
||||
"tags"?: string[];
|
||||
}
|
||||
|
||||
export interface Repository {
|
||||
/** each manifest it holds, by digest */
|
||||
"manifests": Listed[];
|
||||
/** the repository */
|
||||
"name": string;
|
||||
}
|
||||
|
||||
/** is what mirror takes. */
|
||||
export interface MirrorInput {
|
||||
/** its pinned digest, sha256:<hex> */
|
||||
"digest": string;
|
||||
/** the name it is kept under */
|
||||
"name": string;
|
||||
/** the outside image's reference */
|
||||
"source": string;
|
||||
}
|
||||
|
||||
/** is what mirror answers. */
|
||||
export interface MirrorAnswer {
|
||||
/** the digest stored */
|
||||
"digest": string;
|
||||
/** the reference the seat answers */
|
||||
"reference": string;
|
||||
}
|
||||
|
||||
/** is what tags takes. */
|
||||
export interface TagsInput {
|
||||
/** the image */
|
||||
"name": string;
|
||||
}
|
||||
|
||||
export interface TagsAnswerTagsItem {
|
||||
/** its digest */
|
||||
"digest": string;
|
||||
/** the tag */
|
||||
"tag": string;
|
||||
}
|
||||
|
||||
/** is what tags answers. */
|
||||
export interface TagsAnswer {
|
||||
/** the image */
|
||||
"name": string;
|
||||
/** every tag */
|
||||
"tags": TagsAnswerTagsItem[];
|
||||
}
|
||||
|
||||
/** is what hold takes. */
|
||||
export interface HoldInput {
|
||||
/** the digest */
|
||||
"digest": string;
|
||||
/** the name */
|
||||
"name": string;
|
||||
/** which of the sweep's rules keeps it */
|
||||
"reason": string;
|
||||
}
|
||||
|
||||
/** is what hold answers. */
|
||||
export interface HoldAnswer {
|
||||
/** it is held */
|
||||
"held": boolean;
|
||||
/** when the hold began, RFC 3339 */
|
||||
"since"?: string;
|
||||
}
|
||||
|
||||
/** is what release takes. */
|
||||
export interface ReleaseInput {
|
||||
/** the digest */
|
||||
"digest": string;
|
||||
/** the name */
|
||||
"name": string;
|
||||
/** why it is let go */
|
||||
"reason": string;
|
||||
}
|
||||
|
||||
/** is what release answers. */
|
||||
export interface ReleaseAnswer {
|
||||
/** a hold was let go; false when there was none */
|
||||
"released": boolean;
|
||||
}
|
||||
|
||||
/** is what list takes. */
|
||||
export interface ListInput {
|
||||
/** the repository, <module>/<artifact>; every repository of the seat when not given */
|
||||
"name"?: string;
|
||||
}
|
||||
|
||||
/** is what list answers. */
|
||||
export interface ListAnswer {
|
||||
/** each repository named, in order of its name */
|
||||
"repositories": Repository[];
|
||||
}
|
||||
|
||||
/** is the body of the event held. */
|
||||
export type HeldEvent = Hold;
|
||||
|
||||
/** is the body of the event released. */
|
||||
export type ReleasedEvent = Hold;
|
||||
|
||||
/** is one value of the state holds under the key <seat>.<name>.<digest>. */
|
||||
export interface HoldsBySeatNameDigestValue {
|
||||
"by": string;
|
||||
"digest": string;
|
||||
"kind": string;
|
||||
"name": string;
|
||||
"seat": string;
|
||||
/** when (RFC 3339 time) */
|
||||
"since": string;
|
||||
"why"?: string;
|
||||
}
|
||||
|
||||
export interface WindowsByCallerValueDroppedItem {
|
||||
/** when (RFC 3339 time) */
|
||||
"at"?: string;
|
||||
"by"?: string;
|
||||
"node": string;
|
||||
"why"?: string;
|
||||
}
|
||||
|
||||
/** is one value of the state windows under the key <caller>. */
|
||||
export interface WindowsByCallerValue {
|
||||
/** when it last closed (RFC 3339 time) */
|
||||
"closed"?: string;
|
||||
/** retired nodes dropped by hand */
|
||||
"dropped"?: WindowsByCallerValueDroppedItem[];
|
||||
/** the nodes holding it open */
|
||||
"holding"?: string[];
|
||||
"last_holding"?: string[];
|
||||
/** the caller whose window it is */
|
||||
"name": string;
|
||||
"open"?: boolean;
|
||||
/** node to when it began to hold */
|
||||
"since"?: Record<string, string>;
|
||||
/** of each node holding the window, why it holds it */
|
||||
"why"?: Record<string, string>;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,187 @@
|
||||
// Code generated by seatgen from the seat definitions; DO NOT EDIT.
|
||||
|
||||
// The part "registry" several seats include whole: The verbs and events every registry seat shares, written once so the four cannot drift (design 51, design 26). Who may call each verb is the holder's trusted config value listing its callers (ADR 0363's form), checked against the caller the tool runner proved.
|
||||
|
||||
export const verbs = {
|
||||
publish: "publish",
|
||||
publishPart: "publish-part",
|
||||
versions: "versions",
|
||||
remove: "remove",
|
||||
} as const;
|
||||
|
||||
export const events = {
|
||||
published: "published",
|
||||
refused: "refused",
|
||||
removed: "removed",
|
||||
kept: "kept",
|
||||
} as const;
|
||||
|
||||
/** one published thing, as every registry event and answer names it */
|
||||
export interface Artifact {
|
||||
/** the source commit it was built from */
|
||||
"commit"?: string;
|
||||
/** its digest, sha256:<hex> (a Go package's h1: hash beside it in the version's record) */
|
||||
"digest": string;
|
||||
/** the build's fingerprint */
|
||||
"fingerprint"?: string;
|
||||
/** what it is */
|
||||
"kind": "package" | "image" | "bundle" | "archive" | "upstream";
|
||||
/** the package's, image's, bundle's or archive's name; a package's is its name in its own language (design 32): a Go package's the go.mod module path (git.novox.be/novox/mesh-sdk/go), an npm package's package.json's name (@novox/mesh-sdk) */
|
||||
"name": string;
|
||||
/** the tag it carries, for a tagged version */
|
||||
"tag"?: string;
|
||||
/** the version the builder gave it (design 51 *The versions*); empty for a bundle or archive published by its digest alone */
|
||||
"version"?: string;
|
||||
}
|
||||
|
||||
/** the body of published, refused and removed */
|
||||
export interface Outcome {
|
||||
/** the source commit it was built from */
|
||||
"commit"?: string;
|
||||
/** its digest, sha256:<hex> (a Go package's h1: hash beside it in the version's record) */
|
||||
"digest": string;
|
||||
/** the build's fingerprint */
|
||||
"fingerprint"?: string;
|
||||
/** the module holding the seat that said it */
|
||||
"holder": string;
|
||||
/** what it is */
|
||||
"kind": "package" | "image" | "bundle" | "archive" | "upstream";
|
||||
/** the Go module path a Go package is fetched under, equal to name for a Go package (go-package-registry's published); empty for every other kind of artifact */
|
||||
"module"?: string;
|
||||
/** the package's, image's, bundle's or archive's name; a package's is its name in its own language (design 32): a Go package's the go.mod module path (git.novox.be/novox/mesh-sdk/go), an npm package's package.json's name (@novox/mesh-sdk) */
|
||||
"name": string;
|
||||
/** why: a refusal's cause, a removal's (retention, tag-gone, by-hand), or why a removal was kept (shared-digest) */
|
||||
"reason"?: string;
|
||||
/** the reference the seat answers: <registry>/<module>/<artifact>@sha256:<digest>, or <name>@<version> for a package (ADR 0605 §9) */
|
||||
"reference"?: string;
|
||||
/** the tag it carries, for a tagged version */
|
||||
"tag"?: string;
|
||||
/** for kept: the other tags still naming its digest */
|
||||
"tags"?: string[];
|
||||
/** the transfer it ends, when it ended one */
|
||||
"transfer"?: string;
|
||||
/** the version the builder gave it (design 51 *The versions*); empty for a bundle or archive published by its digest alone */
|
||||
"version"?: string;
|
||||
}
|
||||
|
||||
/** one part of a transfer */
|
||||
export interface Part {
|
||||
/** the part's digest, sha256:<hex>: an image's layer or config, a package's file, a bundle's blob */
|
||||
"digest": string;
|
||||
/** its size in bytes */
|
||||
"size": number;
|
||||
}
|
||||
|
||||
/** is what publish takes. */
|
||||
export interface PublishInput {
|
||||
/** the source commit it was built from */
|
||||
"commit"?: string;
|
||||
/** its digest, sha256:<hex> (a Go package's h1: hash beside it in the version's record) */
|
||||
"digest": string;
|
||||
/** the build's fingerprint */
|
||||
"fingerprint"?: string;
|
||||
/** what it is */
|
||||
"kind": "package" | "image" | "bundle" | "archive" | "upstream";
|
||||
/** the package's, image's, bundle's or archive's name; a package's is its name in its own language (design 32): a Go package's the go.mod module path (git.novox.be/novox/mesh-sdk/go), an npm package's package.json's name (@novox/mesh-sdk) */
|
||||
"name": string;
|
||||
/** every part's digest and size; none when the digest is already held */
|
||||
"parts"?: Part[];
|
||||
/** the tag it carries, for a tagged version */
|
||||
"tag"?: string;
|
||||
/** the transfer's name, the caller's own; the same name opens the same transfer again */
|
||||
"transfer": string;
|
||||
/** the version the builder gave it (design 51 *The versions*); empty for a bundle or archive published by its digest alone */
|
||||
"version"?: string;
|
||||
}
|
||||
|
||||
/** is what publish answers. */
|
||||
export interface PublishAnswer {
|
||||
/** the digest is held already: nothing to send; the outcome follows as an event */
|
||||
"held"?: boolean;
|
||||
/** the digests of the parts the holder lacks, to send by publish-part */
|
||||
"lacks"?: string[];
|
||||
/** the transfer */
|
||||
"transfer": string;
|
||||
}
|
||||
|
||||
/** is what publish-part takes. */
|
||||
export interface PublishPartInput {
|
||||
/** the chunk's bytes, base64 (at most 760 KiB before encoding) */
|
||||
"data": string;
|
||||
/** this chunk is the part's last */
|
||||
"last": boolean;
|
||||
/** the part's digest */
|
||||
"part": string;
|
||||
/** the chunk's place in its part, from 0 */
|
||||
"sequence": number;
|
||||
/** the transfer */
|
||||
"transfer": string;
|
||||
}
|
||||
|
||||
/** is what publish-part answers. */
|
||||
export interface PublishPartAnswer {
|
||||
/** the part is whole and its bytes match its digest */
|
||||
"complete"?: boolean;
|
||||
/** the part */
|
||||
"part": string;
|
||||
/** the chunks of the part held so far */
|
||||
"received"?: number;
|
||||
/** the transfer */
|
||||
"transfer": string;
|
||||
}
|
||||
|
||||
/** is what versions takes. */
|
||||
export interface VersionsInput {
|
||||
/** narrowed to one kind (optional) */
|
||||
"kind"?: string;
|
||||
/** the name */
|
||||
"name": string;
|
||||
}
|
||||
|
||||
/** is what versions answers. */
|
||||
export interface VersionsAnswer {
|
||||
/** the name */
|
||||
"name": string;
|
||||
/** newest first */
|
||||
"versions": Artifact[];
|
||||
}
|
||||
|
||||
/** is what remove takes. */
|
||||
export interface RemoveInput {
|
||||
/** the digest (for a bundle, archive or copied base) */
|
||||
"digest"?: string;
|
||||
/** the name */
|
||||
"name": string;
|
||||
/** why it goes */
|
||||
"reason": "tag-gone" | "retention";
|
||||
/** the tag (for a tagged version) */
|
||||
"tag"?: string;
|
||||
/** the version */
|
||||
"version"?: string;
|
||||
}
|
||||
|
||||
/** is what remove answers. */
|
||||
export interface RemoveAnswer {
|
||||
"artifact"?: Artifact;
|
||||
/** it stays whole, its tag and its bytes: the store cannot remove the tag alone while another tag names its digest (reason, tags) */
|
||||
"kept"?: boolean;
|
||||
/** why it was kept: shared-digest, another tag names its digest */
|
||||
"reason"?: "shared-digest";
|
||||
/** it is gone */
|
||||
"removed": boolean;
|
||||
/** the other tags still naming its digest, when kept */
|
||||
"tags"?: string[];
|
||||
}
|
||||
|
||||
/** is the body of the event published. */
|
||||
export type PublishedEvent = Outcome;
|
||||
|
||||
/** is the body of the event refused. */
|
||||
export type RefusedEvent = Outcome;
|
||||
|
||||
/** is the body of the event removed. */
|
||||
export type RemovedEvent = Outcome;
|
||||
|
||||
/** is the body of the event kept. */
|
||||
export type KeptEvent = Outcome;
|
||||
|
||||
@@ -0,0 +1,12 @@
|
||||
{
|
||||
"compilerOptions": {
|
||||
"target": "ES2022",
|
||||
"module": "NodeNext",
|
||||
"moduleResolution": "NodeNext",
|
||||
"noEmit": true,
|
||||
"strict": true,
|
||||
"skipLibCheck": true,
|
||||
"forceConsistentCasingInFileNames": true
|
||||
},
|
||||
"include": ["src/**/*.ts"]
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
{
|
||||
"lockfileVersion": 3,
|
||||
"name": "@novox/mesh-delivery-contracts-go-package-registry",
|
||||
"packages": {
|
||||
"": {
|
||||
"name": "@novox/mesh-delivery-contracts-go-package-registry",
|
||||
"version": "0.1.0"
|
||||
}
|
||||
},
|
||||
"requires": true,
|
||||
"version": "0.1.0"
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
{
|
||||
"description": "The contract of the seat go-package-registry, generated from its definition.",
|
||||
"exports": {
|
||||
".": "./src/index.ts"
|
||||
},
|
||||
"files": [
|
||||
"src"
|
||||
],
|
||||
"name": "@novox/mesh-delivery-contracts-go-package-registry",
|
||||
"type": "module",
|
||||
"types": "./src/index.ts",
|
||||
"version": "0.1.0"
|
||||
}
|
||||
@@ -0,0 +1,54 @@
|
||||
// Code generated by seatgen from the seat definitions; DO NOT EDIT.
|
||||
|
||||
// The contract of the seat "go-package-registry", defined whole (seats/go-package-registry/seat.json): The mesh's Go modules: Go's module proxy protocol's read side, and publishing over the bus.
|
||||
|
||||
import type * as registry from "./parts/registry.js";
|
||||
|
||||
export const seat = "go-package-registry";
|
||||
|
||||
export const verbs = {
|
||||
publish: "publish",
|
||||
publishPart: "publish-part",
|
||||
versions: "versions",
|
||||
remove: "remove",
|
||||
} as const;
|
||||
|
||||
export const events = {
|
||||
published: "published",
|
||||
refused: "refused",
|
||||
removed: "removed",
|
||||
kept: "kept",
|
||||
} as const;
|
||||
|
||||
export const family = { name: "go-package-registry.events", maxAgeDays: 7 } as const;
|
||||
|
||||
export const publishedSubject = "mesh.seat.go-package-registry.event.published";
|
||||
|
||||
export const refusedSubject = "mesh.seat.go-package-registry.event.refused";
|
||||
|
||||
export const removedSubject = "mesh.seat.go-package-registry.event.removed";
|
||||
|
||||
export const keptSubject = "mesh.seat.go-package-registry.event.kept";
|
||||
|
||||
export type Artifact = registry.Artifact;
|
||||
export type KeptEvent = registry.KeptEvent;
|
||||
export type Outcome = registry.Outcome;
|
||||
export type Part = registry.Part;
|
||||
export type PublishAnswer = registry.PublishAnswer;
|
||||
export type PublishInput = registry.PublishInput;
|
||||
export type PublishPartAnswer = registry.PublishPartAnswer;
|
||||
export type PublishPartInput = registry.PublishPartInput;
|
||||
export type PublishedEvent = registry.PublishedEvent;
|
||||
export type RefusedEvent = registry.RefusedEvent;
|
||||
export type RemoveAnswer = registry.RemoveAnswer;
|
||||
export type RemoveInput = registry.RemoveInput;
|
||||
export type RemovedEvent = registry.RemovedEvent;
|
||||
export type VersionsAnswer = registry.VersionsAnswer;
|
||||
export type VersionsInput = registry.VersionsInput;
|
||||
|
||||
/** is the form of the provision go-package-registry. */
|
||||
export interface GoPackageRegistryForm {
|
||||
/** the read side's address */
|
||||
"url": string;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,187 @@
|
||||
// Code generated by seatgen from the seat definitions; DO NOT EDIT.
|
||||
|
||||
// The part "registry" several seats include whole: The verbs and events every registry seat shares, written once so the four cannot drift (design 51, design 26). Who may call each verb is the holder's trusted config value listing its callers (ADR 0363's form), checked against the caller the tool runner proved.
|
||||
|
||||
export const verbs = {
|
||||
publish: "publish",
|
||||
publishPart: "publish-part",
|
||||
versions: "versions",
|
||||
remove: "remove",
|
||||
} as const;
|
||||
|
||||
export const events = {
|
||||
published: "published",
|
||||
refused: "refused",
|
||||
removed: "removed",
|
||||
kept: "kept",
|
||||
} as const;
|
||||
|
||||
/** one published thing, as every registry event and answer names it */
|
||||
export interface Artifact {
|
||||
/** the source commit it was built from */
|
||||
"commit"?: string;
|
||||
/** its digest, sha256:<hex> (a Go package's h1: hash beside it in the version's record) */
|
||||
"digest": string;
|
||||
/** the build's fingerprint */
|
||||
"fingerprint"?: string;
|
||||
/** what it is */
|
||||
"kind": "package" | "image" | "bundle" | "archive" | "upstream";
|
||||
/** the package's, image's, bundle's or archive's name; a package's is its name in its own language (design 32): a Go package's the go.mod module path (git.novox.be/novox/mesh-sdk/go), an npm package's package.json's name (@novox/mesh-sdk) */
|
||||
"name": string;
|
||||
/** the tag it carries, for a tagged version */
|
||||
"tag"?: string;
|
||||
/** the version the builder gave it (design 51 *The versions*); empty for a bundle or archive published by its digest alone */
|
||||
"version"?: string;
|
||||
}
|
||||
|
||||
/** the body of published, refused and removed */
|
||||
export interface Outcome {
|
||||
/** the source commit it was built from */
|
||||
"commit"?: string;
|
||||
/** its digest, sha256:<hex> (a Go package's h1: hash beside it in the version's record) */
|
||||
"digest": string;
|
||||
/** the build's fingerprint */
|
||||
"fingerprint"?: string;
|
||||
/** the module holding the seat that said it */
|
||||
"holder": string;
|
||||
/** what it is */
|
||||
"kind": "package" | "image" | "bundle" | "archive" | "upstream";
|
||||
/** the Go module path a Go package is fetched under, equal to name for a Go package (go-package-registry's published); empty for every other kind of artifact */
|
||||
"module"?: string;
|
||||
/** the package's, image's, bundle's or archive's name; a package's is its name in its own language (design 32): a Go package's the go.mod module path (git.novox.be/novox/mesh-sdk/go), an npm package's package.json's name (@novox/mesh-sdk) */
|
||||
"name": string;
|
||||
/** why: a refusal's cause, a removal's (retention, tag-gone, by-hand), or why a removal was kept (shared-digest) */
|
||||
"reason"?: string;
|
||||
/** the reference the seat answers: <registry>/<module>/<artifact>@sha256:<digest>, or <name>@<version> for a package (ADR 0605 §9) */
|
||||
"reference"?: string;
|
||||
/** the tag it carries, for a tagged version */
|
||||
"tag"?: string;
|
||||
/** for kept: the other tags still naming its digest */
|
||||
"tags"?: string[];
|
||||
/** the transfer it ends, when it ended one */
|
||||
"transfer"?: string;
|
||||
/** the version the builder gave it (design 51 *The versions*); empty for a bundle or archive published by its digest alone */
|
||||
"version"?: string;
|
||||
}
|
||||
|
||||
/** one part of a transfer */
|
||||
export interface Part {
|
||||
/** the part's digest, sha256:<hex>: an image's layer or config, a package's file, a bundle's blob */
|
||||
"digest": string;
|
||||
/** its size in bytes */
|
||||
"size": number;
|
||||
}
|
||||
|
||||
/** is what publish takes. */
|
||||
export interface PublishInput {
|
||||
/** the source commit it was built from */
|
||||
"commit"?: string;
|
||||
/** its digest, sha256:<hex> (a Go package's h1: hash beside it in the version's record) */
|
||||
"digest": string;
|
||||
/** the build's fingerprint */
|
||||
"fingerprint"?: string;
|
||||
/** what it is */
|
||||
"kind": "package" | "image" | "bundle" | "archive" | "upstream";
|
||||
/** the package's, image's, bundle's or archive's name; a package's is its name in its own language (design 32): a Go package's the go.mod module path (git.novox.be/novox/mesh-sdk/go), an npm package's package.json's name (@novox/mesh-sdk) */
|
||||
"name": string;
|
||||
/** every part's digest and size; none when the digest is already held */
|
||||
"parts"?: Part[];
|
||||
/** the tag it carries, for a tagged version */
|
||||
"tag"?: string;
|
||||
/** the transfer's name, the caller's own; the same name opens the same transfer again */
|
||||
"transfer": string;
|
||||
/** the version the builder gave it (design 51 *The versions*); empty for a bundle or archive published by its digest alone */
|
||||
"version"?: string;
|
||||
}
|
||||
|
||||
/** is what publish answers. */
|
||||
export interface PublishAnswer {
|
||||
/** the digest is held already: nothing to send; the outcome follows as an event */
|
||||
"held"?: boolean;
|
||||
/** the digests of the parts the holder lacks, to send by publish-part */
|
||||
"lacks"?: string[];
|
||||
/** the transfer */
|
||||
"transfer": string;
|
||||
}
|
||||
|
||||
/** is what publish-part takes. */
|
||||
export interface PublishPartInput {
|
||||
/** the chunk's bytes, base64 (at most 760 KiB before encoding) */
|
||||
"data": string;
|
||||
/** this chunk is the part's last */
|
||||
"last": boolean;
|
||||
/** the part's digest */
|
||||
"part": string;
|
||||
/** the chunk's place in its part, from 0 */
|
||||
"sequence": number;
|
||||
/** the transfer */
|
||||
"transfer": string;
|
||||
}
|
||||
|
||||
/** is what publish-part answers. */
|
||||
export interface PublishPartAnswer {
|
||||
/** the part is whole and its bytes match its digest */
|
||||
"complete"?: boolean;
|
||||
/** the part */
|
||||
"part": string;
|
||||
/** the chunks of the part held so far */
|
||||
"received"?: number;
|
||||
/** the transfer */
|
||||
"transfer": string;
|
||||
}
|
||||
|
||||
/** is what versions takes. */
|
||||
export interface VersionsInput {
|
||||
/** narrowed to one kind (optional) */
|
||||
"kind"?: string;
|
||||
/** the name */
|
||||
"name": string;
|
||||
}
|
||||
|
||||
/** is what versions answers. */
|
||||
export interface VersionsAnswer {
|
||||
/** the name */
|
||||
"name": string;
|
||||
/** newest first */
|
||||
"versions": Artifact[];
|
||||
}
|
||||
|
||||
/** is what remove takes. */
|
||||
export interface RemoveInput {
|
||||
/** the digest (for a bundle, archive or copied base) */
|
||||
"digest"?: string;
|
||||
/** the name */
|
||||
"name": string;
|
||||
/** why it goes */
|
||||
"reason": "tag-gone" | "retention";
|
||||
/** the tag (for a tagged version) */
|
||||
"tag"?: string;
|
||||
/** the version */
|
||||
"version"?: string;
|
||||
}
|
||||
|
||||
/** is what remove answers. */
|
||||
export interface RemoveAnswer {
|
||||
"artifact"?: Artifact;
|
||||
/** it stays whole, its tag and its bytes: the store cannot remove the tag alone while another tag names its digest (reason, tags) */
|
||||
"kept"?: boolean;
|
||||
/** why it was kept: shared-digest, another tag names its digest */
|
||||
"reason"?: "shared-digest";
|
||||
/** it is gone */
|
||||
"removed": boolean;
|
||||
/** the other tags still naming its digest, when kept */
|
||||
"tags"?: string[];
|
||||
}
|
||||
|
||||
/** is the body of the event published. */
|
||||
export type PublishedEvent = Outcome;
|
||||
|
||||
/** is the body of the event refused. */
|
||||
export type RefusedEvent = Outcome;
|
||||
|
||||
/** is the body of the event removed. */
|
||||
export type RemovedEvent = Outcome;
|
||||
|
||||
/** is the body of the event kept. */
|
||||
export type KeptEvent = Outcome;
|
||||
|
||||
@@ -0,0 +1,12 @@
|
||||
{
|
||||
"compilerOptions": {
|
||||
"target": "ES2022",
|
||||
"module": "NodeNext",
|
||||
"moduleResolution": "NodeNext",
|
||||
"noEmit": true,
|
||||
"strict": true,
|
||||
"skipLibCheck": true,
|
||||
"forceConsistentCasingInFileNames": true
|
||||
},
|
||||
"include": ["src/**/*.ts"]
|
||||
}
|
||||
+12
@@ -0,0 +1,12 @@
|
||||
{
|
||||
"lockfileVersion": 3,
|
||||
"name": "@novox/mesh-delivery-contracts-mesh-catalog",
|
||||
"packages": {
|
||||
"": {
|
||||
"name": "@novox/mesh-delivery-contracts-mesh-catalog",
|
||||
"version": "0.1.0"
|
||||
}
|
||||
},
|
||||
"requires": true,
|
||||
"version": "0.1.0"
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
{
|
||||
"description": "The contract of the seat mesh-catalog, generated from its definition.",
|
||||
"exports": {
|
||||
".": "./src/index.ts"
|
||||
},
|
||||
"files": [
|
||||
"src"
|
||||
],
|
||||
"name": "@novox/mesh-delivery-contracts-mesh-catalog",
|
||||
"type": "module",
|
||||
"types": "./src/index.ts",
|
||||
"version": "0.1.0"
|
||||
}
|
||||
@@ -0,0 +1,430 @@
|
||||
// Code generated by seatgen from the seat definitions; DO NOT EDIT.
|
||||
|
||||
// The contract of the seat "mesh-catalog", defined whole (seats/mesh-catalog/seat.json): The catalog of the mesh: every module the trunk holds with its manifest, fingerprint and verdict, the packages published, and the seat list (ADR 0636 §1, §4).
|
||||
|
||||
export const seat = "mesh-catalog";
|
||||
|
||||
export const verbs = {
|
||||
modules: "modules",
|
||||
module: "module",
|
||||
provides: "provides",
|
||||
dependents: "dependents",
|
||||
} as const;
|
||||
|
||||
export const events = {
|
||||
seatDefinitionChanged: "seat-definition-changed",
|
||||
seatDefinitionRemoved: "seat-definition-removed",
|
||||
} as const;
|
||||
|
||||
export const family = { name: "mesh-catalog.events", maxAgeDays: 7 } as const;
|
||||
|
||||
export const seatDefinitionChangedSubject = "mesh.seat.mesh-catalog.event.seat-definition-changed";
|
||||
|
||||
export const seatDefinitionRemovedSubject = "mesh.seat.mesh-catalog.event.seat-definition-removed";
|
||||
|
||||
export const states = {
|
||||
manifests: "manifests",
|
||||
modules: "modules",
|
||||
candidates: "candidates",
|
||||
verdicts: "verdicts",
|
||||
predefined: "predefined",
|
||||
configCandidates: "config-candidates",
|
||||
packages: "packages",
|
||||
} as const;
|
||||
|
||||
/** The key of one value of the state manifests under the form "<digest>". */
|
||||
export function manifestsByDigestKey(digest: string): string {
|
||||
return digest;
|
||||
}
|
||||
|
||||
/** The parts of a key of the form "<digest>", or undefined for a key of another form. */
|
||||
export function parseManifestsByDigestKey(key: string): { digest: string } | undefined {
|
||||
let rest = key;
|
||||
if (rest === "") return undefined;
|
||||
const digest = rest;
|
||||
rest = "";
|
||||
if (rest !== "") return undefined;
|
||||
return { digest };
|
||||
}
|
||||
|
||||
/** The key of one value of the state modules under the form "<module>". */
|
||||
export function modulesByModuleKey(module: string): string {
|
||||
return module;
|
||||
}
|
||||
|
||||
/** The parts of a key of the form "<module>", or undefined for a key of another form. */
|
||||
export function parseModulesByModuleKey(key: string): { module: string } | undefined {
|
||||
let rest = key;
|
||||
if (rest === "") return undefined;
|
||||
const module = rest;
|
||||
rest = "";
|
||||
if (rest !== "") return undefined;
|
||||
return { module };
|
||||
}
|
||||
|
||||
/** The key of one value of the state candidates under the form "<group>". */
|
||||
export function candidatesByGroupKey(group: string): string {
|
||||
return group;
|
||||
}
|
||||
|
||||
/** The parts of a key of the form "<group>", or undefined for a key of another form. */
|
||||
export function parseCandidatesByGroupKey(key: string): { group: string } | undefined {
|
||||
let rest = key;
|
||||
if (rest === "") return undefined;
|
||||
const group = rest;
|
||||
rest = "";
|
||||
if (rest !== "") return undefined;
|
||||
return { group };
|
||||
}
|
||||
|
||||
/** The key of one value of the state verdicts under the form "<digest>". */
|
||||
export function verdictsByDigestKey(digest: string): string {
|
||||
return digest;
|
||||
}
|
||||
|
||||
/** The parts of a key of the form "<digest>", or undefined for a key of another form. */
|
||||
export function parseVerdictsByDigestKey(key: string): { digest: string } | undefined {
|
||||
let rest = key;
|
||||
if (rest === "") return undefined;
|
||||
const digest = rest;
|
||||
rest = "";
|
||||
if (rest !== "") return undefined;
|
||||
return { digest };
|
||||
}
|
||||
|
||||
/** The key of one value of the state predefined under the form "list". */
|
||||
export function predefinedByListKey(): string {
|
||||
return "list";
|
||||
}
|
||||
|
||||
/** The parts of a key of the form "list", or undefined for a key of another form. */
|
||||
export function parsePredefinedByListKey(key: string): { } | undefined {
|
||||
let rest = key;
|
||||
if (!rest.startsWith("list")) return undefined;
|
||||
rest = rest.slice(4);
|
||||
if (rest !== "") return undefined;
|
||||
return { };
|
||||
}
|
||||
|
||||
/** The key of one value of the state config-candidates under the form "<pull>". */
|
||||
export function configCandidatesByPullKey(pull: string): string {
|
||||
return pull;
|
||||
}
|
||||
|
||||
/** The parts of a key of the form "<pull>", or undefined for a key of another form. */
|
||||
export function parseConfigCandidatesByPullKey(key: string): { pull: string } | undefined {
|
||||
let rest = key;
|
||||
if (rest === "") return undefined;
|
||||
const pull = rest;
|
||||
rest = "";
|
||||
if (rest !== "") return undefined;
|
||||
return { pull };
|
||||
}
|
||||
|
||||
/** The key of one value of the state packages under the form "<module>". */
|
||||
export function packagesByModuleKey(module: string): string {
|
||||
return module;
|
||||
}
|
||||
|
||||
/** The parts of a key of the form "<module>", or undefined for a key of another form. */
|
||||
export function parsePackagesByModuleKey(key: string): { module: string } | undefined {
|
||||
let rest = key;
|
||||
if (rest === "") return undefined;
|
||||
const module = rest;
|
||||
rest = "";
|
||||
if (rest !== "") return undefined;
|
||||
return { module };
|
||||
}
|
||||
|
||||
/** The key of one value of the state packages under the form "<package>.<series>". */
|
||||
export function packagesByPackageSeriesKey(packagePart: string, series: string): string {
|
||||
return packagePart + "." + series;
|
||||
}
|
||||
|
||||
/** The parts of a key of the form "<package>.<series>", or undefined for a key of another form. */
|
||||
export function parsePackagesByPackageSeriesKey(key: string): { packagePart: string; series: string } | undefined {
|
||||
let rest = key;
|
||||
const at0 = rest.indexOf(".");
|
||||
if (at0 <= 0) return undefined;
|
||||
const packagePart = rest.slice(0, at0);
|
||||
rest = rest.slice(at0 + 1);
|
||||
if (rest === "") return undefined;
|
||||
const series = rest;
|
||||
rest = "";
|
||||
if (rest !== "") return undefined;
|
||||
return { packagePart, series };
|
||||
}
|
||||
|
||||
/** is what modules takes. */
|
||||
export interface ModulesInput {
|
||||
}
|
||||
|
||||
/** is what modules answers. */
|
||||
export type ModulesAnswer = Record<string, unknown>;
|
||||
|
||||
/** is what module takes. */
|
||||
export interface ModuleInput {
|
||||
/** the module's name */
|
||||
"module": string;
|
||||
}
|
||||
|
||||
/** is what module answers. */
|
||||
export type ModuleAnswer = Record<string, unknown>;
|
||||
|
||||
/** is what provides takes. */
|
||||
export interface ProvidesInput {
|
||||
/** the provision, such as postgres-database */
|
||||
"provision": string;
|
||||
}
|
||||
|
||||
/** is what provides answers. */
|
||||
export type ProvidesAnswer = Record<string, unknown>;
|
||||
|
||||
/** is what dependents takes. */
|
||||
export interface DependentsInput {
|
||||
/** the module's name */
|
||||
"module": string;
|
||||
}
|
||||
|
||||
/** is what dependents answers. */
|
||||
export type DependentsAnswer = Record<string, unknown>;
|
||||
|
||||
/** is the body of the event seat-definition-changed. */
|
||||
export interface SeatDefinitionChangedEvent {
|
||||
"digest": string;
|
||||
"seat": string;
|
||||
/** trunk */
|
||||
"source": string;
|
||||
}
|
||||
|
||||
/** is the body of the event seat-definition-removed. */
|
||||
export interface SeatDefinitionRemovedEvent {
|
||||
"seat": string;
|
||||
}
|
||||
|
||||
/** is one value of the state manifests under the key <digest>. */
|
||||
export interface ManifestsByDigestValue {
|
||||
/** the manifest, UTF-8 JSON text */
|
||||
"manifest": string;
|
||||
"module": string;
|
||||
/** the layout version */
|
||||
"v"?: number;
|
||||
}
|
||||
|
||||
export interface ModulesByModuleValueInputs2 {
|
||||
"bases"?: string[];
|
||||
"builder"?: string;
|
||||
"contexts"?: Record<string, string>;
|
||||
"import-form"?: string;
|
||||
"imports"?: string[];
|
||||
"locks"?: Record<string, string>;
|
||||
"packages"?: string[];
|
||||
"paths"?: Record<string, string>;
|
||||
"tree": string;
|
||||
}
|
||||
|
||||
/** is one value of the state modules under the key <module>. */
|
||||
export interface ModulesByModuleValue {
|
||||
"commit": string;
|
||||
/** the old-form fingerprint */
|
||||
"fp"?: string;
|
||||
"fp-src"?: number;
|
||||
/** src2: fingerprint */
|
||||
"fp2"?: string;
|
||||
"inputs2"?: ModulesByModuleValueInputs2;
|
||||
/** manifest digest */
|
||||
"manifest"?: string;
|
||||
"needs"?: string[];
|
||||
"none"?: string;
|
||||
/** why it has none */
|
||||
"none2"?: string;
|
||||
"packages"?: Record<string, string>;
|
||||
"path"?: string;
|
||||
"paths"?: Record<string, string[]>;
|
||||
"problems"?: string[];
|
||||
"repository": string;
|
||||
/** the source revision */
|
||||
"src"?: number;
|
||||
"toolchains"?: Record<string, unknown>;
|
||||
"unknown"?: string[];
|
||||
/** the layout version */
|
||||
"v"?: number;
|
||||
"verdict"?: string;
|
||||
"warnings"?: string[];
|
||||
}
|
||||
|
||||
export interface CandidatesByGroupValuePullsItemFpsValue {
|
||||
"fp2"?: string;
|
||||
"manifest"?: string;
|
||||
"none2"?: string;
|
||||
"packages"?: Record<string, string>;
|
||||
"paths"?: Record<string, string>;
|
||||
}
|
||||
|
||||
export interface CandidatesByGroupValuePullsItem {
|
||||
"fps"?: Record<string, CandidatesByGroupValuePullsItemFpsValue>;
|
||||
"head": string;
|
||||
"modules": string[];
|
||||
"number": number;
|
||||
"repository": string;
|
||||
}
|
||||
|
||||
/** is one value of the state candidates under the key <group>. */
|
||||
export interface CandidatesByGroupValue {
|
||||
/** the source revision it is read on */
|
||||
"base": number;
|
||||
"fpset"?: string;
|
||||
"group": string;
|
||||
"heads": string;
|
||||
"pulls": CandidatesByGroupValuePullsItem[];
|
||||
/** the layout version */
|
||||
"v"?: number;
|
||||
}
|
||||
|
||||
/** is one value of the state verdicts under the key <digest>. */
|
||||
export interface VerdictsByDigestValue {
|
||||
/** when (RFC 3339 time) */
|
||||
"at"?: string;
|
||||
"judge"?: string;
|
||||
"module": string;
|
||||
"problems"?: string[];
|
||||
"state": string;
|
||||
"unknown"?: string[];
|
||||
/** the layout version */
|
||||
"v"?: number;
|
||||
"warnings"?: string[];
|
||||
}
|
||||
|
||||
/** is one value of the state predefined under the key list. */
|
||||
export interface PredefinedByListValue {
|
||||
/** when (RFC 3339 time) */
|
||||
"at"?: string;
|
||||
"commit": string;
|
||||
/** the foundation's predefined list as kept */
|
||||
"list": unknown;
|
||||
/** the layout version */
|
||||
"v"?: number;
|
||||
}
|
||||
|
||||
export interface ConfigCandidatesByPullValueDeclaresValueValue {
|
||||
/** the default */
|
||||
"default": unknown;
|
||||
"description"?: string;
|
||||
/** the allowed values */
|
||||
"enum"?: unknown;
|
||||
"kind": string;
|
||||
"maximum"?: number;
|
||||
"minimum"?: number;
|
||||
"pattern"?: string;
|
||||
"predefined"?: string;
|
||||
"required"?: boolean;
|
||||
"runtime"?: boolean;
|
||||
"trusted"?: boolean;
|
||||
"type"?: string;
|
||||
"why": string;
|
||||
}
|
||||
|
||||
/** is one value of the state config-candidates under the key <pull>. */
|
||||
export interface ConfigCandidatesByPullValue {
|
||||
/** when (RFC 3339 time) */
|
||||
"at"?: string;
|
||||
/** module to key to declaration */
|
||||
"declares": Record<string, Record<string, ConfigCandidatesByPullValueDeclaresValueValue>>;
|
||||
"head": string;
|
||||
"pull": string;
|
||||
/** the layout version */
|
||||
"v"?: number;
|
||||
}
|
||||
|
||||
export interface PackagesByModuleValuePackagesItem {
|
||||
"digest": string;
|
||||
"package": string;
|
||||
}
|
||||
|
||||
/** is one value of the state packages under the key <module>. */
|
||||
export interface PackagesByModuleValue {
|
||||
/** when (RFC 3339 time) */
|
||||
"at"?: string;
|
||||
"commit": string;
|
||||
"dir"?: string;
|
||||
/** the Go module path */
|
||||
"module": string;
|
||||
"packages": PackagesByModuleValuePackagesItem[];
|
||||
"repository": string;
|
||||
/** digest of go.mod and go.sum */
|
||||
"requires"?: string;
|
||||
/** the layout version */
|
||||
"v"?: number;
|
||||
}
|
||||
|
||||
export interface PackagesByPackageSeriesValueNewestContentsPackagesItem {
|
||||
"digest": string;
|
||||
"package": string;
|
||||
}
|
||||
|
||||
export interface PackagesByPackageSeriesValueNewestContents {
|
||||
/** when (RFC 3339 time) */
|
||||
"at"?: string;
|
||||
"commit": string;
|
||||
"dir"?: string;
|
||||
/** the Go module path */
|
||||
"module": string;
|
||||
"packages": PackagesByPackageSeriesValueNewestContentsPackagesItem[];
|
||||
"repository": string;
|
||||
/** digest of go.mod and go.sum */
|
||||
"requires"?: string;
|
||||
/** the layout version */
|
||||
"v"?: number;
|
||||
}
|
||||
|
||||
export interface PackagesByPackageSeriesValueNewest {
|
||||
"commit"?: string;
|
||||
"contents"?: PackagesByPackageSeriesValueNewestContents;
|
||||
"digest"?: string;
|
||||
"fingerprint"?: string;
|
||||
"version": string;
|
||||
}
|
||||
|
||||
export interface PackagesByPackageSeriesValueNumberedContentsPackagesItem {
|
||||
"digest": string;
|
||||
"package": string;
|
||||
}
|
||||
|
||||
export interface PackagesByPackageSeriesValueNumberedContents {
|
||||
/** when (RFC 3339 time) */
|
||||
"at"?: string;
|
||||
"commit": string;
|
||||
"dir"?: string;
|
||||
/** the Go module path */
|
||||
"module": string;
|
||||
"packages": PackagesByPackageSeriesValueNumberedContentsPackagesItem[];
|
||||
"repository": string;
|
||||
/** digest of go.mod and go.sum */
|
||||
"requires"?: string;
|
||||
/** the layout version */
|
||||
"v"?: number;
|
||||
}
|
||||
|
||||
export interface PackagesByPackageSeriesValueNumbered {
|
||||
"commit"?: string;
|
||||
"contents"?: PackagesByPackageSeriesValueNumberedContents;
|
||||
"digest"?: string;
|
||||
"fingerprint"?: string;
|
||||
"version": string;
|
||||
}
|
||||
|
||||
/** is one value of the state packages under the key <package>.<series>. */
|
||||
export interface PackagesByPackageSeriesValue {
|
||||
/** when (RFC 3339 time) */
|
||||
"at"?: string;
|
||||
"go-module"?: string;
|
||||
"name": string;
|
||||
"newest"?: PackagesByPackageSeriesValueNewest;
|
||||
"numbered"?: PackagesByPackageSeriesValueNumbered;
|
||||
"package"?: string;
|
||||
"seat"?: string;
|
||||
"series": string;
|
||||
/** the layout version */
|
||||
"v"?: number;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,12 @@
|
||||
{
|
||||
"compilerOptions": {
|
||||
"target": "ES2022",
|
||||
"module": "NodeNext",
|
||||
"moduleResolution": "NodeNext",
|
||||
"noEmit": true,
|
||||
"strict": true,
|
||||
"skipLibCheck": true,
|
||||
"forceConsistentCasingInFileNames": true
|
||||
},
|
||||
"include": ["src/**/*.ts"]
|
||||
}
|
||||
+12
@@ -0,0 +1,12 @@
|
||||
{
|
||||
"lockfileVersion": 3,
|
||||
"name": "@novox/mesh-delivery-contracts-mesh-delivery",
|
||||
"packages": {
|
||||
"": {
|
||||
"name": "@novox/mesh-delivery-contracts-mesh-delivery",
|
||||
"version": "0.1.0"
|
||||
}
|
||||
},
|
||||
"requires": true,
|
||||
"version": "0.1.0"
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
{
|
||||
"description": "The contract of the seat mesh-delivery, generated from its definition.",
|
||||
"exports": {
|
||||
".": "./src/index.ts"
|
||||
},
|
||||
"files": [
|
||||
"src"
|
||||
],
|
||||
"name": "@novox/mesh-delivery-contracts-mesh-delivery",
|
||||
"type": "module",
|
||||
"types": "./src/index.ts",
|
||||
"version": "0.1.0"
|
||||
}
|
||||
@@ -0,0 +1,338 @@
|
||||
// Code generated by seatgen from the seat definitions; DO NOT EDIT.
|
||||
|
||||
// The contract of the seat "mesh-delivery", defined whole (seats/mesh-delivery/seat.json): The delivery gate: reads and steers deliveries, merges and the checks that decide them; the merge is made on the operator's Approve once both checks passed.
|
||||
|
||||
export const seat = "mesh-delivery";
|
||||
|
||||
export const verbs = {
|
||||
deliveries: "deliveries",
|
||||
times: "times",
|
||||
show: "show",
|
||||
groups: "groups",
|
||||
whatIf: "what-if",
|
||||
checks: "checks",
|
||||
table: "table",
|
||||
stalled: "stalled",
|
||||
recheck: "recheck",
|
||||
release: "release",
|
||||
stop: "stop",
|
||||
close: "close",
|
||||
retireHistory: "retire-history",
|
||||
settle: "settle",
|
||||
} as const;
|
||||
|
||||
export const states = {
|
||||
source: "source",
|
||||
merges: "merges",
|
||||
asks: "asks",
|
||||
owed: "owed",
|
||||
turns: "turns",
|
||||
} as const;
|
||||
|
||||
/** The key of one value of the state source under the form "main". */
|
||||
export function sourceByMainKey(): string {
|
||||
return "main";
|
||||
}
|
||||
|
||||
/** The parts of a key of the form "main", or undefined for a key of another form. */
|
||||
export function parseSourceByMainKey(key: string): { } | undefined {
|
||||
let rest = key;
|
||||
if (!rest.startsWith("main")) return undefined;
|
||||
rest = rest.slice(4);
|
||||
if (rest !== "") return undefined;
|
||||
return { };
|
||||
}
|
||||
|
||||
/** The key of one value of the state merges under the form "<group>". */
|
||||
export function mergesByGroupKey(group: string): string {
|
||||
return group;
|
||||
}
|
||||
|
||||
/** The parts of a key of the form "<group>", or undefined for a key of another form. */
|
||||
export function parseMergesByGroupKey(key: string): { group: string } | undefined {
|
||||
let rest = key;
|
||||
if (rest === "") return undefined;
|
||||
const group = rest;
|
||||
rest = "";
|
||||
if (rest !== "") return undefined;
|
||||
return { group };
|
||||
}
|
||||
|
||||
/** The key of one value of the state asks under the form "merge.<group>.<heads>". */
|
||||
export function asksByMergeGroupHeadsKey(group: string, heads: string): string {
|
||||
return "merge." + group + "." + heads;
|
||||
}
|
||||
|
||||
/** The parts of a key of the form "merge.<group>.<heads>", or undefined for a key of another form. */
|
||||
export function parseAsksByMergeGroupHeadsKey(key: string): { group: string; heads: string } | undefined {
|
||||
let rest = key;
|
||||
if (!rest.startsWith("merge.")) return undefined;
|
||||
rest = rest.slice(6);
|
||||
const at1 = rest.indexOf(".");
|
||||
if (at1 <= 0) return undefined;
|
||||
const group = rest.slice(0, at1);
|
||||
rest = rest.slice(at1 + 1);
|
||||
if (rest === "") return undefined;
|
||||
const heads = rest;
|
||||
rest = "";
|
||||
if (rest !== "") return undefined;
|
||||
return { group, heads };
|
||||
}
|
||||
|
||||
/** The key of one value of the state owed under the form "<repository>.<number>.<head>.<act>". */
|
||||
export function owedByRepositoryNumberHeadActKey(repository: string, number: string, head: string, act: string): string {
|
||||
return repository + "." + number + "." + head + "." + act;
|
||||
}
|
||||
|
||||
/** The parts of a key of the form "<repository>.<number>.<head>.<act>", or undefined for a key of another form. */
|
||||
export function parseOwedByRepositoryNumberHeadActKey(key: string): { repository: string; number: string; head: string; act: string } | undefined {
|
||||
let rest = key;
|
||||
const at0 = rest.indexOf(".");
|
||||
if (at0 <= 0) return undefined;
|
||||
const repository = rest.slice(0, at0);
|
||||
rest = rest.slice(at0 + 1);
|
||||
const at2 = rest.indexOf(".");
|
||||
if (at2 <= 0) return undefined;
|
||||
const number = rest.slice(0, at2);
|
||||
rest = rest.slice(at2 + 1);
|
||||
const at4 = rest.indexOf(".");
|
||||
if (at4 <= 0) return undefined;
|
||||
const head = rest.slice(0, at4);
|
||||
rest = rest.slice(at4 + 1);
|
||||
if (rest === "") return undefined;
|
||||
const act = rest;
|
||||
rest = "";
|
||||
if (rest !== "") return undefined;
|
||||
return { repository, number, head, act };
|
||||
}
|
||||
|
||||
/** The key of one value of the state turns under the form "<repository>". */
|
||||
export function turnsByRepositoryKey(repository: string): string {
|
||||
return repository;
|
||||
}
|
||||
|
||||
/** The parts of a key of the form "<repository>", or undefined for a key of another form. */
|
||||
export function parseTurnsByRepositoryKey(key: string): { repository: string } | undefined {
|
||||
let rest = key;
|
||||
if (rest === "") return undefined;
|
||||
const repository = rest;
|
||||
rest = "";
|
||||
if (rest !== "") return undefined;
|
||||
return { repository };
|
||||
}
|
||||
|
||||
/** is what deliveries takes. */
|
||||
export interface DeliveriesInput {
|
||||
/** "true": the final ones of the last thirty days too */
|
||||
"all"?: "true" | "false";
|
||||
/** a group's id (its branch name) */
|
||||
"group"?: string;
|
||||
/** owner/repository */
|
||||
"repository"?: string;
|
||||
/** one state, e.g. delivering or held */
|
||||
"state"?: string;
|
||||
}
|
||||
|
||||
/** is what deliveries answers. */
|
||||
export type DeliveriesAnswer = Record<string, unknown>;
|
||||
|
||||
/** is what times takes. */
|
||||
export interface TimesInput {
|
||||
}
|
||||
|
||||
/** is what times answers. */
|
||||
export type TimesAnswer = Record<string, unknown>;
|
||||
|
||||
/** is what show takes. */
|
||||
export interface ShowInput {
|
||||
/** a delivery's id, or a group's */
|
||||
"id": string;
|
||||
}
|
||||
|
||||
/** is what show answers. */
|
||||
export type ShowAnswer = Record<string, unknown>;
|
||||
|
||||
/** is what groups takes. */
|
||||
export interface GroupsInput {
|
||||
/** "true": the groups that ended too */
|
||||
"all"?: "true" | "false";
|
||||
}
|
||||
|
||||
/** is what groups answers. */
|
||||
export type GroupsAnswer = Record<string, unknown>;
|
||||
|
||||
/** is what what-if takes. */
|
||||
export interface WhatIfInput {
|
||||
}
|
||||
|
||||
/** is what what-if answers. */
|
||||
export type WhatIfAnswer = Record<string, unknown>;
|
||||
|
||||
/** is what checks takes. */
|
||||
export interface ChecksInput {
|
||||
/** a commit's sha, or the start of one, instead of a pull request */
|
||||
"commit"?: string;
|
||||
/** a pull request's number: its head is read */
|
||||
"number"?: string;
|
||||
/** owner/repository */
|
||||
"repository": string;
|
||||
}
|
||||
|
||||
/** is what checks answers. */
|
||||
export type ChecksAnswer = Record<string, unknown>;
|
||||
|
||||
/** is what table takes. */
|
||||
export interface TableInput {
|
||||
}
|
||||
|
||||
/** is what table answers. */
|
||||
export type TableAnswer = Record<string, unknown>;
|
||||
|
||||
/** is what stalled takes. */
|
||||
export interface StalledInput {
|
||||
}
|
||||
|
||||
/** is what stalled answers. */
|
||||
export type StalledAnswer = Record<string, unknown>;
|
||||
|
||||
/** is what recheck takes. */
|
||||
export interface RecheckInput {
|
||||
/** the delivery's id */
|
||||
"id": string;
|
||||
/** why, kept with the transition */
|
||||
"why": string;
|
||||
}
|
||||
|
||||
/** is what recheck answers. */
|
||||
export type RecheckAnswer = Record<string, unknown>;
|
||||
|
||||
/** is what release takes. */
|
||||
export interface ReleaseInput {
|
||||
}
|
||||
|
||||
/** is what release answers. */
|
||||
export type ReleaseAnswer = Record<string, unknown>;
|
||||
|
||||
/** is what stop takes. */
|
||||
export interface StopInput {
|
||||
}
|
||||
|
||||
/** is what stop answers. */
|
||||
export type StopAnswer = Record<string, unknown>;
|
||||
|
||||
/** is what close takes. */
|
||||
export interface CloseInput {
|
||||
/** the delivery's id */
|
||||
"id": string;
|
||||
/** the condition it answers */
|
||||
"why": string;
|
||||
}
|
||||
|
||||
/** is what close answers. */
|
||||
export type CloseAnswer = Record<string, unknown>;
|
||||
|
||||
/** is what retire-history takes. */
|
||||
export interface RetireHistoryInput {
|
||||
}
|
||||
|
||||
/** is what retire-history answers. */
|
||||
export type RetireHistoryAnswer = Record<string, unknown>;
|
||||
|
||||
/** is what settle takes. */
|
||||
export interface SettleInput {
|
||||
/** the held deliveries' ids (owner/repository@commit) */
|
||||
"ids": string[];
|
||||
/** why they may be settled, shown to the operator */
|
||||
"why": string;
|
||||
}
|
||||
|
||||
/** is what settle answers. */
|
||||
export type SettleAnswer = Record<string, unknown>;
|
||||
|
||||
/** is one value of the state source under the key main. */
|
||||
export interface SourceByMainValue {
|
||||
/** the group that moved it */
|
||||
"group": string;
|
||||
/** repository to the merged head, a full commit */
|
||||
"heads": Record<string, string>;
|
||||
/** the layout version */
|
||||
"v"?: number;
|
||||
}
|
||||
|
||||
export interface MergesByGroupValuePullsItem {
|
||||
"commit"?: string;
|
||||
"head": string;
|
||||
"number": number;
|
||||
"repository": string;
|
||||
/** merging, merged or refused */
|
||||
"state": string;
|
||||
"words"?: string;
|
||||
}
|
||||
|
||||
/** is one value of the state merges under the key <group>. */
|
||||
export interface MergesByGroupValue {
|
||||
/** <repository>@<number> to the approved head */
|
||||
"approved"?: Record<string, string>;
|
||||
/** the ask key */
|
||||
"ask": string;
|
||||
"group": string;
|
||||
"pulls": MergesByGroupValuePullsItem[];
|
||||
/** the layout version */
|
||||
"v"?: number;
|
||||
}
|
||||
|
||||
export interface AsksByMergeGroupHeadsValueHeadsItem {
|
||||
/** the full commit */
|
||||
"head": string;
|
||||
"number": number;
|
||||
/** <owner>/<name> */
|
||||
"repository": string;
|
||||
}
|
||||
|
||||
/** is one value of the state asks under the key merge.<group>.<heads>. */
|
||||
export interface AsksByMergeGroupHeadsValue {
|
||||
/** the operator-channel ask as raised */
|
||||
"ask": unknown;
|
||||
/** the number of times it was asked */
|
||||
"asking"?: number;
|
||||
"group": string;
|
||||
"heads": AsksByMergeGroupHeadsValueHeadsItem[];
|
||||
/** the layout version */
|
||||
"v"?: number;
|
||||
}
|
||||
|
||||
/** is one value of the state owed under the key <repository>.<number>.<head>.<act>. */
|
||||
export interface OwedByRepositoryNumberHeadActValue {
|
||||
/** status=<context>, view, note or comment.<seq> */
|
||||
"act": string;
|
||||
"context"?: string;
|
||||
"ref"?: string;
|
||||
"state"?: string;
|
||||
"target"?: string;
|
||||
"text"?: string;
|
||||
/** the layout version */
|
||||
"v"?: number;
|
||||
}
|
||||
|
||||
export interface TurnsByRepositoryValueWaitingItem {
|
||||
/** when (RFC 3339 time) */
|
||||
"approved": string;
|
||||
"ask"?: string;
|
||||
"group": string;
|
||||
"heads": string;
|
||||
/** when (RFC 3339 time) */
|
||||
"since": string;
|
||||
"step"?: string;
|
||||
"waits-for"?: string;
|
||||
/** when (RFC 3339 time) */
|
||||
"waits-since"?: string;
|
||||
}
|
||||
|
||||
/** is one value of the state turns under the key <repository>. */
|
||||
export interface TurnsByRepositoryValue {
|
||||
"repository": string;
|
||||
/** the layout version */
|
||||
"v"?: number;
|
||||
"waiting": TurnsByRepositoryValueWaitingItem[];
|
||||
}
|
||||
|
||||
@@ -0,0 +1,12 @@
|
||||
{
|
||||
"compilerOptions": {
|
||||
"target": "ES2022",
|
||||
"module": "NodeNext",
|
||||
"moduleResolution": "NodeNext",
|
||||
"noEmit": true,
|
||||
"strict": true,
|
||||
"skipLibCheck": true,
|
||||
"forceConsistentCasingInFileNames": true
|
||||
},
|
||||
"include": ["src/**/*.ts"]
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
{
|
||||
"lockfileVersion": 3,
|
||||
"name": "@novox/mesh-delivery-contracts-mesh-delivery-checker",
|
||||
"packages": {
|
||||
"": {
|
||||
"name": "@novox/mesh-delivery-contracts-mesh-delivery-checker",
|
||||
"version": "0.1.0"
|
||||
}
|
||||
},
|
||||
"requires": true,
|
||||
"version": "0.1.0"
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
{
|
||||
"description": "The contract of the seat mesh-delivery-checker, generated from its definition.",
|
||||
"exports": {
|
||||
".": "./src/index.ts"
|
||||
},
|
||||
"files": [
|
||||
"src"
|
||||
],
|
||||
"name": "@novox/mesh-delivery-contracts-mesh-delivery-checker",
|
||||
"type": "module",
|
||||
"types": "./src/index.ts",
|
||||
"version": "0.1.0"
|
||||
}
|
||||
@@ -0,0 +1,341 @@
|
||||
// Code generated by seatgen from the seat definitions; DO NOT EDIT.
|
||||
|
||||
// The contract of the seat "mesh-delivery-checker", defined whole (seats/mesh-delivery-checker/seat.json): The merge check's tries: start one, read one, end one. The checker's own events stay under its own subjects (ADR 0620 §2).
|
||||
|
||||
export const seat = "mesh-delivery-checker";
|
||||
|
||||
export const verbs = {
|
||||
try: "try",
|
||||
tryRead: "try-read",
|
||||
tryEnd: "try-end",
|
||||
} as const;
|
||||
|
||||
export const holderEvents = {
|
||||
verdict: "verdict",
|
||||
tagWanted: "tag-wanted",
|
||||
previewWritten: "preview.written",
|
||||
} as const;
|
||||
|
||||
export function verdictHolderSubject(holder: string): string {
|
||||
return "mesh.stream." + holder + ".event.verdict";
|
||||
}
|
||||
|
||||
export function tagWantedHolderSubject(holder: string): string {
|
||||
return "mesh.stream." + holder + ".event.tag-wanted";
|
||||
}
|
||||
|
||||
export function previewWrittenHolderSubject(holder: string): string {
|
||||
return "mesh.stream." + holder + ".event.preview.written";
|
||||
}
|
||||
|
||||
export const states = {
|
||||
verdicts: "verdicts",
|
||||
kept: "kept",
|
||||
owed: "owed",
|
||||
previews: "previews",
|
||||
} as const;
|
||||
|
||||
/** The key of one value of the state verdicts under the form "<group>.<heads>". */
|
||||
export function verdictsByGroupHeadsKey(group: string, heads: string): string {
|
||||
return group + "." + heads;
|
||||
}
|
||||
|
||||
/** The parts of a key of the form "<group>.<heads>", or undefined for a key of another form. */
|
||||
export function parseVerdictsByGroupHeadsKey(key: string): { group: string; heads: string } | undefined {
|
||||
let rest = key;
|
||||
const at0 = rest.indexOf(".");
|
||||
if (at0 <= 0) return undefined;
|
||||
const group = rest.slice(0, at0);
|
||||
rest = rest.slice(at0 + 1);
|
||||
if (rest === "") return undefined;
|
||||
const heads = rest;
|
||||
rest = "";
|
||||
if (rest !== "") return undefined;
|
||||
return { group, heads };
|
||||
}
|
||||
|
||||
/** The key of one value of the state kept under the form "<level>.<digest>". */
|
||||
export function keptByLevelDigestKey(level: string, digest: string): string {
|
||||
return level + "." + digest;
|
||||
}
|
||||
|
||||
/** The parts of a key of the form "<level>.<digest>", or undefined for a key of another form. */
|
||||
export function parseKeptByLevelDigestKey(key: string): { level: string; digest: string } | undefined {
|
||||
let rest = key;
|
||||
const at0 = rest.indexOf(".");
|
||||
if (at0 <= 0) return undefined;
|
||||
const level = rest.slice(0, at0);
|
||||
rest = rest.slice(at0 + 1);
|
||||
if (rest === "") return undefined;
|
||||
const digest = rest;
|
||||
rest = "";
|
||||
if (rest !== "") return undefined;
|
||||
return { level, digest };
|
||||
}
|
||||
|
||||
/** The key of one value of the state owed under the form "<repository>.<number>.<head>.<act>". */
|
||||
export function owedByRepositoryNumberHeadActKey(repository: string, number: string, head: string, act: string): string {
|
||||
return repository + "." + number + "." + head + "." + act;
|
||||
}
|
||||
|
||||
/** The parts of a key of the form "<repository>.<number>.<head>.<act>", or undefined for a key of another form. */
|
||||
export function parseOwedByRepositoryNumberHeadActKey(key: string): { repository: string; number: string; head: string; act: string } | undefined {
|
||||
let rest = key;
|
||||
const at0 = rest.indexOf(".");
|
||||
if (at0 <= 0) return undefined;
|
||||
const repository = rest.slice(0, at0);
|
||||
rest = rest.slice(at0 + 1);
|
||||
const at2 = rest.indexOf(".");
|
||||
if (at2 <= 0) return undefined;
|
||||
const number = rest.slice(0, at2);
|
||||
rest = rest.slice(at2 + 1);
|
||||
const at4 = rest.indexOf(".");
|
||||
if (at4 <= 0) return undefined;
|
||||
const head = rest.slice(0, at4);
|
||||
rest = rest.slice(at4 + 1);
|
||||
if (rest === "") return undefined;
|
||||
const act = rest;
|
||||
rest = "";
|
||||
if (rest !== "") return undefined;
|
||||
return { repository, number, head, act };
|
||||
}
|
||||
|
||||
/** The key of one value of the state previews under the form "<repository>.<head>.<trunk>". */
|
||||
export function previewsByRepositoryHeadTrunkKey(repository: string, head: string, trunk: string): string {
|
||||
return repository + "." + head + "." + trunk;
|
||||
}
|
||||
|
||||
/** The parts of a key of the form "<repository>.<head>.<trunk>", or undefined for a key of another form. */
|
||||
export function parsePreviewsByRepositoryHeadTrunkKey(key: string): { repository: string; head: string; trunk: string } | undefined {
|
||||
let rest = key;
|
||||
const at0 = rest.indexOf(".");
|
||||
if (at0 <= 0) return undefined;
|
||||
const repository = rest.slice(0, at0);
|
||||
rest = rest.slice(at0 + 1);
|
||||
const at2 = rest.indexOf(".");
|
||||
if (at2 <= 0) return undefined;
|
||||
const head = rest.slice(0, at2);
|
||||
rest = rest.slice(at2 + 1);
|
||||
if (rest === "") return undefined;
|
||||
const trunk = rest;
|
||||
rest = "";
|
||||
if (rest !== "") return undefined;
|
||||
return { repository, head, trunk };
|
||||
}
|
||||
|
||||
/** is what try takes. */
|
||||
export interface TryInput {
|
||||
/** the full commit to try, 40 or 64 hex digits */
|
||||
"head": string;
|
||||
/** unit or integration: only this level; both when absent */
|
||||
"level"?: string;
|
||||
/** <owner>/<name> */
|
||||
"repository": string;
|
||||
/** a go test -run pattern: only those tests run, and nothing is kept */
|
||||
"tests"?: string;
|
||||
/** the trunk commit to merge it onto, or the trunk's branch for its head now; the trunk's head when absent */
|
||||
"trunk"?: string;
|
||||
}
|
||||
|
||||
/** is what try answers. */
|
||||
export interface TryAnswer {
|
||||
/** the try's id */
|
||||
"id": string;
|
||||
/** its state */
|
||||
"state"?: string;
|
||||
}
|
||||
|
||||
/** is what try-read takes. */
|
||||
export interface TryReadInput {
|
||||
/** the try's id, try-<digits> */
|
||||
"id": string;
|
||||
}
|
||||
|
||||
/** is what try-read answers. */
|
||||
export type TryReadAnswer = Record<string, unknown>;
|
||||
|
||||
/** is what try-end takes. */
|
||||
export interface TryEndInput {
|
||||
/** the try's id, try-<digits> */
|
||||
"id": string;
|
||||
/** why it is ended, in words */
|
||||
"why": string;
|
||||
}
|
||||
|
||||
/** is what try-end answers. */
|
||||
export interface TryEndAnswer {
|
||||
/** it is ended */
|
||||
"ended": boolean;
|
||||
/** the try */
|
||||
"id": string;
|
||||
}
|
||||
|
||||
export interface VerdictHolderEventSeriesItem {
|
||||
/** its artifact, or the package's ecosystem name as the module's packages name it */
|
||||
"artifact": string;
|
||||
/** the series on trunk */
|
||||
"from": string;
|
||||
/** the module whose artifact's series changes */
|
||||
"module": string;
|
||||
/** the series the checked head gives it */
|
||||
"to": string;
|
||||
}
|
||||
|
||||
/** is the body of the holder's event verdict. */
|
||||
export interface VerdictHolderEvent {
|
||||
/** the commit checked */
|
||||
"head": string;
|
||||
/** the generated files out of step with their seat definitions, each path (design 26) */
|
||||
"out-of-step"?: string[];
|
||||
/** the pull request's number, for a pull request's check */
|
||||
"pull"?: number;
|
||||
/** owner/repo */
|
||||
"repository": string;
|
||||
/** each change of an artifact's series the checked head makes against trunk's manifests, once each (design 32); absent when it changes none */
|
||||
"series"?: VerdictHolderEventSeriesItem[];
|
||||
/** pass or fail */
|
||||
"verdict": string;
|
||||
}
|
||||
|
||||
/** is the body of the holder's event tag-wanted. */
|
||||
export interface TagWantedHolderEvent {
|
||||
/** the pull request's branch: its tag is removed when the branch ends */
|
||||
"branch": string;
|
||||
/** its head */
|
||||
"head": string;
|
||||
/** the group member */
|
||||
"module": string;
|
||||
/** the package it declares */
|
||||
"package": string;
|
||||
/** the pull request */
|
||||
"pull"?: number;
|
||||
/** owner/repo */
|
||||
"repository": string;
|
||||
/** the tag wanted */
|
||||
"tag"?: string;
|
||||
}
|
||||
|
||||
export interface VerdictsByGroupHeadsValueComposition {
|
||||
"fpset"?: string;
|
||||
"machines"?: string[];
|
||||
"sdk"?: string;
|
||||
/** not-run, pass or fail */
|
||||
"state": string;
|
||||
"words"?: string;
|
||||
}
|
||||
|
||||
export interface VerdictsByGroupHeadsValueFailed {
|
||||
"machine"?: string;
|
||||
"test"?: string;
|
||||
"words"?: string;
|
||||
}
|
||||
|
||||
export interface VerdictsByGroupHeadsValueFailuresItem {
|
||||
"machine"?: string;
|
||||
"test"?: string;
|
||||
"words"?: string;
|
||||
}
|
||||
|
||||
export interface VerdictsByGroupHeadsValueLevelsValue {
|
||||
"kept": number;
|
||||
"run": number;
|
||||
"skipped"?: number;
|
||||
}
|
||||
|
||||
export interface VerdictsByGroupHeadsValueSeriesItem {
|
||||
"artifact": string;
|
||||
"from": string;
|
||||
"module": string;
|
||||
"to": string;
|
||||
}
|
||||
|
||||
/** milliseconds */
|
||||
export interface VerdictsByGroupHeadsValueTook {
|
||||
"clone"?: number;
|
||||
"composition"?: number;
|
||||
"integration-run"?: number;
|
||||
"integration-setup"?: number;
|
||||
"lint"?: number;
|
||||
"queue"?: number;
|
||||
"unit"?: number;
|
||||
}
|
||||
|
||||
/** is one value of the state verdicts under the key <group>.<heads>. */
|
||||
export interface VerdictsByGroupHeadsValue {
|
||||
"beside"?: Record<string, string>;
|
||||
"carried-from"?: Record<string, string>;
|
||||
"composition"?: VerdictsByGroupHeadsValueComposition;
|
||||
"failed"?: VerdictsByGroupHeadsValueFailed;
|
||||
"failures"?: VerdictsByGroupHeadsValueFailuresItem[];
|
||||
"group": string;
|
||||
/** 12 hex digits */
|
||||
"heads": string;
|
||||
/** by level, unit or integration */
|
||||
"levels"?: Record<string, VerdictsByGroupHeadsValueLevelsValue>;
|
||||
"machines"?: string[];
|
||||
"over-bound"?: string[];
|
||||
"sdk"?: string;
|
||||
"series"?: VerdictsByGroupHeadsValueSeriesItem[];
|
||||
/** running, pass or fail */
|
||||
"state": string;
|
||||
/** milliseconds */
|
||||
"took"?: VerdictsByGroupHeadsValueTook;
|
||||
/** repository to the trunk head merged onto */
|
||||
"trunk"?: Record<string, string>;
|
||||
/** the layout version */
|
||||
"v"?: number;
|
||||
}
|
||||
|
||||
/** is one value of the state kept under the key <level>.<digest>. */
|
||||
export interface KeptByLevelDigestValue {
|
||||
/** when (RFC 3339 time) */
|
||||
"at"?: string;
|
||||
"level": string;
|
||||
"package": string;
|
||||
"repository": string;
|
||||
/** pass: a failure is never kept */
|
||||
"state": string;
|
||||
"tests"?: number;
|
||||
"took"?: number;
|
||||
"toolchain"?: string;
|
||||
/** the layout version */
|
||||
"v"?: number;
|
||||
}
|
||||
|
||||
/** is one value of the state owed under the key <repository>.<number>.<head>.<act>. */
|
||||
export interface OwedByRepositoryNumberHeadActValue {
|
||||
/** status=<context>, view, note or comment.<seq> */
|
||||
"act": string;
|
||||
"context"?: string;
|
||||
"ref"?: string;
|
||||
"state"?: string;
|
||||
"target"?: string;
|
||||
"text"?: string;
|
||||
/** the layout version */
|
||||
"v"?: number;
|
||||
}
|
||||
|
||||
export interface PreviewsByRepositoryHeadTrunkValueModulesValue {
|
||||
"locks"?: Record<string, string>;
|
||||
/** manifest digest */
|
||||
"manifest": string;
|
||||
"packages"?: string[];
|
||||
"paths"?: Record<string, string>;
|
||||
"tree": string;
|
||||
}
|
||||
|
||||
/** is one value of the state previews under the key <repository>.<head>.<trunk>. */
|
||||
export interface PreviewsByRepositoryHeadTrunkValue {
|
||||
/** files in conflict */
|
||||
"conflicts"?: string[];
|
||||
"head": string;
|
||||
/** the merge commit */
|
||||
"merge"?: string;
|
||||
/** by directory */
|
||||
"modules"?: Record<string, PreviewsByRepositoryHeadTrunkValueModulesValue>;
|
||||
"repository": string;
|
||||
"trunk": string;
|
||||
/** the layout version */
|
||||
"v"?: number;
|
||||
}
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user