CLI tool to run commands defined in a config file
3
votes
0
answers
418
views
I'm working as part of a team of developers on a software project with many moving parts. We use a Makefile to easily run commands and scripts that are regularly needed during development. Examples might be:
- Set up a Python virtualenv.
- Intialize the local development database.
- Run a linter.
- etc.
Most of these commands don't need to be customizable using additional arguments, so Makefile targets work perfectly:
.PHONY: venv
venv:
python3 -m venv venv
venv/bin/pip install -r requirements.txt
$ make venv
But it gets really cumbersome if additional arguments need to be passed to a command from time to time. Two straight-forwared ways are:
- Pass the aditional arguments via a variable, e.g. make foo FOO_ARGS="lalala"
. Cumbersome and messy if arguments contain spaces.
- Find the target in the Makefile, copy it to the shell prompt and edit it.
### Idea for a solution
I came up with a really simple concept of what I actually need: A command line tool, e.g. called run
, that looks for an executable file called runfile
in the current directory and its parents and executes that file, passing any command line arguments to that file. Implemented in Python it could look like this:
#! /usr/bin/env python3
import sys, os, subprocess, pathlib
cwd = pathlib.Path().resolve()
# Go through the CWD (current working directory) and all its parents and look
# for an executable file called "runfile".
for dir in [cwd, *cwd.parents]:
run_script_path = dir / 'runfile'
if os.access(run_script_path, os.X_OK):
break
else:
print(
f'No executable runfile found in {cwd} or any of its parents.',
file=sys.stderr)
sys.exit(1)
# Run the runfile, forwarding any command line arguments to it. Use the parent
# of the runfile as CWD and pass the original CWD in the environment variable
# "RUN_PWD".
subprocess.run(
[run_script_path, *sys.argv[1:]],
cwd=dir,
env=dict(os.environ, RUN_PWD=str(cwd)))
The runfile could be using any scripting language. I could e.g. be a Bash script:
#! /usr/bin/env bash
case "$1" in
venv)
rm -rf venv
echo python3 -m venv "${@:2}" venv
;;
lint)
pycodestyle --exclude='./venv,./node_modules,./.git' "${@:2}" .
;;
*)
echo "Unknown command: $1" >&2
exit 1
;;
esac
("${@:2}"
inserts the script's arguments following the first one as separate words into a command line).
$ run venv --copies
# Would run "python3 -m venv --copies venv".
### The question
Is there already a tool like this that maybe has more features? It would be ok if the tool used its own language to define commands instead of simply executing the file.
### Additional notes
I know that some build also have support for this as secondary features, e.g. npm
allows defining [scripts
](https://docs.npmjs.com/cli/v8/using-npm/scripts) that can be invoked from the command line:
npm run foo -- args...
But this already requires two words before the name of the script and a --
before additional arguments, not as convenient as it could be.
Installing [console scripts entry points](https://setuptools.pypa.io/en/latest/userguide/entry_point.html#console-scripts) into a virtualenv and activating the virtualenv may sometimes be a solution. But in this case one of the use-cases is to set up the project's virtualenv, so that's a chicken-egg-problem.
I know that I could probably hack $(MAKECMDGOALS)
. No. 😊
Asked by Feuermurmel
(705 rep)
Feb 9, 2022, 03:28 PM
Last activity: Feb 9, 2022, 03:38 PM
Last activity: Feb 9, 2022, 03:38 PM