├── LICENSE ├── README.md ├── rr ├── rr.bat ├── rr_impl.bat └── rrs.bat /LICENSE: -------------------------------------------------------------------------------- 1 | This is free and unencumbered software released into the public domain. 2 | 3 | Anyone is free to copy, modify, publish, use, compile, sell, or 4 | distribute this software, either in source code form or as a compiled 5 | binary, for any purpose, commercial or non-commercial, and by any 6 | means. 7 | 8 | In jurisdictions that recognize copyright laws, the author or authors 9 | of this software dedicate any and all copyright interest in the 10 | software to the public domain. We make this dedication for the benefit 11 | of the public at large and to the detriment of our heirs and 12 | successors. We intend this dedication to be an overt act of 13 | relinquishment in perpetuity of all present and future rights to this 14 | software under copyright law. 15 | 16 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 17 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 18 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. 19 | IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR 20 | OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, 21 | ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR 22 | OTHER DEALINGS IN THE SOFTWARE. 23 | 24 | For more information, please refer to 25 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # RootResolve 2 | 3 | **NOTE**: Since October 2023, [direnv](https://direnv.net/) has [introduced support](https://github.com/direnv/direnv/pull/1171) for Powershell! 4 | 5 | This is for repositories that store their tools side-by-side with their source code, for reasons including: 6 | 7 | * New clients are trivial to provision with no external install steps required. 8 | * Accurate repositories for build can be reproduced for any point in time. 9 | * Source updates can be atomically coupled to tool updates. 10 | * You only have to use one source control technology and not worry about keeping two in sync. 11 | 12 | In the presence of multiple branches it can become tricky to locate these tools when you move around the repository tree. 13 | Hard-coding your `PATH` requires constantly switching it when you switch between branches or move the repo around your HDs. 14 | 15 | The *no-tool* solution is to store `.bat` files alongside your scripts/data that use hard-coded relative paths to the tools. 16 | While this works really well on small projects it gets unwieldy as the number of call sites grows. 17 | 18 | Popular solutions like [direnv](https://direnv.net/) and [autoenv](https://github.com/kennethreitz/autoenv) codify this with 19 | shell-specific hooks that switch between multiple environment profiles on the fly. They work transparently but are complicated, 20 | requiring back-ends for each shell type and with no native Windows `cmd.exe` support. 21 | 22 | RootResolve is much simpler and requires more typing but it's a suitable low-tech investment that will work anywhere. 23 | 24 | Assuming a layout similar to: 25 | 26 | ``` 27 | repo 28 | source 29 | python 30 | script.py 31 | extern 32 | Python 33 | 3.5.1 34 | python.exe 35 | Terraform 36 | 0.11.4 37 | terraform.exe 38 | Putty 39 | putty.exe 40 | ``` 41 | 42 | You can place a `rr.cfg` file in your root at `repo\rr.cfg` and add: 43 | 44 | ``` 45 | extern\Python\3.5.1 46 | extern\Terraform\0.11.4 47 | extern\Putty 48 | ``` 49 | 50 | Now, from wherever you are in the `repo` tree you can prefix all commands with `rr` to quickly locate the necessary command: 51 | 52 | ``` 53 | rr python script.py 54 | ``` 55 | 56 | Usage is generally: 57 | 58 | ``` 59 | rr [options] 60 | ``` 61 | -------------------------------------------------------------------------------- /rr: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | # Isolate the command name 4 | CommandName=$1 5 | if [ -z "$CommandName" ]; then 6 | echo Root Resolve 7 | echo Usage: rr \ [options] 8 | exit 1 9 | fi 10 | 11 | # Shift the command name out of the options list for forwarding 12 | shift 13 | 14 | # Start in the current directory and search parent folders for rr.cfg 15 | ConfigPath=`pwd` 16 | while [[ $ConfigPath != / ]]; 17 | do 18 | ConfigFilename=$ConfigPath/rr.cfg 19 | if [ -f "$ConfigFilename" ]; then 20 | break 21 | fi 22 | ConfigPath="$(readlink -f "$ConfigPath"/..)" 23 | done 24 | 25 | # Locate the root config file 26 | if ! [ -f "$ConfigFilename" ]; then 27 | echo ERROR: Can\'t find \'rr.cfg\' 28 | exit 1 29 | fi 30 | 31 | # Parse config file potential command paths 32 | while read -r line; do 33 | 34 | # Swap \ with /, remove newline and append to the root path 35 | line="${line//\\/\/}" 36 | line=`echo $line | tr -d '\r'` 37 | CommandPath=$ConfigPath/$line 38 | 39 | # Combine tentative command path, command name and all possible extensions to locate the command 40 | Extensions=" .sh .bat .cmd .exe" 41 | for extension in $Extensions; do 42 | Command=$CommandPath/$CommandName$extension 43 | if [ -f "$Command" ]; then 44 | $Command "$@" 45 | exit $? 46 | fi 47 | done 48 | 49 | done <$ConfigFilename 50 | 51 | echo Command \'$CommandName\' not found. 52 | -------------------------------------------------------------------------------- /rr.bat: -------------------------------------------------------------------------------- 1 | @call rr_impl.bat %* -------------------------------------------------------------------------------- /rr_impl.bat: -------------------------------------------------------------------------------- 1 | @echo off 2 | 3 | setlocal 4 | setlocal enabledelayedexpansion 5 | 6 | :: We use the for-loop to tokenize the args in a way to preserve the 7 | :: string contents of the args. 'Standard' arg syntax will remove 8 | :: certain delimiter characters inside args (e.g. '='). 9 | for /f "tokens=1* delims= " %%A in ("%*") do ( 10 | set COMMAND=%%A 11 | set OPTIONS=%%B 12 | ) 13 | 14 | :: Check for missing args 15 | if not defined COMMAND ( 16 | echo Root Resolve 17 | echo Usage: rr ^ [options] 18 | exit /b 1 19 | ) 20 | 21 | :: Locate the root file 22 | call :FindRootFilePath 23 | if not defined ROOT_FILE_PATH ( 24 | echo ERROR: Can't find `rr.cfg` 25 | exit /b 1 26 | ) 27 | 28 | :: Directory for the root file 29 | for %%a in (%ROOT_FILE_PATH%) do set ROOT_FILE_DIR=%%~dpa 30 | 31 | :: List of executable extensions 32 | :: First entry empty to allow for explicit extension specification 33 | set EXTENSIONS[0]= 34 | set EXTENSIONS[1]=.bat 35 | set EXTENSIONS[2]=.cmd 36 | set EXTENSIONS[3]=.exe 37 | 38 | :: Parse the root file looking for paths 39 | for /f %%a in (%ROOT_FILE_PATH%) do ( 40 | set COMMAND_DIR=%ROOT_FILE_DIR%%%a 41 | 42 | :: Search for a valid path/command/extension mix 43 | for /f "tokens=2 delims==" %%s in ('set EXTENSIONS[') do ( 44 | set COMMAND_PATH=!COMMAND_DIR!\%COMMAND%%%s 45 | if exist !COMMAND_PATH! ( 46 | 47 | :: Launch and return the error code 48 | if defined USE_START ( 49 | start !COMMAND_PATH! %OPTIONS% 50 | ) else ( 51 | call !COMMAND_PATH! %OPTIONS% 52 | ) 53 | exit /b %ERRORLEVEL% 54 | ) 55 | ) 56 | ) 57 | 58 | echo Command %COMMAND% not found 59 | exit /b 1 60 | 61 | :FindRootFilePath 62 | 63 | :: Walk up parent directories lookup for root file 64 | set SEARCH_PATH=%cd% 65 | :WhileTrue 66 | 67 | :: Exit search at drive root 68 | if %SEARCH_PATH:~0,3% == %SEARCH_PATH% ( 69 | set ROOT_FILE_PATH= 70 | exit /b 71 | ) 72 | 73 | :: Check to see if the root file can be found at this level 74 | set ROOT_FILE_PATH=%SEARCH_PATH%\rr.cfg 75 | if exist %ROOT_FILE_PATH% ( 76 | exit /b 77 | ) 78 | 79 | :: Hack to get drive and directory 80 | for %%a in (%SEARCH_PATH%) do set SEARCH_PATH=%%~dpa 81 | 82 | :: Remove any trailing slashes 83 | if %SEARCH_PATH:~-1%==\ SET SEARCH_PATH=%SEARCH_PATH:~0,-1% 84 | 85 | goto :WhileTrue 86 | 87 | exit /b 88 | 89 | -------------------------------------------------------------------------------- /rrs.bat: -------------------------------------------------------------------------------- 1 | @setlocal 2 | @set USE_START=1 3 | @rr_impl.bat %* --------------------------------------------------------------------------------