aboutsummaryrefslogtreecommitdiffhomepage
path: root/internal/pkg/clean.go
diff options
context:
space:
mode:
authorOphestra <cat@gensokyo.uk>2026-06-01 14:51:40 +0900
committerOphestra <cat@gensokyo.uk>2026-06-02 13:28:24 +0900
commitfbd2329d50a9ef2b15c0a6b62e9aa24635c2662a (patch)
tree2c8b0c8c8d16412681c9e758e9121dab022cc008 /internal/pkg/clean.go
parentf398f71fa947dbf03587b1a28d314a969aa6b351 (diff)
internal/pkg: garbage collection
This destroys cache entries not referred to by user-specified artifacts and optionally their inputs. Signed-off-by: Ophestra <cat@gensokyo.uk>
Diffstat (limited to 'internal/pkg/clean.go')
-rw-r--r--internal/pkg/clean.go138
1 files changed, 138 insertions, 0 deletions
diff --git a/internal/pkg/clean.go b/internal/pkg/clean.go
new file mode 100644
index 00000000..addcd32d
--- /dev/null
+++ b/internal/pkg/clean.go
@@ -0,0 +1,138 @@
+package pkg
+
+import (
+ "errors"
+ "os"
+ "unique"
+)
+
+// Clean destroys checksum backing entries without any identifier or substitute
+// entry referring to it. If at least one keep [Artifact] is specified,
+// identifier and substitute entries not kept alive by them are destroyed first.
+func (c *Cache) Clean(dry, inputs bool, keep ...Artifact) (
+ []unique.Handle[ID],
+ []unique.Handle[Checksum],
+ error,
+) {
+ c.identMu.Lock()
+ defer c.identMu.Unlock()
+
+ c.checksumMu.Lock()
+ defer c.checksumMu.Unlock()
+
+ dents, err := os.ReadDir(c.base.Append(dirChecksum).String())
+ if err != nil {
+ return nil, nil, err
+ }
+ checksums := make(map[unique.Handle[Checksum]]string, len(dents))
+ var buf Checksum
+ for _, dent := range dents {
+ name := dent.Name()
+ if err = Decode(&buf, name); err != nil {
+ return nil, nil, err
+ }
+ checksums[unique.Make(buf)] = name
+ }
+
+ type identPair struct {
+ id unique.Handle[ID]
+ name string
+ }
+ dents, err = os.ReadDir(c.base.Append(dirIdentifier).String())
+ if err != nil {
+ return nil, nil, err
+ }
+
+ keepIdents := make(map[unique.Handle[ID]]struct{})
+ if inputs {
+ for _, id := range Inputs((*Collect)(&keep)) {
+ keepIdents[id] = struct{}{}
+ }
+ } else {
+ for _, a := range keep {
+ keepIdents[c.Ident(a)] = struct{}{}
+ }
+ }
+
+ idents := make([]identPair, 0, len(dents))
+ for _, dent := range dents {
+ name := dent.Name()
+ if err = Decode(&buf, name); err != nil {
+ return nil, nil, err
+ }
+ id := unique.Make(ID(buf))
+
+ if _, ok := keepIdents[id]; len(keep) == 0 || ok {
+ if err = readlinkChecksum(c.base.Append(
+ dirIdentifier,
+ name,
+ ), &buf); err != nil {
+ return nil, nil, err
+ }
+ delete(checksums, unique.Make(buf))
+ continue
+ }
+
+ c.msg.Verbosef("arranging for destruction of %s...", name)
+ idents = append(idents, identPair{id, name})
+ }
+
+ destroyedIdents := make([]unique.Handle[ID], 0, len(idents))
+ for _, pair := range idents {
+ if !dry {
+ if err = os.Remove(c.base.Append(
+ dirIdentifier,
+ pair.name,
+ ).String()); err != nil {
+ return destroyedIdents, nil, err
+ }
+ }
+ destroyedIdents = append(destroyedIdents, pair.id)
+ }
+
+ destroyedChecksums := make([]unique.Handle[Checksum], 0, len(checksums))
+ for checksum, name := range checksums {
+ if err = c.parent.Err(); err != nil {
+ return destroyedIdents, destroyedChecksums, err
+ }
+ c.msg.Verbosef("destroying checksum %s...", name)
+ if !dry {
+ if err = errors.Join(removeAll(c.base.Append(
+ dirChecksum,
+ name,
+ ))); err != nil {
+ return destroyedIdents, destroyedChecksums, err
+ }
+ }
+ destroyedChecksums = append(destroyedChecksums, checksum)
+ }
+
+ dents, err = os.ReadDir(c.base.Append(dirSubstitute).String())
+ if err != nil {
+ return destroyedIdents, destroyedChecksums, err
+ }
+ for _, dent := range dents {
+ name := dent.Name()
+ if err = readlinkChecksum(c.base.Append(
+ dirSubstitute,
+ name,
+ ), &buf); err != nil {
+ return destroyedIdents, destroyedChecksums, err
+ }
+ if _, ok := checksums[unique.Make(buf)]; !ok {
+ continue
+ }
+
+ c.msg.Verbosef("destroying substitute %s...", name)
+ if !dry {
+ if err = os.Remove(c.base.Append(
+ dirSubstitute,
+ name,
+ ).String()); err != nil {
+ return destroyedIdents, destroyedChecksums, err
+ }
+ }
+ }
+
+ return destroyedIdents, destroyedChecksums, nil
+}