105 lines
3.1 KiB
Go
105 lines
3.1 KiB
Go
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)
|
|
}
|
|
}
|