mirror of
https://github.com/go-task/task.git
synced 2024-12-12 10:45:49 +02:00
247c2586c2
* refactor: ast package * feat: read -> taskfile * refactor: taskfile.Taskfile -> taskfile.Read * refactor: move merge function back into taskfile package * refactor: rename taskfile.go to read.go
127 lines
2.7 KiB
Go
127 lines
2.7 KiB
Go
package fingerprint
|
|
|
|
import (
|
|
"fmt"
|
|
"io"
|
|
"os"
|
|
"path/filepath"
|
|
"regexp"
|
|
"strings"
|
|
|
|
"github.com/zeebo/xxh3"
|
|
|
|
"github.com/go-task/task/v3/internal/filepathext"
|
|
"github.com/go-task/task/v3/taskfile/ast"
|
|
)
|
|
|
|
// ChecksumChecker validates if a task is up to date by calculating its source
|
|
// files checksum
|
|
type ChecksumChecker struct {
|
|
tempDir string
|
|
dry bool
|
|
}
|
|
|
|
func NewChecksumChecker(tempDir string, dry bool) *ChecksumChecker {
|
|
return &ChecksumChecker{
|
|
tempDir: tempDir,
|
|
dry: dry,
|
|
}
|
|
}
|
|
|
|
func (checker *ChecksumChecker) IsUpToDate(t *ast.Task) (bool, error) {
|
|
if len(t.Sources) == 0 {
|
|
return false, nil
|
|
}
|
|
|
|
checksumFile := checker.checksumFilePath(t)
|
|
|
|
data, _ := os.ReadFile(checksumFile)
|
|
oldHash := strings.TrimSpace(string(data))
|
|
|
|
newHash, err := checker.checksum(t)
|
|
if err != nil {
|
|
return false, nil
|
|
}
|
|
|
|
if !checker.dry && oldHash != newHash {
|
|
_ = os.MkdirAll(filepathext.SmartJoin(checker.tempDir, "checksum"), 0o755)
|
|
if err = os.WriteFile(checksumFile, []byte(newHash+"\n"), 0o644); err != nil {
|
|
return false, err
|
|
}
|
|
}
|
|
|
|
if len(t.Generates) > 0 {
|
|
// For each specified 'generates' field, check whether the files actually exist
|
|
for _, g := range t.Generates {
|
|
if g.Negate {
|
|
continue
|
|
}
|
|
generates, err := Glob(t.Dir, g.Glob)
|
|
if os.IsNotExist(err) {
|
|
return false, nil
|
|
}
|
|
if err != nil {
|
|
return false, err
|
|
}
|
|
if len(generates) == 0 {
|
|
return false, nil
|
|
}
|
|
}
|
|
}
|
|
|
|
return oldHash == newHash, nil
|
|
}
|
|
|
|
func (checker *ChecksumChecker) Value(t *ast.Task) (any, error) {
|
|
return checker.checksum(t)
|
|
}
|
|
|
|
func (checker *ChecksumChecker) OnError(t *ast.Task) error {
|
|
if len(t.Sources) == 0 {
|
|
return nil
|
|
}
|
|
return os.Remove(checker.checksumFilePath(t))
|
|
}
|
|
|
|
func (*ChecksumChecker) Kind() string {
|
|
return "checksum"
|
|
}
|
|
|
|
func (c *ChecksumChecker) checksum(t *ast.Task) (string, error) {
|
|
sources, err := Globs(t.Dir, t.Sources)
|
|
if err != nil {
|
|
return "", err
|
|
}
|
|
|
|
h := xxh3.New()
|
|
buf := make([]byte, 128*1024)
|
|
for _, f := range sources {
|
|
// also sum the filename, so checksum changes for renaming a file
|
|
if _, err := io.CopyBuffer(h, strings.NewReader(filepath.Base(f)), buf); err != nil {
|
|
return "", err
|
|
}
|
|
f, err := os.Open(f)
|
|
if err != nil {
|
|
return "", err
|
|
}
|
|
if _, err = io.CopyBuffer(h, f, buf); err != nil {
|
|
return "", err
|
|
}
|
|
f.Close()
|
|
}
|
|
|
|
hash := h.Sum128()
|
|
return fmt.Sprintf("%x%x", hash.Hi, hash.Lo), nil
|
|
}
|
|
|
|
func (checker *ChecksumChecker) checksumFilePath(t *ast.Task) string {
|
|
return filepath.Join(checker.tempDir, "checksum", normalizeFilename(t.Name()))
|
|
}
|
|
|
|
var checksumFilenameRegexp = regexp.MustCompile("[^A-z0-9]")
|
|
|
|
// replaces invalid characters on filenames with "-"
|
|
func normalizeFilename(f string) string {
|
|
return checksumFilenameRegexp.ReplaceAllString(f, "-")
|
|
}
|