diff options
-rw-r--r-- | .gitignore | 1 | ||||
-rw-r--r-- | python-automatix.spec | 1500 | ||||
-rw-r--r-- | sources | 1 |
3 files changed, 1502 insertions, 0 deletions
@@ -0,0 +1 @@ +/automatix-1.11.2.tar.gz diff --git a/python-automatix.spec b/python-automatix.spec new file mode 100644 index 0000000..db372f0 --- /dev/null +++ b/python-automatix.spec @@ -0,0 +1,1500 @@ +%global _empty_manifest_terminate_build 0 +Name: python-automatix +Version: 1.11.2 +Release: 1 +Summary: Automation wrapper for bash and python commands +License: MIT +URL: https://github.com/seibert-media/automatix +Source0: https://mirrors.aliyun.com/pypi/web/packages/ba/aa/c43000d059eb5297515067d532febc10e9d81c3a43d9e579a7e3539aa087/automatix-1.11.2.tar.gz +BuildArch: noarch + +Requires: python3-pyyaml +Requires: python3-importlib-metadata +Requires: python3-argcomplete + +%description +# automatix +Automation wrapper for bash and python commands + + +# DESCRIPTION + +**automatix** is a wrapper for scripted sysadmin tasks. It offers + some useful functionality for easier scripting and having full + control over the automated process. + +The idea of **automatix** is to write down all the commands you would + normally type to your commandline or python console into a YAML file. + Then use **automatix** to execute these commands. + +There are different modes for **automatix** to work. Without any + parameters automatix will try to execute the specified command + pipeline from the script file until an error occurs or the pipeline + is done. The interactive mode (**-i**) asks for every single + commandline step whether to execute, skip or abort. + Forced mode (**-f**) will also proceed if errors occur. + +**automatix** is originally designed for internal //SEIBERT/MEDIA use. + It comes therefore with bundlewrap and teamvault support as well as + the possibility to use your own logging library. + +## Warning: + +Beware that this tool cannot substitute the system administrators + brain and it needs a responsible handling, since you can do + (and destroy) almost everything with it. + +**Automatix** evaluates YAML files and executes defined commands as + shell or python commands. There is no check for harmful commands. + Be aware that this can cause critical damage to your system. + +Please use the interactive mode and doublecheck commands before + executing. Usage of automatix is at your own risk! + + +# INSTALLATION + +Automatix requires Python ≥ 3.6. + +``` +pip install automatix +``` + +# CONFIGURATION + +You can specify a path to a configuration YAML file via the + environment variable **AUTOMATIX_CONFIG**. +Default location is "~/.automatix.cfg.yaml". + +### Example: .automatix.cfg.yaml + + # Path to scripts directory + script_dir: ~/automatix_script_files + + # Global constants for use in pipeline scripts + constants: + apt_update: 'apt-get -qy update' + apt_upgrade: 'DEBIAN_FRONTEND=noninteractive apt-get -qy -o Dpkg::Options::=--force-confold --no-install-recommends upgrade' + apt_full_upgrade: 'DEBIAN_FRONTEND=noninteractive apt-get -qy -o Dpkg::Options::=--force-confold --no-install-recommends full-upgrade' + + # Encoding + encoding: utf-8 + + # Path for shell imports + import_path: '.' + + # SSH Command used for remote connections + ssh_cmd: 'ssh {hostname} sudo ' + + # Temporary directory on remote machines for shell imports + remote_tmp_dir: 'automatix_tmp' + + # Logger + logger: mylogger + + # Logging library (has to implement the init_logger method) + logging_lib: mylib.logging + + # Bundlewrap support, bundlewrap has to be installed (default: false) + bundlewrap: true + + # Teamvault / Secret support, bundlewrap-teamvault has to be installed (default: false) + teamvault: true + +# SYNOPSIS + +**automatix** + \[**--help**|**-h**\] + \[**--systems** \[_SYSTEM1=ADDRESS_OR_NODENAME_ ...\]\] + \[**--vars** \[_VAR1=VALUE1_ ...\]\] + \[**--secrets** \[_SECRET1=SECRETID_ ...\]\] + \[**--vars-file** _VARS_FILE_PATH_ \] + \[**--print-overview**|**-p**\] + \[**--jump-to**|**-j** _JUMP_TO_\] + \[**--interactive**|**-i**\] + \[**--force**|**-f**\] + \[**--debug**|**-d**\] + \[**--**\] **scriptfile** + + +## OPTIONS + +**scriptfile** +: The only required parameter for this tool to work. Use " -- " if + needed to delimit this from argument fields. See **SCRIPTFILE** + section for more information. + +**-h**, **--help** +: View help message and exit. + +**--systems** _SYSTEM1=ADDRESS_OR_NODENAME_ +: Use this to set systems without adding them to the + scriptfile or to overwrite them. You can specify multiple + systems like: --systems v1=string1 v2=string2 v3=string3 + +**--vars** _VAR1=VALUE1_ +: Use this to set vars without adding them to the scriptfile + or to overwrite them. You can specify multiple vars + like: --vars v1=string1 v2=string2 v3=string3 + +**--secrets** _SECRET1=SECRETID_ +: Use this to set secrets without adding them to the + scriptfile or to overwrite them. You can specify multiple + secrets like: --secrets v1=string1 v2=string2 v3=string3 *(only if + teamvault is enabled)* + +**--vars-file** _VARS_FILE_PATH_ +: Use this to specify a CSV file from where **automatix** reads + systems, variables and secrets. First row must contain the field + types and names. You may also specify an `label` field. + Example: `label,systems:mysystem,vars:myvar`. The automatix script will + be processed for each row sequentially. + +**--print-overview**, **-p** +: Just print command pipeline overview with indices then exit without + executing the commandline. Note that the *always pipeline* will be + executed anyway. + +**--jump-to** _JUMP_TO_, **-j** _JUMP_TO_ +: Jump to step with index _JUMP_TO_ instead of starting at the + beginning. Use **-p** or the output messages to determine the + desired step index. You can use negative numbers to start counting + from the end. + +**--interactive**, **-i** +: Confirm actions before executing. + +**--force**, **-f** +: Try always to proceed (except manual steps), even if errors occur + (no retries). + +**--debug**, **-d** +: Activate debug log level. + + +### EXAMPLE: Usage + + automatix -i --systems source=sourcesystem.com target=targetsystem.org -- scriptfile.yaml + + +## SCRIPTFILE + +The **scriptfile** describes your automated process. Therefore it + contains information about systems, variables, secrets and the + command pipeline. + +You can provide a path to your **scriptfile** or place your + scriptfile in the predefined directory (see **CONFIGURATION** + section, _script_dir_). The path has precedence over the predefined + directory, if the file exists at both locations. + +The **scriptfile** has to contain valid YAML. + +### EXAMPLE: scriptfile + + name: Migration Server XY + # Systems you like to refer to in pipeline (accessible via 'SYSTEMS.source') + # If Bundlewrap support is activated use node names instead of hostnames or add preceeding 'hostname!'. + require_version: '1.5.0' + systems: + source: sourcesystem.com + target: targetsystem.org + # Custom vars to use in pipeline + vars: + version: 1.2.3 + domain: 'bla.mein-test-system' + # Teamvault Secrets, if activated (left: like vars, right: SECRETID_FIELD, FIELD=username|password|file) + secrets: + web_user: v6GQag_username + web_pw: v6GQag_password + # Imports for functions you like to use (path may be modified in configuration) + imports: + - myfunctions.sh + # like command pipeline but will be exectuted always beforehand + always: + - python: | + import mylib as nc + PERSISTENT_VARS.update(locals()) + pipeline: + - remote@target: systemctl stop server + - remote@source: zfs snapshot -r tank@before-migration + - manual: Please trigger preparing tasks via webinterface + - myvar=local: curl -L -vvv -k https://{domain}/ + - local: echo "1.1.1.1 {domain}" >> /etc/hosts + - sla=python: NODES.source.metadata.get('sla') + - python: | + sla = '{sla}' + if sla == 'gold': + print('Wow that\'s pretty cool. You have SLA Gold.') + else: + print('Oh. Running out of money? SLA Gold is worth it. You should check your wallet.') + PERSISTENT_VARS['sla'] = sla + - cond=python: sla == 'gold' + - cond?local: echo "This command is only executed if sla is gold." + cleanup: + - local: rm temp_files + + +### FIELDS + +**name** _(string)_ +: Just a name for the process. Does not do anything. + +**require_version** _(string)_ +: Minimum required Automatix version for this script to run. + +**systems** _(associative array)_ +: Define some systems. Value has to be a valid SSH destination like an + IP address or hostname. If Bundlewrap support is enabled, it has to + be a valid and existing Bundlewrap nodename or you can preceed your + IP or hostname with `hostname!` to define a non-Bundlewrap system. +You can refer to these systems in the command pipeline in multiple ways: + +1) remote@systemname as your command action (see below) + +2) via {SYSTEMS.systemname} which will be replaced with the value + +3) via NODES.systemname in python actions to use the Bundlewrap node + object (Bundlewrap systems only) + +**vars** _(associative array)_ +: Define some vars. These are accessible in the command pipeline via + {varname}. Note: Only valid Python variable names are allowed. + You can use "*FILE_*" prefix followed by a file path to assign the file + content to the variable. + +**secrets** _(associative array)_ +: Define teamvault secrets. Value has to be in this format: + _SECRETID_FIELD_. _FIELD_ must be one of username, password or file. + The resolved secret values are accessible in command line via + {secretname}. *(only if teamvault is enabled)* + +**imports** _(list)_ +: Listed shell files (see **CONFIGURATION** section, _import_path_) + will be sourced before every local or remote command execution. + For remote commands, these files are transferred via tar and ssh to + your home directory on the remote system beforehand and deleted + afterwards. This is meant to define some functions you may need. + +**always**, **cleanup** _(list of associative arrays)_ +: See **ALWAYS / CLEANUP PIPELINE** section. + +**pipeline** _(list of associative arrays)_ +: See **PIPELINE** section. + +### PIPELINE + +Here you define the commands automatix shall execute. + +**KEY**: One of these possible command actions: + +1) **manual**: Some manual instruction for the user. The user has to + confirm, that automatix may proceed. + +2) **local**: Local shell command to execute. Imports will be sourced + beforehand. /bin/bash will be used for execution. + +3) **remote@systemname**: Remote shell command to execute. Systemname + has to be a defined system. The command will be run via SSH (without + pseudo-terminal allocation). It uses the standard SSH command. + Therefore your .ssh/config should be respected. + +4) **python**: Python code to execute. + * Notice that there are some modules, constants and functions which + are already imported (check command.py): e.g. + `re, subprocess, quote(from shlex)`. The variable `vars` is used + to store the Automatix variables as a dictionary. You can use it + to access or change the variables directly. + * If bundlewrap is enabled, the Bundlewrap repository object is + available via AUTOMATIX_BW_REPO and system node objects are + available via NODES.systemname. + Use `AUTOMATIX_BW_REPO.reload()` to reinitialize the Bundlewrap + repository from the file system. This can be useful for using + newly created nodes (e.g. remote commands). + + +**ASSIGNMENT**: For **local**, **remote** and **python** action you + can also define a variable to which the output will be assigned. + To do this prefix the desired variablename and = before the action + key, e.g. `myvar=python: NODES.system.hostname`. Be careful when + working with multiline statements. In **python** the first line is + likely to set the variable. All variables will be converted to + strings when used to build commands in following steps. + +**CONDITIONS**: You can define the command only to be executed if + your condition variable evaluates to "True" in Python. To achieve this + write the variable name followed by a question mark at the very + beginning like `cond?python: destroy_system()`. Be aware that all + output from **local** or **remote** commands will lead to a + non-empty string which evaluates to "True" in Python, but empty output + will evaluate to "False". + +**VALUE**: Your command. Variables will be replaced with Python + format function. Therefore, use curly brackets to refer to variables, + systems, secrets and constants. + +Constants are available via CONST.KEY, where KEY is the key of your + constants in your **CONFIGURATION** file. There you can define some + widely used constants. + +In most cases its a good idea to define your command in quotes to + avoid parsing errors, but it is not always necessary. Another way is + to use '|' to indicate a _literal scalar block_. There you can even + define whole program structures for python (see example). + +#### Escaping in Pipeline + +Because automatix uses Python's format() function: +`{` -> `{{` +`}` -> `}}` + +Standard YAML escapes (see also https://yaml.org/spec/1.2/spec.html): +`'` -> `''` +`"` -> `\"` +`\ ` -> `\\` +`:` -> Please use quotes (double or single). + + +### ALWAYS / CLEANUP PIPELINE + +Same usage as the 'normal' command pipeline, but will be executed + every time at start of automatix (**always**) or at the end + (**cleanup**) even if aborted (a). The commands are executed without + --interactive flag, independend of the specified parameters. + +Intended use case for **always**: python imports or informations that + are needed afterwards and do not change anything on systems. + You want to have these available even if using --jump|-j feature. + +Intended use case for **cleanup**: Remove temporary files or artifacts. + + +## ENVIRONMENT + +**AUTOMATIX_CONFIG**: Specify the path to the configuration file. + Default is "~/.automatix.cfg.yaml". + +**AUTOMATIX_TIME**: Set this to an arbitrary value to print the times + for the single steps and the whole script. + +**ENCODING**: Specify output encoding. Default is "UTF-8". + +Additionally you can modify the environment to adjust things to your + needs. + + +# TIPS & TRICKS + +If you want to access variables in **python** action you defined in +preceeding command, you can use the **PERSISTENT_VARS** dictionary +(shortcut: **PVARS**). +This is added to the local scope of **python** actions and the +dictonary keys are also available as attributes. + Examples: +- To make all local variables of the actual command persistent use + `PERSISTENT_VARS.update(locals())`. +- To delete one persistent variable named "myvar" use + `del PERSISTENT_VARS['myvar']` +- To make variable "v2" persistent use `PERSISTENT_VARS['v2'] = v2` + or `PERSISTENT_VARS.v2 = v2` +- Use the shortcut like `PVARS.v2 = v2` + +You can use variables in PERSISTENT_VARS also as condition by +using the shortcut and the attribute notation: + + - python: PVARS.cond = some_function() + - PVARS.cond?local: echo 'This is only printed if "some_function" evaluates to "True"' + +An alternative is to make variables global, but in most cases using + PERSISTENT_VARS is more clean. _**CAUTION: Choosing already existing + (Python) variable names may lead to unexpected behaviour!!!**_ Maybe + you want to check the source code (command.py). +Explanation: automatix is written in Python and uses 'exec' to + execute the command in function context. If you declare variables + globally they remain across commands. + +To abort the current automatix and jump to the next batch item you can + raise the `SkipBatchItemException`. For aborting the whole automatix + process raise `AbortException(return_code: int)`. In both cases the + cleanup pipeline is executed. Same is the case for selecting + `a`:abort or `c`:continue when asked (interactive or error). + + +# BEST PRACTISES + +There are different ways to start scripting with **automatix**. The + author's approach is mainly to consider the process and simply write + down, what to do (manual steps for complex or not automated steps) + and which commands to use. +Then start **automatix** in interactive mode (-i) and adjust the + single steps one by one. Replace manual steps, if suitable. Whenever + adjustment is needed, abort, adjust and restart **automatix** with + jump (-j) to the adjusted step. +Repeat this procedure to automate more and more and increase quality, + whenever you feel like it. + +Consider to put often used paths or code sequences in automatix + variables for better readability. +Do the same with variable content like URLs, to make it possible to + overwrite it by command line options. Where ever possible prefer to + use functions to determine already available information, such as BW + metadata, instead of defining things explicitly. This will make + things easier when using the script with different systems / + parameters. + +Preferred way of using **automatix** is to put often used and complex + algorithms in shell functions or python libraries (shelllib/pylib) + and import them. Advantage of this approach is that you can use your + implemented functions multiple times and build up a toolbox of nice + functionality over time. + + +# NOTES + +**Manual steps** will always cause automatix to stop and wait for + user input. + +Be careful with **assignments** containing line breaks (echo, ...). + Using the variables may lead to unexpected behaviour or errors. + +Assignments containing **null bytes** are currently not supported. + +Because the **always** pipeline should not change anything, aborting + while running this pipeline will not trigger a cleanup. + +If you want to abort the **pipeline** without triggering the + **cleanup** pipeline, use CRTL+C. + +While **aborting remote functions** (via imports), automatix is not + able to determine still running processes invoked by the function, + because it only checks the processes for the commands (in this case + the function name) which is called in the pipeline. + +# EXTRAS + +## Bash completion (experimental) +Automatix supports bash completion for parameters and the script directory via [argcomplete](https://github.com/kislyuk/argcomplete). + +Therefor follow the installation instructions for argcomplete, which is at the current time + + pip install argcomplete + +and either global activation via executing + + activate-global-python-argcomplete + +or activation for automatix (e.g. in `.bashrc`) + + eval "$(register-python-argcomplete automatix)" + +Automatix will recognize the installed module and offer the completion automatically. + + +%package -n python3-automatix +Summary: Automation wrapper for bash and python commands +Provides: python-automatix +BuildRequires: python3-devel +BuildRequires: python3-setuptools +BuildRequires: python3-pip +%description -n python3-automatix +# automatix +Automation wrapper for bash and python commands + + +# DESCRIPTION + +**automatix** is a wrapper for scripted sysadmin tasks. It offers + some useful functionality for easier scripting and having full + control over the automated process. + +The idea of **automatix** is to write down all the commands you would + normally type to your commandline or python console into a YAML file. + Then use **automatix** to execute these commands. + +There are different modes for **automatix** to work. Without any + parameters automatix will try to execute the specified command + pipeline from the script file until an error occurs or the pipeline + is done. The interactive mode (**-i**) asks for every single + commandline step whether to execute, skip or abort. + Forced mode (**-f**) will also proceed if errors occur. + +**automatix** is originally designed for internal //SEIBERT/MEDIA use. + It comes therefore with bundlewrap and teamvault support as well as + the possibility to use your own logging library. + +## Warning: + +Beware that this tool cannot substitute the system administrators + brain and it needs a responsible handling, since you can do + (and destroy) almost everything with it. + +**Automatix** evaluates YAML files and executes defined commands as + shell or python commands. There is no check for harmful commands. + Be aware that this can cause critical damage to your system. + +Please use the interactive mode and doublecheck commands before + executing. Usage of automatix is at your own risk! + + +# INSTALLATION + +Automatix requires Python ≥ 3.6. + +``` +pip install automatix +``` + +# CONFIGURATION + +You can specify a path to a configuration YAML file via the + environment variable **AUTOMATIX_CONFIG**. +Default location is "~/.automatix.cfg.yaml". + +### Example: .automatix.cfg.yaml + + # Path to scripts directory + script_dir: ~/automatix_script_files + + # Global constants for use in pipeline scripts + constants: + apt_update: 'apt-get -qy update' + apt_upgrade: 'DEBIAN_FRONTEND=noninteractive apt-get -qy -o Dpkg::Options::=--force-confold --no-install-recommends upgrade' + apt_full_upgrade: 'DEBIAN_FRONTEND=noninteractive apt-get -qy -o Dpkg::Options::=--force-confold --no-install-recommends full-upgrade' + + # Encoding + encoding: utf-8 + + # Path for shell imports + import_path: '.' + + # SSH Command used for remote connections + ssh_cmd: 'ssh {hostname} sudo ' + + # Temporary directory on remote machines for shell imports + remote_tmp_dir: 'automatix_tmp' + + # Logger + logger: mylogger + + # Logging library (has to implement the init_logger method) + logging_lib: mylib.logging + + # Bundlewrap support, bundlewrap has to be installed (default: false) + bundlewrap: true + + # Teamvault / Secret support, bundlewrap-teamvault has to be installed (default: false) + teamvault: true + +# SYNOPSIS + +**automatix** + \[**--help**|**-h**\] + \[**--systems** \[_SYSTEM1=ADDRESS_OR_NODENAME_ ...\]\] + \[**--vars** \[_VAR1=VALUE1_ ...\]\] + \[**--secrets** \[_SECRET1=SECRETID_ ...\]\] + \[**--vars-file** _VARS_FILE_PATH_ \] + \[**--print-overview**|**-p**\] + \[**--jump-to**|**-j** _JUMP_TO_\] + \[**--interactive**|**-i**\] + \[**--force**|**-f**\] + \[**--debug**|**-d**\] + \[**--**\] **scriptfile** + + +## OPTIONS + +**scriptfile** +: The only required parameter for this tool to work. Use " -- " if + needed to delimit this from argument fields. See **SCRIPTFILE** + section for more information. + +**-h**, **--help** +: View help message and exit. + +**--systems** _SYSTEM1=ADDRESS_OR_NODENAME_ +: Use this to set systems without adding them to the + scriptfile or to overwrite them. You can specify multiple + systems like: --systems v1=string1 v2=string2 v3=string3 + +**--vars** _VAR1=VALUE1_ +: Use this to set vars without adding them to the scriptfile + or to overwrite them. You can specify multiple vars + like: --vars v1=string1 v2=string2 v3=string3 + +**--secrets** _SECRET1=SECRETID_ +: Use this to set secrets without adding them to the + scriptfile or to overwrite them. You can specify multiple + secrets like: --secrets v1=string1 v2=string2 v3=string3 *(only if + teamvault is enabled)* + +**--vars-file** _VARS_FILE_PATH_ +: Use this to specify a CSV file from where **automatix** reads + systems, variables and secrets. First row must contain the field + types and names. You may also specify an `label` field. + Example: `label,systems:mysystem,vars:myvar`. The automatix script will + be processed for each row sequentially. + +**--print-overview**, **-p** +: Just print command pipeline overview with indices then exit without + executing the commandline. Note that the *always pipeline* will be + executed anyway. + +**--jump-to** _JUMP_TO_, **-j** _JUMP_TO_ +: Jump to step with index _JUMP_TO_ instead of starting at the + beginning. Use **-p** or the output messages to determine the + desired step index. You can use negative numbers to start counting + from the end. + +**--interactive**, **-i** +: Confirm actions before executing. + +**--force**, **-f** +: Try always to proceed (except manual steps), even if errors occur + (no retries). + +**--debug**, **-d** +: Activate debug log level. + + +### EXAMPLE: Usage + + automatix -i --systems source=sourcesystem.com target=targetsystem.org -- scriptfile.yaml + + +## SCRIPTFILE + +The **scriptfile** describes your automated process. Therefore it + contains information about systems, variables, secrets and the + command pipeline. + +You can provide a path to your **scriptfile** or place your + scriptfile in the predefined directory (see **CONFIGURATION** + section, _script_dir_). The path has precedence over the predefined + directory, if the file exists at both locations. + +The **scriptfile** has to contain valid YAML. + +### EXAMPLE: scriptfile + + name: Migration Server XY + # Systems you like to refer to in pipeline (accessible via 'SYSTEMS.source') + # If Bundlewrap support is activated use node names instead of hostnames or add preceeding 'hostname!'. + require_version: '1.5.0' + systems: + source: sourcesystem.com + target: targetsystem.org + # Custom vars to use in pipeline + vars: + version: 1.2.3 + domain: 'bla.mein-test-system' + # Teamvault Secrets, if activated (left: like vars, right: SECRETID_FIELD, FIELD=username|password|file) + secrets: + web_user: v6GQag_username + web_pw: v6GQag_password + # Imports for functions you like to use (path may be modified in configuration) + imports: + - myfunctions.sh + # like command pipeline but will be exectuted always beforehand + always: + - python: | + import mylib as nc + PERSISTENT_VARS.update(locals()) + pipeline: + - remote@target: systemctl stop server + - remote@source: zfs snapshot -r tank@before-migration + - manual: Please trigger preparing tasks via webinterface + - myvar=local: curl -L -vvv -k https://{domain}/ + - local: echo "1.1.1.1 {domain}" >> /etc/hosts + - sla=python: NODES.source.metadata.get('sla') + - python: | + sla = '{sla}' + if sla == 'gold': + print('Wow that\'s pretty cool. You have SLA Gold.') + else: + print('Oh. Running out of money? SLA Gold is worth it. You should check your wallet.') + PERSISTENT_VARS['sla'] = sla + - cond=python: sla == 'gold' + - cond?local: echo "This command is only executed if sla is gold." + cleanup: + - local: rm temp_files + + +### FIELDS + +**name** _(string)_ +: Just a name for the process. Does not do anything. + +**require_version** _(string)_ +: Minimum required Automatix version for this script to run. + +**systems** _(associative array)_ +: Define some systems. Value has to be a valid SSH destination like an + IP address or hostname. If Bundlewrap support is enabled, it has to + be a valid and existing Bundlewrap nodename or you can preceed your + IP or hostname with `hostname!` to define a non-Bundlewrap system. +You can refer to these systems in the command pipeline in multiple ways: + +1) remote@systemname as your command action (see below) + +2) via {SYSTEMS.systemname} which will be replaced with the value + +3) via NODES.systemname in python actions to use the Bundlewrap node + object (Bundlewrap systems only) + +**vars** _(associative array)_ +: Define some vars. These are accessible in the command pipeline via + {varname}. Note: Only valid Python variable names are allowed. + You can use "*FILE_*" prefix followed by a file path to assign the file + content to the variable. + +**secrets** _(associative array)_ +: Define teamvault secrets. Value has to be in this format: + _SECRETID_FIELD_. _FIELD_ must be one of username, password or file. + The resolved secret values are accessible in command line via + {secretname}. *(only if teamvault is enabled)* + +**imports** _(list)_ +: Listed shell files (see **CONFIGURATION** section, _import_path_) + will be sourced before every local or remote command execution. + For remote commands, these files are transferred via tar and ssh to + your home directory on the remote system beforehand and deleted + afterwards. This is meant to define some functions you may need. + +**always**, **cleanup** _(list of associative arrays)_ +: See **ALWAYS / CLEANUP PIPELINE** section. + +**pipeline** _(list of associative arrays)_ +: See **PIPELINE** section. + +### PIPELINE + +Here you define the commands automatix shall execute. + +**KEY**: One of these possible command actions: + +1) **manual**: Some manual instruction for the user. The user has to + confirm, that automatix may proceed. + +2) **local**: Local shell command to execute. Imports will be sourced + beforehand. /bin/bash will be used for execution. + +3) **remote@systemname**: Remote shell command to execute. Systemname + has to be a defined system. The command will be run via SSH (without + pseudo-terminal allocation). It uses the standard SSH command. + Therefore your .ssh/config should be respected. + +4) **python**: Python code to execute. + * Notice that there are some modules, constants and functions which + are already imported (check command.py): e.g. + `re, subprocess, quote(from shlex)`. The variable `vars` is used + to store the Automatix variables as a dictionary. You can use it + to access or change the variables directly. + * If bundlewrap is enabled, the Bundlewrap repository object is + available via AUTOMATIX_BW_REPO and system node objects are + available via NODES.systemname. + Use `AUTOMATIX_BW_REPO.reload()` to reinitialize the Bundlewrap + repository from the file system. This can be useful for using + newly created nodes (e.g. remote commands). + + +**ASSIGNMENT**: For **local**, **remote** and **python** action you + can also define a variable to which the output will be assigned. + To do this prefix the desired variablename and = before the action + key, e.g. `myvar=python: NODES.system.hostname`. Be careful when + working with multiline statements. In **python** the first line is + likely to set the variable. All variables will be converted to + strings when used to build commands in following steps. + +**CONDITIONS**: You can define the command only to be executed if + your condition variable evaluates to "True" in Python. To achieve this + write the variable name followed by a question mark at the very + beginning like `cond?python: destroy_system()`. Be aware that all + output from **local** or **remote** commands will lead to a + non-empty string which evaluates to "True" in Python, but empty output + will evaluate to "False". + +**VALUE**: Your command. Variables will be replaced with Python + format function. Therefore, use curly brackets to refer to variables, + systems, secrets and constants. + +Constants are available via CONST.KEY, where KEY is the key of your + constants in your **CONFIGURATION** file. There you can define some + widely used constants. + +In most cases its a good idea to define your command in quotes to + avoid parsing errors, but it is not always necessary. Another way is + to use '|' to indicate a _literal scalar block_. There you can even + define whole program structures for python (see example). + +#### Escaping in Pipeline + +Because automatix uses Python's format() function: +`{` -> `{{` +`}` -> `}}` + +Standard YAML escapes (see also https://yaml.org/spec/1.2/spec.html): +`'` -> `''` +`"` -> `\"` +`\ ` -> `\\` +`:` -> Please use quotes (double or single). + + +### ALWAYS / CLEANUP PIPELINE + +Same usage as the 'normal' command pipeline, but will be executed + every time at start of automatix (**always**) or at the end + (**cleanup**) even if aborted (a). The commands are executed without + --interactive flag, independend of the specified parameters. + +Intended use case for **always**: python imports or informations that + are needed afterwards and do not change anything on systems. + You want to have these available even if using --jump|-j feature. + +Intended use case for **cleanup**: Remove temporary files or artifacts. + + +## ENVIRONMENT + +**AUTOMATIX_CONFIG**: Specify the path to the configuration file. + Default is "~/.automatix.cfg.yaml". + +**AUTOMATIX_TIME**: Set this to an arbitrary value to print the times + for the single steps and the whole script. + +**ENCODING**: Specify output encoding. Default is "UTF-8". + +Additionally you can modify the environment to adjust things to your + needs. + + +# TIPS & TRICKS + +If you want to access variables in **python** action you defined in +preceeding command, you can use the **PERSISTENT_VARS** dictionary +(shortcut: **PVARS**). +This is added to the local scope of **python** actions and the +dictonary keys are also available as attributes. + Examples: +- To make all local variables of the actual command persistent use + `PERSISTENT_VARS.update(locals())`. +- To delete one persistent variable named "myvar" use + `del PERSISTENT_VARS['myvar']` +- To make variable "v2" persistent use `PERSISTENT_VARS['v2'] = v2` + or `PERSISTENT_VARS.v2 = v2` +- Use the shortcut like `PVARS.v2 = v2` + +You can use variables in PERSISTENT_VARS also as condition by +using the shortcut and the attribute notation: + + - python: PVARS.cond = some_function() + - PVARS.cond?local: echo 'This is only printed if "some_function" evaluates to "True"' + +An alternative is to make variables global, but in most cases using + PERSISTENT_VARS is more clean. _**CAUTION: Choosing already existing + (Python) variable names may lead to unexpected behaviour!!!**_ Maybe + you want to check the source code (command.py). +Explanation: automatix is written in Python and uses 'exec' to + execute the command in function context. If you declare variables + globally they remain across commands. + +To abort the current automatix and jump to the next batch item you can + raise the `SkipBatchItemException`. For aborting the whole automatix + process raise `AbortException(return_code: int)`. In both cases the + cleanup pipeline is executed. Same is the case for selecting + `a`:abort or `c`:continue when asked (interactive or error). + + +# BEST PRACTISES + +There are different ways to start scripting with **automatix**. The + author's approach is mainly to consider the process and simply write + down, what to do (manual steps for complex or not automated steps) + and which commands to use. +Then start **automatix** in interactive mode (-i) and adjust the + single steps one by one. Replace manual steps, if suitable. Whenever + adjustment is needed, abort, adjust and restart **automatix** with + jump (-j) to the adjusted step. +Repeat this procedure to automate more and more and increase quality, + whenever you feel like it. + +Consider to put often used paths or code sequences in automatix + variables for better readability. +Do the same with variable content like URLs, to make it possible to + overwrite it by command line options. Where ever possible prefer to + use functions to determine already available information, such as BW + metadata, instead of defining things explicitly. This will make + things easier when using the script with different systems / + parameters. + +Preferred way of using **automatix** is to put often used and complex + algorithms in shell functions or python libraries (shelllib/pylib) + and import them. Advantage of this approach is that you can use your + implemented functions multiple times and build up a toolbox of nice + functionality over time. + + +# NOTES + +**Manual steps** will always cause automatix to stop and wait for + user input. + +Be careful with **assignments** containing line breaks (echo, ...). + Using the variables may lead to unexpected behaviour or errors. + +Assignments containing **null bytes** are currently not supported. + +Because the **always** pipeline should not change anything, aborting + while running this pipeline will not trigger a cleanup. + +If you want to abort the **pipeline** without triggering the + **cleanup** pipeline, use CRTL+C. + +While **aborting remote functions** (via imports), automatix is not + able to determine still running processes invoked by the function, + because it only checks the processes for the commands (in this case + the function name) which is called in the pipeline. + +# EXTRAS + +## Bash completion (experimental) +Automatix supports bash completion for parameters and the script directory via [argcomplete](https://github.com/kislyuk/argcomplete). + +Therefor follow the installation instructions for argcomplete, which is at the current time + + pip install argcomplete + +and either global activation via executing + + activate-global-python-argcomplete + +or activation for automatix (e.g. in `.bashrc`) + + eval "$(register-python-argcomplete automatix)" + +Automatix will recognize the installed module and offer the completion automatically. + + +%package help +Summary: Development documents and examples for automatix +Provides: python3-automatix-doc +%description help +# automatix +Automation wrapper for bash and python commands + + +# DESCRIPTION + +**automatix** is a wrapper for scripted sysadmin tasks. It offers + some useful functionality for easier scripting and having full + control over the automated process. + +The idea of **automatix** is to write down all the commands you would + normally type to your commandline or python console into a YAML file. + Then use **automatix** to execute these commands. + +There are different modes for **automatix** to work. Without any + parameters automatix will try to execute the specified command + pipeline from the script file until an error occurs or the pipeline + is done. The interactive mode (**-i**) asks for every single + commandline step whether to execute, skip or abort. + Forced mode (**-f**) will also proceed if errors occur. + +**automatix** is originally designed for internal //SEIBERT/MEDIA use. + It comes therefore with bundlewrap and teamvault support as well as + the possibility to use your own logging library. + +## Warning: + +Beware that this tool cannot substitute the system administrators + brain and it needs a responsible handling, since you can do + (and destroy) almost everything with it. + +**Automatix** evaluates YAML files and executes defined commands as + shell or python commands. There is no check for harmful commands. + Be aware that this can cause critical damage to your system. + +Please use the interactive mode and doublecheck commands before + executing. Usage of automatix is at your own risk! + + +# INSTALLATION + +Automatix requires Python ≥ 3.6. + +``` +pip install automatix +``` + +# CONFIGURATION + +You can specify a path to a configuration YAML file via the + environment variable **AUTOMATIX_CONFIG**. +Default location is "~/.automatix.cfg.yaml". + +### Example: .automatix.cfg.yaml + + # Path to scripts directory + script_dir: ~/automatix_script_files + + # Global constants for use in pipeline scripts + constants: + apt_update: 'apt-get -qy update' + apt_upgrade: 'DEBIAN_FRONTEND=noninteractive apt-get -qy -o Dpkg::Options::=--force-confold --no-install-recommends upgrade' + apt_full_upgrade: 'DEBIAN_FRONTEND=noninteractive apt-get -qy -o Dpkg::Options::=--force-confold --no-install-recommends full-upgrade' + + # Encoding + encoding: utf-8 + + # Path for shell imports + import_path: '.' + + # SSH Command used for remote connections + ssh_cmd: 'ssh {hostname} sudo ' + + # Temporary directory on remote machines for shell imports + remote_tmp_dir: 'automatix_tmp' + + # Logger + logger: mylogger + + # Logging library (has to implement the init_logger method) + logging_lib: mylib.logging + + # Bundlewrap support, bundlewrap has to be installed (default: false) + bundlewrap: true + + # Teamvault / Secret support, bundlewrap-teamvault has to be installed (default: false) + teamvault: true + +# SYNOPSIS + +**automatix** + \[**--help**|**-h**\] + \[**--systems** \[_SYSTEM1=ADDRESS_OR_NODENAME_ ...\]\] + \[**--vars** \[_VAR1=VALUE1_ ...\]\] + \[**--secrets** \[_SECRET1=SECRETID_ ...\]\] + \[**--vars-file** _VARS_FILE_PATH_ \] + \[**--print-overview**|**-p**\] + \[**--jump-to**|**-j** _JUMP_TO_\] + \[**--interactive**|**-i**\] + \[**--force**|**-f**\] + \[**--debug**|**-d**\] + \[**--**\] **scriptfile** + + +## OPTIONS + +**scriptfile** +: The only required parameter for this tool to work. Use " -- " if + needed to delimit this from argument fields. See **SCRIPTFILE** + section for more information. + +**-h**, **--help** +: View help message and exit. + +**--systems** _SYSTEM1=ADDRESS_OR_NODENAME_ +: Use this to set systems without adding them to the + scriptfile or to overwrite them. You can specify multiple + systems like: --systems v1=string1 v2=string2 v3=string3 + +**--vars** _VAR1=VALUE1_ +: Use this to set vars without adding them to the scriptfile + or to overwrite them. You can specify multiple vars + like: --vars v1=string1 v2=string2 v3=string3 + +**--secrets** _SECRET1=SECRETID_ +: Use this to set secrets without adding them to the + scriptfile or to overwrite them. You can specify multiple + secrets like: --secrets v1=string1 v2=string2 v3=string3 *(only if + teamvault is enabled)* + +**--vars-file** _VARS_FILE_PATH_ +: Use this to specify a CSV file from where **automatix** reads + systems, variables and secrets. First row must contain the field + types and names. You may also specify an `label` field. + Example: `label,systems:mysystem,vars:myvar`. The automatix script will + be processed for each row sequentially. + +**--print-overview**, **-p** +: Just print command pipeline overview with indices then exit without + executing the commandline. Note that the *always pipeline* will be + executed anyway. + +**--jump-to** _JUMP_TO_, **-j** _JUMP_TO_ +: Jump to step with index _JUMP_TO_ instead of starting at the + beginning. Use **-p** or the output messages to determine the + desired step index. You can use negative numbers to start counting + from the end. + +**--interactive**, **-i** +: Confirm actions before executing. + +**--force**, **-f** +: Try always to proceed (except manual steps), even if errors occur + (no retries). + +**--debug**, **-d** +: Activate debug log level. + + +### EXAMPLE: Usage + + automatix -i --systems source=sourcesystem.com target=targetsystem.org -- scriptfile.yaml + + +## SCRIPTFILE + +The **scriptfile** describes your automated process. Therefore it + contains information about systems, variables, secrets and the + command pipeline. + +You can provide a path to your **scriptfile** or place your + scriptfile in the predefined directory (see **CONFIGURATION** + section, _script_dir_). The path has precedence over the predefined + directory, if the file exists at both locations. + +The **scriptfile** has to contain valid YAML. + +### EXAMPLE: scriptfile + + name: Migration Server XY + # Systems you like to refer to in pipeline (accessible via 'SYSTEMS.source') + # If Bundlewrap support is activated use node names instead of hostnames or add preceeding 'hostname!'. + require_version: '1.5.0' + systems: + source: sourcesystem.com + target: targetsystem.org + # Custom vars to use in pipeline + vars: + version: 1.2.3 + domain: 'bla.mein-test-system' + # Teamvault Secrets, if activated (left: like vars, right: SECRETID_FIELD, FIELD=username|password|file) + secrets: + web_user: v6GQag_username + web_pw: v6GQag_password + # Imports for functions you like to use (path may be modified in configuration) + imports: + - myfunctions.sh + # like command pipeline but will be exectuted always beforehand + always: + - python: | + import mylib as nc + PERSISTENT_VARS.update(locals()) + pipeline: + - remote@target: systemctl stop server + - remote@source: zfs snapshot -r tank@before-migration + - manual: Please trigger preparing tasks via webinterface + - myvar=local: curl -L -vvv -k https://{domain}/ + - local: echo "1.1.1.1 {domain}" >> /etc/hosts + - sla=python: NODES.source.metadata.get('sla') + - python: | + sla = '{sla}' + if sla == 'gold': + print('Wow that\'s pretty cool. You have SLA Gold.') + else: + print('Oh. Running out of money? SLA Gold is worth it. You should check your wallet.') + PERSISTENT_VARS['sla'] = sla + - cond=python: sla == 'gold' + - cond?local: echo "This command is only executed if sla is gold." + cleanup: + - local: rm temp_files + + +### FIELDS + +**name** _(string)_ +: Just a name for the process. Does not do anything. + +**require_version** _(string)_ +: Minimum required Automatix version for this script to run. + +**systems** _(associative array)_ +: Define some systems. Value has to be a valid SSH destination like an + IP address or hostname. If Bundlewrap support is enabled, it has to + be a valid and existing Bundlewrap nodename or you can preceed your + IP or hostname with `hostname!` to define a non-Bundlewrap system. +You can refer to these systems in the command pipeline in multiple ways: + +1) remote@systemname as your command action (see below) + +2) via {SYSTEMS.systemname} which will be replaced with the value + +3) via NODES.systemname in python actions to use the Bundlewrap node + object (Bundlewrap systems only) + +**vars** _(associative array)_ +: Define some vars. These are accessible in the command pipeline via + {varname}. Note: Only valid Python variable names are allowed. + You can use "*FILE_*" prefix followed by a file path to assign the file + content to the variable. + +**secrets** _(associative array)_ +: Define teamvault secrets. Value has to be in this format: + _SECRETID_FIELD_. _FIELD_ must be one of username, password or file. + The resolved secret values are accessible in command line via + {secretname}. *(only if teamvault is enabled)* + +**imports** _(list)_ +: Listed shell files (see **CONFIGURATION** section, _import_path_) + will be sourced before every local or remote command execution. + For remote commands, these files are transferred via tar and ssh to + your home directory on the remote system beforehand and deleted + afterwards. This is meant to define some functions you may need. + +**always**, **cleanup** _(list of associative arrays)_ +: See **ALWAYS / CLEANUP PIPELINE** section. + +**pipeline** _(list of associative arrays)_ +: See **PIPELINE** section. + +### PIPELINE + +Here you define the commands automatix shall execute. + +**KEY**: One of these possible command actions: + +1) **manual**: Some manual instruction for the user. The user has to + confirm, that automatix may proceed. + +2) **local**: Local shell command to execute. Imports will be sourced + beforehand. /bin/bash will be used for execution. + +3) **remote@systemname**: Remote shell command to execute. Systemname + has to be a defined system. The command will be run via SSH (without + pseudo-terminal allocation). It uses the standard SSH command. + Therefore your .ssh/config should be respected. + +4) **python**: Python code to execute. + * Notice that there are some modules, constants and functions which + are already imported (check command.py): e.g. + `re, subprocess, quote(from shlex)`. The variable `vars` is used + to store the Automatix variables as a dictionary. You can use it + to access or change the variables directly. + * If bundlewrap is enabled, the Bundlewrap repository object is + available via AUTOMATIX_BW_REPO and system node objects are + available via NODES.systemname. + Use `AUTOMATIX_BW_REPO.reload()` to reinitialize the Bundlewrap + repository from the file system. This can be useful for using + newly created nodes (e.g. remote commands). + + +**ASSIGNMENT**: For **local**, **remote** and **python** action you + can also define a variable to which the output will be assigned. + To do this prefix the desired variablename and = before the action + key, e.g. `myvar=python: NODES.system.hostname`. Be careful when + working with multiline statements. In **python** the first line is + likely to set the variable. All variables will be converted to + strings when used to build commands in following steps. + +**CONDITIONS**: You can define the command only to be executed if + your condition variable evaluates to "True" in Python. To achieve this + write the variable name followed by a question mark at the very + beginning like `cond?python: destroy_system()`. Be aware that all + output from **local** or **remote** commands will lead to a + non-empty string which evaluates to "True" in Python, but empty output + will evaluate to "False". + +**VALUE**: Your command. Variables will be replaced with Python + format function. Therefore, use curly brackets to refer to variables, + systems, secrets and constants. + +Constants are available via CONST.KEY, where KEY is the key of your + constants in your **CONFIGURATION** file. There you can define some + widely used constants. + +In most cases its a good idea to define your command in quotes to + avoid parsing errors, but it is not always necessary. Another way is + to use '|' to indicate a _literal scalar block_. There you can even + define whole program structures for python (see example). + +#### Escaping in Pipeline + +Because automatix uses Python's format() function: +`{` -> `{{` +`}` -> `}}` + +Standard YAML escapes (see also https://yaml.org/spec/1.2/spec.html): +`'` -> `''` +`"` -> `\"` +`\ ` -> `\\` +`:` -> Please use quotes (double or single). + + +### ALWAYS / CLEANUP PIPELINE + +Same usage as the 'normal' command pipeline, but will be executed + every time at start of automatix (**always**) or at the end + (**cleanup**) even if aborted (a). The commands are executed without + --interactive flag, independend of the specified parameters. + +Intended use case for **always**: python imports or informations that + are needed afterwards and do not change anything on systems. + You want to have these available even if using --jump|-j feature. + +Intended use case for **cleanup**: Remove temporary files or artifacts. + + +## ENVIRONMENT + +**AUTOMATIX_CONFIG**: Specify the path to the configuration file. + Default is "~/.automatix.cfg.yaml". + +**AUTOMATIX_TIME**: Set this to an arbitrary value to print the times + for the single steps and the whole script. + +**ENCODING**: Specify output encoding. Default is "UTF-8". + +Additionally you can modify the environment to adjust things to your + needs. + + +# TIPS & TRICKS + +If you want to access variables in **python** action you defined in +preceeding command, you can use the **PERSISTENT_VARS** dictionary +(shortcut: **PVARS**). +This is added to the local scope of **python** actions and the +dictonary keys are also available as attributes. + Examples: +- To make all local variables of the actual command persistent use + `PERSISTENT_VARS.update(locals())`. +- To delete one persistent variable named "myvar" use + `del PERSISTENT_VARS['myvar']` +- To make variable "v2" persistent use `PERSISTENT_VARS['v2'] = v2` + or `PERSISTENT_VARS.v2 = v2` +- Use the shortcut like `PVARS.v2 = v2` + +You can use variables in PERSISTENT_VARS also as condition by +using the shortcut and the attribute notation: + + - python: PVARS.cond = some_function() + - PVARS.cond?local: echo 'This is only printed if "some_function" evaluates to "True"' + +An alternative is to make variables global, but in most cases using + PERSISTENT_VARS is more clean. _**CAUTION: Choosing already existing + (Python) variable names may lead to unexpected behaviour!!!**_ Maybe + you want to check the source code (command.py). +Explanation: automatix is written in Python and uses 'exec' to + execute the command in function context. If you declare variables + globally they remain across commands. + +To abort the current automatix and jump to the next batch item you can + raise the `SkipBatchItemException`. For aborting the whole automatix + process raise `AbortException(return_code: int)`. In both cases the + cleanup pipeline is executed. Same is the case for selecting + `a`:abort or `c`:continue when asked (interactive or error). + + +# BEST PRACTISES + +There are different ways to start scripting with **automatix**. The + author's approach is mainly to consider the process and simply write + down, what to do (manual steps for complex or not automated steps) + and which commands to use. +Then start **automatix** in interactive mode (-i) and adjust the + single steps one by one. Replace manual steps, if suitable. Whenever + adjustment is needed, abort, adjust and restart **automatix** with + jump (-j) to the adjusted step. +Repeat this procedure to automate more and more and increase quality, + whenever you feel like it. + +Consider to put often used paths or code sequences in automatix + variables for better readability. +Do the same with variable content like URLs, to make it possible to + overwrite it by command line options. Where ever possible prefer to + use functions to determine already available information, such as BW + metadata, instead of defining things explicitly. This will make + things easier when using the script with different systems / + parameters. + +Preferred way of using **automatix** is to put often used and complex + algorithms in shell functions or python libraries (shelllib/pylib) + and import them. Advantage of this approach is that you can use your + implemented functions multiple times and build up a toolbox of nice + functionality over time. + + +# NOTES + +**Manual steps** will always cause automatix to stop and wait for + user input. + +Be careful with **assignments** containing line breaks (echo, ...). + Using the variables may lead to unexpected behaviour or errors. + +Assignments containing **null bytes** are currently not supported. + +Because the **always** pipeline should not change anything, aborting + while running this pipeline will not trigger a cleanup. + +If you want to abort the **pipeline** without triggering the + **cleanup** pipeline, use CRTL+C. + +While **aborting remote functions** (via imports), automatix is not + able to determine still running processes invoked by the function, + because it only checks the processes for the commands (in this case + the function name) which is called in the pipeline. + +# EXTRAS + +## Bash completion (experimental) +Automatix supports bash completion for parameters and the script directory via [argcomplete](https://github.com/kislyuk/argcomplete). + +Therefor follow the installation instructions for argcomplete, which is at the current time + + pip install argcomplete + +and either global activation via executing + + activate-global-python-argcomplete + +or activation for automatix (e.g. in `.bashrc`) + + eval "$(register-python-argcomplete automatix)" + +Automatix will recognize the installed module and offer the completion automatically. + + +%prep +%autosetup -n automatix-1.11.2 + +%build +%py3_build + +%install +%py3_install +install -d -m755 %{buildroot}/%{_pkgdocdir} +if [ -d doc ]; then cp -arf doc %{buildroot}/%{_pkgdocdir}; fi +if [ -d docs ]; then cp -arf docs %{buildroot}/%{_pkgdocdir}; fi +if [ -d example ]; then cp -arf example %{buildroot}/%{_pkgdocdir}; fi +if [ -d examples ]; then cp -arf examples %{buildroot}/%{_pkgdocdir}; fi +pushd %{buildroot} +if [ -d usr/lib ]; then + find usr/lib -type f -printf "\"/%h/%f\"\n" >> filelist.lst +fi +if [ -d usr/lib64 ]; then + find usr/lib64 -type f -printf "\"/%h/%f\"\n" >> filelist.lst +fi +if [ -d usr/bin ]; then + find usr/bin -type f -printf "\"/%h/%f\"\n" >> filelist.lst +fi +if [ -d usr/sbin ]; then + find usr/sbin -type f -printf "\"/%h/%f\"\n" >> filelist.lst +fi +touch doclist.lst +if [ -d usr/share/man ]; then + find usr/share/man -type f -printf "\"/%h/%f.gz\"\n" >> doclist.lst +fi +popd +mv %{buildroot}/filelist.lst . +mv %{buildroot}/doclist.lst . + +%files -n python3-automatix -f filelist.lst +%dir %{python3_sitelib}/* + +%files help -f doclist.lst +%{_docdir}/* + +%changelog +* Fri Jun 09 2023 Python_Bot <Python_Bot@openeuler.org> - 1.11.2-1 +- Package Spec generated @@ -0,0 +1 @@ +067bf765d1b280bca9d9cdedb88cee4b automatix-1.11.2.tar.gz |