Raise @novox/mesh-sdk to 0.1.17, and a unit test refuses a packed change that keeps its version (issue 1899, met twice) #75

Merged
jschoubben merged 1 commits from fix/1899-version-guard into main 2026-10-23 22:35:43 +00:00
4 changed files with 108 additions and 3 deletions
+104
View File
@@ -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)
}
}
+2 -2
View File
@@ -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"
+1
View File
@@ -0,0 +1 @@
0.1.17 90cca0bf1acfb386d66ccddb9f612703cf8f0205791b846982a524d0044aa0fb
+1 -1
View File
@@ -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": {