internal/pkg: implement caching for directories
All checks were successful
Test / Create distribution (push) Successful in 45s
Test / Sandbox (push) Successful in 2m30s
Test / Hakurei (push) Successful in 3m38s
Test / ShareFS (push) Successful in 3m40s
Test / Hpkg (push) Successful in 4m29s
Test / Sandbox (race detector) (push) Successful in 4m50s
Test / Hakurei (race detector) (push) Successful in 5m49s
Test / Flake checks (push) Successful in 1m43s
All checks were successful
Test / Create distribution (push) Successful in 45s
Test / Sandbox (push) Successful in 2m30s
Test / Hakurei (push) Successful in 3m38s
Test / ShareFS (push) Successful in 3m40s
Test / Hpkg (push) Successful in 4m29s
Test / Sandbox (race detector) (push) Successful in 4m50s
Test / Hakurei (race detector) (push) Successful in 5m49s
Test / Flake checks (push) Successful in 1m43s
This works on any directories and should be robust against any bad state the artifact curing process might have failed at. Signed-off-by: Ophestra <cat@gensokyo.uk>
This commit is contained in:
@@ -8,7 +8,10 @@ import (
|
||||
"encoding/binary"
|
||||
"errors"
|
||||
"io"
|
||||
"io/fs"
|
||||
"os"
|
||||
"path"
|
||||
"path/filepath"
|
||||
"slices"
|
||||
"sync"
|
||||
|
||||
@@ -190,6 +193,100 @@ func (c *Cache) pathnameIdent(id *ID) *check.Absolute {
|
||||
)
|
||||
}
|
||||
|
||||
// Store looks up an identifier, and if it is not present, calls makeArtifact
|
||||
// with a private working directory and stores its result instead. An optional
|
||||
// checksum can be passed via the result buffer which is used to validate the
|
||||
// produced directory.
|
||||
func (c *Cache) Store(
|
||||
id ID,
|
||||
makeArtifact func(work *check.Absolute) error,
|
||||
buf *Checksum,
|
||||
validate bool,
|
||||
) (
|
||||
pathname *check.Absolute,
|
||||
store bool,
|
||||
err error,
|
||||
) {
|
||||
pathname = c.pathnameIdent(&id)
|
||||
c.mu.Lock()
|
||||
defer c.mu.Unlock()
|
||||
|
||||
_, err = os.Lstat(pathname.String())
|
||||
if err == nil || !errors.Is(err, os.ErrNotExist) {
|
||||
return
|
||||
}
|
||||
store = true
|
||||
|
||||
var (
|
||||
workPathname *check.Absolute
|
||||
workPathnameRaw string
|
||||
)
|
||||
if workPathnameRaw, err = os.MkdirTemp(
|
||||
c.base.Append(dirWork).String(),
|
||||
path.Base(pathname.String()+".*"),
|
||||
); err != nil {
|
||||
return
|
||||
} else if workPathname, err = check.NewAbs(workPathnameRaw); err != nil {
|
||||
return
|
||||
}
|
||||
defer func() {
|
||||
if err != nil {
|
||||
chmodErr := filepath.WalkDir(workPathname.String(), func(path string, d fs.DirEntry, err error) error {
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if d.IsDir() {
|
||||
return os.Chmod(path, 0700)
|
||||
}
|
||||
return nil
|
||||
})
|
||||
removeErr := os.RemoveAll(workPathname.String())
|
||||
if chmodErr != nil || removeErr != nil {
|
||||
err = errors.Join(err, chmodErr, removeErr)
|
||||
} else if errors.Is(err, os.ErrExist) {
|
||||
// two artifacts may be backed by the same file
|
||||
err = nil
|
||||
}
|
||||
}
|
||||
}()
|
||||
if err = os.Chmod(workPathname.String(), 0700); err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
if err = makeArtifact(workPathname); err != nil {
|
||||
return
|
||||
}
|
||||
var checksum Checksum
|
||||
if checksum, err = HashDir(workPathname); err != nil {
|
||||
return
|
||||
}
|
||||
if validate {
|
||||
if checksum != *buf {
|
||||
err = &ChecksumMismatchError{checksum, *buf}
|
||||
return
|
||||
}
|
||||
} else {
|
||||
*buf = checksum
|
||||
}
|
||||
|
||||
checksumPathname := c.pathname(&checksum)
|
||||
if err = os.Rename(
|
||||
workPathname.String(),
|
||||
checksumPathname.String(),
|
||||
); err != nil {
|
||||
if !errors.Is(err, os.ErrExist) {
|
||||
return
|
||||
}
|
||||
}
|
||||
if linkErr := os.Symlink(
|
||||
"../"+dirChecksum+"/"+path.Base(checksumPathname.String()),
|
||||
pathname.String(),
|
||||
); linkErr != nil {
|
||||
err = linkErr
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// storeFile stores the contents of a [File]. An optional checksum can be
|
||||
// passed via the result buffer which is used to validate the submitted data.
|
||||
//
|
||||
|
||||
Reference in New Issue
Block a user