1
0
mirror of https://github.com/go-task/task.git synced 2025-11-06 09:09:13 +02:00

move some packages to the "internal" directory

- this makes it impossible to import these packages outside Task
- as a side effect, it makes the root directory cleaner
This commit is contained in:
Andrey Nering
2017-10-15 17:58:21 -02:00
parent c53db134c6
commit 33b167215d
15 changed files with 11 additions and 16 deletions

View File

@@ -0,0 +1,83 @@
package status
import (
"crypto/md5"
"fmt"
"io"
"io/ioutil"
"os"
"path/filepath"
"regexp"
"strings"
)
// Checksum validades if a task is up to date by calculating its source
// files checksum
type Checksum struct {
Dir string
Task string
Sources []string
}
// IsUpToDate implements the Checker interface
func (c *Checksum) IsUpToDate() (bool, error) {
checksumFile := c.checksumFilePath()
data, _ := ioutil.ReadFile(checksumFile)
oldMd5 := strings.TrimSpace(string(data))
sources, err := glob(c.Dir, c.Sources)
if err != nil {
return false, err
}
newMd5, err := c.checksum(sources...)
if err != nil {
return false, nil
}
_ = os.MkdirAll(filepath.Join(c.Dir, ".task"), 0755)
if err = ioutil.WriteFile(checksumFile, []byte(newMd5+"\n"), 0644); err != nil {
return false, err
}
return oldMd5 == newMd5, nil
}
func (c *Checksum) checksum(files ...string) (string, error) {
h := md5.New()
for _, f := range files {
f, err := os.Open(f)
if err != nil {
return "", err
}
info, err := f.Stat()
if err != nil {
return "", err
}
if info.IsDir() {
continue
}
if _, err := io.Copy(h, f); err != nil {
return "", err
}
}
return fmt.Sprintf("%x", h.Sum(nil)), nil
}
// OnError implements the Checker interface
func (c *Checksum) OnError() error {
return os.Remove(c.checksumFilePath())
}
func (c *Checksum) checksumFilePath() string {
return filepath.Join(c.Dir, ".task", c.normalizeFilename(c.Task))
}
var checksumFilenameRegexp = regexp.MustCompile("[^A-z0-9]")
// replaces invalid caracters on filenames with "-"
func (*Checksum) normalizeFilename(f string) string {
return checksumFilenameRegexp.ReplaceAllString(f, "-")
}

View File

@@ -0,0 +1,21 @@
package status
import (
"testing"
"github.com/stretchr/testify/assert"
)
func TestNormalizeFilename(t *testing.T) {
tests := []struct {
In, Out string
}{
{"foobarbaz", "foobarbaz"},
{"foo/bar/baz", "foo-bar-baz"},
{"foo@bar/baz", "foo-bar-baz"},
{"foo1bar2baz3", "foo1bar2baz3"},
}
for _, test := range tests {
assert.Equal(t, test.Out, (&Checksum{}).normalizeFilename(test.In))
}
}

23
internal/status/glob.go Normal file
View File

@@ -0,0 +1,23 @@
package status
import (
"path/filepath"
"sort"
"github.com/mattn/go-zglob"
)
func glob(dir string, globs []string) (files []string, err error) {
for _, g := range globs {
if !filepath.IsAbs(g) {
g = filepath.Join(dir, g)
}
f, err := zglob.Glob(g)
if err != nil {
return nil, err
}
files = append(files, f...)
}
sort.Strings(files)
return
}

14
internal/status/none.go Normal file
View File

@@ -0,0 +1,14 @@
package status
// None is a no-op Checker
type None struct{}
// IsUpToDate implements the Checker interface
func (None) IsUpToDate() (bool, error) {
return false, nil
}
// OnError implements the Checker interface
func (None) OnError() error {
return nil
}

13
internal/status/status.go Normal file
View File

@@ -0,0 +1,13 @@
package status
var (
_ Checker = &Timestamp{}
_ Checker = &Checksum{}
_ Checker = None{}
)
// Checker is an interface that checks if the status is up-to-date
type Checker interface {
IsUpToDate() (bool, error)
OnError() error
}

View File

@@ -0,0 +1,85 @@
package status
import (
"os"
"time"
)
// Timestamp checks if any source change compared with the generated files,
// using file modifications timestamps.
type Timestamp struct {
Dir string
Sources []string
Generates []string
}
// IsUpToDate implements the Checker interface
func (t *Timestamp) IsUpToDate() (bool, error) {
if len(t.Sources) == 0 || len(t.Generates) == 0 {
return false, nil
}
sources, err := glob(t.Dir, t.Sources)
if err != nil {
return false, nil
}
generates, err := glob(t.Dir, t.Generates)
if err != nil {
return false, nil
}
sourcesMaxTime, err := getMaxTime(sources...)
if err != nil || sourcesMaxTime.IsZero() {
return false, nil
}
generatesMinTime, err := getMinTime(generates...)
if err != nil || generatesMinTime.IsZero() {
return false, nil
}
return !generatesMinTime.Before(sourcesMaxTime), nil
}
func getMinTime(files ...string) (time.Time, error) {
var t time.Time
for _, f := range files {
info, err := os.Stat(f)
if err != nil {
return time.Time{}, err
}
t = minTime(t, info.ModTime())
}
return t, nil
}
func getMaxTime(files ...string) (time.Time, error) {
var t time.Time
for _, f := range files {
info, err := os.Stat(f)
if err != nil {
return time.Time{}, err
}
t = maxTime(t, info.ModTime())
}
return t, nil
}
func minTime(a, b time.Time) time.Time {
if !a.IsZero() && a.Before(b) {
return a
}
return b
}
func maxTime(a, b time.Time) time.Time {
if a.After(b) {
return a
}
return b
}
// OnError implements the Checker interface
func (*Timestamp) OnError() error {
return nil
}