diff --git a/build.bat b/build.bat index e1417fa..7af651e 100644 --- a/build.bat +++ b/build.bat @@ -21,7 +21,8 @@ move nvm.exe %GOBIN% cd ..\ REM Codesign the executable -REM .\buildtools\signtools\x64\signtool.exe sign /debug /tr http://timestamp.digicert.com /td sha256 /fd sha256 /a %GOBIN%\nvm.exe +echo Sign the nvm executable... +buildtools\signtool.exe sign /debug /tr http://timestamp.sectigo.com /td sha256 /fd sha256 /a %GOBIN%\nvm.exe for /f %%i in ('"%GOBIN%\nvm.exe" version') do set AppVersion=%%i echo nvm.exe v%AppVersion% built. @@ -42,13 +43,20 @@ REM Create the "no install" zip version for %%a in ("%GOBIN%") do (buildtools\zip -j -9 -r "%DIST%\nvm-noinstall.zip" "%CD%\LICENSE" %%a\* -x "%GOBIN%\nodejs.ico") REM Generate update utility +echo Generating update utility... cd .\updater go build nvm-update.go +echo Sign the updater... +buildtools\signtool.exe sign /debug /tr http://timestamp.sectigo.com /td sha256 /fd sha256 /a nvm-update.exe move nvm-update.exe %DIST% cd ..\ REM Generate the installer (InnoSetup) +echo Generating installer... buildtools\iscc "%INNOSETUP%" "/o%DIST%" +echo Sign the installer +buildtools\signtool.exe sign /debug /tr http://timestamp.sectigo.com /td sha256 /fd sha256 /a %DIST%\nvm-setup.exe +echo Bundle the installer/updater... buildtools\zip -j -9 -r "%DIST%\nvm-setup.zip" "%DIST%\nvm-setup.exe" buildtools\zip -j -9 -r "%DIST%\nvm-update.zip" "%DIST%\nvm-update.exe" @@ -56,12 +64,14 @@ del %DIST%\nvm-update.exe del %DIST%\nvm-setup.exe REM Generate checksums +echo Generating checksums... for %%f in (%DIST%\*.*) do (certutil -hashfile "%%f" MD5 | find /i /v "md5" | find /i /v "certutil" >> "%%f.checksum.txt") +echo Cleaning up... REM Cleanup del %GOBIN%\nvm.exe -del %GOBIN%\nvm-update.exe -del %GOBIN%\nvm-setup.exe +@REM del %GOBIN%\nvm-update.exe +@REM del %GOBIN%\nvm-setup.exe echo NVM for Windows v%AppVersion% build completed. @echo on diff --git a/src/nvm.go b/src/nvm.go index 79b264b..d56cd71 100644 --- a/src/nvm.go +++ b/src/nvm.go @@ -181,7 +181,7 @@ func update() { */ func getVersion(version string, cpuarch string) (string, string, error) { - arch := strings.ToLower(cpuarch) + cpuarch = strings.ToLower(cpuarch) if cpuarch != "" { if cpuarch != "32" && cpuarch != "64" && cpuarch != "all" { @@ -223,11 +223,12 @@ func getVersion(version string, cpuarch string) (string, string, error) { version = v } + version = strings.Replace(version, "v", "", 1) + v, err := semver.Make(version) if err == nil { err = v.Validate() } - if err == nil { // if the user specifies only the major/minor version, identify the latest // version applicable to what was provided. @@ -237,6 +238,8 @@ func getVersion(version string, cpuarch string) (string, string, error) { } else { version = cleanVersion(version) } + + version = strings.Replace(version, "v", "", 1) } return version, cpuarch, err @@ -434,9 +437,11 @@ func uninstall(version string) { fmt.Printf("Uninstalling node v" + version + "...") v, _ := node.GetCurrentVersion() if v == version { - runElevated(fmt.Sprintf(`"%s" cmd /C rmdir "%s"`, - filepath.Join(env.root, "elevate.cmd"), - filepath.Clean(env.symlink))) + _, err := runElevated(fmt.Sprintf(`"%s" cmd /C rmdir "%s"`, filepath.Join(env.root, "elevate.cmd"), filepath.Clean(env.symlink))) + if err != nil { + fmt.Println(fmt.Sprint(err)) + return + } } e := os.RemoveAll(filepath.Join(env.root, "v"+version)) if e != nil { @@ -460,13 +465,17 @@ func findLatestSubVersion(version string) string { return latest } -func use(version string, cpuarch string) { - v, a, err := getVersion(cersion, cpuarch) +func use(version string, cpuarch string, reload ...bool) { + v, a, err := getVersion(version, cpuarch) version = v cpuarch = a if err != nil { - fmt.Println(err.Error()) + if strings.Contains(err.Error(), "No Major.Minor.Patch") { + fmt.Printf("Unrecognized version/alias: \"%v %v-bit\"", v, a) + } else { + fmt.Println(err.Error()) + } return } @@ -489,18 +498,39 @@ func use(version string, cpuarch string) { // Remove symlink if it already exists sym, _ := os.Stat(env.symlink) if sym != nil { - if !runElevated(fmt.Sprintf(`"%s" cmd /C rmdir "%s"`, - filepath.Join(env.root, "elevate.cmd"), - filepath.Clean(env.symlink))) { - return + _, err := runElevated(fmt.Sprintf(`"%s" cmd /C rmdir "%s"`, filepath.Join(env.root, "elevate.cmd"), filepath.Clean(env.symlink))) + if err != nil { + fmt.Println(fmt.Sprint(err)) } + + // // Return if the symlink already exists + // if ok { + // fmt.Print(err) + // return + // } } // Create new symlink - if !runElevated(fmt.Sprintf(`"%s" cmd /C mklink /D "%s" "%s"`, - filepath.Join(env.root, "elevate.cmd"), - filepath.Clean(env.symlink), - filepath.Join(env.root, "v"+version))) { + var ok bool + ok, err = runElevated(fmt.Sprintf(`"%s" cmd /C mklink /D "%s" "%s"`, filepath.Join(env.root, "elevate.cmd"), filepath.Clean(env.symlink), filepath.Join(env.root, "v"+version))) + if err != nil { + if strings.Contains(err.Error(), "file already exists") { + ok, err = runElevated(fmt.Sprintf(`"%s" cmd /C rmdir "%s"`, filepath.Join(env.root, "elevate.cmd"), filepath.Clean(env.symlink))) + reloadable := true + if len(reload) > 0 { + reloadable = reload[0] + } + if err != nil { + fmt.Println(fmt.Sprint(err)) + } else if reloadable { + use(version, cpuarch, false) + return + } + } else { + fmt.Print(fmt.Sprint(err)) + } + } + if !ok { return } @@ -652,11 +682,13 @@ func enable() { } func disable() { - if !runElevated(fmt.Sprintf(`"%s" cmd /C rmdir "%s"`, - filepath.Join(env.root, "elevate.cmd"), - filepath.Clean(env.symlink))) { + ok, err := runElevated(fmt.Sprintf(`"%s" cmd /C rmdir "%s"`, filepath.Join(env.root, "elevate.cmd"), filepath.Clean(env.symlink))) + if !ok { return } + if err != nil { + fmt.Print(fmt.Sprint(err)) + } fmt.Println("nvm disabled") } @@ -777,7 +809,25 @@ func updateRootDir(path string) { } } -func runElevated(command string) bool { +func runElevated(command string, forceUAC ...bool) (bool, error) { + uac := false + if len(forceUAC) > 0 { + uac = forceUAC[0] + } + + if uac { + log.Print(command) + cmd := exec.Command(filepath.Join(env.root, "elevate.cmd"), command) + var output bytes.Buffer + var _stderr bytes.Buffer + cmd.Stdout = &output + cmd.Stderr = &_stderr + perr := cmd.Run() + if perr != nil { + return false, errors.New(fmt.Sprint(perr) + ": " + _stderr.String()) + } + } + c := exec.Command("cmd") // dummy executable that actually needs to exist but we'll overwrite using .SysProcAttr // Based on the official docs, syscall.SysProcAttr.CmdLine doesn't exist. @@ -791,11 +841,15 @@ func runElevated(command string) bool { err := c.Run() if err != nil { - fmt.Println(fmt.Sprint(err) + ": " + stderr.String()) - return false + msg := stderr.String() + if strings.Contains(msg, "not have sufficient privilege") && uac { + return runElevated(command, false) + } + // fmt.Println(fmt.Sprint(err) + ": " + stderr.String()) + return false, errors.New(fmt.Sprint(err) + ": " + msg) } - return true + return true, nil } func saveSettings() {