Add prune option to apply
This commit is contained in:
111
main.go
111
main.go
@@ -33,7 +33,7 @@ func main() {
|
|||||||
var err error
|
var err error
|
||||||
switch args[0] {
|
switch args[0] {
|
||||||
case "apply":
|
case "apply":
|
||||||
err = applyCmd()
|
err = applyCmd(args[1:])
|
||||||
case "add":
|
case "add":
|
||||||
err = addCmd(args[1:])
|
err = addCmd(args[1:])
|
||||||
case "help", "-h", "--help":
|
case "help", "-h", "--help":
|
||||||
@@ -52,11 +52,20 @@ func main() {
|
|||||||
func usage() {
|
func usage() {
|
||||||
fmt.Println("sigil: minimal dotfile symlink manager")
|
fmt.Println("sigil: minimal dotfile symlink manager")
|
||||||
fmt.Println("usage:")
|
fmt.Println("usage:")
|
||||||
fmt.Println(" sigil apply")
|
fmt.Println(" sigil apply [--prune]")
|
||||||
fmt.Println(" sigil add <path>")
|
fmt.Println(" sigil add <path>")
|
||||||
}
|
}
|
||||||
|
|
||||||
func applyCmd() error {
|
func applyCmd(args []string) error {
|
||||||
|
prune := false
|
||||||
|
for _, arg := range args {
|
||||||
|
if arg == "--prune" {
|
||||||
|
prune = true
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
return fmt.Errorf("unknown flag %q", arg)
|
||||||
|
}
|
||||||
|
|
||||||
repo, err := repoPath()
|
repo, err := repoPath()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
@@ -67,6 +76,8 @@ func applyCmd() error {
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var stales []string
|
||||||
|
|
||||||
for _, entry := range entries {
|
for _, entry := range entries {
|
||||||
if !entry.IsDir() || strings.HasPrefix(entry.Name(), ".") {
|
if !entry.IsDir() || strings.HasPrefix(entry.Name(), ".") {
|
||||||
continue
|
continue
|
||||||
@@ -102,6 +113,28 @@ func applyCmd() error {
|
|||||||
if err := applyPackage(filesDir, targetRoot); err != nil {
|
if err := applyPackage(filesDir, targetRoot); err != nil {
|
||||||
return fmt.Errorf("%s: %w", entry.Name(), err)
|
return fmt.Errorf("%s: %w", entry.Name(), err)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
stale, err := findStaleLinks(filesDir, targetRoot)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("%s: %w", entry.Name(), err)
|
||||||
|
}
|
||||||
|
stales = append(stales, stale...)
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(stales) == 0 {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
if prune {
|
||||||
|
return removeLinks(stales)
|
||||||
|
}
|
||||||
|
|
||||||
|
ok, err := promptYesNo("Found stale links. Prune them?", false)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if ok {
|
||||||
|
return removeLinks(stales)
|
||||||
}
|
}
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
@@ -395,6 +428,24 @@ func promptWithDefault(reader *bufio.Reader, label, def string) string {
|
|||||||
return text
|
return text
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func promptYesNo(message string, def bool) (bool, error) {
|
||||||
|
reader := bufio.NewReader(os.Stdin)
|
||||||
|
defLabel := "y/N"
|
||||||
|
if def {
|
||||||
|
defLabel = "Y/n"
|
||||||
|
}
|
||||||
|
fmt.Printf("%s [%s]: ", message, defLabel)
|
||||||
|
text, err := reader.ReadString('\n')
|
||||||
|
if err != nil {
|
||||||
|
return false, err
|
||||||
|
}
|
||||||
|
text = strings.TrimSpace(strings.ToLower(text))
|
||||||
|
if text == "" {
|
||||||
|
return def, nil
|
||||||
|
}
|
||||||
|
return text == "y" || text == "yes", nil
|
||||||
|
}
|
||||||
|
|
||||||
func moveDirContents(srcDir, destDir string) error {
|
func moveDirContents(srcDir, destDir string) error {
|
||||||
entries, err := os.ReadDir(srcDir)
|
entries, err := os.ReadDir(srcDir)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@@ -418,3 +469,57 @@ func moveDirContents(srcDir, destDir string) error {
|
|||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func findStaleLinks(filesDir, targetRoot string) ([]string, error) {
|
||||||
|
known := make(map[string]struct{})
|
||||||
|
|
||||||
|
err := filepath.WalkDir(filesDir, func(path string, entry fs.DirEntry, err error) error {
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if path == filesDir || entry.IsDir() {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
rel, err := filepath.Rel(filesDir, path)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
known[filepath.Join(targetRoot, rel)] = struct{}{}
|
||||||
|
return nil
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
var stale []string
|
||||||
|
for targetPath := range known {
|
||||||
|
info, err := os.Lstat(targetPath)
|
||||||
|
if errors.Is(err, os.ErrNotExist) {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
if info.Mode()&os.ModeSymlink == 0 {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
src, err := os.Readlink(targetPath)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
if _, err := os.Stat(src); errors.Is(err, os.ErrNotExist) {
|
||||||
|
stale = append(stale, targetPath)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return stale, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func removeLinks(paths []string) error {
|
||||||
|
for _, path := range paths {
|
||||||
|
if err := os.Remove(path); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user