One module, several languages, each bundle packed alone
A module is one piece of software and may still carry a daemon in one language, tools in another and a package in a third. The first cut compiled every bundle into the toolchain's single output directory, so two of them would have overwritten each other and then been packed together — one artifact containing both, published twice. So output is a property of the artifact, not of the toolchain, and the toolchain says how it is told where to write rather than where it writes. Under a directory named for the build rather than beside the source, so a pack never sweeps up the module's own working files. A second toolchain is declared so the multi-language path is exercised rather than asserted — a list with one entry cannot fail the way a list with four will. And the fake compiler in the tests now writes where it was TOLD to. One that always wrote to a fixed place would have passed whether or not each artifact got its own directory, which is the whole of what these tests are for. Claude-Session: https://claude.ai/code/session_01D6qtiYU3P9jk3pnAXyAFyx
This commit is contained in:
@@ -10,7 +10,7 @@ import (
|
||||
|
||||
const aBundle = `{"module":"greeter","version":"1",
|
||||
"build":{"artifacts":[
|
||||
{"name":"code","kind":"bundle","language":"typescript","entrypoints":["dist/index.js"]}]},
|
||||
{"name":"code","kind":"bundle","language":"typescript","entrypoints":["index.js"]}]},
|
||||
"resources":[
|
||||
{"id":"files","type":"archive","path":"/opt/greeter","artifact":"code"}]}`
|
||||
|
||||
@@ -21,15 +21,28 @@ type compiling struct{ *recorded }
|
||||
|
||||
func (c compiling) run(ctx context.Context, dir, name string, args ...string) (string, error) {
|
||||
out, err := c.recorded.run(ctx, dir, name, args...)
|
||||
if name == "docker" && len(args) > 0 && args[0] == "run" {
|
||||
made := filepath.Join(dir, "dist")
|
||||
if err := os.MkdirAll(made, 0o755); err != nil {
|
||||
return "", err
|
||||
}
|
||||
if err := os.WriteFile(filepath.Join(made, "index.js"), []byte("console.log(1)"), 0o644); err != nil {
|
||||
return "", err
|
||||
if name != "docker" || len(args) == 0 || args[0] != "run" {
|
||||
return out, err
|
||||
}
|
||||
// **Writes where it was TOLD to**, rather than to a fixed directory. A fake that always wrote
|
||||
// to one place would pass whether or not the builder gave each artifact its own — which is the
|
||||
// thing being tested.
|
||||
where := ""
|
||||
for i, a := range args {
|
||||
if a == "--outDir" && i+1 < len(args) {
|
||||
where = args[i+1]
|
||||
}
|
||||
}
|
||||
if where == "" {
|
||||
return out, err
|
||||
}
|
||||
made := filepath.Join(dir, where)
|
||||
if err := os.MkdirAll(made, 0o755); err != nil {
|
||||
return "", err
|
||||
}
|
||||
if err := os.WriteFile(filepath.Join(made, "index.js"), []byte("console.log(1)"), 0o644); err != nil {
|
||||
return "", err
|
||||
}
|
||||
return out, err
|
||||
}
|
||||
|
||||
@@ -107,3 +120,51 @@ func TestABundleInAnUnknownLanguageIsRefused(t *testing.T) {
|
||||
t.Fatalf("the refusal does not say what would have worked: %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
// **One module, two bundles, and neither packs the other.**
|
||||
//
|
||||
// The case that matters for real modules: a module is one piece of software and may still carry a
|
||||
// daemon in one language and tools in another (ADR 0040). An earlier version of this compiled
|
||||
// every bundle into the toolchain's single output directory, so two of them would overwrite each
|
||||
// other and then be packed together — one artifact containing both, twice.
|
||||
func TestTwoBundlesInOneModuleArePackedSeparately(t *testing.T) {
|
||||
const two = `{"module":"greeter","version":"1",
|
||||
"build":{"artifacts":[
|
||||
{"name":"daemon","kind":"bundle","language":"typescript","entrypoints":["index.js"]},
|
||||
{"name":"tools","kind":"bundle","language":"typescript","entrypoints":["index.js"]}]},
|
||||
"resources":[
|
||||
{"id":"a","type":"archive","path":"/opt/greeter/daemon","artifact":"daemon"},
|
||||
{"id":"b","type":"archive","path":"/opt/greeter/tools","artifact":"tools"}]}`
|
||||
|
||||
r, workspace := aRepository(t, two, map[string]string{"index.ts": "console.log(1)"})
|
||||
held := map[string]string{"mesh-tools/build": "registry.invalid/mesh-tools/build@sha256:" + strings.Repeat("b", 64)}
|
||||
|
||||
got, err := Build(context.Background(), compiling{r}.run, r,
|
||||
"https://forge.invalid/greeter.git", "", "", workspace, held)
|
||||
if err != nil {
|
||||
t.Fatalf("a module with two bundles did not build: %v", err)
|
||||
}
|
||||
|
||||
// Compiled into two different places.
|
||||
var outputs []string
|
||||
for _, line := range r.ran {
|
||||
for _, part := range strings.Fields(line) {
|
||||
if strings.HasPrefix(part, ".mesh-build/") {
|
||||
outputs = append(outputs, part)
|
||||
}
|
||||
}
|
||||
}
|
||||
if len(outputs) != 2 || outputs[0] == outputs[1] {
|
||||
t.Fatalf("two bundles did not get their own output directories: %v", outputs)
|
||||
}
|
||||
|
||||
// And published as two artifacts, each with its own digest.
|
||||
if len(r.archives) != 2 {
|
||||
t.Fatalf("expected two archives published, got %v", r.archives)
|
||||
}
|
||||
first, _ := got.Manifest.Resources[0]["digest"].(string)
|
||||
second, _ := got.Manifest.Resources[1]["digest"].(string)
|
||||
if first == "" || second == "" {
|
||||
t.Fatalf("a bundle was not pinned: %v", got.Manifest.Resources)
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user