Add unlink and remove commands

This commit is contained in:
2026-02-19 16:57:25 +00:00
parent 5f49036a4d
commit fe34a93702

157
main.go
View File

@@ -4,6 +4,7 @@ import (
"bufio" "bufio"
"errors" "errors"
"fmt" "fmt"
"io"
"io/fs" "io/fs"
"os" "os"
"path/filepath" "path/filepath"
@@ -36,6 +37,10 @@ func main() {
err = applyCmd(args[1:]) err = applyCmd(args[1:])
case "add": case "add":
err = addCmd(args[1:]) err = addCmd(args[1:])
case "unlink":
err = unlinkCmd(args[1:])
case "remove":
err = removeCmd(args[1:])
case "help", "-h", "--help": case "help", "-h", "--help":
usage() usage()
return return
@@ -54,6 +59,8 @@ func usage() {
fmt.Println("usage:") fmt.Println("usage:")
fmt.Println(" sigil apply [--prune]") fmt.Println(" sigil apply [--prune]")
fmt.Println(" sigil add <path>") fmt.Println(" sigil add <path>")
fmt.Println(" sigil unlink <package>")
fmt.Println(" sigil remove <package>")
} }
func applyCmd(args []string) error { func applyCmd(args []string) error {
@@ -238,6 +245,72 @@ func addCmd(args []string) error {
return applyPackage(filesDir, targetRoot) return applyPackage(filesDir, targetRoot)
} }
func unlinkCmd(args []string) error {
if len(args) < 1 {
return errors.New("missing package name")
}
pkgName := args[0]
repo, err := repoPath()
if err != nil {
return err
}
pkgDir := filepath.Join(repo, pkgName)
configPath := filepath.Join(pkgDir, configFileName)
cfg, err := loadConfig(configPath)
if err != nil {
return err
}
targetRoot, err := selectTarget(cfg.targets)
if err != nil {
return err
}
filesDir := filepath.Join(pkgDir, filesDirName)
if _, err := os.Stat(filesDir); err != nil {
return err
}
return restorePackage(filesDir, targetRoot)
}
func removeCmd(args []string) error {
if len(args) < 1 {
return errors.New("missing package name")
}
pkgName := args[0]
repo, err := repoPath()
if err != nil {
return err
}
pkgDir := filepath.Join(repo, pkgName)
configPath := filepath.Join(pkgDir, configFileName)
cfg, err := loadConfig(configPath)
if err != nil {
return err
}
targetRoot, err := selectTarget(cfg.targets)
if err != nil {
return err
}
filesDir := filepath.Join(pkgDir, filesDirName)
if _, err := os.Stat(filesDir); err != nil {
return err
}
if err := restorePackage(filesDir, targetRoot); err != nil {
return err
}
return os.RemoveAll(pkgDir)
}
func applyPackage(filesDir, targetRoot string) error { func applyPackage(filesDir, targetRoot string) error {
if err := ensureDir(targetRoot); err != nil { if err := ensureDir(targetRoot); err != nil {
return err return err
@@ -528,3 +601,87 @@ func removeLinks(paths []string) error {
} }
return nil return nil
} }
func restorePackage(filesDir, targetRoot string) error {
filesAbs, err := filepath.Abs(filesDir)
if err != nil {
return err
}
return filepath.WalkDir(filesDir, func(path string, entry fs.DirEntry, err error) error {
if err != nil {
return err
}
if path == filesDir {
return nil
}
if entry.IsDir() {
return nil
}
rel, err := filepath.Rel(filesDir, path)
if err != nil {
return err
}
targetPath := filepath.Join(targetRoot, rel)
info, err := os.Lstat(targetPath)
if errors.Is(err, os.ErrNotExist) {
return nil
}
if err != nil {
return err
}
if info.Mode()&os.ModeSymlink == 0 {
return nil
}
src, err := os.Readlink(targetPath)
if err != nil {
return err
}
if !filepath.IsAbs(src) {
src = filepath.Join(filepath.Dir(targetPath), src)
}
src = filepath.Clean(src)
if !strings.HasPrefix(src, filesAbs+string(os.PathSeparator)) && src != filesAbs {
return nil
}
if err := os.Remove(targetPath); err != nil {
return err
}
return copyFile(path, targetPath)
})
}
func copyFile(src, dst string) error {
if err := os.MkdirAll(filepath.Dir(dst), 0o755); err != nil {
return err
}
srcFile, err := os.Open(src)
if err != nil {
return err
}
defer srcFile.Close()
info, err := srcFile.Stat()
if err != nil {
return err
}
dstFile, err := os.OpenFile(dst, os.O_CREATE|os.O_WRONLY|os.O_TRUNC, info.Mode())
if err != nil {
return err
}
defer dstFile.Close()
if _, err := io.Copy(dstFile, srcFile); err != nil {
return err
}
return nil
}