mirror of
https://code.forgejo.org/actions/go-hashfiles.git
synced 2024-11-25 11:21:00 -05:00
112 lines
2.8 KiB
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
|
||
|
}
|