Carry mesh package versions in build asks and fingerprints
ADR 0573 §2 has every module build against the latest published version of each mesh package it uses, chosen by the planner and written by the build, never pinned by hand (issue 1585, S4). The ask, the fetch request and the merge preview now carry the packages and their versions, and the new-form fingerprint takes each version, so a package's trunk merge changes every dependent's fingerprint. A module using no package keeps its fingerprint. Claude-Session: https://claude.ai/code/session_01NcbgfJ9Gum47zkFKWRaovL
This commit is contained in:
@@ -11,6 +11,10 @@ import (
|
||||
// SourcePrefix2 opens every source fingerprint of the new form.
|
||||
const SourcePrefix2 = "src2:"
|
||||
|
||||
// NoPackageVersion opens the reason a module has no fingerprint while a mesh package it uses has no published version
|
||||
// the indexer holds (novox/hq ADR 0573 §2); the packages follow, by module path.
|
||||
const NoPackageVersion = "no published version of the package "
|
||||
|
||||
// Toolchain2 is what an artifact's toolchain enters the new fingerprint as (novox/hq ADR 0405 clause 7): the
|
||||
// artifact's language, the installed toolchain's version and the build module's recipe version, in place of the
|
||||
// toolchain image's digest. A toolchain update so changes the fingerprints of that language's modules only.
|
||||
@@ -37,15 +41,19 @@ type SourceInputs2 struct {
|
||||
Contexts map[string]string
|
||||
Locks map[string]string
|
||||
Toolchains map[string]Toolchain2
|
||||
Unpinned string
|
||||
// Packages are the version of each mesh package the module uses, by package (novox/hq ADR 0573 §2): a package with
|
||||
// no version gives no fingerprint. None leaves the fingerprint as it was before packages entered it.
|
||||
Packages map[string]string
|
||||
Unpinned string
|
||||
}
|
||||
|
||||
// SourceFingerprint2 is the module's source fingerprint of the new form, `src2:` and the hex sha256 of its inputs'
|
||||
// lines, or none and the reason when the source does not pin the build: Unpinned when set, "no tree" when Tree is
|
||||
// empty, "no lock" when an artifact resolves packages and has no lock. It is a pure function of its inputs: the order
|
||||
// of maps and a base given twice change nothing. The lines are "module <m>", "tree <t>" and "builder <fp>", then
|
||||
// "base <fp>" (once each), "context <artifact> <tree>", "lock <artifact> <hash>" and "toolchain <artifact> <language>
|
||||
// <version> <recipe>", sorted, all joined by a newline.
|
||||
// "base <fp>" (once each), "context <artifact> <tree>", "lock <artifact> <hash>", "toolchain <artifact> <language>
|
||||
// <version> <recipe>" and "package <package> <version>", sorted, all joined by a newline. A module that uses no mesh
|
||||
// package has no package line, so its fingerprint is the one it had before packages entered (novox/hq ADR 0573 §2).
|
||||
func SourceFingerprint2(in SourceInputs2) (fp, none string) {
|
||||
if in.Unpinned != "" {
|
||||
return "", in.Unpinned
|
||||
@@ -58,6 +66,16 @@ func SourceFingerprint2(in SourceInputs2) (fp, none string) {
|
||||
return "", "no lock"
|
||||
}
|
||||
}
|
||||
var unpublished []string
|
||||
for p, v := range in.Packages {
|
||||
if v == "" {
|
||||
unpublished = append(unpublished, p)
|
||||
}
|
||||
}
|
||||
if len(unpublished) > 0 {
|
||||
sort.Strings(unpublished)
|
||||
return "", NoPackageVersion + strings.Join(unpublished, ", ")
|
||||
}
|
||||
lines := []string{"module " + in.Module, "tree " + in.Tree, "builder " + in.Builder}
|
||||
bases := map[string]bool{}
|
||||
for _, b := range in.Bases {
|
||||
@@ -75,6 +93,9 @@ func SourceFingerprint2(in SourceInputs2) (fp, none string) {
|
||||
for a, t := range in.Toolchains {
|
||||
lines = append(lines, "toolchain "+a+" "+t.Language+" "+t.Version+" "+t.Recipe)
|
||||
}
|
||||
for p, v := range in.Packages {
|
||||
lines = append(lines, "package "+p+" "+v)
|
||||
}
|
||||
sort.Strings(lines[3:])
|
||||
sum := sha256.Sum256([]byte(strings.Join(lines, "\n")))
|
||||
return SourcePrefix2 + hex.EncodeToString(sum[:]), ""
|
||||
|
||||
@@ -62,6 +62,9 @@ func TestFingerprint2Changes(t *testing.T) {
|
||||
"a context": func(in *SourceInputs2) { in.Contexts["mail"] = "t-changed" },
|
||||
"a context added": func(in *SourceInputs2) { in.Contexts["extra"] = "t-root" },
|
||||
"a toolchain added": func(in *SourceInputs2) { in.Toolchains["extra"] = Toolchain2{"go", "1.26.0", "3"} },
|
||||
// Issue 1585 (ADR 0573 §2): a package's new published version, and a package used at all.
|
||||
"a package added": func(in *SourceInputs2) { in.Packages = map[string]string{"git.novox.be/novox/mesh-sdk/go": "c1"} },
|
||||
"a package version": func(in *SourceInputs2) { in.Packages = map[string]string{"git.novox.be/novox/mesh-sdk/go": "c2"} },
|
||||
} {
|
||||
in := inputs2()
|
||||
f(&in)
|
||||
@@ -111,6 +114,17 @@ func TestFingerprint2None(t *testing.T) {
|
||||
if fp, none := SourceFingerprint2(in); fp != "" || none != "no tree" {
|
||||
t.Errorf("no tree: fp = %q, none = %q", fp, none)
|
||||
}
|
||||
// Issue 1585: a package with no published version gives none, naming it; an empty set of packages changes nothing.
|
||||
in = inputs2()
|
||||
in.Packages = map[string]string{"git.novox.be/novox/b/go": "", "git.novox.be/novox/a/go": "", "git.novox.be/novox/c/go": "c1"}
|
||||
if fp, none := SourceFingerprint2(in); fp != "" || none != NoPackageVersion+"git.novox.be/novox/a/go, git.novox.be/novox/b/go" {
|
||||
t.Errorf("no package version: fp = %q, none = %q", fp, none)
|
||||
}
|
||||
in = inputs2()
|
||||
in.Packages = map[string]string{}
|
||||
if fp, _ := SourceFingerprint2(in); fp != mustFP2(t, inputs2()) {
|
||||
t.Errorf("no package changes the fingerprint: %s", fp)
|
||||
}
|
||||
in = inputs2()
|
||||
in.Tree, in.Unpinned = "", "a context is a Go program's build source"
|
||||
if _, none := SourceFingerprint2(in); none != in.Unpinned {
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
package delivery
|
||||
|
||||
import (
|
||||
"reflect"
|
||||
"strings"
|
||||
"testing"
|
||||
)
|
||||
@@ -163,4 +164,35 @@ func TestModuleKeepsInputs2(t *testing.T) {
|
||||
if _, err := EncodeModule(m); err == nil {
|
||||
t.Error("Inputs2 without a manifest digest was encoded")
|
||||
}
|
||||
|
||||
// Issue 1585 (ADR 0573 §2): the mesh packages a go.sum names, those of other repositories, and the latest published
|
||||
// version of each: the trunk commit of the deepest module of its repository whose directory holds it.
|
||||
sum := "git.novox.be/novox/mesh-sdk/go v0.1.7 h1:a=\ngit.novox.be/novox/mesh-sdk/go v0.1.7/go.mod h1:b=\n" +
|
||||
"git.novox.be/novox/mesh-controller/modules/route-proxy/contract v0.1.0 h1:c=\n" +
|
||||
"github.com/nats-io/nats.go v1.0.0 h1:d=\ngit.novox.be/novox/mesh-host v0.0.0-1 h1:e=\n"
|
||||
pkgs := MeshPackagesOf([]byte(sum))
|
||||
if want := []string{"git.novox.be/novox/mesh-controller/modules/route-proxy/contract", "git.novox.be/novox/mesh-host", "git.novox.be/novox/mesh-sdk/go"}; !reflect.DeepEqual(pkgs, want) {
|
||||
t.Errorf("the packages of a go.sum: %v, want %v", pkgs, want)
|
||||
}
|
||||
if got := OtherPackages(pkgs, "novox/mesh-controller"); len(got) != 2 || got[0] != "git.novox.be/novox/mesh-host" {
|
||||
t.Errorf("the packages of other repositories: %v", got)
|
||||
}
|
||||
held := map[string]Module{
|
||||
"mesh-sdk": {Repository: "novox/mesh-sdk", Path: RootPath, Commit: "c-sdk"},
|
||||
"route-proxy": {Repository: "novox/mesh-controller", Path: "modules/route-proxy", Commit: "c-rp"},
|
||||
"controller": {Repository: "novox/mesh-controller", Path: RootPath, Commit: "c-ctl"},
|
||||
"route-proxy2": {Repository: "novox/mesh-controller", Path: "modules/route", Commit: "c-wrong"},
|
||||
"no-path": {Repository: "novox/mesh-host", Commit: "c-host"},
|
||||
}
|
||||
versions, missing := PackageVersionsOf(pkgs, held)
|
||||
if want := (PackageVersions{"git.novox.be/novox/mesh-sdk/go": "c-sdk", "git.novox.be/novox/mesh-controller/modules/route-proxy/contract": "c-rp"}); !reflect.DeepEqual(versions, want) {
|
||||
t.Errorf("versions %v, want %v", versions, want)
|
||||
}
|
||||
if len(missing) != 1 || missing[0] != "git.novox.be/novox/mesh-host" {
|
||||
t.Errorf("missing %v: a module that names no directory holds no package", missing)
|
||||
}
|
||||
kept.Packages = []string{"git.novox.be/novox/mesh-sdk/go", "git.novox.be/novox/mesh-host"}
|
||||
if fp, none := SourceFingerprint2(kept.WithPackages("m", tc, versions)); fp != "" || !strings.HasPrefix(none, NoPackageVersion) {
|
||||
t.Errorf("a package with no version: %q %q", fp, none)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,107 @@
|
||||
package delivery
|
||||
|
||||
import (
|
||||
"maps"
|
||||
"slices"
|
||||
"strings"
|
||||
)
|
||||
|
||||
// **Every module builds against the latest published version of each mesh package it uses** (novox/hq ADR 0573 §2;
|
||||
// issue 1585). A mesh package is a Go module of a repository of the mesh's forge (ForgeModulePrefix): mesh-sdk's Go
|
||||
// module, and a seat's contract its holder's repository publishes (ADR 0573 §1). Its version is the trunk merge the
|
||||
// indexer holds for the module whose directory holds the package (PackageVersionsOf): a merge that changes it changes
|
||||
// the fingerprint of every module that uses it, and the build writes that version into its own go.mod and go.sum
|
||||
// (StepAsk.Packages). Which packages a module uses is read from its lock file by MeshPackagesOf, the same way by the
|
||||
// forge bridge and by the build modules (buildstep.ModuleTrees), so nobody names a version by hand.
|
||||
|
||||
// ForgeModulePrefix is the module path prefix of the Go modules the mesh's forge serves: a Go module whose path starts
|
||||
// with it is a mesh package, git.novox.be/novox/<repository>[/<directory of the module in it>].
|
||||
const ForgeModulePrefix = "git.novox.be/novox/"
|
||||
|
||||
// MeshPackagesOf are the mesh packages a Go module's go.sum names (the module path of each line that starts with
|
||||
// ForgeModulePrefix), sorted and each once. go.sum names every module of the build's graph, so a package used only
|
||||
// through another is named too. Nil for none. A package of the module's own repository comes with its own tree, and
|
||||
// its reader leaves it out (OtherPackages).
|
||||
func MeshPackagesOf(goSum []byte) []string {
|
||||
seen := map[string]bool{}
|
||||
for _, line := range strings.Split(string(goSum), "\n") {
|
||||
f := strings.Fields(line)
|
||||
if len(f) < 2 || !strings.HasPrefix(f[0], ForgeModulePrefix) {
|
||||
continue
|
||||
}
|
||||
if _, _, ok := PackageRepository(f[0]); !ok {
|
||||
continue
|
||||
}
|
||||
seen[f[0]] = true
|
||||
}
|
||||
if len(seen) == 0 {
|
||||
return nil
|
||||
}
|
||||
return slices.Sorted(maps.Keys(seen))
|
||||
}
|
||||
|
||||
// OtherPackages are the packages not published from repository, the module's own, in their order; nil for none.
|
||||
func OtherPackages(packages []string, repository string) []string {
|
||||
var out []string
|
||||
for _, p := range packages {
|
||||
if repo, _, ok := PackageRepository(p); ok && !SameRepository(repo, repository) {
|
||||
out = append(out, p)
|
||||
}
|
||||
}
|
||||
return out
|
||||
}
|
||||
|
||||
// PackageRepository is the repository (<owner>/<name>) a mesh package is published from and the package's directory in
|
||||
// it ("" for its root); false for a path that is not a forge module's.
|
||||
func PackageRepository(pkg string) (repository, dir string, ok bool) {
|
||||
rest, found := strings.CutPrefix(pkg, ForgeModulePrefix)
|
||||
if !found || rest == "" {
|
||||
return "", "", false
|
||||
}
|
||||
name, dir, _ := strings.Cut(rest, "/")
|
||||
if name == "" {
|
||||
return "", "", false
|
||||
}
|
||||
return "novox/" + name, dir, true
|
||||
}
|
||||
|
||||
// PackageVersionsOf is the latest published version of each package from the modules the indexer holds (by key): the
|
||||
// trunk merge commit of the module of the package's repository whose directory holds the package's directory (the
|
||||
// deepest, a module at the repository's root holding every directory). A package no held module holds, or whose
|
||||
// module names no commit or no directory, is in missing, sorted; versions is nil when packages is empty.
|
||||
func PackageVersionsOf(packages []string, modules map[string]Module) (versions PackageVersions, missing []string) {
|
||||
for _, pkg := range packages {
|
||||
repository, dir, ok := PackageRepository(pkg)
|
||||
commit, depth := "", -1
|
||||
if ok {
|
||||
for _, key := range slices.Sorted(maps.Keys(modules)) {
|
||||
m := modules[key]
|
||||
if !SameRepository(m.Repository, repository) || m.Commit == "" {
|
||||
continue
|
||||
}
|
||||
at := strings.Trim(m.Path, "/")
|
||||
switch {
|
||||
case at == "":
|
||||
continue // a value that names no directory says nothing of where the package lies
|
||||
case at == RootPath:
|
||||
at = ""
|
||||
case dir != at && !strings.HasPrefix(dir, at+"/"):
|
||||
continue
|
||||
}
|
||||
if d := len(at); d > depth {
|
||||
commit, depth = m.Commit, d
|
||||
}
|
||||
}
|
||||
}
|
||||
if commit == "" {
|
||||
missing = append(missing, pkg)
|
||||
continue
|
||||
}
|
||||
if versions == nil {
|
||||
versions = PackageVersions{}
|
||||
}
|
||||
versions[pkg] = commit
|
||||
}
|
||||
slices.Sort(missing)
|
||||
return versions, missing
|
||||
}
|
||||
@@ -107,6 +107,8 @@ type PreviewModule struct {
|
||||
// Paths are, of a module whose every artifact names its paths, each named path's git object id at the merge; Tree
|
||||
// is then their NamedTree (novox/hq ADR 0499 clause 2). Absent otherwise.
|
||||
Paths map[string]string `json:"paths,omitempty"`
|
||||
// Packages are the mesh packages the module's lock file names at the merge (MeshPackagesOf, novox/hq ADR 0573 §2).
|
||||
Packages []string `json:"packages,omitempty"`
|
||||
}
|
||||
|
||||
// Preview is the value of a preview key: the repository, the head and the trunk commit it was merged onto, the merge
|
||||
|
||||
+38
-3
@@ -284,6 +284,10 @@ type CandidateModule struct {
|
||||
// Paths are, of a module whose every artifact names its paths, each named path's git object id at the preview
|
||||
// (novox/hq ADR 0499 clause 2), so the builder's fetch request for the candidate names them. Absent otherwise.
|
||||
Paths map[string]string `json:"paths,omitempty"`
|
||||
// Packages are the versions of the mesh packages FP2 was computed with (novox/hq ADR 0573 §2, §4): the latest
|
||||
// published version of each, or the head of the candidate's pull request that publishes it, so a group's candidate
|
||||
// builds against the group's own package. Absent for none.
|
||||
Packages PackageVersions `json:"packages,omitempty"`
|
||||
}
|
||||
|
||||
func (m CandidateModule) check() error {
|
||||
@@ -296,6 +300,9 @@ func (m CandidateModule) check() error {
|
||||
if err := checkPathObjects(m.Paths); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := m.Packages.Check(); err != nil {
|
||||
return err
|
||||
}
|
||||
return checkNewForm(m.FP2, m.Manifest)
|
||||
}
|
||||
|
||||
@@ -415,6 +422,10 @@ type Module struct {
|
||||
// so the checker tells whether a change touches an artifact's build source without reading a manifest (the lead's
|
||||
// decision L8 of R4e). Absent for an artifact that names none, and in a value written before.
|
||||
ArtifactPaths map[string][]string `json:"paths,omitempty"`
|
||||
// Packages are the versions of the mesh packages FP2 was computed with (novox/hq ADR 0573 §2): the trunk merge the
|
||||
// indexer held for each package of Inputs2.Packages. A builder builds the fingerprint against exactly these
|
||||
// (StepAsk.Packages). Empty when FP2 is empty or the module uses no mesh package.
|
||||
Packages PackageVersions `json:"packages,omitempty"`
|
||||
}
|
||||
|
||||
// RootPath is Module.Path of a module whose manifest is its repository's root.
|
||||
@@ -433,6 +444,10 @@ type KeptInputs2 struct {
|
||||
// computation from kept inputs gives the merge's value (novox/hq ADR 0499 clause 2). Absent for every other module,
|
||||
// whose Tree is its directory's tree, and in a value written before.
|
||||
Paths map[string]string `json:"paths,omitempty"`
|
||||
// Packages are the mesh packages the module's lock file names (MeshPackagesOf, novox/hq ADR 0573 §2): the
|
||||
// fingerprint takes the latest published version of each (Module.Packages). Absent for none, and in a value written
|
||||
// before.
|
||||
Packages []string `json:"packages,omitempty"`
|
||||
}
|
||||
|
||||
// checkPathObjects refuses a named path that is not clean and relative to the module's directory, or names no object.
|
||||
@@ -469,6 +484,19 @@ func (k KeptInputs2) With(module string, toolchains map[string]Toolchain2) Sourc
|
||||
Locks: k.Locks, Toolchains: toolchains}
|
||||
}
|
||||
|
||||
// WithPackages is With and the version of each package the module uses (Packages), as PackageVersionsOf read them: a
|
||||
// package with no version enters with none, and the fingerprint is then none (SourceFingerprint2).
|
||||
func (k KeptInputs2) WithPackages(module string, toolchains map[string]Toolchain2, versions PackageVersions) SourceInputs2 {
|
||||
in := k.With(module, toolchains)
|
||||
if len(k.Packages) > 0 {
|
||||
in.Packages = map[string]string{}
|
||||
for _, p := range k.Packages {
|
||||
in.Packages[p] = versions[p]
|
||||
}
|
||||
}
|
||||
return in
|
||||
}
|
||||
|
||||
func (m Module) check() error {
|
||||
switch {
|
||||
case m.FP != "" && m.None != "":
|
||||
@@ -485,6 +513,9 @@ func (m Module) check() error {
|
||||
return fmt.Errorf("delivery: the module's toolchain of %q needs its version and its recipe version", language)
|
||||
}
|
||||
}
|
||||
if err := m.Packages.Check(); err != nil {
|
||||
return err
|
||||
}
|
||||
if m.Inputs2 != nil && m.Manifest == "" {
|
||||
return fmt.Errorf("delivery: a module keeps new-form inputs but has no manifest digest")
|
||||
}
|
||||
@@ -534,9 +565,13 @@ type Forge struct {
|
||||
// id at the same commit as its manifest, by path relative to the directory (novox/hq ADR 0499 clause 3). The
|
||||
// indexer names the paths; a message without them gives such a module none. Older writers send none.
|
||||
Paths map[string]map[string]string `json:"paths,omitempty"`
|
||||
Act string `json:"act,omitempty"`
|
||||
Words string `json:"words,omitempty"`
|
||||
Open []OpenPull `json:"open,omitempty"`
|
||||
// Packages are, of each module directory, the mesh packages its lock file names (MeshPackagesOf; novox/hq ADR 0573
|
||||
// §2): the indexer keeps them in the module's inputs and takes each one's latest published version into its
|
||||
// fingerprint. Absent for a directory that uses none, and from an older writer.
|
||||
Packages map[string][]string `json:"packages,omitempty"`
|
||||
Act string `json:"act,omitempty"`
|
||||
Words string `json:"words,omitempty"`
|
||||
Open []OpenPull `json:"open,omitempty"`
|
||||
// RetiredPlacements is the placement files a bridge before ADR 0567 carried (ADR 0549 §2, ADR 0559 §2): placement is
|
||||
// the seat mesh-placement's state now and no repository holds it (ADR 0567 §11), so a message carrying them is
|
||||
// refused, loudly, rather than read with them dropped.
|
||||
|
||||
@@ -253,6 +253,30 @@ type StepAsk struct {
|
||||
Compose *ComposeJob `json:"compose,omitempty"`
|
||||
// Needs are what the node that runs the ask must have (AskNeeds); a check ask's alone. Empty: any node.
|
||||
Needs []string `json:"needs,omitempty"`
|
||||
// Packages are the version of each mesh package the build or the check builds against, by its module path (a Go
|
||||
// module's path, git.novox.be/novox/mesh-sdk/go; the shape takes a seat's package too), whatever version the
|
||||
// repository's go.mod names (novox/hq ADR 0573 §2, §4; issue 1585): a Go package's version is a full commit, the
|
||||
// trunk merge the indexer holds as the package's latest published version or, in a delivery group's check, the
|
||||
// head of the group's member that publishes it. The build module writes each into its workspace, checksum-verified,
|
||||
// and names them in its log (PackageVersions). A compose takes none: it builds the mesh-sdk its reader names.
|
||||
Packages PackageVersions `json:"packages,omitempty"`
|
||||
}
|
||||
|
||||
// PackageVersions are the versions of the mesh packages a build or a check builds against, by package (a Go module's
|
||||
// path), each a full commit (novox/hq ADR 0573 §2).
|
||||
type PackageVersions map[string]string
|
||||
|
||||
// Check refuses a package that is no module path or a version that is no full commit, naming it.
|
||||
func (p PackageVersions) Check() error {
|
||||
for pkg, version := range p {
|
||||
if pkg == "" || strings.ContainsAny(pkg, " \t\n@") || strings.HasPrefix(pkg, "/") {
|
||||
return fmt.Errorf("delivery: the package %q is no module path", pkg)
|
||||
}
|
||||
if !askCommitOK.MatchString(version) {
|
||||
return fmt.Errorf("delivery: the package %s names the version %q, which is no full commit", pkg, version)
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// ComposeJob is a compose ask's job (novox/hq ADR 0482 clause 4, issue 1622): fetch the commit, build the composer
|
||||
@@ -389,6 +413,11 @@ func (a StepAsk) check() error {
|
||||
return fmt.Errorf("delivery: the ask %s is a check of an image, which has no tests", a.ID)
|
||||
case len(a.Needs) > 0 && a.Check == nil:
|
||||
return fmt.Errorf("delivery: the ask %s names needs and is no check, whose tests alone need a node", a.ID)
|
||||
case len(a.Packages) > 0 && a.Compose != nil:
|
||||
return fmt.Errorf("delivery: the ask %s is a compose and names package versions, which a compose does not take", a.ID)
|
||||
}
|
||||
if err := a.Packages.Check(); err != nil {
|
||||
return fmt.Errorf("the ask %s: %w", a.ID, err)
|
||||
}
|
||||
for _, n := range a.Needs {
|
||||
if err := CheckNeed(n); err != nil {
|
||||
|
||||
@@ -17,7 +17,8 @@ func buildAsk() StepAsk {
|
||||
Build: &BuildJob{Module: "mesh-delivery-view", Path: "modules/mesh-delivery-view", FP: "ab12",
|
||||
Fetch: map[string]any{"repository": "novox/mesh-delivery", "commit": askCommit, "dir": "modules/mesh-delivery-view", "paths": []any{"cmd"}},
|
||||
Artifacts: []AskArtifact{{Artifact: "bundle"}, {Artifact: "base", From: "registry/x@sha256:00"}},
|
||||
Publish: true, Toolchain: &ToolchainUsed{Version: "go1.26.0", Recipe: "3"}}}
|
||||
Publish: true, Toolchain: &ToolchainUsed{Version: "go1.26.0", Recipe: "3"}},
|
||||
Packages: PackageVersions{"git.novox.be/novox/mesh-sdk/go": askCommit}}
|
||||
}
|
||||
|
||||
func checkAsk() StepAsk {
|
||||
@@ -25,10 +26,11 @@ func checkAsk() StepAsk {
|
||||
At: time.Date(2026, 10, 23, 13, 0, 0, 0, time.UTC),
|
||||
Check: &CheckJob{Fetch: map[string]any{"repository": "novox/mesh-catalog", "commit": askCommit, "ref": "refs/pull/3/head", "base": askCommit},
|
||||
Levels: []string{LevelUnit, LevelIntegration}, Kept: true, Runner: "workspace", RunnerWhen: "modules/mesh-build-go/", Run: "",
|
||||
NodeNames: []string{"anchor", "laptop"}}}
|
||||
NodeNames: []string{"anchor", "laptop"}},
|
||||
Packages: PackageVersions{"git.novox.be/novox/mesh-sdk/go": askCommit, "git.novox.be/novox/mesh-controller/modules/route-proxy/contract": askCommit}}
|
||||
}
|
||||
|
||||
// 1887/A1a: an ask encodes and decodes with every field.
|
||||
// 1887/A1a: an ask encodes and decodes with every field; 1585/S4: the package versions too.
|
||||
func TestAskRoundTrips(t *testing.T) {
|
||||
for _, a := range []StepAsk{buildAsk(), checkAsk()} {
|
||||
raw, err := EncodeStepAsk(a)
|
||||
@@ -58,6 +60,13 @@ func TestAskRefusesUnknownKindAndLanguage(t *testing.T) {
|
||||
"commit": func(a *StepAsk) { a.Build.Fetch = map[string]any{"repository": "novox/x", "commit": "main"} },
|
||||
"path": func(a *StepAsk) { a.Build.Path = "" },
|
||||
"needs": func(a *StepAsk) { a.Needs = []string{NeedNodeBuildNode} }, // issue 890: a build needs no node
|
||||
// Issue 1585: a package's version is a full commit, of a module path.
|
||||
"version": func(a *StepAsk) { a.Packages["git.novox.be/novox/mesh-sdk/go"] = "v0.1.7" },
|
||||
"package": func(a *StepAsk) { a.Packages["git.novox.be/novox/x y"] = askCommit },
|
||||
"compose": func(a *StepAsk) {
|
||||
a.Kind, a.Build = AskCheck, nil
|
||||
a.Compose = &ComposeJob{Fetch: map[string]any{"repository": "novox/mesh-sdk", "commit": askCommit}, Dir: "go", Package: "x", Request: json.RawMessage(`{}`)}
|
||||
},
|
||||
}
|
||||
for name, change := range cases {
|
||||
a := buildAsk()
|
||||
|
||||
Reference in New Issue
Block a user