Skip to content

Commit

Permalink
Don't unecessarily change the package.lock and package.yaml files (#54)
Browse files Browse the repository at this point in the history
  • Loading branch information
floitsch authored Aug 6, 2024
1 parent 36314dd commit e24aef3
Show file tree
Hide file tree
Showing 9 changed files with 115 additions and 31 deletions.
1 change: 1 addition & 0 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ BUILD_DIR := build

GO_PACKAGE := github.com/toitlang/tpkg

# When rebuilding gold files, the TOITC_PATH must be changed to be absolute.
TOITC_PATH ?= toit.compile
TOITLSP_PATH ?= toit.lsp
TOITVM_PATH ?= toit.run
Expand Down
11 changes: 8 additions & 3 deletions pkg/tpkg/lock_file.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ package tpkg

import (
"fmt"
"io/ioutil"
"os"
"path/filepath"
"strings"

Expand Down Expand Up @@ -76,7 +76,7 @@ func (pe PackageEntry) Validate(ui UI) error {
// ReadLockFile reads the lock-file at the given path.
func ReadLockFile(path string) (*LockFile, error) {
// TODO(florian): should we validate the file here?
b, err := ioutil.ReadFile(path)
b, err := os.ReadFile(path)
if err != nil {
return nil, err
}
Expand All @@ -91,11 +91,16 @@ func ReadLockFile(path string) (*LockFile, error) {
}

func (lf *LockFile) WriteToFile() error {
// Write the YAML to memory first, and then compare it with any
// existing file.
// We don't want to touch files if they don't change.

b, err := yaml.Marshal(lf)
if err != nil {
return err
}
return ioutil.WriteFile(lf.path, b, 0644)

return writeFileIfChanged(lf.path, b)
}

// PrintLockFile prints the contents of the lock file for the current project.
Expand Down
19 changes: 8 additions & 11 deletions pkg/tpkg/spec.go
Original file line number Diff line number Diff line change
Expand Up @@ -16,9 +16,9 @@
package tpkg

import (
"bytes"
"fmt"
"io"
"io/ioutil"
"os"
"path/filepath"
"regexp"
Expand Down Expand Up @@ -111,7 +111,7 @@ func (s *Spec) Validate(ui UI) error {

func (s *Spec) ParseFile(filename string, ui UI) error {
s.path = filename
b, err := ioutil.ReadFile(filename)
b, err := os.ReadFile(filename)
if err != nil {
return err
}
Expand All @@ -134,18 +134,15 @@ func (s *Spec) WriteYAML(writer io.Writer) error {
}

func (s *Spec) WriteToFile() error {
file, err := os.Create(s.path)
// Write the YAML to memory first, and then compare it with any
// existing file.
// We don't want to touch files if they don't change.
var b bytes.Buffer
err := s.WriteYAML(&b)
if err != nil {
return err
}
defer func() {
e := file.Close()
if e != nil {
err = e
}
}()

return s.WriteYAML(file)
return writeFileIfChanged(s.path, b.Bytes())
}

// BuildLockFile generates a lock file using the given solution.
Expand Down
13 changes: 13 additions & 0 deletions pkg/tpkg/ui.go
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,19 @@ func (ui fmtUI) ReportInfo(format string, a ...interface{}) {
fmt.Printf("Info: "+format+"\n", a...)
}

// nullUI implements a UI that does nothing.
type nullUI struct{}

func (ui nullUI) ReportError(format string, a ...interface{}) error {
return ErrAlreadyReported
}

func (ui nullUI) ReportWarning(format string, a ...interface{}) {
}

func (ui nullUI) ReportInfo(format string, a ...interface{}) {
}

var (
// ErrAlreadyReported can be used to signal that an error has
// been reported, and that no further action needs to be taken.
Expand Down
26 changes: 26 additions & 0 deletions pkg/tpkg/util.go
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@
package tpkg

import (
"bytes"
"fmt"
"os"
"path/filepath"
Expand Down Expand Up @@ -74,3 +75,28 @@ func sdkConstraintToMinSDK(sdk string) (*version.Version, error) {
}
return minSDK, nil
}

func writeFileIfChanged(path string, content []byte) error {
// Check whether the file already exists and has the same content.
// We don't want to touch files if they don't change.
if _, err := os.Stat(path); err == nil {
oldContent, err := os.ReadFile(path)
if err == nil && bytes.Equal(oldContent, content) {
return nil
}
}

file, err := os.Create(path)
if err != nil {
return err
}
defer func() {
e := file.Close()
if e != nil {
err = e
}
}()

_, err = file.Write(content)
return err
}
9 changes: 9 additions & 0 deletions tests/assets/pkg/InstallNoMTimeChange/gold/test.gold
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
// Add registry so we can find packages.
===================
pkg registry add --local test-reg <TEST>/registry_git_pkgs
Exit Code: 0
===================
// Just 'install' doesn't add the missing dependencies.
===================
pkg install
Exit Code: 0
6 changes: 6 additions & 0 deletions tests/assets/pkg/InstallNoMTimeChange/main.toit
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
// Copyright (C) 2024 Toitware ApS. All rights reserved.
import pkg1

main:
print pkg1.identify
7 changes: 7 additions & 0 deletions tests/assets/pkg/InstallNoMTimeChange/package.lock
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
prefixes:
pkg1: pkg1

packages:
pkg1:
url: '<[*TEST_GIT_DIR_ESCAPE*]>/git_pkgs/pkg1'
version: 1.0.0
54 changes: 37 additions & 17 deletions tests/pkg_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -290,7 +290,7 @@ func copyRec(t *tedi.T, testDir string, sourceDir string, targetDir string) {
}
return unzip(p, filepath.Dir(target))
}
data, err := ioutil.ReadFile(p)
data, err := os.ReadFile(p)
if err != nil {
return err
}
Expand All @@ -300,7 +300,7 @@ func copyRec(t *tedi.T, testDir string, sourceDir string, targetDir string) {
data = bytes.ReplaceAll(data, []byte(testDirGitPattern), []byte(testDirGitURL))
escapedTestDirGitURL := string(compiler.ToURIPath(testDirGitURL))
data = bytes.ReplaceAll(data, []byte(testDirGitEscapePattern), []byte(escapedTestDirGitURL))
return ioutil.WriteFile(target, data, info.Mode().Perm())
return os.WriteFile(target, data, info.Mode().Perm())
})
require.NoError(t, err)
}
Expand Down Expand Up @@ -515,7 +515,7 @@ func (pt PkgTest) updateGold(name string, newGold string) {
}
require.NoError(pt.t, err)
goldPath := filepath.Join(goldDir, name+".gold")
oldBytes, err := ioutil.ReadFile(goldPath)
oldBytes, err := os.ReadFile(goldPath)
if err == nil {
oldGold := string(oldBytes)
if string(oldBytes) != newGold {
Expand All @@ -524,7 +524,7 @@ func (pt PkgTest) updateGold(name string, newGold string) {
} else if os.IsNotExist(err) {
fmt.Printf("Creating new gold %s with content:\n%s", goldPath, newGold)
}
err = ioutil.WriteFile(goldPath, []byte(newGold), 0644)
err = os.WriteFile(goldPath, []byte(newGold), 0644)
require.NoError(pt.t, err)
}

Expand All @@ -534,7 +534,7 @@ func (pt PkgTest) checkGold(name string, actual string) {
return
}
goldPath := filepath.Join(pt.dir, "gold", name+".gold")
contentBytes, err := ioutil.ReadFile(goldPath)
contentBytes, err := os.ReadFile(goldPath)
require.NoError(pt.t, err)
gold := string(contentBytes)
// On windows the gold files come with '\r\n'...
Expand Down Expand Up @@ -648,12 +648,12 @@ func test_toitPkg(t *tedi.T) {
Branch: plumbing.NewTagReferenceName("1.0.0"),
})
require.NoError(t, err)
data, err := ioutil.ReadFile(filepath.Join(gitDir, "a"))
data, err := os.ReadFile(filepath.Join(gitDir, "a"))
require.NoError(t, err)
dataStr := strings.ReplaceAll(string(data), "\r\n", "\n")
// Notice that we implicitly check the correct `<[*TEST_DIR*>]` replacement.
assert.Equal(t, dirInFiles+"/a 1.0.0\n", dataStr)
data, err = ioutil.ReadFile(filepath.Join(gitDir, "b"))
data, err = os.ReadFile(filepath.Join(gitDir, "b"))
require.NoError(t, err)
dataStr = strings.ReplaceAll(string(data), "\r\n", "\n")
assert.Equal(t, dirInFiles+"/b 1.0.0\n", dataStr)
Expand All @@ -666,13 +666,13 @@ func test_toitPkg(t *tedi.T) {
Branch: plumbing.NewTagReferenceName("2.0.0"),
})
require.NoError(t, err)
data, err = ioutil.ReadFile(filepath.Join(gitDir, "a"))
data, err = os.ReadFile(filepath.Join(gitDir, "a"))
require.NoError(t, err)
dataStr = strings.ReplaceAll(string(data), "\r\n", "\n")
assert.Equal(t, dirInFiles+"/a 2.0.0\n", dataStr)
_, err = os.Stat(filepath.Join(gitDir, "b"))
assert.True(t, os.IsNotExist(err))
data, err = ioutil.ReadFile(filepath.Join(gitDir, "c"))
data, err = os.ReadFile(filepath.Join(gitDir, "c"))
require.NoError(t, err)
dataStr = strings.ReplaceAll(string(data), "\r\n", "\n")
assert.Equal(t, dirInFiles+"/c 2.0.0\n", dataStr)
Expand Down Expand Up @@ -1130,10 +1130,10 @@ func test_toitPkg(t *tedi.T) {
{"pkg", "list"},
})

data, err := ioutil.ReadFile(filepath.Join(pt.dir, yamlFile))
data, err := os.ReadFile(filepath.Join(pt.dir, yamlFile))
require.NoError(t, err)
pkgTestSpecPath := filepath.Join(regPath, yamlFile)
err = ioutil.WriteFile(pkgTestSpecPath, data, 0644)
err = os.WriteFile(pkgTestSpecPath, data, 0644)
require.NoError(t, err)

repository, err := git.PlainOpen(regPath)
Expand Down Expand Up @@ -1179,7 +1179,7 @@ func test_toitPkg(t *tedi.T) {
// Touch a file in the cache path, so we can see that the clear-cache
// actually removes it.
cacheFile := filepath.Join(regCachePath, "foo")
err := ioutil.WriteFile(cacheFile, []byte("foo"), 0644)
err := os.WriteFile(cacheFile, []byte("foo"), 0644)
require.NoError(t, err)

if i == 0 {
Expand Down Expand Up @@ -1359,7 +1359,7 @@ func test_toitPkg(t *tedi.T) {
})

yamlPath := filepath.Join(pt.dir, "package.yaml")
err := ioutil.WriteFile(yamlPath, []byte{}, 0644)
err := os.WriteFile(yamlPath, []byte{}, 0644)
assert.NoError(t, err)

pt.GoldToit("test2", [][]string{
Expand All @@ -1378,7 +1378,7 @@ func test_toitPkg(t *tedi.T) {
{"pkg", "registry", "add", "toit", "github.com/toitware/registry"},
})
configPath := filepath.Join(pt.dir, "config.yaml")
data, err := ioutil.ReadFile(configPath)
data, err := os.ReadFile(configPath)
assert.NoError(t, err)
assert.Contains(t, string(data), "toitware/registry")
})
Expand Down Expand Up @@ -1473,7 +1473,7 @@ func test_toitPkg(t *tedi.T) {
})
descPath := filepath.Join(pt.dir, "out", "packages", "github.com", "toitware", "toit-morse", "1.0.6", "desc.yaml")
assert.FileExists(t, descPath)
_, err := ioutil.ReadFile(descPath)
_, err := os.ReadFile(descPath)
assert.NoError(t, err)
})

Expand Down Expand Up @@ -1647,13 +1647,13 @@ func test_toitPkg(t *tedi.T) {
// Modifying the version constraint in the package.spec is copied to the
// lock file.
packageSpecPath := filepath.Join(pt.dir, "package.yaml")
data, err := ioutil.ReadFile(packageSpecPath)
data, err := os.ReadFile(packageSpecPath)
assert.NoError(t, err)
str := string(data) + `
environment:
sdk: ^1.20.0
`
err = ioutil.WriteFile(packageSpecPath, []byte(str), 0644)
err = os.WriteFile(packageSpecPath, []byte(str), 0644)
assert.NoError(t, err)
pt.GoldToit("test3", [][]string{
{"// sdkVersion = "},
Expand Down Expand Up @@ -1709,4 +1709,24 @@ environment:
}
wg.Wait()
})

t.Run("InstallNoMTimeChange", func(t *tedi.T, pt PkgTest) {
regPath := filepath.Join(pt.dir, "registry_git_pkgs")
lockPath := filepath.Join(pt.dir, "package.lock")
info, err := os.Stat(lockPath)
assert.NoError(t, err)
mtimeBefore := info.ModTime()

pt.GoldToit("test", [][]string{
{"// Add registry so we can find packages."},
{"pkg", "registry", "add", "--local", "test-reg", regPath},
{"// Just 'install' doesn't add the missing dependencies."},
{"pkg", "install"},
})

info, err = os.Stat(lockPath)
assert.NoError(t, err)
mtimeAfter := info.ModTime()
assert.Equal(t, mtimeBefore, mtimeAfter)
})
}

0 comments on commit e24aef3

Please sign in to comment.