github.com/graemephi/kahugo@v0.62.3-0.20211121071557-d78c0423784d/cache/filecache/filecache_pruner.go (about)

     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  	"io"
    18  	"os"
    19  
    20  	"github.com/gohugoio/hugo/hugofs"
    21  
    22  	"github.com/pkg/errors"
    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, errors.Wrapf(err, "failed to prune cache %q", k)
    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  }