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