From f4417ac665c724e75dbb87d07e022ecba96d2bdb Mon Sep 17 00:00:00 2001 From: David Genest Date: Fri, 8 Nov 2019 09:36:13 -0500 Subject: [PATCH] only write jsonnnet files if we made changes (#56) Adding a dep, or updating a dependency version makes writes to the jsonnet files. We evaluate the changes on each of the files. An empty jsonnetfile.json does not create a corresponding lockfile, as a missing lockfile is not different from its previous (non existent). --- cmd/jb/install.go | 28 ++++++++-- cmd/jb/install_test.go | 94 ++++++++++++++++++++++++++++++++-- pkg/jsonnetfile/jsonnetfile.go | 14 +++-- 3 files changed, 124 insertions(+), 12 deletions(-) diff --git a/cmd/jb/install.go b/cmd/jb/install.go index 7d64b6f..35fd140 100644 --- a/cmd/jb/install.go +++ b/cmd/jb/install.go @@ -34,14 +34,20 @@ func installCommand(dir, jsonnetHome string, uris []string) int { dir = "." } - jsonnetFile, err := jsonnetfile.Load(filepath.Join(dir, jsonnetfile.File)) + jbfilebytes, err := ioutil.ReadFile(filepath.Join(dir, jsonnetfile.File)) kingpin.FatalIfError(err, "failed to load jsonnetfile") - lockFile, err := jsonnetfile.Load(filepath.Join(dir, jsonnetfile.LockFile)) + jsonnetFile, err := jsonnetfile.Unmarshal(jbfilebytes) + kingpin.FatalIfError(err, "") + + jblockfilebytes, err := ioutil.ReadFile(filepath.Join(dir, jsonnetfile.LockFile)) if !os.IsNotExist(err) { kingpin.FatalIfError(err, "failed to load lockfile") } + lockFile, err := jsonnetfile.Unmarshal(jblockfilebytes) + kingpin.FatalIfError(err, "") + kingpin.FatalIfError( os.MkdirAll(filepath.Join(dir, jsonnetHome, ".tmp"), os.ModePerm), "creating vendor folder") @@ -65,10 +71,11 @@ func installCommand(dir, jsonnetHome string, uris []string) int { kingpin.FatalIfError(err, "failed to install packages") kingpin.FatalIfError( - writeJSONFile(filepath.Join(dir, jsonnetfile.File), jsonnetFile), + writeChangedJsonnetFile(jbfilebytes, &jsonnetFile, filepath.Join(dir, jsonnetfile.File)), "updating jsonnetfile.json") + kingpin.FatalIfError( - writeJSONFile(filepath.Join(dir, jsonnetfile.LockFile), spec.JsonnetFile{Dependencies: locked}), + writeChangedJsonnetFile(jblockfilebytes, &spec.JsonnetFile{Dependencies: locked}, filepath.Join(dir, jsonnetfile.LockFile)), "updating jsonnetfile.lock.json") return 0 @@ -91,3 +98,16 @@ func writeJSONFile(name string, d interface{}) error { return ioutil.WriteFile(name, b, 0644) } + +func writeChangedJsonnetFile(originalBytes []byte, modified *spec.JsonnetFile, path string) error { + origJsonnetFile, err := jsonnetfile.Unmarshal(originalBytes) + if err != nil { + return err + } + + if reflect.DeepEqual(origJsonnetFile, *modified) { + return nil + } + + return writeJSONFile(path, *modified) +} diff --git a/cmd/jb/install_test.go b/cmd/jb/install_test.go index 2bf44c5..776380a 100644 --- a/cmd/jb/install_test.go +++ b/cmd/jb/install_test.go @@ -24,6 +24,7 @@ import ( "github.com/stretchr/testify/assert" "github.com/jsonnet-bundler/jsonnet-bundler/pkg/jsonnetfile" + "github.com/jsonnet-bundler/jsonnet-bundler/spec" ) func TestInstallCommand(t *testing.T) { @@ -35,10 +36,9 @@ func TestInstallCommand(t *testing.T) { ExpectedJsonnetLockFile []byte }{ { - Name: "NoURLs", - ExpectedCode: 0, - ExpectedJsonnetFile: []byte(`{"dependencies":null}`), - ExpectedJsonnetLockFile: []byte(`{"dependencies":null}`), + Name: "NoURLs", + ExpectedCode: 0, + ExpectedJsonnetFile: []byte(`{}`), }, { Name: "OneURL", URIs: []string{"github.com/jsonnet-bundler/jsonnet-bundler@v0.1.0"}, @@ -77,7 +77,9 @@ func TestInstallCommand(t *testing.T) { installCommand("", "vendor", tc.URIs) jsonnetFileContent(t, jsonnetfile.File, tc.ExpectedJsonnetFile) - jsonnetFileContent(t, jsonnetfile.LockFile, tc.ExpectedJsonnetLockFile) + if tc.ExpectedJsonnetLockFile != nil { + jsonnetFileContent(t, jsonnetfile.LockFile, tc.ExpectedJsonnetLockFile) + } }) } @@ -93,3 +95,85 @@ func jsonnetFileContent(t *testing.T, filename string, content []byte) { t.Log(string(bytes)) } } + +func TestWriteChangedJsonnetFile(t *testing.T) { + testcases := []struct { + Name string + JsonnetFileBytes []byte + NewJsonnetFile spec.JsonnetFile + ExpectWrite bool + }{ + { + Name: "NoDiffEmpty", + JsonnetFileBytes: []byte(`{}`), + NewJsonnetFile: spec.New(), + ExpectWrite: false, + }, + { + Name: "NoDiffNotEmpty", + JsonnetFileBytes: []byte(`{"dependencies": [{"name": "foobar"}]}`), + NewJsonnetFile: spec.JsonnetFile{ + Dependencies: map[string]spec.Dependency{ + "foobar": { + Name: "foobar", + }, + }, + }, + ExpectWrite: false, + }, + { + Name: "DiffVersion", + JsonnetFileBytes: []byte(`{"dependencies": [{"name": "foobar", "version": "1.0"}]}`), + NewJsonnetFile: spec.JsonnetFile{ + Dependencies: map[string]spec.Dependency{ + "foobar": { + Name: "foobar", + Version: "2.0", + }, + }, + }, + ExpectWrite: true, + }, + { + Name: "Diff", + JsonnetFileBytes: []byte(`{}`), + NewJsonnetFile: spec.JsonnetFile{ + Dependencies: map[string]spec.Dependency{ + "foobar": { + Name: "foobar", + Source: spec.Source{ + GitSource: &spec.GitSource{ + Remote: "https://github.com/foobar/foobar", + Subdir: "", + }, + }, + Version: "master", + DepSource: "", + }}, + }, + ExpectWrite: true, + }, + } + outputjsonnetfile := "changedjsonnet.json" + for _, tc := range testcases { + _ = t.Run(tc.Name, func(t *testing.T) { + clean := func() { + _ = os.Remove(outputjsonnetfile) + } + clean() + defer clean() + + err := writeChangedJsonnetFile(tc.JsonnetFileBytes, &tc.NewJsonnetFile, outputjsonnetfile) + assert.NoError(t, err) + + if tc.ExpectWrite { + assert.FileExists(t, outputjsonnetfile) + } else { + _, err := os.Lstat(outputjsonnetfile) + if err != nil { + assert.True(t, os.IsNotExist(err)) + } + } + }) + } +} diff --git a/pkg/jsonnetfile/jsonnetfile.go b/pkg/jsonnetfile/jsonnetfile.go index a6cb124..1a2dab1 100644 --- a/pkg/jsonnetfile/jsonnetfile.go +++ b/pkg/jsonnetfile/jsonnetfile.go @@ -33,13 +33,21 @@ var ErrNoFile = errors.New("no jsonnetfile") // Load reads a jsonnetfile.(lock).json from disk func Load(filepath string) (spec.JsonnetFile, error) { - m := spec.New() - bytes, err := ioutil.ReadFile(filepath) if err != nil { - return m, err + return spec.New(), err } + return Unmarshal(bytes) +} + +// Unmarshal creates a spec.JsonnetFile from bytes. Empty bytes +// will create an empty spec. +func Unmarshal(bytes []byte) (spec.JsonnetFile, error) { + m := spec.New() + if len(bytes) == 0 { + return m, nil + } if err := json.Unmarshal(bytes, &m); err != nil { return m, errors.Wrap(err, "failed to unmarshal file") }