mirror of
https://github.com/coreybutler/nvm-windows.git
synced 2026-01-14 07:03:17 +08:00
Initial Code
This commit is contained in:
2
.gitignore
vendored
2
.gitignore
vendored
@@ -22,3 +22,5 @@ _testmain.go
|
||||
*.exe
|
||||
*.test
|
||||
*.prof
|
||||
|
||||
src/v*
|
||||
|
||||
69
README.md
69
README.md
@@ -1,4 +1,67 @@
|
||||
wnvm
|
||||
====
|
||||
# Node Version Manager (nvm) for Windows
|
||||
|
||||
An experimental node.js version management utility for Windows. Ironically written in Go.
|
||||
Manage multiple installations of node.js on a Windows computer.
|
||||
|
||||
**tl;dr** This is the Windows version of [nvm](https://github.com/creationix/nvm).
|
||||
|
||||
There are situations where the ability to switch between different versions of Node.js can be very
|
||||
useful and save a lot of time. For example, if you want to test a module you're developing with the latest
|
||||
bleeding edge version without uninstalling the stable version of node, this utility can help.
|
||||
|
||||
### Installation
|
||||
|
||||
Download the latest from the [releases](https://github.com/coreybutler/nvm/releases) and run the installer.
|
||||
|
||||
### Usage
|
||||
|
||||
nvm for Windows is a command line tool. Simply type `nvm` in the console for help. The basic commands are:
|
||||
|
||||
- `nvm install <version>`: Install a specific version, i.e. `0.10.32`. This will also accept `latest`, which will install the latest stable version.
|
||||
- `nvm uninstall <version>`: Uninstall a specific version.
|
||||
- `nvm use <version>`: Switch to a specific version of node.
|
||||
- `nvm list <installed | available>`: List the versions of node that are currently installed or the versions available from nodejs.org.
|
||||
- `nvm on`: Turn on nvm management.
|
||||
- `nvm off`: Turn off nvm entirely.
|
||||
|
||||
### Gotcha!
|
||||
|
||||
Please note that any global npm modules you may have installed are **not** shared between the various versions of node.js installed.
|
||||
|
||||
---
|
||||
|
||||
## Why another version manager?
|
||||
|
||||
There are several version managers for node.js. Tools like [nvm](https://github.com/creationix/nvm) and [n](https://github.com/visionmedia/n)
|
||||
are specifically designed for Mac OSX and Linux. [nvmw](https://github.com/hakobera/nvmw) and [nodist](https://github.com/marcelklehr/nodist)
|
||||
are both designed for Windows. So, why another version manager for Windows?
|
||||
|
||||
Right around node 0.10.30, the installation structure changed a little, causing some issues with the other modules. Additionally, some users
|
||||
struggle to install those modules. The architecture of most node managers on Windows focus primarily around the use of `bat` files, which
|
||||
do some clever hackery to set environment variables. Some of them use node itself (once it's downloaded), which is admirable, but prone to
|
||||
problems.
|
||||
|
||||
## What's the difference?
|
||||
|
||||
First and foremost, this version of nvm has no dependency on node. It's written in [Go](http://golang.org/), which is a much more structured
|
||||
approach than using a `.bat` file. It does not rely on having an existing node installation. Plus, the potential for creating a
|
||||
Mac/Linux version is substanially easier than converting a bunch of `.bat --> .sh` logic.
|
||||
|
||||
The approach is also quite different. There are two general ideas for supporting multiple node installations and readily switching between them.
|
||||
One is to modify the system `PATH` any time you switch versions. This always seemed a little hackish to me, and it has some quirks. The other option
|
||||
is to use a symlink. This concept requires one to put the symlink in the system `PATH`, then just update the symlink to point to whichever node
|
||||
installation directory you want to use. This is a more straightforward approach, and the one most people recommend.... until they realize just how much
|
||||
of a pain symlinks are on Windows. In order to create/modify a symlink, you must be running as an admin, and you must get around Windows UAC (that
|
||||
annoying prompt). Luckily, this is a challenge I already solved with some helper scripts in [node-windows](http://github.com/coreybutler/node-windows).
|
||||
|
||||
This version of of nvm for Windows comes with an installer, courtesy of a byproduct of the node-webkit work I did on [Fenix Web Server](http://fenixwebserver.com).
|
||||
|
||||
Overall, this project brings together some robust new technology, a few battle-hardened pieces of other modules, and support for newer versions of node.
|
||||
|
||||
## Why?
|
||||
|
||||
I needed it, plain and simple. Additionally, it's apparent that [support for multiple versions](https://github.com/joyent/node/issues/8075) is not
|
||||
coming to node core. It was also an excuse to play with Go :)
|
||||
|
||||
## License
|
||||
|
||||
MIT. See the LICENSE file.
|
||||
|
||||
5
src/elevate.cmd
Normal file
5
src/elevate.cmd
Normal file
@@ -0,0 +1,5 @@
|
||||
@setlocal
|
||||
@echo off
|
||||
set CMD=%*
|
||||
set APP=%1
|
||||
start wscript //nologo "%~dpn0.vbs" %*
|
||||
13
src/elevate.vbs
Normal file
13
src/elevate.vbs
Normal file
@@ -0,0 +1,13 @@
|
||||
Set Shell = CreateObject("Shell.Application")
|
||||
Set WShell = WScript.CreateObject("WScript.Shell")
|
||||
Set ProcEnv = WShell.Environment("PROCESS")
|
||||
|
||||
cmd = ProcEnv("CMD")
|
||||
app = ProcEnv("APP")
|
||||
args= Right(cmd,(Len(cmd)-Len(app)))
|
||||
|
||||
If (WScript.Arguments.Count >= 1) Then
|
||||
Shell.ShellExecute app, args, "", "runas", 0
|
||||
Else
|
||||
WScript.Quit
|
||||
End If
|
||||
238
src/nvm.go
Normal file
238
src/nvm.go
Normal file
@@ -0,0 +1,238 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
"os/exec"
|
||||
"strings"
|
||||
"path/filepath"
|
||||
"net/http"
|
||||
"io"
|
||||
"io/ioutil"
|
||||
"regexp"
|
||||
"bytes"
|
||||
)
|
||||
|
||||
var root = ""
|
||||
|
||||
func main() {
|
||||
args := os.Args
|
||||
detail := ""
|
||||
|
||||
setRootDir(filepath.Dir(args[0]))
|
||||
|
||||
// Capture any additional arguments
|
||||
if (len(args) > 2) {
|
||||
detail = strings.ToLower(args[2])
|
||||
}
|
||||
|
||||
// Run the appropriate method
|
||||
switch args[1] {
|
||||
case "install": install(detail)
|
||||
case "uninstall": uninstall(detail)
|
||||
case "use": use(detail)
|
||||
case "list": list(detail)
|
||||
case "enable": enable()
|
||||
case "disable": disable()
|
||||
//case "root": setRootDir(detail)
|
||||
default: help()
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
func install(version string) {
|
||||
if version == "" {
|
||||
fmt.Println("\nInvalid version.\n")
|
||||
help()
|
||||
return
|
||||
}
|
||||
|
||||
// If user specifies "latest" version, find out what version is
|
||||
if version == "latest" {
|
||||
content := getRemoteTextFile("http://nodejs.org/dist/latest/SHASUMS.txt")
|
||||
re := regexp.MustCompile("node-v(.+)+msi")
|
||||
reg := regexp.MustCompile("node-v|-x.+")
|
||||
version = reg.ReplaceAllString(re.FindString(content),"")
|
||||
}
|
||||
|
||||
// Check to see if the version is already installed
|
||||
if !isVersionInstalled(version) {
|
||||
|
||||
// If the version does not exist, download it to the temp directory.
|
||||
success := download(version);
|
||||
|
||||
// Run the installer
|
||||
if success {
|
||||
fmt.Printf("Installing v"+version+"... ")
|
||||
os.Mkdir(root+"\\v"+version,os.ModeDir)
|
||||
cmd := exec.Command("msiexec.exe","/i",os.TempDir()+"\\"+"node-v"+version+".msi","INSTALLDIR="+root+"\\v"+version,"/qb")
|
||||
err := cmd.Run()
|
||||
if err != nil {
|
||||
fmt.Println("ERROR")
|
||||
fmt.Print(err)
|
||||
os.Exit(1)
|
||||
}
|
||||
fmt.Printf("done.")
|
||||
}
|
||||
|
||||
// Clean up
|
||||
fmt.Printf("\nCleaning up... ")
|
||||
os.Remove(os.TempDir()+"\\"+"node-v"+version+".msi")
|
||||
fmt.Printf("done.\n")
|
||||
|
||||
return
|
||||
} else {
|
||||
fmt.Println("Version "+version+" is already installed.")
|
||||
return
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
func uninstall(version string) {
|
||||
// Make sure a version is specified
|
||||
if len(version) == 0 {
|
||||
fmt.Println("Provide the version you want to uninstall.")
|
||||
help()
|
||||
return
|
||||
}
|
||||
|
||||
// Determine if the version exists and skip if it doesn't
|
||||
if isVersionInstalled(version) {
|
||||
fmt.Printf("\nUninstalling node v"+version+"...")
|
||||
e := os.RemoveAll(root+"\\v"+version)
|
||||
if e != nil {
|
||||
fmt.Println("Error removing node v"+version)
|
||||
fmt.Println("Check to assure "+root+"\\v"+version+" no longer exists.")
|
||||
}
|
||||
fmt.Printf(" done")
|
||||
} else {
|
||||
fmt.Println("node v"+version+" is not installed. Type \"nvm list\" to see what is installed.")
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func use(version string) {
|
||||
// Make sure the version is installed. If not, warn.
|
||||
if !isVersionInstalled(version) {
|
||||
fmt.Println("node v"+version+" is not installed.")
|
||||
return
|
||||
}
|
||||
|
||||
// Create the symlink
|
||||
c := exec.Command(root+"\\elevate.cmd", "cmd", "/C", "mklink", "/D", root+"\\action", root+"\\v"+version)
|
||||
var out bytes.Buffer
|
||||
var stderr bytes.Buffer
|
||||
c.Stdout = &out
|
||||
c.Stderr = &stderr
|
||||
err := c.Run()
|
||||
if err != nil {
|
||||
fmt.Println(fmt.Sprint(err) + ": " + stderr.String())
|
||||
return
|
||||
}
|
||||
fmt.Println("Now using node v"+version)
|
||||
}
|
||||
|
||||
func list(listtype string) {
|
||||
if listtype == "" {
|
||||
listtype = "installed"
|
||||
}
|
||||
if listtype != "installed" && listtype != "available" {
|
||||
fmt.Println("\nInvalid list option.\n\nPlease use on of the following\n - wnvm list\n - wnvm list installed\n - wnvm list available")
|
||||
help()
|
||||
return
|
||||
}
|
||||
fmt.Printf("List "+listtype)
|
||||
}
|
||||
|
||||
func enable() {
|
||||
// Prompt user, warning them what they're going to do
|
||||
fmt.Printf("Enable by setting the PATH to use the root with a symlink")
|
||||
}
|
||||
|
||||
func disable() {
|
||||
// Prompt user, warning them what they're going to do
|
||||
fmt.Printf("Disable by removing the symlink in PATH var")
|
||||
}
|
||||
|
||||
func setRootDir(path string) {
|
||||
// Prompt user, warning them what they're going to do
|
||||
rootdir, err := filepath.Abs(filepath.Dir(path+"\\"))
|
||||
if err != nil {
|
||||
fmt.Println("Error setting root directory")
|
||||
os.Exit(1)
|
||||
}
|
||||
root = rootdir
|
||||
fmt.Println("\nSet the root path to "+root)
|
||||
}
|
||||
|
||||
func help() {
|
||||
fmt.Println("\nUsage:\n")
|
||||
fmt.Println(" nvm install <version> : The version can be a node.js version or \"latest\" for the latest stable version.")
|
||||
fmt.Println(" nvm uninstall <version> : The version must be a specific version.")
|
||||
fmt.Println(" nvm use <version> : Switch to use the specified version.")
|
||||
fmt.Println(" nvm list [type] : type can be \"available\" (from nodejs.org),")
|
||||
fmt.Println(" \"installed\" (what is currently on the computer),")
|
||||
fmt.Println(" or left blank (same as \"installed\").")
|
||||
fmt.Println(" nvm enable : Enable node.js version management.")
|
||||
fmt.Println(" nvm disable : Disable node.js version management.")
|
||||
fmt.Println(" nvm root <path> : Set the directory where wnvm should install different node.js versions.\n")
|
||||
}
|
||||
|
||||
func getRemoteTextFile(url string) string {
|
||||
response, httperr := http.Get(url)
|
||||
if httperr != nil {
|
||||
fmt.Println("\nCould not retrieve "+url+".\n\n")
|
||||
fmt.Printf("%s", httperr)
|
||||
os.Exit(1)
|
||||
} else {
|
||||
defer response.Body.Close()
|
||||
contents, readerr := ioutil.ReadAll(response.Body)
|
||||
if readerr != nil {
|
||||
fmt.Printf("%s", readerr)
|
||||
os.Exit(1)
|
||||
}
|
||||
return string(contents)
|
||||
}
|
||||
os.Exit(1)
|
||||
return ""
|
||||
}
|
||||
|
||||
// Download an MSI to the temp directory
|
||||
func download(v string) bool {
|
||||
|
||||
url := "http://nodejs.org/dist/v"+v+"/node-v"+v+"-x86.msi"
|
||||
fileName := os.TempDir()+"\\"+"node-v"+v+".msi"
|
||||
|
||||
fmt.Printf("\nDownloading node.js version "+v+"... ")
|
||||
|
||||
output, err := os.Create(fileName)
|
||||
if err != nil {
|
||||
fmt.Println("Error while creating", fileName, "-", err)
|
||||
}
|
||||
defer output.Close()
|
||||
|
||||
response, err := http.Get(url)
|
||||
if err != nil {
|
||||
fmt.Println("Error while downloading", url, "-", err)
|
||||
}
|
||||
defer response.Body.Close()
|
||||
|
||||
n, err := io.Copy(output, response.Body)
|
||||
if err != nil {
|
||||
fmt.Println("Error while downloading", url, "-", err)
|
||||
}
|
||||
|
||||
if response.Status[0:3] == "200" {
|
||||
fmt.Println(n, "bytes downloaded.")
|
||||
} else {
|
||||
fmt.Println("ERROR")
|
||||
}
|
||||
|
||||
return response.Status[0:3] == "200"
|
||||
}
|
||||
|
||||
func isVersionInstalled(version string) bool {
|
||||
src, err := os.Stat(root+"\\v"+version)
|
||||
src = src
|
||||
return err == nil
|
||||
}
|
||||
@@ -1,7 +0,0 @@
|
||||
package main
|
||||
|
||||
import "fmt"
|
||||
|
||||
func main() {
|
||||
fmt.Printf("hello, world\n")
|
||||
}
|
||||
Reference in New Issue
Block a user