Initial release
This commit is contained in:
parent
804082112a
commit
d1eb53b952
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
|
||||
|
||||
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