mirror of
				https://github.com/therootcompany/pathman.git
				synced 2024-11-16 17:09:01 +00:00 
			
		
		
		
	Compare commits
	
		
			15 Commits
		
	
	
		
	
	| Author | SHA1 | Date | |
|---|---|---|---|
| 2c283a2864 | |||
| 472d7bda0f | |||
| 12f8b86bbe | |||
| ca0a7fecb8 | |||
| 5f6e47244e | |||
| 74f81f519c | |||
| db19301520 | |||
| 571d639092 | |||
| 1ef3680b72 | |||
| 520c4a3b22 | |||
| c067c820cd | |||
| 3478e86919 | |||
| fcc35e88f6 | |||
| aa3bc63d25 | |||
| 964972846a | 
							
								
								
									
										158
									
								
								README.md
									
									
									
									
									
								
							
							
						
						
									
										158
									
								
								README.md
									
									
									
									
									
								
							| @ -1,126 +1,106 @@ | ||||
| # [pathman](https://git.rootprojects.org/root/pathman) | ||||
| 
 | ||||
| Manage PATH on Windows, Mac, and Linux with various Shells | ||||
| Manage PATH on **Windows 10**, **Mac**, and **Linux** with various Shells | ||||
| 
 | ||||
| ```bash | ||||
| pathman list | ||||
| pathman add ~/.local/bin | ||||
| pathman remove ~/.local/bin | ||||
| pathman version | ||||
| pathman help | ||||
| ``` | ||||
| 
 | ||||
| Windows: stores PATH in the registry. | ||||
| Where is the PATH managed? | ||||
| 
 | ||||
| Mac & Linux: stores PATH in `~/.config/envman/PATH.sh` | ||||
| -   **Windows 10**: stores `PATH` in the registry. | ||||
| -   **Mac** & **Linux**: stores `PATH` in `~/.config/envman/PATH.env` | ||||
| 
 | ||||
| ## Downloads | ||||
| Note for **Windows 10** users: due to differences in how `cmd.exe`, PowerShell, and `pathman` use and interpret strings, spaces, paths, and variables, you'll get more consistent results if you: | ||||
| 
 | ||||
| ### MacOS | ||||
| -   Use `~` rather than `%USERPROFILE%` or `$Env:USERPROFILE` | ||||
| -   Use `/` rather than `\` for delimiting paths | ||||
| 
 | ||||
| MacOS (darwin): [64-bit Download ](https://rootprojects.org/pathman/dist/darwin/amd64/pathman) | ||||
| ## Install | ||||
| 
 | ||||
| ``` | ||||
| curl https://rootprojects.org/pathman/dist/darwin/amd64/pathman -o pathman | ||||
| **Mac**, **Linux**: | ||||
| 
 | ||||
| ```bash | ||||
| curl -s https://webinstall.dev/pathman | bash | ||||
| ``` | ||||
| 
 | ||||
| ### Windows | ||||
| **Windows 10**: | ||||
| 
 | ||||
| <details> | ||||
| <summary>See download options</summary> | ||||
| Windows 10: [64-bit Download](https://rootprojects.org/pathman/dist/windows/amd64/pathman.exe) | ||||
| This can be run from `cmd.exe` or PowerShell (`curl.exe` is a native part of Windows 10). | ||||
| 
 | ||||
| ``` | ||||
| powershell.exe $ProgressPreference = 'SilentlyContinue'; Invoke-WebRequest https://rootprojects.org/pathman/dist/windows/amd64/pathman.exe -OutFile pathman.exe | ||||
| ```bash | ||||
| curl.exe -sA "MS" https://webinstall.dev/pathman | powershell | ||||
| ``` | ||||
| 
 | ||||
| **Debug version**: | ||||
| ### Manual Install | ||||
| 
 | ||||
| ``` | ||||
| powershell.exe $ProgressPreference = 'SilentlyContinue'; Invoke-WebRequest https://rootprojects.org/pathman/dist/windows/amd64/pathman.debug.exe -OutFile pathman.debug.exe | ||||
| 1. [Download](#downloads) | ||||
| 2. Add to `PATH` | ||||
| 
 | ||||
| Or install via `npm`: | ||||
| 
 | ||||
| ```bash | ||||
| npm install -g pathman | ||||
| ``` | ||||
| 
 | ||||
| Windows 7: [32-bit Download](https://rootprojects.org/pathman/dist/windows/386/pathman.exe) | ||||
| #### Windows | ||||
| 
 | ||||
| ``` | ||||
| powershell.exe "(New-Object Net.WebClient).DownloadFile('https://rootprojects.org/pathman/dist/windows/386/pathman.exe', 'pathman.exe')" | ||||
| ``` | ||||
| 
 | ||||
| **Debug version**: | ||||
| 
 | ||||
| ``` | ||||
| powershell.exe "(New-Object Net.WebClient).DownloadFile('https://rootprojects.org/pathman/dist/windows/386/pathman.debug.exe', 'pathman.debug.exe')" | ||||
| ``` | ||||
| 
 | ||||
| </details> | ||||
| 
 | ||||
| ### Linux | ||||
| 
 | ||||
| <details> | ||||
| <summary>See download options</summary> | ||||
| 
 | ||||
| Linux (64-bit): [Download](https://rootprojects.org/pathman/dist/linux/amd64/pathman) | ||||
| 
 | ||||
| ``` | ||||
| curl https://rootprojects.org/pathman/dist/linux/amd64/pathman -o pathman | ||||
| ``` | ||||
| 
 | ||||
| Linux (32-bit): [Download](https://rootprojects.org/pathman/dist/linux/386/pathman) | ||||
| 
 | ||||
| ``` | ||||
| curl https://rootprojects.org/pathman/dist/linux/386/pathman -o pathman | ||||
| ``` | ||||
| 
 | ||||
| </details> | ||||
| 
 | ||||
| ### Raspberry Pi (Linux ARM) | ||||
| 
 | ||||
| <details> | ||||
| <summary>See download options</summary> | ||||
| 
 | ||||
| RPi 4 (64-bit armv8): [Download](https://rootprojects.org/pathman/dist/linux/armv8/pathman) | ||||
| 
 | ||||
| ``` | ||||
| curl https://rootprojects.org/pathman/dist/linux/armv8/pathman -o pathman` | ||||
| ``` | ||||
| 
 | ||||
| RPi 3 (armv7): [Download](https://rootprojects.org/pathman/dist/linux/armv7/pathman) | ||||
| 
 | ||||
| ``` | ||||
| curl https://rootprojects.org/pathman/dist/linux/armv7/pathman -o pathman | ||||
| ``` | ||||
| 
 | ||||
| ARMv6: [Download](https://rootprojects.org/pathman/dist/linux/armv6/pathman) | ||||
| 
 | ||||
| ``` | ||||
| curl https://rootprojects.org/pathman/dist/linux/armv6/pathman -o pathman | ||||
| ``` | ||||
| 
 | ||||
| RPi Zero (armv5): [Download](https://rootprojects.org/pathman/dist/linux/armv5/pathman) | ||||
| 
 | ||||
| ``` | ||||
| curl https://rootprojects.org/pathman/dist/linux/armv5/pathman -o pathman | ||||
| ``` | ||||
| 
 | ||||
| </details> | ||||
| 
 | ||||
| ### Add to PATH | ||||
| 
 | ||||
| **Windows** | ||||
| 
 | ||||
| ``` | ||||
| ```cmd | ||||
| mkdir %userprofile%\bin | ||||
| move pathman.exe %userprofile%\bin\pathman.exe | ||||
| %userprofile%\bin\pathman.exe ~\bin | ||||
| %userprofile%\bin\pathman.exe add ~/bin | ||||
| ``` | ||||
| 
 | ||||
| **All Others** | ||||
| #### Mac, Linux, etc | ||||
| 
 | ||||
| ``` | ||||
| chmod a+x ./pathman | ||||
| ```bash | ||||
| mkdir -p ~/.local/bin | ||||
| mv ./pathman ~/.local/bin | ||||
| pathman add ~/.local/bin | ||||
| ``` | ||||
| 
 | ||||
| ## Downloads | ||||
| 
 | ||||
| [Webi](https://webinstall.dev/pathman) (<https://webinstall.dev/pathman>) is the preferred install method, | ||||
| but you can also download from [Git Releases](https://git.rootprojects.org/root/pathman/releases): | ||||
| <https://git.rootprojects.org/root/pathman/releases>. | ||||
| 
 | ||||
| MacOS (including Apple Silicon M1), Linux, Raspberry Pi: | ||||
| 
 | ||||
| ```bash | ||||
| tar xvf pathman-v*.tar.gz | ||||
| chmod a+x ./pathman | ||||
| ./pathman --help | ||||
| ``` | ||||
| 
 | ||||
| Windows 10: | ||||
| 
 | ||||
| ```bash | ||||
| tar.exe xvf pathman-v*.zip | ||||
| .\pathman.exe --help | ||||
| ``` | ||||
| 
 | ||||
| ### Supported Platforms | ||||
| 
 | ||||
| -   MacOS | ||||
|     -   Apple Silicon M1 | ||||
|     -   Intel x86_64 | ||||
| -   Windows 10, 8, 7 | ||||
| -   Linux | ||||
|     -   amd64 / x86_64 | ||||
|     -   386 | ||||
| -   Raspberry Pi (Linux ARM) | ||||
|     -   RPi 4 (64-bit armv8) | ||||
|     -   RPi 3 (armv7) | ||||
|     -   ARMv6 | ||||
|     -   RPi Zero (armv5) | ||||
| 
 | ||||
| # CLI Help (API) | ||||
| 
 | ||||
| # add | ||||
| 
 | ||||
| ```bash | ||||
|  | ||||
| @ -14,10 +14,9 @@ echo "" | ||||
| echo "Windows amd64" | ||||
| #GOOS=windows GOARCH=amd64 go build -mod=vendor -o dist/windows/amd64/${exe}.exe -ldflags "-s -w -H=windowsgui" $gocmd | ||||
| GOOS=windows GOARCH=amd64 go build -mod=vendor -o dist/windows/amd64/${exe}.exe -ldflags "-s -w" $gocmd | ||||
| GOOS=windows GOARCH=amd64 go build -mod=vendor -o dist/windows/amd64/${exe}.debug.exe | ||||
| #GOOS=windows GOARCH=amd64 go build -mod=vendor -o dist/windows/amd64/${exe}.debug.exe | ||||
| echo "Windows 386" | ||||
| GOOS=windows GOARCH=386 go build -mod=vendor -o dist/windows/386/${exe}.exe -ldflags "-s -w" $gocmd | ||||
| GOOS=windows GOARCH=386 go build -mod=vendor -o dist/windows/386/${exe}.debug.exe | ||||
| 
 | ||||
| echo "" | ||||
| echo "Darwin (macOS) amd64" | ||||
|  | ||||
| @ -69,10 +69,11 @@ func initializeShells(home string) error { | ||||
| 
 | ||||
| 	var hasRC bool | ||||
| 	var nativeMatch *envConfig | ||||
| 	shell := strings.TrimSuffix(filepath.Base(os.Getenv("SHELL")), ".exe") | ||||
| 	for i := range confs { | ||||
| 		c := confs[i] | ||||
| 
 | ||||
| 		if filepath.Base(os.Getenv("SHELL")) == c.shell { | ||||
| 		if shell == c.shell { | ||||
| 			nativeMatch = c | ||||
| 		} | ||||
| 
 | ||||
| @ -103,7 +104,7 @@ func initializeShells(home string) error { | ||||
| 	} | ||||
| 
 | ||||
| 	// MacOS is special. It *requires* .bash_profile in order to read .bashrc | ||||
| 	if "darwin" == runtime.GOOS && "bash" == os.Getenv("SHELL") { | ||||
| 	if "darwin" == runtime.GOOS && "bash" == shell { | ||||
| 		if err := ensureBashProfile(home); nil != err { | ||||
| 			return err | ||||
| 		} | ||||
|  | ||||
							
								
								
									
										1
									
								
								npm/.gitignore
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										1
									
								
								npm/.gitignore
									
									
									
									
										vendored
									
									
										Normal file
									
								
							| @ -0,0 +1 @@ | ||||
| node_modules | ||||
							
								
								
									
										41
									
								
								npm/README.md
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										41
									
								
								npm/README.md
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,41 @@ | ||||
| # pathman | ||||
| 
 | ||||
| A cross-platform PATH manager | ||||
| 
 | ||||
| Manage PATH on Windows, Mac, and Linux with various Shells | ||||
| 
 | ||||
| ```bash | ||||
| pathman list | ||||
| pathman add ~/.local/bin | ||||
| pathman remove ~/.local/bin | ||||
| pathman version | ||||
| pathman help | ||||
| ``` | ||||
| 
 | ||||
| Windows: stores PATH in the registry. | ||||
| 
 | ||||
| Mac & Linux: stores PATH in `~/.config/envman/PATH.sh` | ||||
| 
 | ||||
| ## Meta Package | ||||
| 
 | ||||
| This is a meta-package to fetch and install the correction version of | ||||
| [go-pathman](https://git.rootprojects.org/root/pathman) | ||||
| for your architecture and platform. | ||||
| 
 | ||||
| ```bash | ||||
| npm install -g @root/pathman | ||||
| ``` | ||||
| 
 | ||||
| # Supported Shells | ||||
| 
 | ||||
| In theory, anything with bourne-compatible exports. Specifically: | ||||
| 
 | ||||
| -   bash | ||||
| -   zsh | ||||
| -   fish | ||||
| 
 | ||||
| On Windows, all shells inherit from the registry. | ||||
| 
 | ||||
| -   cmd.exe | ||||
| -   PowerShell | ||||
| -   Git Bash | ||||
							
								
								
									
										1
									
								
								npm/bin/pathman
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										1
									
								
								npm/bin/pathman
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1 @@ | ||||
| # this will be replaced by the postinstall script | ||||
							
								
								
									
										12
									
								
								npm/index.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										12
									
								
								npm/index.js
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,12 @@ | ||||
| 'use strict'; | ||||
| 
 | ||||
| var spawner = require('./lib/exec.js'); | ||||
| module.exports = spawner; | ||||
| module.exports.install = require('./lib/install.js'); | ||||
| 
 | ||||
| if (require.main === module) { | ||||
| 	// node index.js --foo bar => [ '--foo', 'bar' ]
 | ||||
| 	spawner(process.argv.slice(2)).catch(function(err) { | ||||
| 		console.error(err.message); | ||||
| 	}); | ||||
| } | ||||
							
								
								
									
										36
									
								
								npm/lib/exec.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										36
									
								
								npm/lib/exec.js
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,36 @@ | ||||
| 'use strict'; | ||||
| 
 | ||||
| var pkg = require('../package.json'); | ||||
| var spawn = require('child_process').spawn; | ||||
| var os = require('os'); | ||||
| var path = require('path'); | ||||
| var ext = /^win/i.test(os.platform()) ? '.exe' : ''; | ||||
| 
 | ||||
| // @scope/packagename => packagename
 | ||||
| // { bin: { "packagename": "bin/runner" } } => "bin/runner"
 | ||||
| var bin = path.resolve(__dirname, '..', pkg.bin[pkg.name.replace(/.*\//, '')]); | ||||
| 
 | ||||
| function spawner(args) { | ||||
| 	return new Promise(function(resolve, reject) { | ||||
| 		var runner = spawn(path.join(bin + ext), args, { | ||||
| 			windowsHide: true | ||||
| 		}); | ||||
| 		runner.stdout.on('data', function(chunk) { | ||||
| 			console.info(chunk.toString('utf8')); | ||||
| 		}); | ||||
| 		runner.stderr.on('data', function(chunk) { | ||||
| 			console.error(chunk.toString('utf8')); | ||||
| 		}); | ||||
| 		runner.on('exit', function(code) { | ||||
| 			if (0 !== code) { | ||||
| 				reject( | ||||
| 					new Error("exited with non-zero status code '" + code + "'") | ||||
| 				); | ||||
| 				return; | ||||
| 			} | ||||
| 			resolve({ code: code }); | ||||
| 		}); | ||||
| 	}); | ||||
| } | ||||
| 
 | ||||
| module.exports = spawner; | ||||
							
								
								
									
										219
									
								
								npm/lib/install.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										219
									
								
								npm/lib/install.js
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,219 @@ | ||||
| 'use strict'; | ||||
| 
 | ||||
| var pkg = require('../package.json'); | ||||
| var path = require('path'); | ||||
| var os = require('os'); | ||||
| 
 | ||||
| // https://nodejs.org/api/os.html#os_os_arch
 | ||||
| // 'arm', 'arm64', 'ia32', 'mips', 'mipsel', 'ppc', 'ppc64', 's390', 's390x', 'x32', and 'x64'
 | ||||
| var arch = os.arch(); // process.arch
 | ||||
| 
 | ||||
| // https://nodejs.org/api/os.html#os_os_platform
 | ||||
| // 'aix', 'darwin', 'freebsd', 'linux', 'openbsd', 'sunos', 'win32'
 | ||||
| var platform = os.platform(); // process.platform
 | ||||
| var ext = /^win/i.test(platform) ? '.exe' : ''; | ||||
| 
 | ||||
| // This is _probably_ right. It's good enough for us
 | ||||
| // https://github.com/nodejs/node/issues/13629
 | ||||
| if ('arm' === arch) { | ||||
| 	arch += 'v' + process.config.variables.arm_version; | ||||
| } | ||||
| 
 | ||||
| var map = { | ||||
| 	// arches
 | ||||
| 	armv6: 'armv6', | ||||
| 	armv7: 'armv7', | ||||
| 	arm64: 'armv8', | ||||
| 	ia32: '386', | ||||
| 	x32: '386', | ||||
| 	x64: 'amd64', | ||||
| 	// platforms
 | ||||
| 	darwin: 'darwin', | ||||
| 	linux: 'linux', | ||||
| 	win32: 'windows' | ||||
| }; | ||||
| 
 | ||||
| arch = map[arch]; | ||||
| platform = map[platform]; | ||||
| 
 | ||||
| var pkg = require('../package.json'); | ||||
| var newVer = pkg.version; | ||||
| var fs = require('fs'); | ||||
| var request = require('@root/request'); | ||||
| var mkdirp = require('@root/mkdirp'); | ||||
| 
 | ||||
| function install(name, bindirs, getVersion, parseVersion, urlTpl) { | ||||
| 	if (!arch || !platform) { | ||||
| 		console.error( | ||||
| 			"'" + | ||||
| 				os.platform() + | ||||
| 				"' on '" + | ||||
| 				os.arch() + | ||||
| 				"' isn't supported yet." | ||||
| 		); | ||||
| 		console.error( | ||||
| 			'Please open an issue at https://git.rootprojects.org/root/pathman/issues' | ||||
| 		); | ||||
| 		process.exit(1); | ||||
| 	} | ||||
| 
 | ||||
| 	var url = urlTpl | ||||
| 		.replace(/{{ .Version }}/g, newVer) | ||||
| 		.replace(/{{ .Platform }}/g, platform) | ||||
| 		.replace(/{{ .Arch }}/g, arch) | ||||
| 		.replace(/{{ .Ext }}/g, ext); | ||||
| 
 | ||||
| 	console.info('Installing from', url); | ||||
| 	return request({ uri: url, encoding: null }, function(err, resp) { | ||||
| 		if (err) { | ||||
| 			console.error(err); | ||||
| 			return; | ||||
| 		} | ||||
| 
 | ||||
| 		//console.log(resp.body.byteLength);
 | ||||
| 		//console.log(typeof resp.body);
 | ||||
| 		var bin = name + ext; | ||||
| 		function next() { | ||||
| 			if (!bindirs.length) { | ||||
| 				return; | ||||
| 			} | ||||
| 			var bindir = bindirs.pop(); | ||||
| 			return mkdirp(bindir, function(err) { | ||||
| 				if (err) { | ||||
| 					console.error(err); | ||||
| 					return; | ||||
| 				} | ||||
| 
 | ||||
| 				var localbin = path.join(bindir, bin); | ||||
| 				return fs.writeFile(localbin, resp.body, function(err) { | ||||
| 					next(); | ||||
| 					if (err) { | ||||
| 						console.error(err); | ||||
| 						return; | ||||
| 					} | ||||
| 					fs.chmodSync(localbin, parseInt('0755', 8)); | ||||
| 					console.info('Wrote', bin, 'to', bindir); | ||||
| 				}); | ||||
| 			}); | ||||
| 		} | ||||
| 		next(); | ||||
| 	}); | ||||
| } | ||||
| 
 | ||||
| function shouldUpdate(oldVer, newVer) { | ||||
| 	// "v1.0.0-pre" is BEHIND "v1.0.0"
 | ||||
| 	newVer = newVer | ||||
| 		.replace(/^v/, '') | ||||
| 		.split(/[\.\-\+]/) | ||||
| 		.filter(Boolean); | ||||
| 	oldVer = oldVer | ||||
| 		.replace(/^v/, '') | ||||
| 		.split(/[\.\-\+]/) | ||||
| 		.filter(Boolean); | ||||
| 
 | ||||
| 	if (!oldVer.length) { | ||||
| 		return true; | ||||
| 	} | ||||
| 
 | ||||
| 	// ex: v1.0.0-pre vs v1.0.0
 | ||||
| 	if (newVer[3] && !oldVer[3]) { | ||||
| 		// don't install beta over stable
 | ||||
| 		return false; | ||||
| 	} | ||||
| 
 | ||||
| 	// ex: old is v1.0.0-pre
 | ||||
| 	if (oldVer[3]) { | ||||
| 		if (oldVer[2] > 0) { | ||||
| 			oldVer[2] -= 1; | ||||
| 		} else if (oldVer[1] > 0) { | ||||
| 			oldVer[2] = 999; | ||||
| 			oldVer[1] -= 1; | ||||
| 		} else if (oldVer[0] > 0) { | ||||
| 			oldVer[2] = 999; | ||||
| 			oldVer[1] = 999; | ||||
| 			oldVer[0] -= 1; | ||||
| 		} else { | ||||
| 			// v0.0.0
 | ||||
| 			return true; | ||||
| 		} | ||||
| 	} | ||||
| 
 | ||||
| 	// ex: v1.0.1 vs v1.0.0-pre
 | ||||
| 	if (newVer[3]) { | ||||
| 		if (newVer[2] > 0) { | ||||
| 			newVer[2] -= 1; | ||||
| 		} else if (newVer[1] > 0) { | ||||
| 			newVer[2] = 999; | ||||
| 			newVer[1] -= 1; | ||||
| 		} else if (newVer[0] > 0) { | ||||
| 			newVer[2] = 999; | ||||
| 			newVer[1] = 999; | ||||
| 			newVer[0] -= 1; | ||||
| 		} else { | ||||
| 			// v0.0.0
 | ||||
| 			return false; | ||||
| 		} | ||||
| 	} | ||||
| 
 | ||||
| 	// ex: v1.0.1 vs v1.0.0
 | ||||
| 	if (oldVer[0] > newVer[0]) { | ||||
| 		return false; | ||||
| 	} else if (oldVer[0] < newVer[0]) { | ||||
| 		return true; | ||||
| 	} else if (oldVer[1] > newVer[1]) { | ||||
| 		return false; | ||||
| 	} else if (oldVer[1] < newVer[1]) { | ||||
| 		return true; | ||||
| 	} else if (oldVer[2] > newVer[2]) { | ||||
| 		return false; | ||||
| 	} else if (oldVer[2] < newVer[2]) { | ||||
| 		return true; | ||||
| 	} else if (!oldVer[3] && newVer[3]) { | ||||
| 		return false; | ||||
| 	} else if (oldVer[3] && !newVer[3]) { | ||||
| 		return true; | ||||
| 	} else { | ||||
| 		return false; | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| /* | ||||
| // Same version
 | ||||
| console.log(false === shouldUpdate('0.5.0', '0.5.0')); | ||||
| // No previous version
 | ||||
| console.log(true === shouldUpdate('', '0.5.1')); | ||||
| // The new version is slightly newer
 | ||||
| console.log(true === shouldUpdate('0.5.0', '0.5.1')); | ||||
| console.log(true === shouldUpdate('0.4.999-pre1', '0.5.0-pre1')); | ||||
| // The new version is slightly older
 | ||||
| console.log(false === shouldUpdate('0.5.0', '0.5.0-pre1')); | ||||
| console.log(false === shouldUpdate('0.5.1', '0.5.0')); | ||||
| */ | ||||
| 
 | ||||
| function checkVersion(getVersion, parseVersion) { | ||||
| 	var exec = require('child_process').exec; | ||||
| 
 | ||||
| 	return new Promise(function(resolve) { | ||||
| 		exec(getVersion, { windowsHide: true }, function(err, stdout) { | ||||
| 			var oldVer = parseVersion(stdout); | ||||
| 			resolve(oldVer); | ||||
| 			/* | ||||
| 			//console.log('old:', oldVer, 'new:', newVer);
 | ||||
| 			if (!shouldUpdate(oldVer, newVer)) { | ||||
| 				console.info( | ||||
| 					'Current ' + name + ' version is new enough:', | ||||
| 					oldVer, | ||||
| 					newVer | ||||
| 				); | ||||
| 				return; | ||||
| 				//} else {
 | ||||
| 				//	console.info('Current version is older:', oldVer, newVer);
 | ||||
| 			} | ||||
|       */ | ||||
| 		}); | ||||
| 	}); | ||||
| } | ||||
| 
 | ||||
| module.exports = install; | ||||
| module.exports._shouldUpdate = shouldUpdate; | ||||
| module.exports._checkVersion = checkVersion; | ||||
							
								
								
									
										18
									
								
								npm/package-lock.json
									
									
									
										generated
									
									
									
										Normal file
									
								
							
							
						
						
									
										18
									
								
								npm/package-lock.json
									
									
									
										generated
									
									
									
										Normal file
									
								
							| @ -0,0 +1,18 @@ | ||||
| { | ||||
| 	"name": "@root/pathman", | ||||
| 	"version": "0.5.2-alpha.5", | ||||
| 	"lockfileVersion": 1, | ||||
| 	"requires": true, | ||||
| 	"dependencies": { | ||||
| 		"@root/mkdirp": { | ||||
| 			"version": "1.0.0", | ||||
| 			"resolved": "https://registry.npmjs.org/@root/mkdirp/-/mkdirp-1.0.0.tgz", | ||||
| 			"integrity": "sha512-hxGAYUx5029VggfG+U9naAhQkoMSXtOeXtbql97m3Hi6/sQSRL/4khKZPyOF6w11glyCOU38WCNLu9nUcSjOfA==" | ||||
| 		}, | ||||
| 		"@root/request": { | ||||
| 			"version": "1.3.11", | ||||
| 			"resolved": "https://registry.npmjs.org/@root/request/-/request-1.3.11.tgz", | ||||
| 			"integrity": "sha512-3a4Eeghcjsfe6zh7EJ+ni1l8OK9Fz2wL1OjP4UCa0YdvtH39kdXB9RGWuzyNv7dZi0+Ffkc83KfH0WbPMiuJFw==" | ||||
| 		} | ||||
| 	} | ||||
| } | ||||
							
								
								
									
										40
									
								
								npm/package.json
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										40
									
								
								npm/package.json
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,40 @@ | ||||
| { | ||||
| 	"name": "@root/pathman", | ||||
| 	"version": "0.5.3-pre.3", | ||||
| 	"description": "A cross-platform PATH manager", | ||||
| 	"main": "index.js", | ||||
| 	"homepage": "https://git.rootprojects.org/root/pathman/src/branch/master/npm", | ||||
| 	"files": [ | ||||
| 		"bin/", | ||||
| 		"lib/", | ||||
| 		"scripts/" | ||||
| 	], | ||||
| 	"bin": { | ||||
| 		"pathman": "bin/pathman" | ||||
| 	}, | ||||
| 	"scripts": { | ||||
| 		"pathman": "pathman", | ||||
| 		"postinstall": "node scripts/fetch-prebuilt.js", | ||||
| 		"test": "echo \"Error: no test specified\" && exit 1" | ||||
| 	}, | ||||
| 	"repository": { | ||||
| 		"type": "git", | ||||
| 		"url": "https://git.rootprojects.org/root/pathman.git" | ||||
| 	}, | ||||
| 	"keywords": [ | ||||
| 		"launchd", | ||||
| 		"systemd", | ||||
| 		"winsvc", | ||||
| 		"launchctl", | ||||
| 		"systemctl", | ||||
| 		"HKEY_CURRENT_USER", | ||||
| 		"HKCU", | ||||
| 		"Run" | ||||
| 	], | ||||
| 	"author": "AJ ONeal <coolaj86@gmail.com> (https://coolaj86.com/)", | ||||
| 	"license": "MPL-2.0", | ||||
| 	"dependencies": { | ||||
| 		"@root/mkdirp": "^1.0.0", | ||||
| 		"@root/request": "^1.3.11" | ||||
| 	} | ||||
| } | ||||
							
								
								
									
										88
									
								
								npm/scripts/fetch-prebuilt.js
									
									
									
									
									
										Executable file
									
								
							
							
						
						
									
										88
									
								
								npm/scripts/fetch-prebuilt.js
									
									
									
									
									
										Executable file
									
								
							| @ -0,0 +1,88 @@ | ||||
| #!/usr/bin/env node
 | ||||
| 
 | ||||
| 'use strict'; | ||||
| 
 | ||||
| var pkg = require('../package.json'); | ||||
| var os = require('os'); | ||||
| var path = require('path'); | ||||
| var fs = require('fs'); | ||||
| var manager = require('../index.js'); | ||||
| 
 | ||||
| if (require.main === module) { | ||||
| 	run(); | ||||
| } | ||||
| 
 | ||||
| function run() { | ||||
| 	var ext = /^win/i.test(os.platform()) ? '.exe' : ''; | ||||
| 	//var homedir = require('os').homedir();
 | ||||
| 	//var bindir = path.join(homedir, '.local', 'bin');
 | ||||
| 	var bindir = path.resolve(__dirname, '..', 'bin'); | ||||
| 	var name = pkg.name.replace(/.*\//, ''); | ||||
| 	if ('.exe' === ext) { | ||||
| 		winpmstall(pkg.name, name, bindir); | ||||
| 	} | ||||
| 
 | ||||
| 	return manager.install( | ||||
| 		name, | ||||
| 		[bindir], | ||||
| 		'pathman version', | ||||
| 		function parseVersion(stdout) { | ||||
| 			return (stdout || '').split(' ')[0]; | ||||
| 		}, | ||||
| 		'https://rootprojects.org/pathman/dist/{{ .Platform }}/{{ .Arch }}/pathman{{ .Ext }}' | ||||
| 	); | ||||
| } | ||||
| 
 | ||||
| function winpmstall(pkgname, name, bindir) { | ||||
| 	var dd = /\//.test(pkgname) ? '../' : ''; | ||||
| 	var pkgpath = pkgname.replace(/@/g, '\\@'); | ||||
| 
 | ||||
| 	try { | ||||
| 		fs.writeFileSync( | ||||
| 			path.join(bindir, name), | ||||
| 			[ | ||||
| 				'#!/usr/bin/env bash', | ||||
| 				'"$(dirname "$0")/' + name + '.exe" "$@"', | ||||
| 				'exit $?' | ||||
| 			].join('\n') | ||||
| 		); | ||||
| 	} catch (e) { | ||||
| 		// ignore
 | ||||
| 	} | ||||
| 
 | ||||
| 	// because bugs in npm + git bash oddities, of course
 | ||||
| 	// https://npm.community/t/globally-installed-package-does-not-execute-in-git-bash-on-windows/9394
 | ||||
| 	try { | ||||
| 		fs.writeFileSync( | ||||
| 			path.join(__dirname, dd + '../../.bin', name), | ||||
| 			[ | ||||
| 				'#!/bin/sh', | ||||
| 				'# manual bugfix patch for npm on windows', | ||||
| 				'basedir=$(dirname "$(echo "$0" | sed -e \'s,\\\\,/,g\')")', | ||||
| 				'"$basedir/../' + pkgpath + '/bin/' + name + '"   "$@"', | ||||
| 				'exit $?' | ||||
| 			].join('\n') | ||||
| 		); | ||||
| 	} catch (e) { | ||||
| 		// ignore
 | ||||
| 	} | ||||
| 	try { | ||||
| 		fs.writeFileSync( | ||||
| 			path.join(__dirname, dd + '../../..', name), | ||||
| 			[ | ||||
| 				'#!/bin/sh', | ||||
| 				'# manual bugfix patch for npm on windows', | ||||
| 				'basedir=$(dirname "$(echo "$0" | sed -e \'s,\\\\,/,g\')")', | ||||
| 				'"$basedir/node_modules/' + | ||||
| 					pkgname + | ||||
| 					'/bin/' + | ||||
| 					name + | ||||
| 					'"   "$@"', | ||||
| 				'exit $?' | ||||
| 			].join('\n') | ||||
| 		); | ||||
| 	} catch (e) { | ||||
| 		// ignore
 | ||||
| 	} | ||||
| 	// end bugfix
 | ||||
| } | ||||
							
								
								
									
										68
									
								
								pathman.go
									
									
									
									
									
								
							
							
						
						
									
										68
									
								
								pathman.go
									
									
									
									
									
								
							| @ -46,36 +46,10 @@ func main() { | ||||
| 		entry = os.Args[2] | ||||
| 	} | ||||
| 
 | ||||
| 	// https://superuser.com/a/69190/73857 | ||||
| 	// https://github.com/rust-lang-nursery/rustup.rs/issues/686#issuecomment-253982841 | ||||
| 	// exec source $HOME/.profile | ||||
| 	shell := os.Getenv("SHELL") | ||||
| 	shell = filepath.Base(shell) | ||||
| 	switch shell { | ||||
| 	case "": | ||||
| 		if strings.HasSuffix(os.Getenv("COMSPEC"), "/cmd.exe") { | ||||
| 			shell = "cmd" | ||||
| 		} | ||||
| 	case "fish": | ||||
| 		// ignore | ||||
| 	case "zsh": | ||||
| 		// ignore | ||||
| 	case "bash": | ||||
| 		// ignore | ||||
| 	default: | ||||
| 		// warn and try anyway | ||||
| 		fmt.Fprintf( | ||||
| 			os.Stderr, | ||||
| 			"%q isn't a recognized shell. Please open an issue at https://git.rootprojects.org/root/pathman/issues?q=%s", | ||||
| 			shell, | ||||
| 			shell, | ||||
| 		) | ||||
| 	} | ||||
| 
 | ||||
| 	home, _ := os.UserHomeDir() | ||||
| 	if "" != entry && '~' == entry[0] { | ||||
| 		// Let windows users not to have to type %USERPROFILE% or \Users\me every time | ||||
| 		entry = strings.Replace(entry, "~", home, 0) | ||||
| 		entry = strings.Replace(entry, "~", home, 1) | ||||
| 	} | ||||
| 	switch action { | ||||
| 	default: | ||||
| @ -92,8 +66,10 @@ func main() { | ||||
| 		} | ||||
| 		list() | ||||
| 	case "add": | ||||
| 		checkShell() | ||||
| 		add(entry) | ||||
| 	case "remove": | ||||
| 		checkShell() | ||||
| 		remove(entry) | ||||
| 	} | ||||
| } | ||||
| @ -157,7 +133,7 @@ func add(entry string) { | ||||
| 
 | ||||
| 	modified, err := addPath(entry) | ||||
| 	if nil != err { | ||||
| 		fmt.Fprintf(os.Stderr, "failed to add %q to PATH: %s", entry, err) | ||||
| 		fmt.Fprintf(os.Stderr, "%sfailed to add %q to PATH: %s", pathstore, entry, err) | ||||
| 		os.Exit(1) | ||||
| 	} | ||||
| 
 | ||||
| @ -223,6 +199,38 @@ func remove(entry string) { | ||||
| 	fmt.Println(msg + "\n") | ||||
| } | ||||
| 
 | ||||
| // warns if this is an unknown / untested shell | ||||
| func checkShell() { | ||||
| 	// https://superuser.com/a/69190/73857 | ||||
| 	// https://github.com/rust-lang-nursery/rustup.rs/issues/686#issuecomment-253982841 | ||||
| 	// exec source $HOME/.profile | ||||
| 	shellexe := filepath.Base(os.Getenv("SHELL")) | ||||
| 	shell := strings.TrimSuffix(shellexe, ".exe") | ||||
| 	switch shell { | ||||
| 	case ".": | ||||
| 		shell = "" | ||||
| 		fallthrough | ||||
| 	case "": | ||||
| 		if strings.HasSuffix(os.Getenv("COMSPEC"), "\\cmd.exe") { | ||||
| 			shell = "cmd" | ||||
| 		} | ||||
| 	case "fish": | ||||
| 		// ignore | ||||
| 	case "zsh": | ||||
| 		// ignore | ||||
| 	case "bash": | ||||
| 		// ignore | ||||
| 	default: | ||||
| 		// warn and try anyway | ||||
| 		fmt.Fprintf( | ||||
| 			os.Stderr, | ||||
| 			"%q isn't a recognized shell. Please open an issue at https://git.rootprojects.org/root/pathman/issues?q=%s\n", | ||||
| 			shellexe, | ||||
| 			shellexe, | ||||
| 		) | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| // Paths returns path entries in the current environment | ||||
| func Paths() []string { | ||||
| 	cur := os.Getenv("PATH") | ||||
| @ -242,7 +250,7 @@ func Paths() []string { | ||||
| // path entry to the current shell session | ||||
| func Add(p string) string { | ||||
| 	if isCmdExe() { | ||||
| 		return fmt.Sprintf(`PATH %s;%PATH%`, p) | ||||
| 		return fmt.Sprintf(`PATH %s;%%PATH%%`, strings.Replace(p, "%", "%%", -1)) | ||||
| 	} | ||||
| 	return fmt.Sprintf(`export PATH="%s:$PATH"`, p) | ||||
| } | ||||
| @ -257,5 +265,5 @@ func Remove(entries []string) string { | ||||
| } | ||||
| 
 | ||||
| func isCmdExe() bool { | ||||
| 	return "" == os.Getenv("SHELL") && strings.Contains(strings.ToLower(os.Getenv("COMSPEC")), "/cmd.exe") | ||||
| 	return "" == os.Getenv("SHELL") && strings.HasSuffix(strings.ToLower(os.Getenv("COMSPEC")), "\\cmd.exe") | ||||
| } | ||||
|  | ||||
| @ -6,6 +6,8 @@ import ( | ||||
| 	"git.rootprojects.org/root/pathman/envpath" | ||||
| ) | ||||
| 
 | ||||
| var pathstore = "" | ||||
| 
 | ||||
| func addPath(p string) (bool, error) { | ||||
| 	return envpath.Add(p) | ||||
| } | ||||
|  | ||||
| @ -6,6 +6,8 @@ import ( | ||||
| 	"git.rootprojects.org/root/pathman/winpath" | ||||
| ) | ||||
| 
 | ||||
| var pathstore = "[winpath] " | ||||
| 
 | ||||
| func addPath(p string) (bool, error) { | ||||
| 	return winpath.Add(p) | ||||
| } | ||||
|  | ||||
| @ -56,7 +56,7 @@ func NormalizePathEntry(pathentry string) (string, string) { | ||||
| 	if strings.HasPrefix(strings.ToLower(absentry)+sep, strings.ToLower(home)+sep) { | ||||
| 		// %USERPROFILE% is allowed, but only for user PATH | ||||
| 		// https://superuser.com/a/442163/73857 | ||||
| 		homeentry = `%USERPROFILE%` + pathentry[len(home):] | ||||
| 		homeentry = `%USERPROFILE%` + absentry[len(home):] | ||||
| 	} | ||||
| 
 | ||||
| 	if absentry == pathentry { | ||||
|  | ||||
| @ -1,4 +1,4 @@ | ||||
| // +build windows,unsafe | ||||
| // +build windows,!nounsafe | ||||
| 
 | ||||
| package winpath | ||||
| 
 | ||||
|  | ||||
| @ -29,12 +29,6 @@ func add(p string) (bool, error) { | ||||
| 		return false, nil | ||||
| 	} | ||||
| 
 | ||||
| 	k, err := registry.OpenKey(registry.CURRENT_USER, `Environment`, registry.SET_VALUE) | ||||
| 	if err != nil { | ||||
| 		return false, err | ||||
| 	} | ||||
| 	defer k.Close() | ||||
| 
 | ||||
| 	cur = append([]string{p}, cur...) | ||||
| 	err = write(cur) | ||||
| 	if nil != err { | ||||
| @ -63,7 +57,7 @@ func remove(p string) (bool, error) { | ||||
| 		} | ||||
| 	} | ||||
| 
 | ||||
| 	err = write(cur) | ||||
| 	err = write(newpaths) | ||||
| 	if nil != err { | ||||
| 		return false, err | ||||
| 	} | ||||
| @ -74,15 +68,15 @@ func remove(p string) (bool, error) { | ||||
| func write(cur []string) error { | ||||
| 	// TODO --system to add to the system PATH rather than the user PATH | ||||
| 
 | ||||
| 	k, err := registry.OpenKey(registry.CURRENT_USER, `Environment`, registry.QUERY_VALUE) | ||||
| 	k, err := registry.OpenKey(registry.CURRENT_USER, `Environment`, registry.SET_VALUE) | ||||
| 	if err != nil { | ||||
| 		return err | ||||
| 		return fmt.Errorf("Can't open HKCU Environment for writes: %s", err) | ||||
| 	} | ||||
| 	defer k.Close() | ||||
| 
 | ||||
| 	err = k.SetStringValue(`Path`, strings.Join(cur, string(os.PathListSeparator))) | ||||
| 	if nil != err { | ||||
| 		return err | ||||
| 		return fmt.Errorf("Can't set HKCU Environment[Path]: %s", err) | ||||
| 	} | ||||
| 
 | ||||
| 	err = k.Close() | ||||
| @ -104,7 +98,7 @@ func paths() ([]string, error) { | ||||
| 	// TBH, it's a mess to do this on *nix systems. | ||||
| 	k, err := registry.OpenKey(registry.CURRENT_USER, `Environment`, registry.QUERY_VALUE) | ||||
| 	if err != nil { | ||||
| 		return nil, err | ||||
| 		return nil, fmt.Errorf("Can't open HKCU Environment for reads: %s", err) | ||||
| 	} | ||||
| 	defer k.Close() | ||||
| 
 | ||||
| @ -112,7 +106,7 @@ func paths() ([]string, error) { | ||||
| 	// PATH, Path, path will all work. | ||||
| 	s, _, err := k.GetStringValue("Path") | ||||
| 	if err != nil { | ||||
| 		return nil, err | ||||
| 		return nil, fmt.Errorf("Can't query HKCU Environment[Path]: %s", err) | ||||
| 	} | ||||
| 
 | ||||
| 	// ";" on Windows | ||||
|  | ||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user