Support unlinking package subpaths
This commit is contained in:
151
main.go
151
main.go
@@ -87,11 +87,33 @@ func parsePackageFlags(args []string) (packageFlags, string, error) {
|
|||||||
pkg = arg
|
pkg = arg
|
||||||
}
|
}
|
||||||
if pkg == "" {
|
if pkg == "" {
|
||||||
return flags, "", errors.New("missing package name")
|
return flags, "", errors.New("missing package")
|
||||||
}
|
}
|
||||||
return flags, pkg, nil
|
return flags, pkg, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func splitPackageSpec(spec string) (string, string, error) {
|
||||||
|
if spec == "" {
|
||||||
|
return "", "", errors.New("missing package")
|
||||||
|
}
|
||||||
|
|
||||||
|
parts := strings.SplitN(spec, ":", 2)
|
||||||
|
pkg := parts[0]
|
||||||
|
rel := ""
|
||||||
|
if len(parts) == 2 {
|
||||||
|
rel = parts[1]
|
||||||
|
}
|
||||||
|
|
||||||
|
pkg = strings.Trim(pkg, "/")
|
||||||
|
rel = strings.TrimPrefix(rel, "/")
|
||||||
|
|
||||||
|
if pkg == "" {
|
||||||
|
return "", "", errors.New("invalid package")
|
||||||
|
}
|
||||||
|
|
||||||
|
return pkg, rel, nil
|
||||||
|
}
|
||||||
|
|
||||||
func applyCmd(args []string) error {
|
func applyCmd(args []string) error {
|
||||||
prune := false
|
prune := false
|
||||||
for _, arg := range args {
|
for _, arg := range args {
|
||||||
@@ -275,7 +297,12 @@ func addCmd(args []string) error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func unlinkCmd(args []string) error {
|
func unlinkCmd(args []string) error {
|
||||||
flags, pkgName, err := parsePackageFlags(args)
|
flags, pkgSpec, err := parsePackageFlags(args)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
pkgName, relPath, err := splitPackageSpec(pkgSpec)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
@@ -302,7 +329,11 @@ func unlinkCmd(args []string) error {
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
return restorePackage(filesDir, targetRoot, flags.dryRun)
|
if relPath == "" {
|
||||||
|
return restorePackage(filesDir, targetRoot, flags.dryRun)
|
||||||
|
}
|
||||||
|
|
||||||
|
return restorePath(filesDir, targetRoot, relPath, flags.dryRun)
|
||||||
}
|
}
|
||||||
|
|
||||||
func removeCmd(args []string) error {
|
func removeCmd(args []string) error {
|
||||||
@@ -708,44 +739,88 @@ func restorePackage(filesDir, targetRoot string, dryRun bool) error {
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
targetPath := filepath.Join(targetRoot, rel)
|
targetPath := filepath.Join(targetRoot, rel)
|
||||||
|
return restoreOne(path, targetPath, filesAbs, dryRun)
|
||||||
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 dryRun {
|
|
||||||
fmt.Printf("dry-run: restore %s\n", targetPath)
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
if err := os.Remove(targetPath); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
return copyFile(path, targetPath)
|
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func restorePath(filesDir, targetRoot, relPath string, dryRun bool) error {
|
||||||
|
filesAbs, err := filepath.Abs(filesDir)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
relPath = filepath.Clean(relPath)
|
||||||
|
if strings.HasPrefix(relPath, "..") || filepath.IsAbs(relPath) {
|
||||||
|
return fmt.Errorf("invalid relative path %q", relPath)
|
||||||
|
}
|
||||||
|
|
||||||
|
sourcePath := filepath.Join(filesDir, relPath)
|
||||||
|
info, err := os.Lstat(sourcePath)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
if info.IsDir() {
|
||||||
|
return filepath.WalkDir(sourcePath, func(path string, entry fs.DirEntry, err error) error {
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if entry.IsDir() {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
rel, err := filepath.Rel(filesDir, path)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
targetPath := filepath.Join(targetRoot, rel)
|
||||||
|
return restoreOne(path, targetPath, filesAbs, dryRun)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
rel, err := filepath.Rel(filesDir, sourcePath)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return restoreOne(sourcePath, filepath.Join(targetRoot, rel), filesAbs, dryRun)
|
||||||
|
}
|
||||||
|
|
||||||
|
func restoreOne(sourcePath, targetPath, filesAbs string, dryRun bool) error {
|
||||||
|
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 dryRun {
|
||||||
|
fmt.Printf("dry-run: restore %s\n", targetPath)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := os.Remove(targetPath); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
return copyFile(sourcePath, targetPath)
|
||||||
|
}
|
||||||
|
|
||||||
func copyFile(src, dst string) error {
|
func copyFile(src, dst string) error {
|
||||||
if err := os.MkdirAll(filepath.Dir(dst), 0o755); err != nil {
|
if err := os.MkdirAll(filepath.Dir(dst), 0o755); err != nil {
|
||||||
return err
|
return err
|
||||||
|
|||||||
Reference in New Issue
Block a user