filecache_pruner.go (2742B)
1 // Copyright 2018 The Hugo Authors. All rights reserved.
2 //
3 // Licensed under the Apache License, Version 2.0 (the "License");
4 // you may not use this file except in compliance with the License.
5 // You may obtain a copy of the License at
6 // http://www.apache.org/licenses/LICENSE-2.0
7 //
8 // Unless required by applicable law or agreed to in writing, software
9 // distributed under the License is distributed on an "AS IS" BASIS,
10 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
11 // See the License for the specific language governing permissions and
12 // limitations under the License.
13
14 package filecache
15
16 import (
17 "fmt"
18 "io"
19 "os"
20
21 "github.com/gohugoio/hugo/hugofs"
22
23 "github.com/spf13/afero"
24 )
25
26 // Prune removes expired and unused items from this cache.
27 // The last one requires a full build so the cache usage can be tracked.
28 // Note that we operate directly on the filesystem here, so this is not
29 // thread safe.
30 func (c Caches) Prune() (int, error) {
31 counter := 0
32 for k, cache := range c {
33
34 count, err := cache.Prune(false)
35
36 counter += count
37
38 if err != nil {
39 if os.IsNotExist(err) {
40 continue
41 }
42 return counter, fmt.Errorf("failed to prune cache %q: %w", k, err)
43 }
44
45 }
46
47 return counter, nil
48 }
49
50 // Prune removes expired and unused items from this cache.
51 // If force is set, everything will be removed not considering expiry time.
52 func (c *Cache) Prune(force bool) (int, error) {
53 if c.pruneAllRootDir != "" {
54 return c.pruneRootDir(force)
55 }
56
57 counter := 0
58
59 err := afero.Walk(c.Fs, "", func(name string, info os.FileInfo, err error) error {
60 if info == nil {
61 return nil
62 }
63
64 name = cleanID(name)
65
66 if info.IsDir() {
67 f, err := c.Fs.Open(name)
68 if err != nil {
69 // This cache dir may not exist.
70 return nil
71 }
72 defer f.Close()
73 _, err = f.Readdirnames(1)
74 if err == io.EOF {
75 // Empty dir.
76 err = c.Fs.Remove(name)
77 }
78
79 if err != nil && !os.IsNotExist(err) {
80 return err
81 }
82
83 return nil
84 }
85
86 shouldRemove := force || c.isExpired(info.ModTime())
87
88 if !shouldRemove && len(c.nlocker.seen) > 0 {
89 // Remove it if it's not been touched/used in the last build.
90 _, seen := c.nlocker.seen[name]
91 shouldRemove = !seen
92 }
93
94 if shouldRemove {
95 err := c.Fs.Remove(name)
96 if err == nil {
97 counter++
98 }
99
100 if err != nil && !os.IsNotExist(err) {
101 return err
102 }
103
104 }
105
106 return nil
107 })
108
109 return counter, err
110 }
111
112 func (c *Cache) pruneRootDir(force bool) (int, error) {
113 info, err := c.Fs.Stat(c.pruneAllRootDir)
114 if err != nil {
115 if os.IsNotExist(err) {
116 return 0, nil
117 }
118 return 0, err
119 }
120
121 if !force && !c.isExpired(info.ModTime()) {
122 return 0, nil
123 }
124
125 return hugofs.MakeReadableAndRemoveAllModulePkgDir(c.Fs, c.pruneAllRootDir)
126 }