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 }