Skip to content

Commit 83a18bb

Browse files
committed
added custom Rename function with support across disparate hard disk volumes. Closes #1206.
1 parent 9b6e27f commit 83a18bb

File tree

2 files changed

+131
-14
lines changed

2 files changed

+131
-14
lines changed

src/nvm.go

+14-14
Original file line numberDiff line numberDiff line change
@@ -711,10 +711,11 @@ func install(version string, cpuarch string) {
711711

712712
if file.Exists(filepath.Join(root, "v"+version, "node_modules", "npm")) {
713713
utility.DebugLogf("move %v to %v", filepath.Join(root, "v"+version), filepath.Join(env.root, "v"+version))
714-
if rnerr := os.Rename(filepath.Join(root, "v"+version), filepath.Join(env.root, "v"+version)); rnerr != nil {
714+
if rnerr := utility.Rename(filepath.Join(root, "v"+version), filepath.Join(env.root, "v"+version)); rnerr != nil {
715715
status <- Status{Err: err}
716716
}
717717
utility.DebugFn(func() {
718+
utility.DebugLogf("env root: %v", env.root)
718719
cmd := exec.Command("cmd", "/C", "dir", filepath.Join(env.root, "v"+version))
719720
out, err := cmd.CombinedOutput()
720721
if err != nil {
@@ -768,13 +769,13 @@ func install(version string, cpuarch string) {
768769
}
769770

770771
// Standard npm support
771-
os.Rename(filepath.Join(tempNpmBin, "npm"), filepath.Join(root, "v"+version, "npm"))
772-
os.Rename(filepath.Join(tempNpmBin, "npm.cmd"), filepath.Join(root, "v"+version, "npm.cmd"))
772+
utility.Rename(filepath.Join(tempNpmBin, "npm"), filepath.Join(root, "v"+version, "npm"))
773+
utility.Rename(filepath.Join(tempNpmBin, "npm.cmd"), filepath.Join(root, "v"+version, "npm.cmd"))
773774

774775
// npx support
775776
if _, err := os.Stat(filepath.Join(tempNpmBin, "npx")); err == nil {
776-
os.Rename(filepath.Join(tempNpmBin, "npx"), filepath.Join(root, "v"+version, "npx"))
777-
os.Rename(filepath.Join(tempNpmBin, "npx.cmd"), filepath.Join(root, "v"+version, "npx.cmd"))
777+
utility.Rename(filepath.Join(tempNpmBin, "npx"), filepath.Join(root, "v"+version, "npx"))
778+
utility.Rename(filepath.Join(tempNpmBin, "npx.cmd"), filepath.Join(root, "v"+version, "npx.cmd"))
778779
}
779780

780781
npmSourcePath := filepath.Join(tempDir, "nvm-npm", "npm-"+npmv)
@@ -783,21 +784,20 @@ func install(version string, cpuarch string) {
783784
npmSourcePath = filepath.Join(tempDir, "nvm-npm", "cli-"+npmv)
784785
}
785786

786-
moveNpmErr := os.Rename(npmSourcePath, filepath.Join(root, "v"+version, "node_modules", "npm"))
787+
moveNpmErr := utility.Rename(npmSourcePath, filepath.Join(root, "v"+version, "node_modules", "npm"))
787788
if moveNpmErr != nil {
788789
// sometimes Windows can take some time to enable access to large amounts of files after unzip, use exponential backoff to wait until it is ready
789790
for _, i := range [5]int{1, 2, 4, 8, 16} {
790791
time.Sleep(time.Duration(i) * time.Second)
791-
moveNpmErr = os.Rename(npmSourcePath, filepath.Join(root, "v"+version, "node_modules", "npm"))
792+
moveNpmErr = utility.Rename(npmSourcePath, filepath.Join(root, "v"+version, "node_modules", "npm"))
792793
if moveNpmErr == nil {
793794
break
794795
}
795796
}
796-
797797
}
798798

799799
if err == nil && moveNpmErr == nil {
800-
err = os.Rename(filepath.Join(root, "v"+version), filepath.Join(env.root, "v"+version))
800+
err = utility.Rename(filepath.Join(root, "v"+version), filepath.Join(env.root, "v"+version))
801801
if err != nil {
802802
status <- Status{Err: err}
803803
}
@@ -812,7 +812,7 @@ func install(version string, cpuarch string) {
812812
status <- Status{Err: fmt.Errorf("Failed to extract npm: %v", err), Done: true}
813813
}
814814
} else {
815-
err = os.Rename(filepath.Join(root, "v"+version), filepath.Join(env.root, "v"+version))
815+
err = utility.Rename(filepath.Join(root, "v"+version), filepath.Join(env.root, "v"+version))
816816
if err != nil {
817817
status <- Status{Err: err}
818818
}
@@ -1233,15 +1233,15 @@ func use(version string, cpuarch string, reload ...bool) {
12331233
nodeexists := file.Exists(nodepath)
12341234
if node32exists && cpuarch == "32" { // user wants 32, but node.exe is 64
12351235
if nodeexists {
1236-
os.Rename(nodepath, node64path) // node.exe -> node64.exe
1236+
utility.Rename(nodepath, node64path) // node.exe -> node64.exe
12371237
}
1238-
os.Rename(node32path, nodepath) // node32.exe -> node.exe
1238+
utility.Rename(node32path, nodepath) // node32.exe -> node.exe
12391239
}
12401240
if node64exists && cpuarch == "64" { // user wants 64, but node.exe is 32
12411241
if nodeexists {
1242-
os.Rename(nodepath, node32path) // node.exe -> node32.exe
1242+
utility.Rename(nodepath, node32path) // node.exe -> node32.exe
12431243
}
1244-
os.Rename(node64path, nodepath) // node64.exe -> node.exe
1244+
utility.Rename(node64path, nodepath) // node64.exe -> node.exe
12451245
}
12461246

12471247
status <- Status{Done: true}

src/utility/rename.go

+117
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,117 @@
1+
package utility
2+
3+
import (
4+
"fmt"
5+
"io"
6+
"os"
7+
"path/filepath"
8+
)
9+
10+
func Rename(old, new string) error {
11+
old_drive := filepath.VolumeName(old)
12+
new_drive := filepath.VolumeName(new)
13+
14+
if old_drive == new_drive {
15+
return os.Rename(old, new)
16+
}
17+
18+
// Get file or directory info
19+
info, err := os.Stat(old)
20+
if err != nil {
21+
return fmt.Errorf("failed to stat source: %w", err)
22+
}
23+
24+
// If old is a directory, copy recursively
25+
if info.IsDir() {
26+
err = copyDir(old, new)
27+
if err != nil {
28+
return fmt.Errorf("failed to copy directory: %w", err)
29+
}
30+
} else {
31+
// Otherwise, copy a single file
32+
err = copyFile(old, new)
33+
if err != nil {
34+
return fmt.Errorf("failed to copy file: %w", err)
35+
}
36+
}
37+
38+
// Remove the original source
39+
err = os.RemoveAll(old)
40+
if err != nil {
41+
return fmt.Errorf("failed to remove source: %w", err)
42+
}
43+
44+
return nil
45+
}
46+
47+
// copyFile copies a single file from source (old) to destination (new).
48+
func copyFile(old, new string) error {
49+
srcFile, err := os.Open(old)
50+
if err != nil {
51+
return fmt.Errorf("failed to open source file: %w", err)
52+
}
53+
defer srcFile.Close()
54+
55+
// Ensure destination directory exists
56+
destDir := filepath.Dir(new)
57+
err = os.MkdirAll(destDir, os.ModePerm)
58+
if err != nil {
59+
return fmt.Errorf("failed to create destination directory: %w", err)
60+
}
61+
62+
destFile, err := os.Create(new)
63+
if err != nil {
64+
return fmt.Errorf("failed to create destination file: %w", err)
65+
}
66+
defer destFile.Close()
67+
68+
_, err = io.Copy(destFile, srcFile)
69+
if err != nil {
70+
return fmt.Errorf("failed to copy data: %w", err)
71+
}
72+
73+
// Copy file permissions
74+
info, err := srcFile.Stat()
75+
if err != nil {
76+
return fmt.Errorf("failed to get source file info: %w", err)
77+
}
78+
err = os.Chmod(new, info.Mode())
79+
if err != nil {
80+
return fmt.Errorf("failed to set permissions on destination file: %w", err)
81+
}
82+
83+
return nil
84+
}
85+
86+
// copyDir recursively copies a directory from old path to new path.
87+
func copyDir(old, new string) error {
88+
entries, err := os.ReadDir(old)
89+
if err != nil {
90+
return fmt.Errorf("failed to read source directory: %w", err)
91+
}
92+
93+
// Ensure destination directory exists
94+
err = os.MkdirAll(new, os.ModePerm)
95+
if err != nil {
96+
return fmt.Errorf("failed to create destination directory: %w", err)
97+
}
98+
99+
for _, entry := range entries {
100+
srcPath := filepath.Join(old, entry.Name())
101+
destPath := filepath.Join(new, entry.Name())
102+
103+
if entry.IsDir() {
104+
err = copyDir(srcPath, destPath)
105+
if err != nil {
106+
return fmt.Errorf("failed to copy subdirectory: %w", err)
107+
}
108+
} else {
109+
err = copyFile(srcPath, destPath)
110+
if err != nil {
111+
return fmt.Errorf("failed to copy file: %w", err)
112+
}
113+
}
114+
}
115+
116+
return nil
117+
}

0 commit comments

Comments
 (0)