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)
|
# [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
|
```bash
|
||||||
pathman list
|
pathman list
|
||||||
pathman add ~/.local/bin
|
pathman add ~/.local/bin
|
||||||
pathman remove ~/.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
|
||||||
|
|
||||||
```
|
**Mac**, **Linux**:
|
||||||
curl https://rootprojects.org/pathman/dist/darwin/amd64/pathman -o pathman
|
|
||||||
|
```bash
|
||||||
|
curl -s https://webinstall.dev/pathman | bash
|
||||||
```
|
```
|
||||||
|
|
||||||
### Windows
|
**Windows 10**:
|
||||||
|
|
||||||
<details>
|
This can be run from `cmd.exe` or PowerShell (`curl.exe` is a native part of Windows 10).
|
||||||
<summary>See download options</summary>
|
|
||||||
Windows 10: [64-bit Download](https://rootprojects.org/pathman/dist/windows/amd64/pathman.exe)
|
|
||||||
|
|
||||||
```
|
```bash
|
||||||
powershell.exe $ProgressPreference = 'SilentlyContinue'; Invoke-WebRequest https://rootprojects.org/pathman/dist/windows/amd64/pathman.exe -OutFile pathman.exe
|
curl.exe -sA "MS" https://webinstall.dev/pathman | powershell
|
||||||
```
|
```
|
||||||
|
|
||||||
**Debug version**:
|
### Manual Install
|
||||||
|
|
||||||
```
|
1. [Download](#downloads)
|
||||||
powershell.exe $ProgressPreference = 'SilentlyContinue'; Invoke-WebRequest https://rootprojects.org/pathman/dist/windows/amd64/pathman.debug.exe -OutFile pathman.debug.exe
|
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
|
||||||
|
|
||||||
```
|
```cmd
|
||||||
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**
|
|
||||||
|
|
||||||
```
|
|
||||||
mkdir %userprofile%\bin
|
mkdir %userprofile%\bin
|
||||||
move pathman.exe %userprofile%\bin\pathman.exe
|
move pathman.exe %userprofile%\bin\pathman.exe
|
||||||
%userprofile%\bin\pathman.exe ~\bin
|
%userprofile%\bin\pathman.exe add ~/bin
|
||||||
```
|
```
|
||||||
|
|
||||||
**All Others**
|
#### Mac, Linux, etc
|
||||||
|
|
||||||
```
|
```bash
|
||||||
chmod a+x ./pathman
|
|
||||||
mkdir -p ~/.local/bin
|
mkdir -p ~/.local/bin
|
||||||
mv ./pathman ~/.local/bin
|
mv ./pathman ~/.local/bin
|
||||||
pathman add ~/.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
|
# add
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
|
@ -14,10 +14,9 @@ echo ""
|
|||||||
echo "Windows amd64"
|
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 -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}.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"
|
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}.exe -ldflags "-s -w" $gocmd
|
||||||
GOOS=windows GOARCH=386 go build -mod=vendor -o dist/windows/386/${exe}.debug.exe
|
|
||||||
|
|
||||||
echo ""
|
echo ""
|
||||||
echo "Darwin (macOS) amd64"
|
echo "Darwin (macOS) amd64"
|
||||||
|
@ -69,10 +69,11 @@ func initializeShells(home string) error {
|
|||||||
|
|
||||||
var hasRC bool
|
var hasRC bool
|
||||||
var nativeMatch *envConfig
|
var nativeMatch *envConfig
|
||||||
|
shell := strings.TrimSuffix(filepath.Base(os.Getenv("SHELL")), ".exe")
|
||||||
for i := range confs {
|
for i := range confs {
|
||||||
c := confs[i]
|
c := confs[i]
|
||||||
|
|
||||||
if filepath.Base(os.Getenv("SHELL")) == c.shell {
|
if shell == c.shell {
|
||||||
nativeMatch = c
|
nativeMatch = c
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -103,7 +104,7 @@ func initializeShells(home string) error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// MacOS is special. It *requires* .bash_profile in order to read .bashrc
|
// 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 {
|
if err := ensureBashProfile(home); nil != err {
|
||||||
return 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]
|
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()
|
home, _ := os.UserHomeDir()
|
||||||
if "" != entry && '~' == entry[0] {
|
if "" != entry && '~' == entry[0] {
|
||||||
// Let windows users not to have to type %USERPROFILE% or \Users\me every time
|
// 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 {
|
switch action {
|
||||||
default:
|
default:
|
||||||
@ -92,8 +66,10 @@ func main() {
|
|||||||
}
|
}
|
||||||
list()
|
list()
|
||||||
case "add":
|
case "add":
|
||||||
|
checkShell()
|
||||||
add(entry)
|
add(entry)
|
||||||
case "remove":
|
case "remove":
|
||||||
|
checkShell()
|
||||||
remove(entry)
|
remove(entry)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -157,7 +133,7 @@ func add(entry string) {
|
|||||||
|
|
||||||
modified, err := addPath(entry)
|
modified, err := addPath(entry)
|
||||||
if nil != err {
|
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)
|
os.Exit(1)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -223,6 +199,38 @@ func remove(entry string) {
|
|||||||
fmt.Println(msg + "\n")
|
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
|
// Paths returns path entries in the current environment
|
||||||
func Paths() []string {
|
func Paths() []string {
|
||||||
cur := os.Getenv("PATH")
|
cur := os.Getenv("PATH")
|
||||||
@ -242,7 +250,7 @@ func Paths() []string {
|
|||||||
// path entry to the current shell session
|
// path entry to the current shell session
|
||||||
func Add(p string) string {
|
func Add(p string) string {
|
||||||
if isCmdExe() {
|
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)
|
return fmt.Sprintf(`export PATH="%s:$PATH"`, p)
|
||||||
}
|
}
|
||||||
@ -257,5 +265,5 @@ func Remove(entries []string) string {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func isCmdExe() bool {
|
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"
|
"git.rootprojects.org/root/pathman/envpath"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
var pathstore = ""
|
||||||
|
|
||||||
func addPath(p string) (bool, error) {
|
func addPath(p string) (bool, error) {
|
||||||
return envpath.Add(p)
|
return envpath.Add(p)
|
||||||
}
|
}
|
||||||
|
@ -6,6 +6,8 @@ import (
|
|||||||
"git.rootprojects.org/root/pathman/winpath"
|
"git.rootprojects.org/root/pathman/winpath"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
var pathstore = "[winpath] "
|
||||||
|
|
||||||
func addPath(p string) (bool, error) {
|
func addPath(p string) (bool, error) {
|
||||||
return winpath.Add(p)
|
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) {
|
if strings.HasPrefix(strings.ToLower(absentry)+sep, strings.ToLower(home)+sep) {
|
||||||
// %USERPROFILE% is allowed, but only for user PATH
|
// %USERPROFILE% is allowed, but only for user PATH
|
||||||
// https://superuser.com/a/442163/73857
|
// https://superuser.com/a/442163/73857
|
||||||
homeentry = `%USERPROFILE%` + pathentry[len(home):]
|
homeentry = `%USERPROFILE%` + absentry[len(home):]
|
||||||
}
|
}
|
||||||
|
|
||||||
if absentry == pathentry {
|
if absentry == pathentry {
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
// +build windows,unsafe
|
// +build windows,!nounsafe
|
||||||
|
|
||||||
package winpath
|
package winpath
|
||||||
|
|
||||||
|
@ -29,12 +29,6 @@ func add(p string) (bool, error) {
|
|||||||
return false, nil
|
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...)
|
cur = append([]string{p}, cur...)
|
||||||
err = write(cur)
|
err = write(cur)
|
||||||
if nil != err {
|
if nil != err {
|
||||||
@ -63,7 +57,7 @@ func remove(p string) (bool, error) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
err = write(cur)
|
err = write(newpaths)
|
||||||
if nil != err {
|
if nil != err {
|
||||||
return false, err
|
return false, err
|
||||||
}
|
}
|
||||||
@ -74,15 +68,15 @@ func remove(p string) (bool, error) {
|
|||||||
func write(cur []string) error {
|
func write(cur []string) error {
|
||||||
// TODO --system to add to the system PATH rather than the user PATH
|
// 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 {
|
if err != nil {
|
||||||
return err
|
return fmt.Errorf("Can't open HKCU Environment for writes: %s", err)
|
||||||
}
|
}
|
||||||
defer k.Close()
|
defer k.Close()
|
||||||
|
|
||||||
err = k.SetStringValue(`Path`, strings.Join(cur, string(os.PathListSeparator)))
|
err = k.SetStringValue(`Path`, strings.Join(cur, string(os.PathListSeparator)))
|
||||||
if nil != err {
|
if nil != err {
|
||||||
return err
|
return fmt.Errorf("Can't set HKCU Environment[Path]: %s", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
err = k.Close()
|
err = k.Close()
|
||||||
@ -104,7 +98,7 @@ func paths() ([]string, error) {
|
|||||||
// TBH, it's a mess to do this on *nix systems.
|
// TBH, it's a mess to do this on *nix systems.
|
||||||
k, err := registry.OpenKey(registry.CURRENT_USER, `Environment`, registry.QUERY_VALUE)
|
k, err := registry.OpenKey(registry.CURRENT_USER, `Environment`, registry.QUERY_VALUE)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, fmt.Errorf("Can't open HKCU Environment for reads: %s", err)
|
||||||
}
|
}
|
||||||
defer k.Close()
|
defer k.Close()
|
||||||
|
|
||||||
@ -112,7 +106,7 @@ func paths() ([]string, error) {
|
|||||||
// PATH, Path, path will all work.
|
// PATH, Path, path will all work.
|
||||||
s, _, err := k.GetStringValue("Path")
|
s, _, err := k.GetStringValue("Path")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, fmt.Errorf("Can't query HKCU Environment[Path]: %s", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
// ";" on Windows
|
// ";" on Windows
|
||||||
|
Loading…
x
Reference in New Issue
Block a user