build_node_fetch takes buildstep.FetchRequest (dir and paths narrow the workspace) and an
optional package {repository, commit, ref}: the group's package member, built and packed
shut off and published nowhere. Each member's lock decides its SDK: the group's packed
file when the version and integrity are the lock's, else the registry's (integrity
checked); a version the registry lacks fails waits for: @novox/mesh-sdk@<version>. A
package artifact is built by its own script and packed with npm pack, shut off; only a
trunk build publishes it, from this process with the write credential (never in a
workspace): absent is published, the same file is done, another is version taken:. The
registry configuration file now lies outside every workspace.
126 lines
6.3 KiB
Go
126 lines
6.3 KiB
Go
//go:build integration
|
|
|
|
package main
|
|
|
|
import (
|
|
"context"
|
|
"encoding/json"
|
|
"os"
|
|
"os/exec"
|
|
"path/filepath"
|
|
"strings"
|
|
"testing"
|
|
|
|
"git.novox.be/novox/mesh-sdk/go/buildstep"
|
|
"git.novox.be/novox/mesh-sdk/go/forgekey"
|
|
)
|
|
|
|
// sdkFixture is a package named as the SDK at version 9.9.9 with no dependency, whose build script writes dist/: what
|
|
// the group's package member is to its TypeScript members.
|
|
var sdkFixture = map[string]string{
|
|
"module.json": `{"module":"mesh-sdk","version":"1","build":{"artifacts":[{"name":"lib","kind":"package","language":"typescript"}]}}`,
|
|
"package.json": `{"name":"@novox/mesh-sdk","version":"9.9.9","type":"module","files":["dist"],"scripts":{"build":"node build.mjs"}}`,
|
|
"package-lock.json": `{"name":"@novox/mesh-sdk","version":"9.9.9","lockfileVersion":3,"requires":true,"packages":{"":{"name":"@novox/mesh-sdk","version":"9.9.9"}}}`,
|
|
"build.mjs": "import fs from 'node:fs';\nfs.mkdirSync('dist', {recursive: true});\nfs.writeFileSync('dist/index.js', 'export const v = \"9.9.9\";\\n');\n",
|
|
}
|
|
|
|
// forgeRepo makes a bare repository o/<name> under root/forge holding files at one commit, and answers the commit.
|
|
func forgeRepo(t *testing.T, root, name string, files map[string]string) string {
|
|
t.Helper()
|
|
bare, work := filepath.Join(root, "forge", "o", name+".git"), filepath.Join(root, "work-"+name)
|
|
os.MkdirAll(bare, 0o755)
|
|
os.MkdirAll(work, 0o755)
|
|
git(t, bare, "init", "-q", "--bare")
|
|
git(t, work, "init", "-q", "-b", "main")
|
|
for n, c := range files {
|
|
write(t, filepath.Join(work, filepath.FromSlash(n)), c, 0o644)
|
|
}
|
|
git(t, work, "add", ".")
|
|
git(t, work, "commit", "-q", "-m", "c")
|
|
git(t, work, "push", "-q", bare, "HEAD:refs/heads/main")
|
|
return git(t, work, "rev-parse", "HEAD")
|
|
}
|
|
|
|
// ADR 0500 §5 with the lead's decision L10, on real git, npm and bubblewrap: the fetch packs the group's package member
|
|
// (its build script and npm pack shut off, nothing published), and a member whose lock names that version with that
|
|
// integrity gets it in its node_modules; a member whose lock names it with another integrity fails naming both.
|
|
func TestTheFetchPacksTheGroupsPackageAndServesItToAMember(t *testing.T) {
|
|
for _, tool := range []string{"node", "npm", "git", "bwrap"} {
|
|
if _, err := exec.LookPath(tool); err != nil {
|
|
t.Skipf("NOT RUN: no %s where this check runs; every build node has it", tool)
|
|
}
|
|
}
|
|
root := t.TempDir()
|
|
sdkCommit := forgeRepo(t, root, "sdk", sdkFixture)
|
|
m, _, _ := newMachine(t)
|
|
m.Run = forgekey.ExecRunner
|
|
m.RemoteBase = "file://" + filepath.Join(root, "forge") + "/"
|
|
m.FetchOptions = []buildstep.Option{buildstep.AllowFileRemote()}
|
|
fakeRegistry(t, m, nil) // the registry holds no 9.9.9: only the group can serve it
|
|
// What the group's member packs to, as the member's lock would name it: packed once here the way the fetch packs it.
|
|
probe, err := m.fetch(context.Background(), map[string]any{"repository": "o/sdk", "commit": sdkCommit,
|
|
"package": map[string]any{"repository": "o/sdk", "commit": sdkCommit}})
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
group := strings.Fields(probe.(FetchResult).Group)
|
|
if len(group) != 2 || group[0] != "@novox/mesh-sdk@9.9.9" || !strings.HasPrefix(group[1], "sha512-") {
|
|
t.Fatalf("the fetch answered the group %q", probe.(FetchResult).Group)
|
|
}
|
|
member := func(integrity string) string {
|
|
lock, _ := json.Marshal(map[string]any{"name": "m", "lockfileVersion": 3, "requires": true, "packages": map[string]any{
|
|
"": map[string]any{"name": "m", "dependencies": map[string]any{"@novox/mesh-sdk": "9.9.9"}},
|
|
"node_modules/@novox/mesh-sdk": map[string]any{"version": "9.9.9", "integrity": integrity}}})
|
|
files := typeScriptModule(`{"name":"m","dependencies":{"@novox/mesh-sdk":"9.9.9"}}`)
|
|
files["modules/m/package-lock.json"] = string(lock)
|
|
return forgeRepo(t, root, "m-"+integrity[len(integrity)-6:len(integrity)-2], files)
|
|
}
|
|
commit := member(group[1])
|
|
name := "m-" + group[1][len(group[1])-6:len(group[1])-2]
|
|
out, err := m.fetch(context.Background(), map[string]any{"repository": "o/" + name, "commit": commit,
|
|
"package": map[string]any{"repository": "o/sdk", "commit": sdkCommit}})
|
|
if err != nil {
|
|
t.Fatalf("a member locking the group's version: %v", err)
|
|
}
|
|
placed := filepath.Join(m.Work, out.(FetchResult).Workspace, "tree", "modules", "m", "node_modules", "@novox", "mesh-sdk")
|
|
if raw, err := os.ReadFile(filepath.Join(placed, "dist", "index.js")); err != nil || !strings.Contains(string(raw), "9.9.9") {
|
|
t.Errorf("the member's SDK is not the group's built package: %q %v", raw, err)
|
|
}
|
|
other := member("sha512-AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA==")
|
|
_, err = m.fetch(context.Background(), map[string]any{"repository": "o/m-AAAA", "commit": other,
|
|
"package": map[string]any{"repository": "o/sdk", "commit": sdkCommit}})
|
|
if err == nil || !strings.Contains(err.Error(), group[1]) {
|
|
t.Errorf("a member locking another integrity answered %v", err)
|
|
}
|
|
}
|
|
|
|
// ADR 0499 §4 (issue 1758): a fetch asked with the module's directory and its named paths leaves only those, the
|
|
// manifest and the lock files; another module of the commit is not listed or installed.
|
|
func TestAFetchWithNamedPathsNarrowsTheWorkspace(t *testing.T) {
|
|
root := t.TempDir()
|
|
files := typeScriptModule(`{"name":"m"}`)
|
|
files["modules/m/tools/index.ts"] = "export {}\n"
|
|
files["modules/m/docs/a.md"] = "not named\n"
|
|
files["modules/other/module.json"] = files["modules/m/module.json"]
|
|
files["README.md"] = "the repository\n"
|
|
commit := forgeRepo(t, root, "narrow", files)
|
|
m, _, _ := newMachine(t)
|
|
m.Run = forgekey.ExecRunner
|
|
m.RemoteBase = "file://" + filepath.Join(root, "forge") + "/"
|
|
m.FetchOptions = []buildstep.Option{buildstep.AllowFileRemote()}
|
|
out, err := m.fetch(context.Background(), map[string]any{"repository": "o/narrow", "commit": commit, "dir": "modules/m", "paths": []any{"tools"}})
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
r := out.(FetchResult)
|
|
tree := filepath.Join(m.Work, r.Workspace, "tree")
|
|
for _, gone := range []string{"README.md", "modules/m/docs/a.md", "modules/other/module.json"} {
|
|
if _, err := os.Stat(filepath.Join(tree, gone)); err == nil {
|
|
t.Errorf("%s is still in the workspace", gone)
|
|
}
|
|
}
|
|
if _, err := os.Stat(filepath.Join(tree, "modules/m/tools/index.ts")); err != nil || strings.Join(r.Modules, ",") != "modules/m" {
|
|
t.Errorf("the named path or the module went: %v, modules %v", err, r.Modules)
|
|
}
|
|
}
|