go-hashfiles/main.go

112 lines
2.8 KiB
Go

package main
import (
"crypto/sha256"
"encoding/hex"
"encoding/json"
"fmt"
"io"
"io/fs"
"os"
"path/filepath"
"strings"
"github.com/go-git/go-git/v5/plumbing/format/gitignore"
gha "github.com/sethvargo/go-githubactions"
)
func main() {
ghCtx, err := gha.Context()
if err != nil {
gha.Fatalf("gha.Context error: %v", err)
}
workdir := gha.GetInput("workdir")
if len(workdir) == 0 {
workdir = ghCtx.Workspace
}
inputPatterns := gha.GetInput("patterns")
gha.Infof("input patterns: %s", inputPatterns)
matchedFiles, hashResult, err := hashFiles(workdir, strings.Split(inputPatterns, "\n"))
if err != nil {
gha.Fatalf("hashFiles error: %v", err)
}
gha.SetOutput("hash", hashResult)
gha.Infof("computed hash: %s", hashResult)
matchedFilesJSON, _ := json.Marshal(matchedFiles)
gha.SetOutput("matched-files", string(matchedFilesJSON))
gha.Infof("matched files: %s", matchedFilesJSON)
}
func hashFiles(workdir string, patterns []string) ([]string, string, error) {
var ps []gitignore.Pattern
const cwdPrefix = "." + string(filepath.Separator)
const excludeCwdPrefix = "!" + cwdPrefix
for _, p := range patterns {
cleanPattern := strings.TrimSpace(p)
if len(cleanPattern) == 0 {
continue
}
if strings.HasPrefix(cleanPattern, cwdPrefix) {
cleanPattern = cleanPattern[len(cwdPrefix):]
} else if strings.HasPrefix(cleanPattern, excludeCwdPrefix) {
cleanPattern = "!" + cleanPattern[len(excludeCwdPrefix):]
}
ps = append(ps, gitignore.ParsePattern(cleanPattern, nil))
}
matcher := gitignore.NewMatcher(ps)
var files []string
if err := filepath.Walk(workdir, func(path string, fi fs.FileInfo, err error) error {
if err != nil {
return err
}
sansPrefix := strings.TrimPrefix(path, workdir+string(filepath.Separator))
parts := strings.Split(sansPrefix, string(filepath.Separator))
if fi.IsDir() || !matcher.Match(parts, fi.IsDir()) {
return nil
}
files = append(files, path)
return nil
}); err != nil {
return nil, "", fmt.Errorf("unable to filepath.Walk: %v", err)
}
if len(files) == 0 {
return nil, "", nil
}
matchedFiles := make([]string, 0, len(files))
resultHasher := sha256.New()
for _, file := range files {
f, err := os.Open(file)
if err != nil {
return nil, "", fmt.Errorf("unable to os.Open: %v", err)
}
hasher := sha256.New()
if _, err := io.Copy(hasher, f); err != nil {
return nil, "", fmt.Errorf("unable to io.Copy: %v", err)
}
if err := f.Close(); err != nil {
return nil, "", fmt.Errorf("unable to Close file: %v", err)
}
if _, err := resultHasher.Write(hasher.Sum(nil)); err != nil {
return nil, "", fmt.Errorf("unable to write resultHasher: %v", err)
}
matchedFiles = append(matchedFiles, strings.TrimPrefix(file, workdir+string(filepath.Separator)))
}
return matchedFiles, hex.EncodeToString(resultHasher.Sum(nil)), nil
}