All checks were successful
Test / Create distribution (push) Successful in 45s
Test / Sandbox (push) Successful in 2m30s
Test / Hakurei (push) Successful in 3m30s
Test / ShareFS (push) Successful in 3m27s
Test / Hpkg (push) Successful in 4m19s
Test / Sandbox (race detector) (push) Successful in 4m40s
Test / Hakurei (race detector) (push) Successful in 5m37s
Test / Flake checks (push) Successful in 1m40s
This change contains primitives for validating and caching single-file artifacts. Signed-off-by: Ophestra <cat@gensokyo.uk>
69 lines
1.6 KiB
Go
69 lines
1.6 KiB
Go
package pkg
|
|
|
|
import (
|
|
"crypto/sha512"
|
|
"encoding/gob"
|
|
"io"
|
|
"io/fs"
|
|
"os"
|
|
|
|
"hakurei.app/container/check"
|
|
)
|
|
|
|
// FlatEntry is the representation of a directory entry via [Flatten].
|
|
type FlatEntry struct {
|
|
Name string // base name of the file
|
|
Mode fs.FileMode // file mode bits
|
|
Data []byte // file content or symlink destination
|
|
}
|
|
|
|
// Flatten writes a deterministic representation of the contents of fsys to w.
|
|
// The resulting data can be hashed to produce a deterministic checksum for the
|
|
// directory.
|
|
func Flatten(fsys fs.FS, root string, w io.Writer) error {
|
|
e := gob.NewEncoder(w)
|
|
return fs.WalkDir(fsys, root, func(path string, d fs.DirEntry, err error) error {
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
var fi fs.FileInfo
|
|
fi, err = d.Info()
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
ent := FlatEntry{
|
|
Name: fi.Name(),
|
|
Mode: fi.Mode(),
|
|
}
|
|
if ent.Mode.IsRegular() {
|
|
if ent.Data, err = fs.ReadFile(fsys, path); err != nil {
|
|
return err
|
|
}
|
|
} else if ent.Mode&fs.ModeSymlink != 0 {
|
|
var newpath string
|
|
if newpath, err = fs.ReadLink(fsys, path); err != nil {
|
|
return err
|
|
}
|
|
ent.Data = []byte(newpath)
|
|
}
|
|
|
|
return e.Encode(&ent)
|
|
})
|
|
}
|
|
|
|
// HashFS returns a checksum produced by hashing the result of [Flatten].
|
|
func HashFS(fsys fs.FS, root string) (Checksum, error) {
|
|
h := sha512.New384()
|
|
if err := Flatten(fsys, root, h); err != nil {
|
|
return Checksum{}, err
|
|
}
|
|
return (Checksum)(h.Sum(nil)), nil
|
|
}
|
|
|
|
// HashDir returns a checksum produced by hashing the result of [Flatten].
|
|
func HashDir(pathname *check.Absolute) (Checksum, error) {
|
|
return HashFS(os.DirFS(pathname.String()), ".")
|
|
}
|