summaryrefslogtreecommitdiff
path: root/internal/resource/link/symlink.go
diff options
context:
space:
mode:
Diffstat (limited to 'internal/resource/link/symlink.go')
-rw-r--r--internal/resource/link/symlink.go56
1 files changed, 56 insertions, 0 deletions
diff --git a/internal/resource/link/symlink.go b/internal/resource/link/symlink.go
new file mode 100644
index 0000000..c7cb361
--- /dev/null
+++ b/internal/resource/link/symlink.go
@@ -0,0 +1,56 @@
+package link
+
+import (
+ "fmt"
+ "log"
+ "os"
+)
+
+// ensureSymlink ensures l.path is a symlink pointing at l.target.
+//
+// Idempotency and clobber policy:
+// - already points at the target: nothing to do.
+// - points elsewhere: the link is removed and recreated.
+// - a real file/dir is in the way: it is renamed to "<path>.old" before the
+// link is created (matching the Rexfile's rename-existing behavior).
+func ensureSymlink(l *Link) error {
+ log.Printf("processing symlink: %s -> %s", l.path, l.target)
+
+ if l.target == "" {
+ return fmt.Errorf("symlink %s has no target", l.path)
+ }
+
+ info, err := os.Lstat(l.path)
+ switch {
+ case err == nil && info.Mode()&os.ModeSymlink != 0:
+ current, err := os.Readlink(l.path)
+ if err != nil {
+ return fmt.Errorf("failed to read symlink %s: %w", l.path, err)
+ }
+ if current == l.target {
+ log.Printf("symlink %s already points at %s", l.path, l.target)
+ return nil
+ }
+ log.Printf("repointing symlink %s from %s to %s", l.path, current, l.target)
+ if err := os.Remove(l.path); err != nil {
+ return fmt.Errorf("failed to remove stale symlink %s: %w", l.path, err)
+ }
+
+ case err == nil:
+ old := l.path + ".old"
+ log.Printf("%s is a real file/dir, renaming to %s", l.path, old)
+ if err := os.Rename(l.path, old); err != nil {
+ return fmt.Errorf("failed to move existing %s aside: %w", l.path, err)
+ }
+
+ case !os.IsNotExist(err):
+ return fmt.Errorf("failed to stat %s: %w", l.path, err)
+ }
+
+ if err := os.Symlink(l.target, l.path); err != nil {
+ return fmt.Errorf("failed to create symlink %s -> %s: %w", l.path, l.target, err)
+ }
+
+ log.Printf("created symlink %s -> %s", l.path, l.target)
+ return nil
+}