aboutsummaryrefslogtreecommitdiffhomepage
path: root/internal/pkg/pkg.go
diff options
context:
space:
mode:
authorOphestra <cat@gensokyo.uk>2026-01-03 22:49:01 +0900
committerOphestra <cat@gensokyo.uk>2026-01-03 22:54:46 +0900
commit40081e7a0694db0ebe2b59e7545ecc43128c8f40 (patch)
tree72c58eaf013ae9bac34d1db8fe822c3b7079d172 /internal/pkg/pkg.go
parent863d3dcf9f51015f800a26c5dc63576f114d4380 (diff)
internal/pkg: implement caching for directories
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>
Diffstat (limited to 'internal/pkg/pkg.go')
-rw-r--r--internal/pkg/pkg.go97
1 files changed, 97 insertions, 0 deletions
diff --git a/internal/pkg/pkg.go b/internal/pkg/pkg.go
index 64df5df4..e8547d16 100644
--- a/internal/pkg/pkg.go
+++ b/internal/pkg/pkg.go
@@ -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.
//