From de6595686c81dd6e48fe32e3cd37a421dc26cab9 Mon Sep 17 00:00:00 2001 From: Omer Caspi Date: Sat, 7 Sep 2024 18:10:43 +0300 Subject: [PATCH] tr: configuration file love (yaml support) Modify the way TR handles configuration files: - Add support for yaml files - When none explicit files are used the following order of files are searched: ".tasks.yaml", "tasks.yaml", ".tasks.json", "tasks.json". Also replaces all the internal [.]tasks.json files with their yaml counterparts. Signed-off-by: Omer Caspi --- .tasks.json | 58 ------- .tasks.yaml | 41 +++++ README.md | 212 +++++++++++-------------- pyproject.toml | 3 +- requirements.txt | 1 + src/tr/config.py | 52 +++--- tests/tasks.json | 400 ----------------------------------------------- tests/tasks.yaml | 310 ++++++++++++++++++++++++++++++++++++ 8 files changed, 482 insertions(+), 595 deletions(-) delete mode 100644 .tasks.json create mode 100644 .tasks.yaml delete mode 100644 tests/tasks.json create mode 100644 tests/tasks.yaml diff --git a/.tasks.json b/.tasks.json deleted file mode 100644 index deafe32..0000000 --- a/.tasks.json +++ /dev/null @@ -1,58 +0,0 @@ -{ - "$schema": "/home/omer/devel/taskrunner/schemas/taskrunner_v0.11.json", - "tasks": { - "test": { - "short_desc": "Run test", - "commands": [ - "tests/runtests {{cliArgs}}" - ], - "cwd": "{{taskRoot}}" - }, - "build": { - "short_desc": "Build package", - "commands": [ - "rm -rf dist", - "python3 -m build" - ], - "cwd": "{{taskRoot}}" - }, - "upload-testpypi": { - "short_desc": "Build package to testpypi", - "commands": [ - "python3 -m twine upload --repository testpypi dist/*" - ], - "cwd": "{{taskRoot}}" - }, - "upload-pypi": { - "short_desc": "Build package to pypi", - "commands": [ - "python3 -m twine upload dist/*" - ], - "cwd": "{{taskRoot}}" - }, - "test_pkg_base": { - "short_desc": "Basic package test", - "commands": [ - "{{taskRoot}}/tests/run_pkg_tests" - ], - "abstract": true - }, - "test_pkg_testpypi": { - "short_desc": "Basic package test from testpypi", - "base": "test_pkg_base", - "env": { - "TEST_PYPI": "1" - } - }, - "test_pkg_pypi": { - "short_desc": "Basic package test from pypi", - "base": "test_pkg_base", - "env": { - "TEST_PYPI": "0" - } - } - }, - "default_task": "test", - "default_container_tool": "/usr/bin/podman", - "use_default_include": false -} diff --git a/.tasks.yaml b/.tasks.yaml new file mode 100644 index 0000000..3785861 --- /dev/null +++ b/.tasks.yaml @@ -0,0 +1,41 @@ +$schema: /home/omer/devel/taskrunner/schemas/taskrunner_v0.11.json +tasks: + test: + short_desc: Run test + commands: + - tests/runtests {{cliArgs}} + cwd: '{{taskRoot}}' + build: + short_desc: Build package + commands: + - rm -rf dist + - python3 -m build + cwd: '{{taskRoot}}' + upload-testpypi: + short_desc: Build package to testpypi + commands: + - python3 -m twine upload --repository testpypi dist/* + cwd: '{{taskRoot}}' + upload-pypi: + short_desc: Build package to pypi + commands: + - python3 -m twine upload dist/* + cwd: '{{taskRoot}}' + test_pkg_base: + short_desc: Basic package test + commands: + - '{{taskRoot}}/tests/run_pkg_tests' + abstract: true + test_pkg_testpypi: + short_desc: Basic package test from testpypi + base: test_pkg_base + env: + TEST_PYPI: "1" + test_pkg_pypi: + short_desc: Basic package test from pypi + base: test_pkg_base + env: + TEST_PYPI: "0" +default_task: test +default_container_tool: /usr/bin/podman +use_default_include: false diff --git a/README.md b/README.md index afd8585..3705a9c 100644 --- a/README.md +++ b/README.md @@ -33,7 +33,7 @@ For installation from source: 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 usage -TR 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.json` from the current working directory up to root. +TR uses yaml or json configuration files (By default, named `.tasks.yaml`) 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.yaml` from the current working directory up to root. Once set, use the following commands: @@ -41,7 +41,13 @@ Once set, use the following commands: 2. Getting info on a task - by running `task info ` 3. Listing available tasks - by running `task list` -As implied, configuration files are json files. Each task is a json object with a name and a list of commands to execute. +## Configuration file search order +TR searches for configuration files in the following order: +1. The current working directory, and recursively up to the root directory. +2. The user's home directory, in the `.config` directory. +In any of these directories, the first configuration file found is used. TR searchs for the following files (in order): `.tasks.yaml`, `.tasks.yml`, `.tasks.json` and `tasks.json`. + +Configuration files may include other files, as well as the default configuration file located in the user's home directory. By default, the default configuration file is not included. This behaviour can be disabled by setting `use_default_include` to `true`. ## Example - building projects [_source code projects are used in the following examples, but any sort of task is valid - `scp` some files, start a service through `systemctl`, open a video with `vlc`, etc._]: @@ -65,18 +71,15 @@ Often running basic `cmake` commands in such environments requires: 1. Entering the project's `build` directory 2. Running `cmake ..` -Not very complicated, but can get tedious when actively developing cmake files all over the tree. By adding the following `.tasks.json` file into `cmake-project` directory, rerunning the relevant cmake command becomes trivial one-liner: -```json -{ - "tasks": { - "cmake": { - "short_desc": "Run cmake", - "description": "Run 'cmake ..' for the cmake-project", - "commands": ["cmake .."], - "cwd": "{{taskRoot}}/build" - } - } -} +Not very complicated, but can get tedious when actively developing cmake files all over the tree. By adding the following `.tasks.yaml` file into `cmake-project` directory, rerunning the relevant cmake command becomes trivial one-liner: +```yaml +tasks: + cmake: + short_desc: Run cmake + description: Run 'cmake ..' for the cmake-project + commands: + - cmake .. + cwd: '{{taskRoot}}/build' ``` **Once set, running `task run cmake` from _anywhere_ under `cmake-project` will run the `cmake ..` with `cmake-project/build` as the working directory**. @@ -106,35 +109,26 @@ Command: cmake .. ``` Now lets assume there's a need to occasionally run another task, similar to the one already defined. For example, with some `cmake` definition like `-DSOMEVAR=somevalue`. And another task that actually builds the project with `cmake --build .. -j8` , both, like the first task, need to be invoked from the `build` directory (Again, if you don't care about `cmake` and 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 where _they_ are, using TR tasks simplify the workflow: -```json -{ - "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" - } - } -} +```yaml +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 under `cmake-project`: @@ -197,42 +191,31 @@ There are 3 types of variables with different priorities: The following example show the different types of variables definition and usage. -```json -{ - "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}}" - } - } - } -} +```yaml +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. `task0` overrides the global `var1` and sets its own `var2`. `task1` overrides the inherited `task0` `var2`. Note the environment variable setting using the TR variables. @@ -245,19 +228,18 @@ When a task is invoked its commands are invoked with the current set of system e ## Container based tasks TR includes special support for running tasks inside a container. The main container setting is `c_image` defining a container image to use. The following task runs `make` inside a container with a volume, CWD set, tty allocated, interactive mode, wrapped in a `/usr/bin/sh -c`: -```json -"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"], -}, +```yaml +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"`. @@ -268,27 +250,21 @@ For a detailed list of all container configuration settings, refer to the [confi ## Inheritance A task might inherit another task settings by using the `base` settings. If task `a` inherits task `b`, all of `b`'s settings are inherited. Setting redefined in task `a` will 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): -```json -{ - "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" - } - } -} +```yaml +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 inclusion @@ -299,11 +275,11 @@ A special file located in `${HOME}/.config/tasks.json` is always included if it ### Passing command line arguments to commands Command 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. Running `task run ls -- -l somefile.txt` in the following task will run `ls -l somefile.txt`: -```json -"ls": { - "commands": ["ls {{cliArgs}}"], - "short_desc": "Clean build" -} +```yaml +ls: + commands: + - ls {{cliArgs}} + short_desc: Clean build ``` ## TR CLI options diff --git a/pyproject.toml b/pyproject.toml index 447d139..4743a3c 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -23,7 +23,8 @@ classifiers = [ dependencies = [ "argcomplete>=2.0.0", "pydantic>=2.8", - "setuptools>=72" + "setuptools>=72", + "PyYAML>=6.0.2<7.0" ] requires-python = ">=3.10,<4" diff --git a/requirements.txt b/requirements.txt index 7c37436..5a3d906 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,3 +1,4 @@ setuptools>=72 argcomplete>=2.0.0 pydantic>=2.8 +PyYAML>=6.0.2<7.0 diff --git a/src/tr/config.py b/src/tr/config.py index 1a96cbb..26df9a6 100644 --- a/src/tr/config.py +++ b/src/tr/config.py @@ -8,10 +8,20 @@ from typing import Any from argparse import Namespace as Args from pydantic import BaseModel, Field, ConfigDict, ValidationError +import yaml -_CONF_FILE_NAME = "tasks.json" -_DFLT_CONF_FILE_NAME = str(pathlib.Path.home()) + "/.config/" + _CONF_FILE_NAME +_COMMON_CONF_FILES: list[str] = [".tasks.yaml", "tasks.yaml", ".tasks.json", "tasks.json"] +_DFLT_CONF_DIR = str(pathlib.Path.home()) + "/.config/" +_DFLT_CONF_FILES = [os.path.join(_DFLT_CONF_DIR, f) for f in _COMMON_CONF_FILES] + + +def _find_default_config_file(directory: str) -> str | None: + for f in _COMMON_CONF_FILES: + file_path = os.path.join(directory, f) + if os.path.isfile(file_path): + return file_path + return None HIDDEN = "hidden" @@ -93,10 +103,17 @@ def validate_task_model(name: str, data: dict) -> TaskModel: class Config: @staticmethod def _read_config_file(file_path: str) -> ConfigFileModel: + # Find the configuration file extension try: - data: dict = json.load(open(file_path, 'r')) + ext = os.path.splitext(file_path)[1] + if ext == ".json": + data: dict = json.load(open(file_path, 'r')) + elif ext == ".yaml" or ext == ".yml": + data: dict = yaml.safe_load(open(file_path, 'r')) + else: + raise TaskException("Unsupported configuration file format") return validate_config_file_schema(data) - except (IOError, TypeError, ValueError, TaskException) as e: + except (IOError, TypeError, ValueError, TaskException, IndexError, yaml.YAMLError) as e: raise TaskException(f"Error parsing {file_path} - {e}") def _read_configuration(self, file_path: str, read_files: set | None = None) -> ConfigFileModel: @@ -111,11 +128,12 @@ def _read_configuration(self, file_path: str, read_files: set | None = None) -> # Add the default configuration file to includes list but only for the original # configuration file, and the behavior isn't turned off (again, relevant ONLY to # original file) + dflt_conf_file_path = _find_default_config_file(_DFLT_CONF_DIR) if len(read_files) == 0 and \ - file_path != _DFLT_CONF_FILE_NAME and \ - os.path.isfile(_DFLT_CONF_FILE_NAME) and \ - (base_config_model is None or base_config_model.use_default_include): - includes.insert(0, _DFLT_CONF_FILE_NAME) + file_path not in _DFLT_CONF_FILES and \ + dflt_conf_file_path and \ + base_config_model.use_default_include: + includes.insert(0, dflt_conf_file_path) included_tasks = {} included_variables = {} @@ -140,16 +158,14 @@ def _read_configuration(self, file_path: str, read_files: set | None = None) -> def _get_conf_file_path() -> str | None: directory = os.getcwd() while True: - conf_path = directory + "/." + _CONF_FILE_NAME - if os.path.isfile(conf_path): + conf_path = _find_default_config_file(directory) + if conf_path: return conf_path if directory == "/": break directory = os.path.dirname(directory) - if os.path.isfile(_DFLT_CONF_FILE_NAME): - return _DFLT_CONF_FILE_NAME - return None + return _find_default_config_file(_DFLT_CONF_DIR) def __init__(self, args: Args | None) -> None: self.args: Args = args # type: ignore @@ -165,11 +181,11 @@ def __init__(self, args: Args | None) -> None: const_vars = {} cwd = os.getcwd() const_vars[AutoVarsKeys.CWD] = cwd - if conf_path: - conf_dir_name = os.path.dirname(conf_path) - if conf_dir_name: - const_vars[AutoVarsKeys.TASK_ROOT] = os.path.dirname(conf_path) - const_vars.setdefault(AutoVarsKeys.TASK_ROOT, cwd) + conf_dir_name = os.path.dirname(conf_path) + if conf_dir_name: + const_vars[AutoVarsKeys.TASK_ROOT] = os.path.dirname(conf_path) + else: + const_vars.setdefault(AutoVarsKeys.TASK_ROOT, cwd) if args and args.__contains__(AutoVarsKeys.TASK_CLI_ARGS): const_vars[AutoVarsKeys.TASK_CLI_ARGS] = " ".join( args.__getattribute__(AutoVarsKeys.TASK_CLI_ARGS)) diff --git a/tests/tasks.json b/tests/tasks.json deleted file mode 100644 index de909bb..0000000 --- a/tests/tasks.json +++ /dev/null @@ -1,400 +0,0 @@ -{ - "$schema": "/home/omer/devel/taskrunner/schemas/taskrunner_v0.11.json", - "include": [ - "{{taskRoot}}/base_tasks_b.json" - ], - "variables": { - "var0": "var0", - "var1": "var1", - "var2": "var2", - "var0_1_2": "{{var0}} {{var1}} {{var2}}", - "base_var2": "from main tasks file", - "optDir": "/opt", - "env_name": "global_env_name", - "env_value": "global_env_value" - }, - "tasks": { - "001a_simple": { - "short_desc": "Simple echo", - "commands": [ - "echo \"XXX\"" - ] - }, - "001b_simple": { - "base": "001a_simple", - "short_desc": "Simple echo (based on 001_simple)" - }, - "002a_simple_env": { - "short_desc": "Simple echo with environment variable", - "env": { - "V0": "42" - }, - "commands": [ - "printenv V0" - ] - }, - "002b_simple_env": { - "base": "002a_simple_env", - "short_desc": "Simple echo with environment variable (based on 002_simple_env)", - "env": { - "V0": "43" - } - }, - "002c_env_inherit": { - "base": "002a_simple_env", - "short_desc": "Inherit environment variable from", - "env": { - "V1": "43" - }, - "commands": [ - "printenv V0", - "printenv V1" - ] - }, - "002d_no_env_inherit": { - "base": "002a_simple_env", - "short_desc": "Don't inherit environment from base", - "env": { - "V1": "43" - }, - "inherit_env": false, - "commands": [ - "printenv V1", - "printenv V0", - "echo shouldn't be printed as V0 is not inherited and previous printenv should fail" - ] - }, - "003a_stop_on_error": { - "short_desc": "Validate stop on error behavior", - "commands": [ - "true", - "echo message1", - "false", - "echo message2" - ] - }, - "003b_continue_on_error": { - "base": "003a_stop_on_error", - "short_desc": "Validate continue on error behavior", - "stop_on_error": false - }, - "004a_bash_multi_cmds_no_fail": { - "short_desc": "Validate mulit bash commands not allowed to fail", - "shell": true, - "commands": [ - "true && echo message1 && false && echo message2" - ] - }, - "004b_bash_multi_cmds_allow_fail": { - "short_desc": "Validate mulit bash commands allowed to fail", - "shell": true, - "commands": [ - "true; echo message1; false; echo message2" - ] - }, - "010_list_tasks": { - "short_desc": "Validate working directory as /", - "variables": { - "list_dir": "for_list_test" - }, - "commands": [ - "task --conf {{list_dir}}/tasks-for-list.json list", - "task --conf {{list_dir}}/tasks-for-list.json list -a" - ] - }, - "os_env_inherit_base": { - "env": { - "int_var": "int_var_value" - }, - "commands": [ - "printenv int_var", - "printenv ext_var" - ], - "abstract": true - }, - "015a_os_env_inherit": { - "base": "os_env_inherit_base", - "short_desc": "Check environment inheritance (enabled)", - "inherit_os_env": true - }, - "015b_no_os_env_inherit": { - "short_desc": "Check environment inheritance (disabled)", - "base": "os_env_inherit_base", - "inherit_os_env": false - }, - "020a_workdir_root": { - "short_desc": "Validate working directory as /", - "cwd": "/", - "commands": [ - "pwd" - ] - }, - "020b_workdir_opt": { - "short_desc": "Validate working directory as /opt", - "cwd": "/opt", - "commands": [ - "pwd" - ] - }, - "020c_workdir_taskroot": { - "short_desc": "Validate working directory as task root (autodef)", - "cwd": "{{taskRoot}}", - "shell": true, - "shell_path": "/bin/bash", - "commands": [ - "[[ \"$(pwd)\" == \"{{taskRoot}}\" ]] && echo Silence is golden" - ] - }, - "023_var_expansion": { - "short_desc": "Validate variables expansion", - "env": { - "MY_ENV": "{{var2}}" - }, - "commands": [ - "echo {{var0}} {{var0}}", - "echo __{{var1}}__", - "echo __{{var0_1_2}}__" - ] - }, - "024_var_expansion_env": { - "short_desc": "Validate variables expansion for environment variables", - "env": { - "MY_ENV0": "{{var0}}", - "MY_ENV1": "{{var1}}", - "MY_ENV2": "{{var2}}" - }, - "shell": true, - "commands": [ - "echo __$MY_ENV0 XX $MY_ENV1 __${MY_ENV2}" - ] - }, - "025_env_whitespace": { - "short_desc": "Check weird environment configrations", - "env": { - "ENV0": " value0", - "ENV1 ": "value1", - " ENV2": "\tvalue2", - "ENV3": "\"value3\"", - "ENV4": "'value4'", - "ENV5": "'\\\\value5\\t x'" - }, - "commands": [ - "printenv ENV0", - "printenv 'ENV1 '", - "printenv ' ENV2'", - "printenv ENV3", - "printenv ENV4", - "printenv ENV5" - ] - }, - "030_args_handling": { - "short_desc": "Check arguments handling", - "commands": [ - "echo cliArgs={{cliArgs}}", - "echo \"no args\"", - "echo cliArgs={{cliArgs}}" - ] - }, - "031_args_override": { - "short_desc": "Check task setting override by CLI", - "long_desc": "Check some task settings override by CLI args (env, shell usage, cwd and command)", - "commands": [ - "false" - ], - "cwd": "/nowhere", - "env": { - "MY_ENV0": "orig_value" - }, - "shell": false - }, - "032_args_var_override": { - "short_desc": "Check variables override by CLI args", - "commands": [ - "echo {{var0}}" - ] - }, - "033a_task_vars": { - "short_desc": "Check variables override by CLI args", - "variables": { - "var0": "task_override_var0", - "var1": "task_override_var1", - "tvar0": "task_own_var0", - "tvar1": "task_own_var1", - "complex_var": "{{var2}} {{nothing}} {{var0}} {{tvar1}}" - }, - "env": { - "{{env_name}}": "{{env_value}}" - }, - "commands": [ - "printenv {{env_name}}", - "echo var0={{var0}}", - "echo var1={{var1}}", - "echo var2={{var2}}", - "echo tvar0={{tvar0}}", - "echo tvar1={{tvar1}}", - "echo complex_var={{complex_var}}" - ] - }, - "033b_task_vars": { - "base": "033a_task_vars", - "short_desc": "Check variables override by CLI args", - "variables": { - "var0": "derived_task_override_var0", - "env_value": "derived_task_env_value" - } - }, - "033c_task_vars_no_inherit": { - "base": "033a_task_vars", - "short_desc": "Check variables override by CLI args", - "inherit_variables": false, - "variables": { - "var1": "derived_task_override_var1", - "var2": "derived_task_override_var2" - } - }, - "040_config_file_include_defs": { - "short_desc": "Check variables from included files", - "commands": [ - "echo base_var0={{base_var0}}", - "echo base_var1={{base_var1}}", - "echo base_var2={{base_var2}}" - ] - }, - "041_included": { - "short_desc": "Check overridden task, 1 deep", - "commands": [ - "echo main config file" - ] - }, - "042_included": { - "short_desc": "Check overridden task, 2 deep", - "commands": [ - "echo main config file" - ] - }, - "base_container_test": { - "short_desc": "Basic container support", - "commands": [ - "pwd; echo $CENV0; cat /etc/os-release | grep -E \"^(ID|VERSION_ID)\"; cat /vol/volume_file.txt" - ], - "c_cwd": "/opt", - "c_shell": true, - "c_env": { - "CENV0": "value0" - }, - "c_volumes": [ - "{{taskRoot}}/volumes/vol0:/vol" - ], - "abstract": true - }, - "050a_container_ubuntu": { - "base": "base_container_test", - "short_desc": "Basic container support o/Ubuntu 24.04", - "c_image": "docker.io/library/ubuntu:24.04" - }, - "050b_container_rocky": { - "base": "base_container_test", - "short_desc": "Basic container support o/Rocky 9.3", - "c_image": "docker.io/rockylinux/rockylinux:9.3" - }, - "051_container_no_inherit": { - "base": "base_container_test", - "short_desc": "Check no inheritance settings for container", - "c_image": "docker.io/rockylinux/rockylinux:9.3", - "c_inherit_volumes": false, - "c_inherit_env": false, - "c_volumes": [ - "{{taskRoot}}/volumes/vol1:/vol" - ] - }, - "info_task": { - "short_desc": "Dummy task for info/dump validations", - "long_desc": "'task' running 'task'? blesphamy!", - "env": { - "{{var0}}": "{{var1}}", - "{{var1}}": "{{var0}}" - }, - "hidden": true, - "abstract": true, - "cwd": "{{optDir}}", - "commands": [ - "a command", - "another command", - "yet another command" - ] - }, - "info_task_container": { - "base": "info_task", - "c_image": "a-wonderfull-image", - "c_remove": false, - "c_interactive": true, - "c_volumes": ["/to-be-mapped-to-opt:{{optDir}}"], - "c_shell": true, - "c_shell_path": "/usr/bin/acme-shell" - }, - "info_task_container_exec": { - "base": "info_task_container", - "c_image": "a-wonderfull-container", - "c_exec": true, - "c_interactive": true, - "c_shell": false, - "c_flags": "--priviliged" - }, - "060_info": { - "short_desc": "Validate task info", - "variables": { - "task_info_cmd": "task info" - }, - "commands": [ - "echo ----- Task info, simple, not expanded ------", - "{{task_info_cmd}} info_task", - "echo", - "echo ----- Task info, simple, expanded ------", - "{{task_info_cmd}} -x info_task", - "echo", - "echo ----- Task info, container, run, not expanded ------", - "{{task_info_cmd}} info_task_container", - "echo", - "echo ----- Task info, container, run, expanded ------", - "{{task_info_cmd}} -x info_task_container", - "echo", - "echo ----- Task info, container, exec, not expanded ------", - "{{task_info_cmd}} info_task_container_exec", - "echo", - "echo ----- Task info, container, exec, expanded ------", - "{{task_info_cmd}} -x info_task_container_exec" - ] - }, - "061_dump": { - "short_desc": "Validate task dump", - "variables": { - "task_dump_cmd": "task dump" - }, - "commands": [ - "echo ----- Task descriptor, simple ------", - "{{task_dump_cmd}} info_task", - "echo", - "echo ----- Task descriptor, container, no inclusion ------", - "{{task_dump_cmd}} info_task_container", - "echo", - "echo ----- Task descriptor, container, with inclusion ------", - "{{task_dump_cmd}} -i info_task_container", - "echo", - "echo ----- Task descriptor, container, ,exec, no inclusion ------", - "{{task_dump_cmd}} info_task_container_exec", - "echo", - "echo ----- Task descriptor, container, ,exec, with inclusion ------", - "{{task_dump_cmd}} -i info_task_container_exec" - ] - }, - "080_recursive_fail": { - "base": "080_recursive_fail", - "short_desc": "Recursive task failure", - "commands": [ - "echo This should never be printed" - ] - } - }, - "use_default_include": false, - "default_container_tool": "/usr/bin/podman" -} diff --git a/tests/tasks.yaml b/tests/tasks.yaml new file mode 100644 index 0000000..4c22578 --- /dev/null +++ b/tests/tasks.yaml @@ -0,0 +1,310 @@ +$schema: /home/omer/devel/taskrunner/schemas/taskrunner_v0.11.json +include: + - '{{taskRoot}}/base_tasks_b.json' +variables: + var0: var0 + var1: var1 + var2: var2 + var0_1_2: '{{var0}} {{var1}} {{var2}}' + base_var2: from main tasks file + optDir: /opt + env_name: global_env_name + env_value: global_env_value +tasks: + 001a_simple: + short_desc: Simple echo + commands: + - echo "XXX" + 001b_simple: + base: 001a_simple + short_desc: Simple echo (based on 001_simple) + 002a_simple_env: + short_desc: Simple echo with environment variable + env: + V0: "42" + commands: + - printenv V0 + 002b_simple_env: + base: 002a_simple_env + short_desc: Simple echo with environment variable (based on 002_simple_env) + env: + V0: "43" + 002c_env_inherit: + base: 002a_simple_env + short_desc: Inherit environment variable from + env: + V1: "43" + commands: + - printenv V0 + - printenv V1 + 002d_no_env_inherit: + base: 002a_simple_env + short_desc: Don't inherit environment from base + env: + V1: "43" + inherit_env: false + commands: + - printenv V1 + - printenv V0 + - echo shouldn't be printed as V0 is not inherited and previous printenv should fail + 003a_stop_on_error: + short_desc: Validate stop on error behavior + commands: + - "true" + - echo message1 + - "false" + - echo message2 + 003b_continue_on_error: + base: 003a_stop_on_error + short_desc: Validate continue on error behavior + stop_on_error: false + 004a_bash_multi_cmds_no_fail: + short_desc: Validate mulit bash commands not allowed to fail + shell: true + commands: + - true && echo message1 && false && echo message2 + 004b_bash_multi_cmds_allow_fail: + short_desc: Validate mulit bash commands allowed to fail + shell: true + commands: + - true; echo message1; false; echo message2 + 010_list_tasks: + short_desc: Validate working directory as / + variables: + list_dir: for_list_test + commands: + - task --conf {{list_dir}}/tasks-for-list.json list + - task --conf {{list_dir}}/tasks-for-list.json list -a + os_env_inherit_base: + env: + int_var: int_var_value + commands: + - printenv int_var + - printenv ext_var + abstract: true + 015a_os_env_inherit: + base: os_env_inherit_base + short_desc: Check environment inheritance (enabled) + inherit_os_env: true + 015b_no_os_env_inherit: + short_desc: Check environment inheritance (disabled) + base: os_env_inherit_base + inherit_os_env: false + 020a_workdir_root: + short_desc: Validate working directory as / + cwd: / + commands: + - pwd + 020b_workdir_opt: + short_desc: Validate working directory as /opt + cwd: /opt + commands: + - pwd + 020c_workdir_taskroot: + short_desc: Validate working directory as task root (autodef) + cwd: '{{taskRoot}}' + shell: true + shell_path: /bin/bash + commands: + - '[[ "$(pwd)" == "{{taskRoot}}" ]] && echo Silence is golden' + 023_var_expansion: + short_desc: Validate variables expansion + env: + MY_ENV: '{{var2}}' + commands: + - echo {{var0}} {{var0}} + - echo __{{var1}}__ + - echo __{{var0_1_2}}__ + 024_var_expansion_env: + short_desc: Validate variables expansion for environment variables + env: + MY_ENV0: '{{var0}}' + MY_ENV1: '{{var1}}' + MY_ENV2: '{{var2}}' + shell: true + commands: + - echo __$MY_ENV0 XX $MY_ENV1 __${MY_ENV2} + 025_env_whitespace: + short_desc: Check weird environment configrations + env: + ENV0: ' value0' + 'ENV1 ': value1 + ' ENV2': "\tvalue2" + ENV3: '"value3"' + ENV4: '''value4''' + ENV5: '''\\value5\t x''' + commands: + - printenv ENV0 + - printenv 'ENV1 ' + - printenv ' ENV2' + - printenv ENV3 + - printenv ENV4 + - printenv ENV5 + 030_args_handling: + short_desc: Check arguments handling + commands: + - echo cliArgs={{cliArgs}} + - echo "no args" + - echo cliArgs={{cliArgs}} + 031_args_override: + short_desc: Check task setting override by CLI + long_desc: Check some task settings override by CLI args (env, shell usage, cwd and command) + commands: + - "false" + cwd: /nowhere + env: + MY_ENV0: orig_value + shell: false + 032_args_var_override: + short_desc: Check variables override by CLI args + commands: + - echo {{var0}} + 033a_task_vars: + short_desc: Check variables override by CLI args + variables: + var0: task_override_var0 + var1: task_override_var1 + tvar0: task_own_var0 + tvar1: task_own_var1 + complex_var: '{{var2}} {{nothing}} {{var0}} {{tvar1}}' + env: + '{{env_name}}': '{{env_value}}' + commands: + - printenv {{env_name}} + - echo var0={{var0}} + - echo var1={{var1}} + - echo var2={{var2}} + - echo tvar0={{tvar0}} + - echo tvar1={{tvar1}} + - echo complex_var={{complex_var}} + 033b_task_vars: + base: 033a_task_vars + short_desc: Check variables override by CLI args + variables: + var0: derived_task_override_var0 + env_value: derived_task_env_value + 033c_task_vars_no_inherit: + base: 033a_task_vars + short_desc: Check variables override by CLI args + inherit_variables: false + variables: + var1: derived_task_override_var1 + var2: derived_task_override_var2 + 040_config_file_include_defs: + short_desc: Check variables from included files + commands: + - echo base_var0={{base_var0}} + - echo base_var1={{base_var1}} + - echo base_var2={{base_var2}} + 041_included: + short_desc: Check overridden task, 1 deep + commands: + - echo main config file + 042_included: + short_desc: Check overridden task, 2 deep + commands: + - echo main config file + base_container_test: + short_desc: Basic container support + commands: + - pwd; echo $CENV0; cat /etc/os-release | grep -E "^(ID|VERSION_ID)"; cat /vol/volume_file.txt + c_cwd: /opt + c_shell: true + c_env: + CENV0: value0 + c_volumes: + - '{{taskRoot}}/volumes/vol0:/vol' + abstract: true + 050a_container_ubuntu: + base: base_container_test + short_desc: Basic container support o/Ubuntu 24.04 + c_image: docker.io/library/ubuntu:24.04 + 050b_container_rocky: + base: base_container_test + short_desc: Basic container support o/Rocky 9.3 + c_image: docker.io/rockylinux/rockylinux:9.3 + 051_container_no_inherit: + base: base_container_test + short_desc: Check no inheritance settings for container + c_image: docker.io/rockylinux/rockylinux:9.3 + c_inherit_volumes: false + c_inherit_env: false + c_volumes: + - '{{taskRoot}}/volumes/vol1:/vol' + info_task: + short_desc: Dummy task for info/dump validations + long_desc: '''task'' running ''task''? blesphamy!' + env: + '{{var0}}': '{{var1}}' + '{{var1}}': '{{var0}}' + hidden: true + abstract: true + cwd: '{{optDir}}' + commands: + - a command + - another command + - yet another command + info_task_container: + base: info_task + c_image: a-wonderfull-image + c_remove: false + c_interactive: true + c_volumes: + - /to-be-mapped-to-opt:{{optDir}} + c_shell: true + c_shell_path: /usr/bin/acme-shell + info_task_container_exec: + base: info_task_container + c_image: a-wonderfull-container + c_exec: true + c_interactive: true + c_shell: false + c_flags: --priviliged + 060_info: + short_desc: Validate task info + variables: + task_info_cmd: task info + commands: + - echo ----- Task info, simple, not expanded ------ + - '{{task_info_cmd}} info_task' + - echo + - echo ----- Task info, simple, expanded ------ + - '{{task_info_cmd}} -x info_task' + - echo + - echo ----- Task info, container, run, not expanded ------ + - '{{task_info_cmd}} info_task_container' + - echo + - echo ----- Task info, container, run, expanded ------ + - '{{task_info_cmd}} -x info_task_container' + - echo + - echo ----- Task info, container, exec, not expanded ------ + - '{{task_info_cmd}} info_task_container_exec' + - echo + - echo ----- Task info, container, exec, expanded ------ + - '{{task_info_cmd}} -x info_task_container_exec' + 061_dump: + short_desc: Validate task dump + variables: + task_dump_cmd: task dump + commands: + - echo ----- Task descriptor, simple ------ + - '{{task_dump_cmd}} info_task' + - echo + - echo ----- Task descriptor, container, no inclusion ------ + - '{{task_dump_cmd}} info_task_container' + - echo + - echo ----- Task descriptor, container, with inclusion ------ + - '{{task_dump_cmd}} -i info_task_container' + - echo + - echo ----- Task descriptor, container, ,exec, no inclusion ------ + - '{{task_dump_cmd}} info_task_container_exec' + - echo + - echo ----- Task descriptor, container, ,exec, with inclusion ------ + - '{{task_dump_cmd}} -i info_task_container_exec' + 080_recursive_fail: + base: 080_recursive_fail + short_desc: Recursive task failure + commands: + - echo This should never be printed +use_default_include: false +default_container_tool: /usr/bin/podman