70 lines
2.1 KiB
Go
70 lines
2.1 KiB
Go
package buildstep
|
|
|
|
import (
|
|
"fmt"
|
|
"os"
|
|
"path"
|
|
"path/filepath"
|
|
"strings"
|
|
)
|
|
|
|
// narrowKept are the files of a module's directory a narrowed build keeps beside its named paths: its manifest and
|
|
// its lock files.
|
|
var narrowKept = []string{"module.json", "package.json", "package-lock.json", "go.mod", "go.sum"}
|
|
|
|
// Narrow leaves in the fetched tree only what a build of an artifact that names its paths may see (novox/hq ADR 0499
|
|
// clause 4): r.Dir's named paths, its manifest and lock files, and .git; everything else is removed, so a file outside
|
|
// them cannot change what is built without changing its key. A named path absent at the tree is an error naming it,
|
|
// and nothing is removed then. With no paths in r it changes nothing. Both build modules' fetch steps call it.
|
|
func Narrow(tree string, r FetchRequest) error {
|
|
if len(r.Paths) == 0 {
|
|
return nil
|
|
}
|
|
keep := map[string]bool{".git": true}
|
|
for _, p := range r.Paths {
|
|
rel := path.Join(r.Dir, p)
|
|
if strings.HasPrefix(rel, "../") || rel == ".." || path.IsAbs(p) {
|
|
return fmt.Errorf("the named path %q leaves the module's directory", p)
|
|
}
|
|
if _, err := os.Lstat(filepath.Join(tree, filepath.FromSlash(rel))); err != nil {
|
|
return fmt.Errorf("the named path %q is not in the fetched tree: %w", p, err)
|
|
}
|
|
keep[rel] = true
|
|
}
|
|
for _, f := range narrowKept {
|
|
keep[path.Join(r.Dir, f)] = true
|
|
}
|
|
return prune(tree, "", keep)
|
|
}
|
|
|
|
// prune removes from dir (rel within the tree) every entry that is neither kept nor on the way to one.
|
|
func prune(root, rel string, keep map[string]bool) error {
|
|
entries, err := os.ReadDir(filepath.Join(root, filepath.FromSlash(rel)))
|
|
if err != nil {
|
|
return err
|
|
}
|
|
for _, e := range entries {
|
|
p := path.Join(rel, e.Name())
|
|
if keep[p] {
|
|
continue
|
|
}
|
|
ancestor := false
|
|
for k := range keep {
|
|
if strings.HasPrefix(k, p+"/") {
|
|
ancestor = true
|
|
break
|
|
}
|
|
}
|
|
if ancestor && e.IsDir() {
|
|
if err := prune(root, p, keep); err != nil {
|
|
return err
|
|
}
|
|
continue
|
|
}
|
|
if err := os.RemoveAll(filepath.Join(root, filepath.FromSlash(p))); err != nil {
|
|
return err
|
|
}
|
|
}
|
|
return nil
|
|
}
|