Package: pytaskrunner
What is this?Taskrunner (henceforth, TR) allows users to run tasks with relation to the current working directory. Somewhat similar to VScode tasks, TR tasks are not bound to a specific platform and can be invoked from anywhere. A trivial environment for TR is a shell console, but any environment with ability to run executables is suitable.One can think of TR is as a commands repository for a directory or a way to enable dynamic PATH, where commands becomes available and unavailable according to the working directory.TR was written as a successor torunevn, a tool I wrote for running containers commands in similar manner which was extremely popular between myself and a guy I knew from work.runenvis great for sparing the user from remembering container settings for a given command, but is limited for a single task, isn't built for tasks that aren't container based and has limited features.Why do I need it?Assume working on a project in a directoryproject-dir, where a long and complicated commands like:my-command -vv --with-this-flags --and-this-flag=5 THIS_ARG AND_THIS_ARGare often required. Remembering and repeating it can be be annoying and time consuming.One could rely on history, but commands history browsing can be as annoying as typing it. Besides, after a long period of time, the required entry it might not even be there, and if another user needs it, he need to figure it out by his own.Using shell script is a possible solution - place the logic in a script likemy-task.shand run it instead. This would work, of course, but now we need to remembermy-task.shand where it resides. Unless placed in the search PATH, it's not just a matter of of runningmy-taskbutpath/to/my-task. And again, if a another user needs to run this command by herself, she has to have knowledge of this script in advance.An alias approach can also be tried: Aliasmy-command-aliasto our command. Again, this works but suffers from drawbacks: you are either bound to have themy-commandavailable in PATH or have the entire project pinned to a given directory. In addition, aliases are user level entities: A different user or placing the project in a different machine will require redefining the alias for that login. And once again - we need to know and remember what aliases are relevant - there's no way to differentiate between project and global aliases.The situation might get more complicated if other commands are needed as well - with other arguments, other working directories, maybe some environment variables, etc.With TR, all these problems are solved. All that is required is a project level configuration file defining a task namedmy-taskto run the horriblemy-command...and the command becomes available all over the project hierarchy, to all users with access to the project.task listcan be used to see what tasks are available andtask infoto understand what they are doing. As long as TR is installed, moving the project to another machine will provide all the tasks with it.Installation and requirementsTR is python based tool, verified for python 3.7 and above. Older versions might work as well, but are not actively supported and packaged so manuall install is required.To install runpip3 install pytaskrunner.For installation from source:Clone the repositoryInstall the requirements using pippip3 install -r requirements.txtMake sure the executable filetaskis accessible from to the execution search path (i.e. $PATH and so)TR should work for any environment with compatible python, however it is developed on and maintained for Linux machines. CI tests are ran for MacOS as well without container support. It is known to work on Cygwin systems (again, without containers support).Basic configuration and usageTR uses json configuration files (By default, named.tasks.json) to describe tasks. These tasks are available when the current work directory is anywhere in the directory hierarchy under the directory the configuration file was placed in - TR recursively looks for.tasks.jsonfrom the current working directory up to root.Once set, use the following commands:Running tasks - by runningtask run <TASK>Getting info on a task - by runningtask info <TASK>Listing available tasks  - by runningtask listAs implied, configuration files are json files. Each task is a json object with a name and a list of commands to execute.Example - building projects[source code projects are used in the following examples, but any sort of task is valid -scpsome files, start a service throughsystemctl, open a video withvlc, etc.]:Assume acmakebased project in a directory namedcmake-projectwith lots of sub directories. It might look something like this:user@host $ tree cmake-project
.
├── build
├── subdir0
├── subdir1
│   └── subdir3
│       └── subdir4
└── subdir5
    └── subdir6Often running basiccmakecommands in such environments requires:Entering the project'sbuilddirectoryRunningcmake ..Not very complicated, but can get tedious when actively developing cmake files all over the tree. By adding the following.tasks.jsonfile intocmake-projectdirectory, rerunning the relevant cmake command becomes trivial one-liner:{"tasks":{"cmake":{"short_desc":"Run cmake","description":"Run 'cmake ..' for the cmake-project","commands":["cmake .."],"cwd":"{{taskRoot}}/build"}}}Once set, runningtask run cmakefromanywhereundercmake-projectwill run thecmake ..withcmake-project/buildas the working directory.In the tasks section our task, namedcmakeis defined:short_descanddescriptionare for documentation and self-explanatory.commandssets a list of commands for this task to execute. In this case, a singlecmake ..command is set.cwdsets the working directory for this task. Its commands are executed with this setting value as their working directory. The{{taskRoot}}is an automatic variable with the value of the location of the configuration file. Using it incwdallows moving the project around without the need to update it. In our case, if the configuration file is placed incmake-projectroot, say,/home/user/projects/cmake-project, the expanded value is/home/user/projects/cmake-project/build.To list the available tasks, runtask list:user@host $ task list
Name                    Flags Description
----                    ----- -----------
cmake                         Run cmakeAnd for the task full details, runtask info cmake, an a-xflag values expansion; in our case, the working directory:user@host $ task info -x cmake
Task name:              cmake
Short description:      Run cmake
Description:            Run 'cmake ..' for the cmake-project
Hidden                  No
Use shell:              No
Working directory:      /home/user/projects/cmake-project/build
Command:                cmake ..Now lets assume there's a need to occasionally run another task, similar to the one already defined. For example, with somecmakedefinition like-DSOMEVAR=somevalue. And another task that actually builds the project withcmake --build .. -j8, both, like the first task, need to be invoked from thebuilddirectory (Again, if you don't care aboutcmakeand source building, don't worry about what each command does. Anything can be used here). Instead of remembering each command or placing these in shell scripts and then trying to remember wheretheyare, using TR tasks simplify the workflow:{"tasks":{"cmake":{"short_desc":"Run cmake","description":"Run 'cmake ..' for the cmake-project","commands":["cmake .."],"cwd":"{{taskRoot}}/build"},"cmake-with-def":{"short_desc":"Run cmake with cmake define flag","description":"Run 'cmake ..' for the cmake-project, with a cmake define flag","commands":["cmake -DSOMEVAR=somevalue .."],"cwd":"{{taskRoot}}/build"},"build":{"short_desc":"Build the project","description":"Build the project using cmake's build command","commands":["cmake --build .."],"cwd":"{{taskRoot}}/build"}}}And now all commands are available, from any directory undercmake-project:user@host $ task list
Name                    Flags Description
----                    ----- -----------
cmake                         Run cmake
cmake-with-def                Run cmake with cmake define flag
build                         Build the project

user@host $ task info build
Task name:              build
Short description:      Build the project
Description:            Build the project using cmake's build command
Hidden:                 No
Abstract:               No
Use shell:              No
Working directory:      {{taskRoot}}/build
Command:                cmake --build ..

user@host $ task run build
...The above example can be further simplified by usingvariablesandinheritance.Multiple projects - same semantics, different detailsExtending the example above, assume multiple projects, each with its own way of doing things. Let's say (based on true events), that other than the notoriouscmake-projectwe have 2 additional projects namedmake-projectandscons-project(andninja-projectandmesonproject etc...).user@host $ tree projects
projects
├── cmake-project
├── make-project
└── scons-projectAs these new projects name imply, they are built aroundmakeandsconsbuild systems so their build process them is different than our oldcmake-projectyet still we want to 'build' each one. We can try to remember each build command by our own and make sure we are in the correct project, or we can use TR: After adding a tasks file on each of these projects root directories each with its ownbuildtask, (like thebuildtask exampled forcmake-project) the user can just runtask run buildinany of these projects, and the correct build command will be invoked. If her environment is consistent, she might add an alias like (Bash style)alias build='task run build'for even easier build - and whenever inside any of the source projects directories, runningbuildinvokes the correct command for the current project.Advanced usageThis section will describe some of advanced configuration and usage topics. For a detailed list of all configuration settings, refer to theconfiguration documentationVariablesTR allows definitions of variables to be used through out the configuration.Variables are evaluated as strings.A variableVARcan be referred as{{VAR}}Variables can be used in the following task properties: commands, environment variables, current working directory, container image, container working directory and container volumes.A variable can be defined using other variablesThere are 3 types of variables with different priorities:Global variables - Defined in global level usingvariablesas key word. They are available for every task with the scope of the tasks file.Task variables - Defined in task definitions level usingvariablesas key word. They are available for the task they are defined in and those inherit from it. Inherited variables are overridden by the inheriting task variables. All task variables override global variables with the same name.System variables - set by TR and will override any variables with the same name. The following variables are defined:cwd- for the current working directorytaskRoot- for the path of the directory the found configuration file was found. This is helpful when a task needs to refer to a directory relatively to the project (see previous examples for its usage).cliArgs- refers for arguments given by the user through CLI. See section aboutpassing command CLI argumentsTR auto variables override any user variable, so avoid setting variables with reserved names.The following example show the different types of variables definition and usage.{"variables":{"ENV_NAME":"global_env_name","ENV_VALUE":"global_env_value","var0":"global_var0_value","var1":"global_var1_value"},"tasks":{"task0":{"variables":{"var1":"task0_var1_value","var2":"task0_var2_value"},"env":{"{{ENV_NAME}}":"{{ENV_VALUE}}"},"commands":["printenv {{ENV_NAME}}","echo {{var0}}","echo {{var1}}","echo {{var2}}"]},"task1":{"base":"task0","variables":{"ENV_VALUE":"task1_env_value","var2":"task1_var2_value"},"env":{"{{ENV_NAME}}":"{{ENV_VALUE}}"}}}}In the above example, a few global vars are defined.task0overrides the globalvar1and sets its ownvar2.task1overrides the inheritedtask0var2. Note the environment variable setting using the TR variables.
See theinheritance sectionfor more deatils about task inheritance.Environment variablesEnvironment variables can be referred with{{$VAR}}, whereVARis the variable name. Environment variables can be used whereever TR variables can be used. Note the difference between environment variables used in the task setting and environment variables that are set for a given task using theenvkeyword.When a task is invoked its commands are invoked with the current set of system environment variables merged with task specific variables if such exist either from the task configuration or from CLI. Task variables take precedence over system variables. This behavior can be modified by setting the task'senv_inherit. Set tofalse, TR ignores the system environment variables, and passes the task commands only the task environment variables. By defaultenv_inheritis set totrue.Container based tasksTR includes special support for running tasks inside a container. The main container setting isc_imagedefining a container image to use. The following task runsmakeinside a container with a volume, CWD set, tty allocated, interactive mode, wrapped in a/usr/bin/sh -c:"build":{"short_desc":"Build","c_image":"localhost/media-builder:latest","c_volumes":["{cwd}:{cwd}"],"c_tty":true,"c_interactive":true,"c_shell":true,"c_cwd":"{{taskRoot}}""commands":["ls -l"],},The actual command the task invokes is/usr/bin/docker -i -t --rm -w {{taskRoot}} -v {{cwd}}:{{cwd}} localhost/media-builder:latest /usr/bin/sh -c "ls -l".Container support is synthetic sugar, and putting the complete container command as acommandsentry instead of setting a task container settings is completely valid and equivalent for having TR assemble the command instead, but usually it's much easier to let TR handle these settings.For a detailed list of all container configuration settings, refer to theconfiguration documentation.InheritanceA task might inherit another task settings by using thebasesettings. If taskainherits taskb, all ofb's settings are inherited. Setting redefined in taskawill override inherited setting. The following example demonstrates how to utilize task inheritance for creating multiple tasks with similar characteristics that differ in a small details (working directory):{"tasks":{"base-task":{"abstract":true,"commands":["very_complicated command --with --lots=of --flags and arguments"],"env":{"and":"some","environment":"variables"}},"task-1":{"base":"base-task","cwd":"/opt/task-1_dir"},"task-2":{"base":"base-task","cwd":"/opt/task-2_dir"}}}Configuration files inclusionEvery configuration file can include multiple files using a globalincludesetting. Global settings, variables and tasks are all included and overridden if exist in a following include file, and finally in the original configuration file.A special file located in${HOME}/.config/tasks.jsonis always included if it exists. This behavior can be disabled by settinguse_default_includetofalse.Passing command line arguments to commandsCommand line arguments are transferred to a task run with the--convention: text written after the 'dash dash' token is transferred as an arguments. The arguments aren't passed to the commands automatically. In order for a command to use CLI arguments, it must be explicitly use it with the{{cliArgs}}variable. The allows fine grain control of which commands and where inside the command the CLI arguments are used. In fact, since arguments are translates to a TR variable,{{cliArgs}}can be used in every setting with variables support.
Runningtask run ls -- -l somefile.txtin the following task will runls -l somefile.txt:"ls":{"commands":["ls {{cliArgs}}"],"short_desc":"Clean build"}TR CLI optionsTR allows overriding almost any configuration setting using the CLI. In general these should be avoided unless debugging or tweaking a task temporarily. For example, if one wants to run a task but just tweak the command it runs (retaining other settings like container settings, environment variable, etc.), runningtask run -c "my new command" <TASK>will do the trick.Runtask -handtask <CMD> -hfor the full list of CLI options.Bash completionTR usesargcompletefor bash auto complete. Seeargcompletedocumentation for more details.LoggingTR Logging can be enabled with the--log_file <FILE>CLI option. Use-vto increase its verbosity.StatusWhile tested and works, TR is still a work in progress. Breaking changes are expected. Version and schema validations will issue error messages if incompatibility is detected.