Initial release
* commit 'd1eb53b952e3dae04c288ac81f1cc0fdd7c6547a': Initial release
This commit is contained in:
commit
668f60aaaa
6
.gitignore
vendored
Normal file
6
.gitignore
vendored
Normal file
|
@ -0,0 +1,6 @@
|
||||||
|
build/
|
||||||
|
dist/
|
||||||
|
nimcache/
|
||||||
|
|
||||||
|
*.stamp
|
||||||
|
*.bottle.tar.gz
|
4
.gitmodules
vendored
Normal file
4
.gitmodules
vendored
Normal file
|
@ -0,0 +1,4 @@
|
||||||
|
[submodule "submodules/commandant"]
|
||||||
|
path = submodules/commandant
|
||||||
|
url = https://github.com/tomekwojcik/commandant.git
|
||||||
|
branch = tomekwojcik_0_15_1
|
12
CHANGELOG.md
Normal file
12
CHANGELOG.md
Normal file
|
@ -0,0 +1,12 @@
|
||||||
|
# Changelog
|
||||||
|
|
||||||
|
All notable changes to this project will be documented in this file.
|
||||||
|
|
||||||
|
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
|
||||||
|
and this project adheres to
|
||||||
|
[Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
||||||
|
|
||||||
|
## [1.0.2] - 2023-12-05
|
||||||
|
|
||||||
|
### Added
|
||||||
|
- Initial release.
|
21
LICENSE
Normal file
21
LICENSE
Normal file
|
@ -0,0 +1,21 @@
|
||||||
|
pathsd by BTHLabs <contact@bthlabs.pl> (https://bthlabs.pl)
|
||||||
|
|
||||||
|
Copyright (c) 2023-present BTHLabs. All rights reserved.
|
||||||
|
|
||||||
|
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
of this software and associated documentation files (the "Software"), to deal
|
||||||
|
in the Software without restriction, including without limitation the rights
|
||||||
|
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
copies of the Software, and to permit persons to whom the Software is
|
||||||
|
furnished to do so, subject to the following conditions:
|
||||||
|
|
||||||
|
The above copyright notice and this permission notice shall be included in all
|
||||||
|
copies or substantial portions of the Software.
|
||||||
|
|
||||||
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||||
|
SOFTWARE.
|
72
Makefile
Normal file
72
Makefile
Normal file
|
@ -0,0 +1,72 @@
|
||||||
|
VERSION = 1.0.1
|
||||||
|
PREFIX ?= /usr/local
|
||||||
|
|
||||||
|
OS != uname -o
|
||||||
|
|
||||||
|
SOURCES := src/pathsd.nim
|
||||||
|
VENDOR_SOURCES := submodules/commandant/commandant.nim
|
||||||
|
ALL_SOURCES := $(SOURCES) $(VENDOR_SOURCES)
|
||||||
|
|
||||||
|
ALL_STAMPS := ${ALL_SOURCES:.nim=.stamp}
|
||||||
|
|
||||||
|
OUT := build/pathsd
|
||||||
|
|
||||||
|
.POSIX:
|
||||||
|
.SUFFIXES:
|
||||||
|
.SUFFIXES: .stamp .nim
|
||||||
|
.PHONY: all test install-license install-license-freebsd install clean sdist bdist-freebsd
|
||||||
|
|
||||||
|
all: $(OUT)
|
||||||
|
|
||||||
|
build:
|
||||||
|
mkdir build
|
||||||
|
|
||||||
|
dist:
|
||||||
|
mkdir dist
|
||||||
|
|
||||||
|
# This is such a hack. I love it ;).
|
||||||
|
.nim.stamp:
|
||||||
|
touch $@
|
||||||
|
|
||||||
|
$(OUT): build $(ALL_STAMPS)
|
||||||
|
nimble build -d:release
|
||||||
|
|
||||||
|
test:
|
||||||
|
nimble test
|
||||||
|
|
||||||
|
install-license:
|
||||||
|
install LICENSE "$(DESTDIR)$(PREFIX)/share/doc/pathsd/"
|
||||||
|
|
||||||
|
install-license-freebsd:
|
||||||
|
install -d "$(DESTDIR)$(PREFIX)/share/licenses/pathsd-$(VERSION)/"
|
||||||
|
install freebsd/LICENSE "$(DESTDIR)$(PREFIX)/share/licenses/pathsd-$(VERSION)/"
|
||||||
|
install freebsd/catalog.mk "$(DESTDIR)$(PREFIX)/share/licenses/pathsd-$(VERSION)/" # Hmm?
|
||||||
|
install LICENSE "$(DESTDIR)$(PREFIX)/share/licenses/pathsd-$(VERSION)/MIT"
|
||||||
|
|
||||||
|
install: $(OUT) dist
|
||||||
|
install -d "$(DESTDIR)$(PREFIX)/bin"
|
||||||
|
install -d "$(DESTDIR)$(PREFIX)/share/doc/pathsd"
|
||||||
|
|
||||||
|
install -s build/pathsd "$(DESTDIR)$(PREFIX)/bin/"
|
||||||
|
install README.md "$(DESTDIR)$(PREFIX)/share/doc/pathsd/"
|
||||||
|
install CHANGELOG.md "$(DESTDIR)$(PREFIX)/share/doc/pathsd/"
|
||||||
|
install NOTICE.txt "$(DESTDIR)$(PREFIX)/share/doc/pathsd/"
|
||||||
|
|
||||||
|
if [ "$(OS)" = "FreeBSD" ];then make install-license-freebsd; else make install-license; fi
|
||||||
|
|
||||||
|
clean:
|
||||||
|
nimble clean
|
||||||
|
rm -f $(ALL_STAMPS)
|
||||||
|
rm -rf build/ dist/
|
||||||
|
|
||||||
|
sdist: build dist
|
||||||
|
rm -rf build/sdistroot/pathsd-${VERSION}
|
||||||
|
mkdir -p build/sdistroot/pathsd-${VERSION}
|
||||||
|
git ls-files --recurse-submodules | cpio -pd build/sdistroot/pathsd-${VERSION}
|
||||||
|
(cd build/sdistroot; tar cvf ../../dist/pathsd-${VERSION}.tar.gz pathsd-${VERSION}/)
|
||||||
|
|
||||||
|
bdist-freebsd: $(OUT) dist
|
||||||
|
make install DESTDIR=build/pkgroot
|
||||||
|
cat freebsd/manifest.in | sed -e 's|%%VERSION%%|${VERSION}|' | sed -e 's|%%PREFIX%%|${PREFIX}|' | sed -e 's|%%FLATSIZE%%|${:! du -c build/pkgroot | tail -n 1 | cut -f 1!:}|' > build/manifest
|
||||||
|
echo ${:! find build/pkgroot -type f !:C/^build\/pkgroot//} | tr ' ' '\n' > build/pkg-plist
|
||||||
|
pkg create -M build/manifest -p build/pkg-plist -r build/pkgroot/ -o dist/ -f tgz
|
18
NOTICE.txt
Normal file
18
NOTICE.txt
Normal file
|
@ -0,0 +1,18 @@
|
||||||
|
pathsd by BTHLabs <contact@bthlabs.pl> (https://bthlabs.pl)
|
||||||
|
|
||||||
|
Copyright (c) 2023-present BTHLabs. All rights reserved.
|
||||||
|
|
||||||
|
Licensed under terms of the MIT License
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
pathsd by BTHLabs includes the following third party software
|
||||||
|
|
||||||
|
commandant
|
||||||
|
Copyright (c) 2021 Casey McMahon
|
||||||
|
Copyright (c) 2013 Guillaume Viger
|
||||||
|
Licensed under terms of the MIT License
|
||||||
|
|
||||||
|
Nim -- a Compiler for Nim. https://nim-lang.org/
|
||||||
|
Copyright (C) 2006-2023 Andreas Rumpf. All rights reserved.
|
||||||
|
Licensed under terms of the MIT License
|
57
README.md
57
README.md
|
@ -1,3 +1,58 @@
|
||||||
# pathsd
|
# pathsd
|
||||||
|
|
||||||
This repository contains the *pathsd* project.
|
*pathsd* is a small CLI utility to manage the `PATH` enironment variable.
|
||||||
|
|
||||||
|
## Building and installing
|
||||||
|
|
||||||
|
*pathsd* is written in Nim programming language. To build it, you need to
|
||||||
|
install the compiler. Consult
|
||||||
|
[nim documentation](https://nim-lang.org/install.html) for instructions on
|
||||||
|
installing nim on your OS. The required version is 2.0.0 or newer.
|
||||||
|
|
||||||
|
To build *pathsd*, issue the following command:
|
||||||
|
|
||||||
|
```
|
||||||
|
$ make
|
||||||
|
```
|
||||||
|
|
||||||
|
The built binary will be placed in `build/pathsd`. To install, copy it
|
||||||
|
somewhere.
|
||||||
|
|
||||||
|
## Usage
|
||||||
|
|
||||||
|
*pathsd* requires at least one directory of _parts_ to operate on, e.g.
|
||||||
|
|
||||||
|
```
|
||||||
|
paths.d/
|
||||||
|
├── 01-bilbo
|
||||||
|
└── 02-homebrew
|
||||||
|
```
|
||||||
|
|
||||||
|
Each of the files should have one or more lines, each line being a single
|
||||||
|
entry in the rendered `PATH` variable.
|
||||||
|
|
||||||
|
Running the program with such a directory would yield the following result:
|
||||||
|
|
||||||
|
```
|
||||||
|
$ pathsd paths.d/
|
||||||
|
export PATH="/Users/bilbo/opt/bin:/opt/homebrew/bin:/opt/homebrew/sbin":$PATH
|
||||||
|
```
|
||||||
|
|
||||||
|
You can specify multiple directories. They'll be processed one by one in the
|
||||||
|
order specified on the command line.
|
||||||
|
|
||||||
|
The program is best used in your shell's startup file, e.g.
|
||||||
|
|
||||||
|
```bash
|
||||||
|
eval $(pathsd paths.d/)
|
||||||
|
```
|
||||||
|
|
||||||
|
At the time of writing, only `bash` is supported.
|
||||||
|
|
||||||
|
## Author
|
||||||
|
|
||||||
|
*pathsd* is developed by [Tomek Wójcik](https://www.bthlabs.pl/)
|
||||||
|
|
||||||
|
## License
|
||||||
|
|
||||||
|
*pathsd* is licensed under the MIT License.
|
||||||
|
|
1
freebsd/LICENSE
Normal file
1
freebsd/LICENSE
Normal file
|
@ -0,0 +1 @@
|
||||||
|
This package has a single license: MIT (MIT license / X11 license).
|
5
freebsd/catalog.mk
Normal file
5
freebsd/catalog.mk
Normal file
|
@ -0,0 +1,5 @@
|
||||||
|
_LICENSE=MIT
|
||||||
|
_LICENSE_NAME=MIT license / X11 license
|
||||||
|
_LICENSE_PERMS=dist-mirror dist-sell pkg-mirror pkg-sell auto-accept
|
||||||
|
_LICENSE_GROUPS=COPYFREE FSF GPL OSI
|
||||||
|
_LICENSE_DISTFILES=pathsd-1.0.0.tar.gz
|
13
freebsd/manifest.in
Normal file
13
freebsd/manifest.in
Normal file
|
@ -0,0 +1,13 @@
|
||||||
|
name: pathsd
|
||||||
|
version: %%VERSION%%
|
||||||
|
origin: bthlabs/infra
|
||||||
|
comment: BTHLabs pathsd
|
||||||
|
www: https://www.bthlabs.pl/
|
||||||
|
maintainer: contact@bthlabs.pl
|
||||||
|
prefix: %%PREFIX%%
|
||||||
|
flatsize: %%FLATSIZE%%
|
||||||
|
desc: <<EOD
|
||||||
|
BTHLabs pathsd
|
||||||
|
EOD
|
||||||
|
deps: {
|
||||||
|
}
|
5
nimble.lock
Normal file
5
nimble.lock
Normal file
|
@ -0,0 +1,5 @@
|
||||||
|
{
|
||||||
|
"version": 2,
|
||||||
|
"packages": {},
|
||||||
|
"tasks": {}
|
||||||
|
}
|
16
pathsd.nimble
Normal file
16
pathsd.nimble
Normal file
|
@ -0,0 +1,16 @@
|
||||||
|
# Package
|
||||||
|
|
||||||
|
version = "1.0.1"
|
||||||
|
author = "Tomek Wójcik <contact@bthlabs.pl>"
|
||||||
|
description = "pathsd by BTHLabs"
|
||||||
|
license = "MIT"
|
||||||
|
|
||||||
|
binDir = "build"
|
||||||
|
srcDir = "src"
|
||||||
|
|
||||||
|
bin = @["pathsd"]
|
||||||
|
|
||||||
|
# Dependencies
|
||||||
|
|
||||||
|
requires "nim >= 2.0.0"
|
||||||
|
# requires "commandant 0.15.1"
|
120
src/pathsd.nim
Normal file
120
src/pathsd.nim
Normal file
|
@ -0,0 +1,120 @@
|
||||||
|
import std/[algorithm, dirs, logging, options, paths, strutils, syncio]
|
||||||
|
import strformat
|
||||||
|
|
||||||
|
import commandant
|
||||||
|
|
||||||
|
type
|
||||||
|
ErrorCode = typeof(QuitSuccess)
|
||||||
|
MainResult* = object
|
||||||
|
output*: seq[string]
|
||||||
|
errorCode*: ErrorCode
|
||||||
|
|
||||||
|
const version = "1.0.1"
|
||||||
|
const usage_string = """usage: pathsd [--version | --help] [-v | -q] [-s shell] search_path ..."""
|
||||||
|
const version_string = fmt"""pathsd {version}"""
|
||||||
|
const help_string = fmt"""pathsd {version} by BTHLabs
|
||||||
|
Developed by Tomek Wójcik <contact@btlabs.pl> (https://bthlabs.pl/)
|
||||||
|
(c) 2023-present by BTHLabs | MIT License
|
||||||
|
Usage:
|
||||||
|
pathsd [options] search_path ...
|
||||||
|
Options:
|
||||||
|
-s --shell target shell (defaults to bash)
|
||||||
|
-v --verbose turn debugging messages on
|
||||||
|
-q --quiet supress all logging messages
|
||||||
|
--version show the version
|
||||||
|
--help show this help"""
|
||||||
|
|
||||||
|
const QuitInvalidArgs: ErrorCode = 64
|
||||||
|
|
||||||
|
var logger*: ConsoleLogger = newConsoleLogger(
|
||||||
|
fmtStr = "$datetime $appname $levelname: ",
|
||||||
|
levelThreshold = lvlInfo,
|
||||||
|
useStderr = true,
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
proc handleCommandantError(reason: ExitReason,
|
||||||
|
msg: string = "",
|
||||||
|
token: Option[CmdToken] = none(CmdToken),
|
||||||
|
) =
|
||||||
|
case reason
|
||||||
|
of ExitReason.missingArgumentValue, ExitReason.missingOptionValue:
|
||||||
|
quit(usage_string, QuitInvalidArgs)
|
||||||
|
of ExitReason.exception:
|
||||||
|
logger.log(lvlError, msg)
|
||||||
|
quit(QuitFailure)
|
||||||
|
|
||||||
|
proc readPart(path: Path): seq[string] =
|
||||||
|
logger.log(lvlDebug, fmt"Processing part: {path.string}")
|
||||||
|
var pathFile: File = open(path.string)
|
||||||
|
|
||||||
|
result = @[]
|
||||||
|
try:
|
||||||
|
var line: string = readLine(pathFile)
|
||||||
|
while line != "":
|
||||||
|
result.add(line)
|
||||||
|
line = readLine(pathFile)
|
||||||
|
except EOFError as exception:
|
||||||
|
discard
|
||||||
|
finally:
|
||||||
|
close(pathFile)
|
||||||
|
|
||||||
|
return result
|
||||||
|
|
||||||
|
|
||||||
|
proc main*(searchPaths: seq[string]): MainResult =
|
||||||
|
var parts: seq[string] = @[]
|
||||||
|
|
||||||
|
logger.log(lvlDebug, fmt"Processing search paths: {searchPaths}")
|
||||||
|
for searchPath in searchPaths:
|
||||||
|
var searchPathPath: Path = Path(searchPath)
|
||||||
|
if not dirExists(searchPathPath):
|
||||||
|
logger.log(lvlNotice, fmt"Skipping {searchPath}: does not exist?")
|
||||||
|
continue
|
||||||
|
|
||||||
|
var partPaths: seq[Path] = @[]
|
||||||
|
for pathComponent, path in walkDir(searchPathPath, true, false, true):
|
||||||
|
if pathComponent in [PathComponent.pcFile, PathComponent.pcLinkToFile]:
|
||||||
|
partPaths.add(searchPathPath / path)
|
||||||
|
|
||||||
|
partPaths.sort do (x: Path, y: Path) -> int:
|
||||||
|
result = cmp(x.string, y.string)
|
||||||
|
|
||||||
|
for partPath in partPaths:
|
||||||
|
parts = parts & readPart(partPath)
|
||||||
|
|
||||||
|
return MainResult(output: parts, errorCode: QuitSuccess)
|
||||||
|
|
||||||
|
|
||||||
|
when isMainModule:
|
||||||
|
commandline:
|
||||||
|
arguments(searchPaths, string, true)
|
||||||
|
commandant.option(shell, string, "shell", "s", "bash")
|
||||||
|
flag(verbose, "verbose", "v")
|
||||||
|
flag(quiet, "quiet", "q")
|
||||||
|
exitoption("help", "h", help_string, QuitFailure)
|
||||||
|
exitoption("version", "", version_string, QuitFailure)
|
||||||
|
errorproc(handleCommandantError)
|
||||||
|
|
||||||
|
var loggerLevel: Level = lvlInfo
|
||||||
|
if quiet:
|
||||||
|
loggerLevel = lvlNone
|
||||||
|
elif verbose:
|
||||||
|
loggerLevel = lvlDebug
|
||||||
|
|
||||||
|
logger.levelThreshold = loggerLevel
|
||||||
|
|
||||||
|
var mainResult = main(searchPaths)
|
||||||
|
|
||||||
|
if mainResult.output != @[] and mainResult.errorCode == QuitSuccess:
|
||||||
|
var output: string = ""
|
||||||
|
|
||||||
|
case shell
|
||||||
|
of "bash":
|
||||||
|
let pathComponents = join(mainResult.output, ":")
|
||||||
|
output = fmt"""export PATH="{pathComponents}":$PATH"""
|
||||||
|
|
||||||
|
if output != "":
|
||||||
|
echo(output)
|
||||||
|
|
||||||
|
quit(mainResult.errorCode)
|
1
submodules/commandant
Submodule
1
submodules/commandant
Submodule
|
@ -0,0 +1 @@
|
||||||
|
Subproject commit 0a2094c7b93d1e2385856e2f499eebc75c2f4af6
|
1
tests/fixtures/paths.d/01-bilbo
vendored
Normal file
1
tests/fixtures/paths.d/01-bilbo
vendored
Normal file
|
@ -0,0 +1 @@
|
||||||
|
/Users/bilbo/opt/bin
|
2
tests/fixtures/paths.d/02-homebrew
vendored
Normal file
2
tests/fixtures/paths.d/02-homebrew
vendored
Normal file
|
@ -0,0 +1,2 @@
|
||||||
|
/opt/homebrew/bin
|
||||||
|
/opt/homebrew/sbin
|
30
tests/pathsd/test_main.nim
Normal file
30
tests/pathsd/test_main.nim
Normal file
|
@ -0,0 +1,30 @@
|
||||||
|
import std/[logging, paths]
|
||||||
|
import unittest
|
||||||
|
|
||||||
|
import ../../src/pathsd
|
||||||
|
|
||||||
|
suite "Test main() proc":
|
||||||
|
setup:
|
||||||
|
const fixturesPath = Path(currentSourcePath) / Path("..") / Path("..") / Path("fixtures")
|
||||||
|
const pathsdFixturePath = fixturesPath / Path("paths.d")
|
||||||
|
const expectedOutput = @["/Users/bilbo/opt/bin", "/opt/homebrew/bin", "/opt/homebrew/sbin"]
|
||||||
|
|
||||||
|
pathsd.logger.levelThreshold = lvlNone
|
||||||
|
|
||||||
|
test "Test happy path":
|
||||||
|
# When
|
||||||
|
var mainResult = pathsd.main(@[pathsdFixturePath.string])
|
||||||
|
|
||||||
|
# Then
|
||||||
|
check mainResult.output == expectedOutput
|
||||||
|
check mainResult.errorCode == QuitSuccess
|
||||||
|
|
||||||
|
test "Test skipping search paths that don't exist":
|
||||||
|
# When
|
||||||
|
var mainResult = pathsd.main(@[
|
||||||
|
pathsdFixturePath.string,
|
||||||
|
(fixturesPath / Path("idontexist")).string,
|
||||||
|
])
|
||||||
|
|
||||||
|
# Then
|
||||||
|
check mainResult.output == expectedOutput
|
1
tests/tester.nim
Normal file
1
tests/tester.nim
Normal file
|
@ -0,0 +1 @@
|
||||||
|
import pathsd/test_main
|
Loading…
Reference in New Issue
Block a user