diff --git a/go/packageguard/packageversion_test.go b/go/packageguard/packageversion_test.go new file mode 100644 index 0000000..2ec2d8c --- /dev/null +++ b/go/packageguard/packageversion_test.go @@ -0,0 +1,104 @@ +package packageguard + +import ( + "crypto/sha256" + "encoding/hex" + "encoding/json" + "fmt" + "io/fs" + "os" + "path/filepath" + "sort" + "strings" + "testing" +) + +// The registry refuses a second, different package under one version +// ("version taken", issues 1899 and 1331). This guard fails before a merge when +// what npm packs changes while the version stays: package-version.sum holds the +// version and a sha256 over the inputs of the packed dist (package.json without +// its version, src/, tsconfig.json) and of the README npm always packs. +// The lock file is not packed, so it is not hashed. + +const sumFile = "package-version.sum" + +func packedInputs(root string) (version, sum string, err error) { + raw, err := os.ReadFile(filepath.Join(root, "package.json")) + if err != nil { + return "", "", err + } + var pkg map[string]any + if err := json.Unmarshal(raw, &pkg); err != nil { + return "", "", err + } + version, _ = pkg["version"].(string) + delete(pkg, "version") + canon, err := json.Marshal(pkg) // map keys are sorted: stable bytes + if err != nil { + return "", "", err + } + files := map[string][]byte{"package.json": canon} + for _, name := range []string{"tsconfig.json", "README.md"} { + b, err := os.ReadFile(filepath.Join(root, name)) + if err != nil { + return "", "", err + } + files[name] = b + } + err = filepath.WalkDir(filepath.Join(root, "src"), func(p string, d fs.DirEntry, err error) error { + if err != nil || d.IsDir() { + return err + } + b, err := os.ReadFile(p) + if err != nil { + return err + } + rel, _ := filepath.Rel(root, p) + files[filepath.ToSlash(rel)] = b + return nil + }) + if err != nil { + return "", "", err + } + names := make([]string, 0, len(files)) + for n := range files { + names = append(names, n) + } + sort.Strings(names) + h := sha256.New() + for _, n := range names { + fmt.Fprintf(h, "%s\x00%d\x00", n, len(files[n])) + h.Write(files[n]) + } + return version, hex.EncodeToString(h.Sum(nil)), nil +} + +func TestPackageVersionMatchesPackedInputs(t *testing.T) { + root := "../.." + if r := os.Getenv("MESH_SDK_ROOT"); r != "" { + root = r + } + version, sum, err := packedInputs(root) + if err != nil { + t.Fatal(err) + } + line := version + " " + sum + "\n" + if os.Getenv("UPDATE_PACKAGE_VERSION_SUM") != "" { + if err := os.WriteFile(filepath.Join(root, sumFile), []byte(line), 0o644); err != nil { + t.Fatal(err) + } + return + } + raw, err := os.ReadFile(filepath.Join(root, sumFile)) + if err != nil { + t.Fatalf("%s is missing: %v", sumFile, err) + } + recVersion, recSum, _ := strings.Cut(strings.TrimSpace(string(raw)), " ") + switch { + case recVersion == version && recSum == sum: + case recVersion == version: + t.Fatalf("what npm packs for @novox/mesh-sdk changed while its version stayed %s: the registry would refuse a second, different %s. Raise @novox/mesh-sdk's version (package.json and package-lock.json) and update %s (UPDATE_PACKAGE_VERSION_SUM=1 go test -run TestPackageVersionMatchesPackedInputs in go/packageguard)", version, version, sumFile) + default: + t.Fatalf("package.json says %s but %s records %s: update %s (UPDATE_PACKAGE_VERSION_SUM=1 go test -run TestPackageVersionMatchesPackedInputs in go/packageguard)", version, sumFile, recVersion, sumFile) + } +} diff --git a/package-lock.json b/package-lock.json index 781fd1a..acf4da6 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "@novox/mesh-sdk", - "version": "0.1.16", + "version": "0.1.17", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "@novox/mesh-sdk", - "version": "0.1.16", + "version": "0.1.17", "devDependencies": { "@types/node": "^22.0.0", "typescript": "^5.6.0" diff --git a/package-version.sum b/package-version.sum new file mode 100644 index 0000000..6925adf --- /dev/null +++ b/package-version.sum @@ -0,0 +1 @@ +0.1.17 90cca0bf1acfb386d66ccddb9f612703cf8f0205791b846982a524d0044aa0fb diff --git a/package.json b/package.json index e88f736..cd0af54 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@novox/mesh-sdk", - "version": "0.1.16", + "version": "0.1.17", "description": "The stable spine a Novox Mesh module's own code builds against.", "type": "module", "exports": {