ansible-taskrunner


Nameansible-taskrunner JSON
Version 2.11.0 PyPI version JSON
download
home_pagehttps://github.com/berttejeda/ansible-taskrunner.git
SummaryAnsible Task Runner
upload_time2024-05-26 19:25:08
maintainerNone
docs_urlNone
authorEngelbert Tejeda
requires_python>=2.7
licenseNone
keywords ansible playbook wrapper bash python click task-runner subprocess yaml cli options
VCS
bugtrack_url
requirements No requirements were recorded.
Travis-CI
coveralls test coverage No coveralls.
            <!-- START doctoc generated TOC please keep comment here to allow auto update -->
<!-- DON'T EDIT THIS SECTION, INSTEAD RE-RUN doctoc TO UPDATE -->
**Table of Contents**  *generated with [DocToc](https://github.com/thlorenz/doctoc)*

- [Overview](#overview)
- [TL;DR](#tldr)
- [Use case and example](#use-case-and-example)
    - [Given](#given)
    - [The Challenge](#the-challenge)
    - [Investigation](#investigation)
    - [Assessment](#assessment)
    - [Proposed Solution](#proposed-solution)
- [Technical Details](#technical-details)
- [Creating a task manifest file](#creating-a-task-manifest-file)
    - [Add the hosts block](#add-the-hosts-block)
    - [Add the vars block](#add-the-vars-block)
    - [Populate the vars block - defaults](#populate-the-vars-block---defaults)
    - [Populate the vars block - define global options](#populate-the-vars-block---define-global-options)
    - [Populate the vars block - define subcommands](#populate-the-vars-block---define-subcommands)
      - [Populate the vars block - cli options - mapped variables](#populate-the-vars-block---cli-options---mapped-variables)
    - [Populate the vars block - help/message](#populate-the-vars-block---helpmessage)
    - [Populate the vars block - embedded shell functions](#populate-the-vars-block---embedded-shell-functions)
      - [More about embedded shell functions](#more-about-embedded-shell-functions)
        - [Bash example:](#bash-example)
        - [Python example:](#python-example)
        - [Ruby example:](#ruby-example)
    - [Populate the vars block - dynamic inventory expression](#populate-the-vars-block---dynamic-inventory-expression)
    - [Populate the vars block - inventory file](#populate-the-vars-block---inventory-file)
    - [Add tasks](#add-tasks)
- [Usage Examples](#usage-examples)
- [Installation](#installation)
    - [More Examples](#more-examples)
- [Appendix](#appendix)
    - [The Options Separator](#the-options-separator)
    - [Bastion Mode](#bastion-mode)
    - [Special Variables](#special-variables)
      - [ansible_playbook_command](#ansible_playbook_command)
      - [pre_execution](#pre_execution)
      - [post_execution](#post_execution)
      - [environment_vars](#environment_vars)
        - [ANSIBLE_ Variables](#ansible_-variables)
      - [cli_provider](#cli_provider)
      - [__ansible_run_flags__](#__ansible_run_flags__)
      - [__tasks_file__](#__tasks_file__)
      - [__command__](#__command__)
    - [Mutually Exclusive Options](#mutually-exclusive-options)
    - [Cloned subcommands](#cloned-subcommands)
    - [Simple Templating](#simple-templating)
    - [Single-Executable Releases](#single-executable-releases)
    - [Unit Testing](#unit-testing)
- [TODO - Add more tests!](#todo---add-more-tests)
- [License and Credits](#license-and-credits)

<!-- END doctoc generated TOC please keep comment here to allow auto update -->


<a name="top"></a>
<a name="overview"></a>

# Overview

*ansible-taskrunner* is a cli app that is essentially an ansible wrapper.

It reads an ansible playbook file as its input, which serves as a _task manifest_.

If no task manifest is specified, the app will search for 'Taskfile.yaml' in the current working directory.

The inspiration for the tool comes from the gnu make command, which operates in similar fashion, i.e.

- A Makefile defines available build steps
- The make command consumes the Makefile at runtime and exposes these steps as command-line options

If you are running this tool from Windows, please read the section on [Bastion Mode](#bastion_mode)

# TL;DR

- Ever wanted to add custom switches to the `ansible-playbook` command? Something like this:<br /> 
`ansible-playbook -i myinventory.txt -d dbhost1 -w webhost1 -t value1 myplaybook.yaml`
- Well, you can through the use of an ansible-playbook wrapper
- That's where `tasks` comes in:<br />
`tasks -s -b bar -f foo1`<br />
translates to:<br />
`ansible-playbook -i /tmp/ansible-inventory16xdkrjd.tmp.ini \
-e "{'some_foo_variable':'foo1'}" -e "{'some_bar_variable':'bar'}" -e "{'playbook_targets':'local'}" Taskfile.yaml`

1. Jump down to the [usage examples](#usage-examples) to see this in action
2. Review the [installation](#installation) instructions if you want to test-drive it
3. Read on if you want to dig deeper into the tool

<a name="use-case-and-example"></a>

# Use case and example

<a name="given"></a>

## Given

1. An enterprise-grade application named contoso-app
2. Multiple teams:
    - Development
    - Engineering
    - DBA
    - Operations
    - QA
3. Ansible is the primary means of invoking business and operational processes across the numerous environments

<a name="the-challenge"></a>

## The Challenge

You must ensure all teams adopt a standardized approach to running ansible workloads

<a name="investigation"></a>

## Investigation

Upon investigating the current approach, you observe the following: 

- Users tend to create wrapper scripts that call the ansible-playbook command
- These scripts don't follow any naming convention, as you've noted:
  - run.sh
  - start.sh
  - playbook.sh
- These shell scripts have common attributes:
  - Dynamically populate ansible-playbook variables via the --extra-vars option
  - Dynamically creating ansible inventories
  - Performing pre/post-flight tasks
  - Providing a command-line interface

<a name="assessment"></a>

## Assessment

Advantages to the above approach:
- Quick-n-dirty, anyone can get started relatively quickly with writing ansible automation

Disadvantages:
- Lack of standards: 
- Leads to difficulty in collaboration and code refactoring
- Decreased re-usability of codebase
  - This design encourages standalone playbooks
  - Makes it more difficult to package actions as roles
  - Duplicate efforts across codebase

<a name="proposed-solution"></a>

## Proposed Solution

Employ a pre-execution script that operates at a layer above the `ansible-playbook` command:

- Accomplishes the same as the above, but in more uniform manner
- Support for custom command-line parameters/flags
- Dynamic inventory expression
- Embedded shell functions

Advantages to this approach:

- Easier to manage
  - If you know YAML and Ansible, you can get started relatively quickly with writing ansible automation
- Single executable (/usr/local/bin/tasks)

Disadvantages:

- Target ansible controller needs to have the `tasks` command installed

[Back To Top](#top)
<a name="technical-details"></a>

# Technical Details

As stated in the [overview](#overview), this tool functions much like the *make* command in that 
it accepts an input file that essentially extends its cli options.

We create a specially formatted ansible-playbook that serves as a task manifest file (by default, Taskfile.yaml).

This task manifest file:

- Extends the `tasks` command
- Is a valid ansible playbook (Taskfile.yaml), and can thus be launched with the `ansible-playbook` command
- Variables available to the pre-execution phase are also available to the ansible execution phase

<a name="creating-a-task-manifest-file"></a>

# Creating a task manifest file

In the following sections, we'll be building a sample manifest/playbook.

Start by opening up your favorite text/IDE/editor and creating 
a new _task manifest file_ named *Taskfile.yaml*.

<a name="add-the-hosts-block"></a>

## Add the hosts block

Add hosts, gather_facts, etc:

<details>
  <summary>Click to Expand</summary>

*Taskfile.yaml*

```
### The hosts block
- hosts: myhosts
  gather_facts: true
  become: true
```

</details>

<a name="add-the-vars-block"></a>

## Add the vars block

Remember, the task runner will ultimately be calling the `ansible-playbook` command against 
this very same file, <br />so it must be a valid ansible playbook.

Let's add the 'vars' block, which allows us to populate some default values:

<details>
  <summary>Click to Expand</summary>

*Taskfile.yaml*

```
### The hosts block
- hosts: myhosts
  gather_facts: true
  become: true
### The vars block
  vars:
```

</details>

<a name="populate-the-vars-block---defaults"></a>

## Populate the vars block - defaults

Let's add some default variables to the playbook:

<details>
  <summary>Click to Expand</summary>

*Taskfile.yaml*

```
### The hosts block
- hosts: myhosts
  gather_facts: true
  become: true
  ### The vars block  
  vars:
    var1: value1
    var2: value2
    var3: value3
    var4: |-
      This is a multi-line value
      of type string
    var5:
      - listvalue1
      - listvalue2
      - listvalue3
      - listvalue4
    var6:
      some_key:
        some_child_key: dictvalue1
    var7: $(echo some_value)
    var8: 8000
    dbhosts:
      - dbhost1
      - dbhost2
      - dbhost3
    webhosts:
      - webhost1
      - webhost2
      - webhost3
```
</details>

<br />As you can see, we've defined a number of variables holding different values.

The rules for evaluation of these are as follows:<br /><br />


```
Variable                                     | Ansible Evaluation      | Shell Function Evaluation
-------------------------------------------- | ----------------------- | -----------------------
str_var: value1                              | String                  | String
bool_var: True                               | Boolean                 | String
num_var: 3                                   | Integer                 | Integer
multiline_var: |                             | Multiline String        | String (heredoc)
  This is a multi-line value                 |                         |
  of type string                             |                         |
list_var:                                    | List Object             | String (heredoc)
  - item1                                    |                         |
  - item2                                    |                         |
dict_var:                                    | Dictionary Object       | JSON String (heredoc)
  key1: somevalue1                           |                         |
  key2: somevalue2                           |                         |
shell_var: $(grep somestring /some/file.txt) | String                  | Depends on output
```

Additionally, arguments supplied from the command-line conform<br />
to the type specified in the options definition, with "string" being the default type.

[Back To Top](#top)

<a name="populate-the-vars-block---define-global-options"></a>
## Populate the vars block - define global options

Global options are available to all subcommands.

These are defined under the `vars.globals.options` key.

Let's add a simple example:

<details>
  <summary>Click to Expand</summary>

*Taskfile.yaml*

```
### The hosts block
- hosts: myhosts
  gather_facts: true
  become: true
  ### The vars block  
  vars:
    var1: value1
    var2: value2
    var3: value3
    var4: |-
      This is a multi-line value
      of type string
    var5:
      - listvalue1
      - listvalue2
      - listvalue3
      - listvalue4
    var6:
      some_key:
        some_child_key: dictvalue1
    var7: $(echo some_value)
    var8: 8000
    dbhosts:
      - dbhost1
      - dbhost2
      - dbhost3
    webhosts:
      - webhost1
      - webhost2
      - webhost3
    globals:
      options:
          my_global_option:
            help: "This is my global option"
            short: -g
            long: --global
            var: some_global_variable

```  
</details>

<a name="populate-the-vars-block---define-subcommands"></a>
## Populate the vars block - define subcommands

Next, we define the available subcommands and their options.

Let's add a sub-command named `run` along with its command-line options:

<details>
  <summary>Click to Expand</summary>

*Taskfile.yaml*

```
### The hosts block
- hosts: myhosts
  gather_facts: true
  become: true
  ### The vars block  
  vars:
    var1: value1
    var2: value2
    var3: value3
    var4: |-
      This is a multi-line value
      of type string
    var5:
      - listvalue1
      - listvalue2
      - listvalue3
      - listvalue4
    var6:
      some_key:
        some_child_key: dictvalue1
    var7: $(echo some_value)
    var8: 8000
    dbhosts:
      - dbhost1
      - dbhost2
      - dbhost3
    webhosts:
      - webhost1
      - webhost2
      - webhost3
    ### Global Options Block
    globals:
      options:
          my_global_option:
            help: "This is my global option"
            short: -g
            long: --global
            var: some_global_variable    
    ### The commands block
    commands:
      run:
        options:
          foo:
            help: "This is some foo option"
            short: -f
            long: --foo
            type: choice
            var: some_foo_variable
            required: True
            not_required_if: 
              - some_bar_variable
            options:
              - foo1
              - foo2
          bar:
            help: "This is some bar option"
            short: -b
            long: --bar 
            type: str
            var: some_bar_variable
            required: False
            required_if: 
              - hello
              - some_baz_variable
          baz:
            help: "This is some baz option"
            short: -z
            long: --baz
            type: str
            var: some_baz_variable
            required: False
            mutually_exclusive_with: 
              - some_bar_variable
              - some_foo_variable
          envvar:
            help: "The value for this argument can be derived from an Environmental Variable"
            short: -E
            long: --env-var
            type: str
            var: env_var
            env_var: SOME_ENVIRONMENT_VARIABLE
            env_var_show: True
          num:
            help: "This is a numeric argument"
            short: -n
            long: --number
            var: some_num_variable
            type: int
            required: False 
            env_var_show: True
          targets:
            help: "Playbook targets"
            short: -t
            long: --targets
            type: str
            var: playbook_targets
            required: True
          multiple:
            help: |-
              This option can be specified multiple times
            short: -m
            long: --multiple
            type: str
            var: multiple_arg
            allow_multiple: True
          some_switch:
            help: |-
              This is some boolean option, behaves like Click's switches,
              holds the value of True if specified
              see: https://github.com/pallets/click
            short: -s
            long: --some-switch
            is_flag: true
            var: some_switch
            required: True
          say_hello:
            help: "Invoke the 'hello' embedded shell function"
            short: -hello
            long: --say-hello
            type: str
            var: hello
            is_flag: True
          say_goodbye:
            help: "Invoke the 'goodbye' embedded shell function"
            short: -goodbye
            long: --say-goodbye
            type: str
            var: goodbye
            is_flag: True
          hidden_option:
            help: "This is a hidden option"
            short: -O
            long: --hidden-option
            is_hidden: True
            type: str
            var: hidden
            is_flag: True            
          verbose:
            help: |-
              This is a sample paramter that supports counting, as with:
              -v, -vv, -vvv, which would evaluate to 1, 2, and 3, respectively
            short: -v
            allow_counting: True
            var: verbosity 
```   
</details>

<br />

As you can see, commands are defined via YAML, and the syntax is mostly self-explanatory.

Currently, the parameters available to any given option are <br />
consistent with click version 8.1.x, see [API — Click Documentation (8.1.x)](https://click.palletsprojects.com/en/8.1.x/api/)

**Important Notes**: 

- An option's _var_ key:
    - In the case of standard options, this variable holds the value of the arguments passed to the option
    - In the case of flags/switches, this variable is a Boolean
    - The variable is available during the entire runtime
- In the above example, the `-hello` and `-goodbye` options point to special mapped<br />
  variables that themselves map to corresponding shell functions defined in the subcommand's<br />
  functions directive. We'll discuss this more in section [embedded-shell-functions](#embedded-shell-functions).

<a name="populate-the-vars-block---cli-options---mapped-variables"></a>

### Populate the vars block - cli options - mapped variables

As I mentioned before, the above mapped variables can be used **during runtime**.<br />
That is, they can be referenced in any defined shell functions, <br />
dynamic inventory expression logic, as well as during ansible execution.

Consider the `-f|-foo` from the example.

Whatever argument you pass to this option becomes the value for the mapped variable `some_foo_variable`.

Again, this variable is made available to the underlying subprocess call, and thus to ansible.

So when we call the tasks command like so `tasks run -f foo1`, the value for the `some_foo_variable` becomes `foo`.

<a name="populate-the-vars-block---helpmessage"></a>

## Populate the vars block - help/message

Next, we add the help/message section:

<details>
  <summary>Click to Expand</summary>

*Taskfile.yaml*

```
### The hosts block
- hosts: myhosts
  gather_facts: true
  become: true
  ### The vars block  
  vars:
    var1: value1
    var2: value2
    var3: value3
    var4: |-
      This is a multi-line value
      of type string
    var5:
      - listvalue1
      - listvalue2
      - listvalue3
      - listvalue4
    var6:
      some_key:
        some_child_key: dictvalue1
    var7: $(echo some_value)
    var8: 8000
    dbhosts:
      - dbhost1
      - dbhost2
      - dbhost3
    webhosts:
      - webhost1
      - webhost2
      - webhost3
    ### Global Options Block
    globals:
      options:
          my_global_option:
            help: "This is my global option"
            short: -g
            long: --global
            var: some_global_variable 
    ### The commands block
    commands:
      run:
        options:
          foo:
            help: "This is some foo option"
            short: -f
            long: --foo
            type: choice
            var: some_foo_variable
            required: True
            not_required_if: 
              - some_bar_variable
            options:
              - foo1
              - foo2
          bar:
            help: "This is some bar option"
            short: -b
            long: --bar 
            type: str
            var: some_bar_variable
            required: False
            required_if: 
              - hello
              - some_baz_variable
          baz:
            help: "This is some baz option"
            short: -z
            long: --baz
            type: str
            var: some_baz_variable
            required: False
            mutually_exclusive_with: 
              - some_bar_variable
              - some_foo_variable
          envvar:
            help: "The value for this argument can be derived from an Environmental Variable"
            short: -E
            long: --env-var
            type: str
            var: env_var
            env_var: SOME_ENVIRONMENT_VARIABLE
            env_var_show: True
          num:
            help: "This is a numeric argument"
            short: -n
            long: --number
            var: some_num_variable
            type: int
            required: False 
            env_var_show: True
          targets:
            help: "Playbook targets"
            short: -t
            long: --targets
            type: str
            var: playbook_targets
            required: True
          multiple:
            help: |-
              This option can be specified multiple times
            short: -m
            long: --multiple
            type: str
            var: multiple_arg
            allow_multiple: True
          some_switch:
            help: |-
              This is some boolean option, behaves like Click's switches,
              holds the value of True if specified
              see: https://github.com/pallets/click
            short: -s
            long: --some-switch
            is_flag: true
            var: some_switch
            required: True
          say_hello:
            help: "Invoke the 'hello' embedded shell function"
            short: -hello
            long: --say-hello
            type: str
            var: hello
            is_flag: True
          say_goodbye:
            help: "Invoke the 'goodbye' embedded shell function"
            short: -goodbye
            long: --say-goodbye
            type: str
            var: goodbye
            is_flag: True
          hidden_option:
            help: "This is a hidden option"
            short: -O
            long: --hidden-option
            is_hidden: True
            type: str
            var: hidden
            is_flag: True            
          verbose:
            help: |-
              This is a sample paramter that supports counting, as with:
              -v, -vv, -vvv, which would evaluate to 1, 2, and 3, respectively
            short: -v
            allow_counting: True
            var: verbosity
        help:
          message: |
            Invoke the 'run' command 
          epilog: |
            This line will be displayed at the end of the help text message
          examples:
            - example1: |
                tasks $command
            - example2: |
                Usage example 2
```

</details>

Running `tasks run --help` should return the list of parameters along with the help message you defined.

<a name="populate-the-vars-block---embedded-shell-functions"></a>

## Populate the vars block - embedded shell functions

<details>
  <summary>Add embedded shell functions: </summary>

*Taskfile.yaml*

```
### The hosts block
- hosts: myhosts
  gather_facts: true
  become: true
  ### The vars block  
  vars:
    var1: value1
    var2: value2
    var3: value3
    var4: |-
      This is a multi-line value
      of type string
    var5:
      - listvalue1
      - listvalue2
      - listvalue3
      - listvalue4
    var6:
      some_key:
        some_child_key: dictvalue1
    var7: $(echo some_value)
    var8: 8000
    dbhosts:
      - dbhost1
      - dbhost2
      - dbhost3
    webhosts:
      - webhost1
      - webhost2
      - webhost3
    ### Global Options Block
    globals:
      options:
          my_global_option:
            help: "This is my global option"
            short: -g
            long: --global
            var: some_global_variable 
    ### The commands block
    commands:
      run:
        options:
          foo:
            help: "This is some foo option"
            short: -f
            long: --foo
            type: choice
            var: some_foo_variable
            required: True
            not_required_if: 
              - some_bar_variable
            options:
              - foo1
              - foo2
          bar:
            help: "This is some bar option"
            short: -b
            long: --bar 
            type: str
            var: some_bar_variable
            required: False
            required_if: 
              - hello
              - some_baz_variable
          baz:
            help: "This is some baz option"
            short: -z
            long: --baz
            type: str
            var: some_baz_variable
            required: False
            mutually_exclusive_with: 
              - some_bar_variable
              - some_foo_variable
          envvar:
            help: "The value for this argument can be derived from an Environmental Variable"
            short: -E
            long: --env-var
            type: str
            var: env_var
            env_var: SOME_ENVIRONMENT_VARIABLE
            env_var_show: True
          num:
            help: "This is a numeric argument"
            short: -n
            long: --number
            var: some_num_variable
            type: int
            required: False 
            env_var_show: True
          targets:
            help: "Playbook targets"
            short: -t
            long: --targets
            type: str
            var: playbook_targets
            required: True
          multiple:
            help: |-
              This option can be specified multiple times
            short: -m
            long: --multiple
            type: str
            var: multiple_arg
            allow_multiple: True
          some_switch:
            help: |-
              This is some boolean option, behaves like Click's switches,
              holds the value of True if specified
              see: https://github.com/pallets/click
            short: -s
            long: --some-switch
            is_flag: true
            var: some_switch
            required: True
          say_hello:
            help: "Invoke the 'hello' embedded shell function"
            short: -hello
            long: --say-hello
            type: str
            var: hello
            is_flag: True
          say_goodbye:
            help: "Invoke the 'goodbye' embedded shell function"
            short: -goodbye
            long: --say-goodbye
            type: str
            var: goodbye
            is_flag: True
          hidden_option:
            help: "This is a hidden option"
            short: -O
            long: --hidden-option
            is_hidden: True
            type: str
            var: hidden
            is_flag: True            
          verbose:
            help: |-
              This is a sample paramter that supports counting, as with:
              -v, -vv, -vvv, which would evaluate to 1, 2, and 3, respectively
            short: -v
            allow_counting: True
            var: verbosity
        ### The help message
        help:
          message: |
            Invoke the 'run' command 
          epilog: |
            This line will be displayed at the end of the help text message
          examples:
            - example1: |
                tasks $command
            - example2: |
                Usage example 2
        ### Embedded shell functions
        functions:
          hello:
            shell: bash
            help: Say hello
            source: |-
              echo Hello! The value for var1 is $var1
          goodbye:
            shell: bash
            help: Say goodbye
            source: |-
              echo The value for var1 is $var1. Goodbye!
```

</details>

<a name="embedded-shell-functions"></a>

Again, notice the two switches `-hello` and `-goodbye`, with mapped variables _hello_ and _goodbye_, respectively.

These mapped variables correspond to keys in the `functions` block with matching names.

As such, specifying either or both `-hello` and `-goodbye` in your `tasks run` invocation<br />
will short-circuit normal operation and execute the corresponding functions<br /> 
**in the order in which you call them**.

Try it yourself by running:

- `tasks run -t local -s -b bar -m one -m two -vvv -O -hello -goodbye`
- `tasks run -t local -s -b bar -m one -m two -vvv -O -goodbye -hello`

There is also a special flag `---invoke-function` that is globally available to all subcommands.

Invocation is as follows: `tasks <subcommand> ---invoke-function <function_name>`.

In our example, we would run: `tasks run -t local -s -b bar -m one -m two -vvv -O ---invoke-function hello`

For more usage examples, see the [appendix](#usage-examples).

<a name="more-about-embedded-shell-functions"></a>

### More about embedded shell functions

Let's briefly side-step into embedded shell functions.

The syntax for nesting these under the _functions_ key is as follows:

```
          name_of_function:
            shell: bash, ruby, or python
            help: Help Text to Display
            hidden: false/true
            source: |-
              {{ code }}
```

[Back To Top](#top)
<a name="bash-example"></a>

#### Bash example:

```
          hello:
            shell: bash
            help: Hello World in Bash
            hidden: false
            source: |-
              echo 'Hello World!'
```

<a name="python-example"></a>

#### Python example:

```
          hello:
            shell: python
            help: Hello World in Python
            hidden: false
            source: |-
              print('Hello World!')
```

<a name="ruby-example"></a>

#### Ruby example:

```
          hello:
            shell: ruby
            help: Hello World in Ruby
            hidden: false
            source: |-
              puts 'Hello World!'
```

[Back To Top](#top)
<a name="populate-the-vars-block---dynamic-inventory-expression"></a>

## Populate the vars block - dynamic inventory expression

A useful feature of this tool is the ability to define your ansible<br />
inventory as a dynamic expression in the Taskfile itself.

To do so, we populate the with the _inventory_expression_ key.

When the inventory is defined in this manner, the logic is as follows:

- The inventory expression is evaluated
- An ephemeral inventory file is created with the<br />
  contents of this file being the output, or result, of that expression
- The fully qualified path to the ephemeral inventory file is specified as the<br />
  argument to the `ansible-playbook` inventory parameter `-i`

Let's define our inventory expression:

<details>
  <summary>Click to Expand</summary>

*Taskfile.yaml*

```
### The hosts block
- hosts: myhosts
  gather_facts: true
  become: true
  ### The vars block  
  vars:
    var1: value1
    var2: value2
    var3: value3
    var4: |-
      This is a multi-line value
      of type string
    var5:
      - listvalue1
      - listvalue2
      - listvalue3
      - listvalue4
    var6:
      some_key:
        some_child_key: dictvalue1
    var7: $(echo some_value)
    var8: 8000
    dbhosts:
      - dbhost1
      - dbhost2
      - dbhost3
    webhosts:
      - webhost1
      - webhost2
      - webhost3
    ### Global Options Block
    globals:
      options:
          my_global_option:
            help: "This is my global option"
            short: -g
            long: --global
            var: some_global_variable 
    ### The commands block
    commands:
      run:
        options:
          foo:
            help: "This is some foo option"
            short: -f
            long: --foo
            type: choice
            var: some_foo_variable
            required: True
            not_required_if: 
              - some_bar_variable
            options:
              - foo1
              - foo2
          bar:
            help: "This is some bar option"
            short: -b
            long: --bar 
            type: str
            var: some_bar_variable
            required: False
            required_if: 
              - hello
              - some_baz_variable
          baz:
            help: "This is some baz option"
            short: -z
            long: --baz
            type: str
            var: some_baz_variable
            required: False
            mutually_exclusive_with: 
              - some_bar_variable
              - some_foo_variable
          envvar:
            help: "The value for this argument can be derived from an Environmental Variable"
            short: -E
            long: --env-var
            type: str
            var: env_var
            env_var: SOME_ENVIRONMENT_VARIABLE
            env_var_show: True
          num:
            help: "This is a numeric argument"
            short: -n
            long: --number
            var: some_num_variable
            type: int
            required: False 
            env_var_show: True
          targets:
            help: "Playbook targets"
            short: -t
            long: --targets
            type: str
            var: playbook_targets
            required: True
          multiple:
            help: |-
              This option can be specified multiple times
            short: -m
            long: --multiple
            type: str
            var: multiple_arg
            allow_multiple: True
          some_switch:
            help: |-
              This is some boolean option, behaves like Click's switches,
              holds the value of True if specified
              see: https://github.com/pallets/click
            short: -s
            long: --some-switch
            is_flag: true
            var: some_switch
            required: True
          say_hello:
            help: "Invoke the 'hello' embedded shell function"
            short: -hello
            long: --say-hello
            type: str
            var: hello
            is_flag: True
          say_goodbye:
            help: "Invoke the 'goodbye' embedded shell function"
            short: -goodbye
            long: --say-goodbye
            type: str
            var: goodbye
            is_flag: True
          hidden_option:
            help: "This is a hidden option"
            short: -O
            long: --hidden-option
            is_hidden: True
            type: str
            var: hidden
            is_flag: True            
          verbose:
            help: |-
              This is a sample paramter that supports counting, as with:
              -v, -vv, -vvv, which would evaluate to 1, 2, and 3, respectively
            short: -v
            allow_counting: True
            var: verbosity
        ### The help message
        help:
          message: |
            Invoke the 'run' command 
          epilog: |
            This line will be displayed at the end of the help text message
          examples:
            - example1: |
                tasks $command
            - example2: |
                Usage example 2
        ### Embedded shell functions
        functions:
          hello:
            shell: bash
            help: Say hello
            source: |-
              echo Hello! The value for var1 is $var1
          goodbye:
            shell: bash
            help: Say goodbye
            source: |-
              echo The value for var1 is $var1. Goodbye!
    ### The inventory expression              
    inventory_expression: |
      [local]
      localhost ansible_connection=local
      [web_hosts]
      $(echo -e "${webhosts}" | tr ',' '\n')
      [db_hosts]
      $(echo -e "${dbhosts}" | tr ',' '\n')
      [myhosts:children]
      web_hosts
      db_hosts
```

</details>

As you can see, the inventory expression is dynamic, as<br />
it gets evaluated based on the output of inline shell commands.

Let's focus on the variable _$webhosts_.

As per the logic described [above](#populate-the-vars-block---defaults), the variable $webhosts is a heredoc with a value of:

```
webhosts='
webhost1
webhost2
webhost3
'
```

As such, the _web_hosts_ group in the inventory expression ...
```
      [web_hosts]
      $(echo -e "${webhosts}" | tr ',' '\n')
```

... will evaluate to:

```
[web_hosts]
webhost1
webhost2
webhost3
```

Also, notice how the inline shell command transforms commas into newline characters by way of the transform (`tr`) command.

This makes it so that if we were to have defined the _webhosts_ variable<br />
in the Tasksfile as `webhosts: webhost1,webhost2,webhost3`, it would have had the same outcome<br />
as defining it as a list object in the _vars_ block.

<a name="populate-the-vars-block---inventory-file"></a>

## Populate the vars block - inventory file

We can specify an inventory file instead of an inventory expression with the _inventory_file_ key.

<details>
  <summary>Click to Expand</summary>

*Taskfile.yaml*

```
### The hosts block
- hosts: myhosts
  gather_facts: true
  become: true
  ### The vars block  
  vars:
    var1: value1
    var2: value2
    var3: value3
    var4: |-
      This is a multi-line value
      of type string
    var5:
      - listvalue1
      - listvalue2
      - listvalue3
      - listvalue4
    var6:
      some_key:
        some_child_key: dictvalue1
    var7: $(echo some_value)
    var8: 8000
    dbhosts:
      - dbhost1
      - dbhost2
      - dbhost3
    webhosts:
      - webhost1
      - webhost2
      - webhost3
    ### Global Options Block
    globals:
      options:
          my_global_option:
            help: "This is my global option"
            short: -g
            long: --global
            var: some_global_variable 
    ### The commands block
    commands:
      run:
        options:
          foo:
            help: "This is some foo option"
            short: -f
            long: --foo
            type: choice
            var: some_foo_variable
            required: True
            not_required_if: 
              - some_bar_variable
            options:
              - foo1
              - foo2
          bar:
            help: "This is some bar option"
            short: -b
            long: --bar 
            type: str
            var: some_bar_variable
            required: False
            required_if: 
              - hello
              - some_baz_variable
          baz:
            help: "This is some baz option"
            short: -z
            long: --baz
            type: str
            var: some_baz_variable
            required: False
            mutually_exclusive_with: 
              - some_bar_variable
              - some_foo_variable
          envvar:
            help: "The value for this argument can be derived from an Environmental Variable"
            short: -E
            long: --env-var
            type: str
            var: env_var
            env_var: SOME_ENVIRONMENT_VARIABLE
            env_var_show: True
          num:
            help: "This is a numeric argument"
            short: -n
            long: --number
            var: some_num_variable
            type: int
            required: False 
            env_var_show: True
          targets:
            help: "Playbook targets"
            short: -t
            long: --targets
            type: str
            var: playbook_targets
            required: True
          multiple:
            help: |-
              This option can be specified multiple times
            short: -m
            long: --multiple
            type: str
            var: multiple_arg
            allow_multiple: True
          some_switch:
            help: |-
              This is some boolean option, behaves like Click's switches,
              holds the value of True if specified
              see: https://github.com/pallets/click
            short: -s
            long: --some-switch
            is_flag: true
            var: some_switch
            required: True
          say_hello:
            help: "Invoke the 'hello' embedded shell function"
            short: -hello
            long: --say-hello
            type: str
            var: hello
            is_flag: True
          say_goodbye:
            help: "Invoke the 'goodbye' embedded shell function"
            short: -goodbye
            long: --say-goodbye
            type: str
            var: goodbye
            is_flag: True
          hidden_option:
            help: "This is a hidden option"
            short: -O
            long: --hidden-option
            is_hidden: True
            type: str
            var: hidden
            is_flag: True            
          verbose:
            help: |-
              This is a sample paramter that supports counting, as with:
              -v, -vv, -vvv, which would evaluate to 1, 2, and 3, respectively
            short: -v
            allow_counting: True
            var: verbosity
        ### The help message
        help:
          message: |
            Invoke the 'run' command 
          epilog: |
            This line will be displayed at the end of the help text message
          examples:
            - example1: |
                tasks $command
            - example2: |
                Usage example 2
        ### Embedded shell functions
        functions:
          hello:
            shell: bash
            help: Say hello
            source: |-
              echo Hello! The value for var1 is $var1
          goodbye:
            shell: bash
            help: Say goodbye
            source: |-
              echo The value for var1 is $var1. Goodbye!
    ### Inventory file
    inventory_file: '/some/path/some/inventory.yaml'
```

</details>

**Notes of Importance**:

- The value you provide to the _inventory_file_ key supports templating
    - That is, any of the variables available runtime variables can be used, for example:
        - `inventory_file: '/some/path/some/inventory_$foo_variable.yaml'`
        - `inventory_file: '/some/path/some/inventory_$var1.yaml'`
- You should not be specifying both an _inventory_file_ and an _inventory_expression_, as you get unexpected results.

[Back To Top](#top)
<a name="add-tasks"></a>

## Add tasks

Finally, let's add some proper ansible tasks!

<details>
  <summary>Click to Expand</summary>

*Taskfile.yaml*

```
### The hosts block
- hosts: myhosts
  gather_facts: true
  become: true
  ### The vars block  
  vars:
    var1: value1
    var2: value2
    var3: value3
    var4: |-
      This is a multi-line value
      of type string
    var5:
      - listvalue1
      - listvalue2
      - listvalue3
      - listvalue4
    var6:
      some_key:
        some_child_key: dictvalue1
    var7: $(echo some_value)
    var8: 8000
    dbhosts:
      - dbhost1
      - dbhost2
      - dbhost3
    webhosts:
      - webhost1
      - webhost2
      - webhost3
    ### Global Options Block
    globals:
      options:
          my_global_option:
            help: "This is my global option"
            short: -g
            long: --global
            var: some_global_variable 
    ### The commands block
    commands:
      run:
        options:
          foo:
            help: "This is some foo option"
            short: -f
            long: --foo
            type: choice
            var: some_foo_variable
            required: True
            not_required_if: 
              - some_bar_variable
            options:
              - foo1
              - foo2
          bar:
            help: "This is some bar option"
            short: -b
            long: --bar 
            type: str
            var: some_bar_variable
            required: False
            required_if: 
              - hello
              - some_baz_variable
          baz:
            help: "This is some baz option"
            short: -z
            long: --baz
            type: str
            var: some_baz_variable
            required: False
            mutually_exclusive_with: 
              - some_bar_variable
              - some_foo_variable
          envvar:
            help: "The value for this argument can be derived from an Environmental Variable"
            short: -E
            long: --env-var
            type: str
            var: env_var
            env_var: SOME_ENVIRONMENT_VARIABLE
            env_var_show: True
          num:
            help: "This is a numeric argument"
            short: -n
            long: --number
            var: some_num_variable
            type: int
            required: False 
            env_var_show: True
          targets:
            help: "Playbook targets"
            short: -t
            long: --targets
            type: str
            var: playbook_targets
            required: True
          multiple:
            help: |-
              This option can be specified multiple times
            short: -m
            long: --multiple
            type: str
            var: multiple_arg
            allow_multiple: True
          some_switch:
            help: |-
              This is some boolean option, behaves like Click's switches,
              holds the value of True if specified
              see: https://github.com/pallets/click
            short: -s
            long: --some-switch
            is_flag: true
            var: some_switch
            required: True
          say_hello:
            help: "Invoke the 'hello' embedded shell function"
            short: -hello
            long: --say-hello
            type: str
            var: hello
            is_flag: True
          say_goodbye:
            help: "Invoke the 'goodbye' embedded shell function"
            short: -goodbye
            long: --say-goodbye
            type: str
            var: goodbye
            is_flag: True
          hidden_option:
            help: "This is a hidden option"
            short: -O
            long: --hidden-option
            is_hidden: True
            type: str
            var: hidden
            is_flag: True            
          verbose:
            help: |-
              This is a sample paramter that supports counting, as with:
              -v, -vv, -vvv, which would evaluate to 1, 2, and 3, respectively
            short: -v
            allow_counting: True
            var: verbosity
        ### The help message
        help:
          message: |
            Invoke the 'run' command 
          epilog: |
            This line will be displayed at the end of the help text message
          examples:
            - example1: |
                tasks $command
            - example2: |
                Usage example 2
        ### Embedded shell functions
        functions:
          hello:
            shell: bash
            help: Say hello
            source: |-
              echo Hello! The value for var1 is $var1
          goodbye:
            shell: bash
            help: Say goodbye
            source: |-
              echo The value for var1 is $var1. Goodbye!
    ### The inventory expression
    inventory_expression: |
      [local]
      localhost ansible_connection=local
      [web_hosts]
      $(echo -e "${webhosts}" | tr ',' '\n')
      [db_hosts]
      $(echo -e "${dbhosts}" | tr ',' '\n')
      [myhosts:children]
      web_hosts
      db_hosts            
  tasks:
    - name: Show Variables
      debug:
        msg: |-
          {{ hostvars[inventory_hostname] | to_nice_json }}
```

</details>

<br />
The task above will display all available host variables.

<a name="usage-examples"></a>

# Usage Examples

Quick usage examples:

* Display help for main command<br />
  `tasks --help`
* Display help for the *run* subcommand<br />
  `tasks run --help`
* Initialize your workspace<br />
  `tasks init`<br />
* Run the Taskfile.yaml playbook, passing in additional options to the underlying subprocess<br />
  `tasks run -t local -s -b bar -m one -m two`</br>
* Don't do anything, just echo the underlying shell command<br />
  `tasks run -t local -s -b bar -m one -m two -O ---echo`<br />
  Result should be similar to:<br />
  `ansible-playbook -i /var/folders/5f/4g4xnnv958q52108qxd2rj_r0000gn/T/ansible-inventorytlmz2hpz.tmp.ini \
  -e "{'var1':'${var1}'}" ... Taskfile.yaml`
* Run the embedded function `hello`<br />
  `tasks run -t local -s -b bar -m one -m two -hello`
* Run the embedded functions `hello` and `goodbye`<br />
  `run -t local -s -b bar -m one -m two -hello -goodbye`

[Back To Top](#top)
<a name="installation"></a>

# Installation

Ansible-taskrunner consists of the `tasks` command.

It can be installed in a few ways:

1. pip install ansible-taskrunner
2. pip install git+https://github.com/berttejeda/ansible-taskrunner.git
3. Obtaining a [release](#single-executable-releases) (these lag behind the pip distributions)

Note: You'll need to pre-install a python distribution for the Windows MSI release.
Not yet sure if I am doing something wrong or if that's by design.
I lean toward the former :|

<a name="more-examples"></a>

## More Examples

Review the [examples](examples) directory for more hands-on usage samples.

<a name="appendix"></a>

# Appendix


<a name="the-options-separator"></a>
## The Options Separator

When you pass the `---` options separator to any subcommand, anything
after the separator is passed directly to the ansible subprocess.

<a name="bastion-mode"></a>
## Bastion Mode

If you're launching the `tasks` command from a Windows host, this tool will automatically execute in _Bastion Mode_

Under Bastion Mode, the `tasks` command will:
- Execute the `ansible-playbook` subprocess via a _bastion host_, i.e. a remote machine that _should have_ `ansible` installed
- This is done via ssh using the [paramiko](http://www.paramiko.org/) module

Running in Bastion Mode requires a configuration file containing the ssh connection settings.

To initialize this configuration file, you can simply run `tasks init`.

For full usage options, enter in `tasks init --help`.

Once you've initialized the configuration file, you should see *sftp-config.json* in your workspace.

This configuration file is fashioned after the [sftp](https://packagecontrol.io/packages/SFTP)<br />
plugin for [Sublime Text](https://www.sublimetext.com/) and is thus compatible.

<a name="special-variables"></a>

## Special Variables

<a name="ansible_playbook_command"></a>

### ansible_playbook_command

If you define the playbook variable *ansible_playbook_command*, this will override the underlying ansible-playbook command invocation.

As an example, suppose I define this variable in the above *Taskfile.yaml*, as follows:

```
- hosts: myhosts
  gather_facts: true
  become: true
  vars:
    ansible_playbook_command: 'python ${HOME}/ansible_2.7.8/ansible-playbook'
    var1: value1
    var2: value2
    var3: value3
    # ...
```
Upon invoking the `tasks` command with the `---echo` flag:

- The temporary inventory would be revealed as:<br />

```
inventory_is_ephemeral=True
if [[ "$inventory_is_ephemeral" == "True" ]];then
echo -e """${inventory_expression}"""| while read line;do
 eval "echo -e ${line}" >> "/var/folders/some/path/ansible-inventoryo4fw4ttc.tmp.ini";
done
fi;
```

\*_The above inventory file path will differ, of course_

- And the underlying shell command would be revealed as:<br />

```
python ${HOME}/ansible_2.7.8/ansible-playbook \
-i /var/folders/some/path/ansible-inventoryo4fw4ttc.tmp.ini \
-e "{'var1':'${var1}'}" \
-e "{'var2':'${var2}'}" \
-e "{'var3':'${var3}'}" \
... Taskfile.yaml
```

[Back To Top](#top)
<a name="pre_execution"></a>

### pre_execution

Anything defined under the *pre_execution* variable will be evaluated
**before** all other statements in the underlying shell expression.

As an example, suppose I define the pre_execution variable in the above *Taskfile.yaml*, as follows:

```
- hosts: myhosts
  gather_facts: true
  become: true
  vars:
    pre_execution: |-
      export pxe_var=some_value
      touch /tmp/.run.lock
```

Upon invoking the `tasks` command with the `---echo` flag:

- The underlying shell expression would be revealed as:<br />

```
...
export pxe_var=some_value
touch /tmp/.run.lock
...
```

The commands above are always placed **before**<br />
all variables declarations in the underlying shell expresison.

<a name="post_execution"></a>

### post_execution

This is similar to *pre_execution*, except that anything defined under the *post_execution* variable will be evaluated
**after** all other statements in the underlying shell expression.

<a name="environment_vars"></a>

### environment_vars

By defining the playbook dictionary variable *environment_vars*,<br />
the following occurs:

- For each dictionary `key: value` pair:
    - A corresponding `export` statement is defined in the underlying shell expression

As an example, suppose I define this variable in the above *Taskfile.yaml*, as follows:

```
- hosts: myhosts
  gather_facts: true
  become: true
  vars:
    ansible_playbook_command: 'python ${HOME}/ansible_2.7.8/ansible-playbook'
    var1: value1
    var2: value2
    var3: value3
    some_path: /some/path
    environment_vars:
      MY_ENV_VAR1: "${some_path}/${var1}"    
      MY_ENV_VAR2: "${some_path}/${var2}"    
    # ...
```

Upon invoking the `tasks` command with the `---echo` flag:

- The underlying shell expression would be revealed as:<br />

```
var1="value1"
var2="value2"
export MY_ENV_VAR1="${some_path}/${var1}"
export MY_ENV_VAR2="${some_path}/${var2}"
```

These export statements are always placed **after**<br />
all variables declarations in the underlying shell expresison.

#### ANSIBLE_ Variables

Any variables matching ANSIBLE_.* will automatically be expressed as export statements.

As an example, suppose I define such variables in the above *Taskfile.yaml*, as follows:

```
- hosts: myhosts
  gather_facts: true
  become: true
  vars:
    var1: value1
    var2: value2
    ANSIBLE_VAULT_PASSWORD_FILE: /some/path/some/password_file
    ANSIBLE_CALLBACK_PLUGINS: /some/other/path/some/plugins
    # ...
```

Upon invoking the `tasks` command with the `---echo` flag:

- The underlying shell expression would be revealed as:<br />

```
var1="value1"
var2="value2"
export ANSIBLE_VAULT_PASSWORD_FILE="/some/path/some/password_file"
export ANSIBLE_CALLBACK_PLUGINS="/some/other/path/some/plugins"
```

<a name="cli_provider"></a>

### cli_provider

You can override the underlying command-line provider in two ways:

- Via the tasks config file (see [examples](#examples))
- By defining the variable *cli_provider* in the specified Taskfile

As an example, suppose I define this variable in the above *Taskfile.yaml*, as follows:

```
- hosts: myhosts
  gather_facts: true
  become: true
  vars:
    cli_provider: bash
    # ...
```

Upon invoking the `tasks` command, you will note that the app no longer operates in an **ansible-playbook** mode, but rather as yaml-abstracted bash-script.

There are three cli-providers built in to the tasks command:

- ansible
- bash
- vagrant

<a name="__ansible_run_flags__"></a>

### __ansible_run_flags__

Apart from utilizing the `---` options separator, you can specify additional options to pass to the underlying `ansible-playbook` subprocess by setting an appropriate value for **\_\_ansible_run_flags\_\_** 
via Environmental variable or Taskfile, as with:

`__ansible_run_flags__: --diff`

or 

`export __ansible_run_flags__='--diff'`

<a name="__tasks_file__"></a>

### __tasks_file__

The **\_\_tasks_file\_\_** variable points to the current Taskfile.

It is available to the underlying subprocess shell.

### __command__

The **\_\_command\_\_** variable points to the name of the invoked subcommand.

It is available to the underlying subprocess shell.

[Back To Top](#top)
<a name="advanced-options"></a>

## Mutually Exclusive Options

This tool supports the following advanced options:

  - Mutually Exclusive, see [Mutually exclusive option groups in python Click - Stack Overflow](https://stackoverflow.com/questions/37310718/mutually-exclusive-option-groups-in-python-click).
  - Mutually Inclusive
  - Conditionally required

Suppose you want a set of options such that:
- You want to accept one option but only if another, related option has not been specified

You can accomplish this by defining your option with the following parameters:

```
  - required: False
  - mutually_exclusive_with: 
    - some_bar_variable
    - some_foo_variable
```

In the above configuration, calling this option along with options<br /> 
`-f|-foo` and `-b|-bar` will trigger an illegal usage error, since you've<br />
marked the option as mutually exclusive with either of these two options.

Feel free to review the [Taskfile.yaml](Taskfile.yaml), as you'll find an example of:

- mutually exclusive
- mutually inclusive
- conditionally required

<a name="cloned-subcommands"></a>

## Cloned subcommands

There might exist edge cases in which you need two different
subcommands to be defined with identical commandline options.

This could be useful for, say, subcommands like `install` or `uninstall`,
where the only differentiating factor between the two would be the
name of the subcommand being invoked.

To enable this, simply declare your subcommand with a pipe `|` delimiter, as with:

```
    commands:
      install|uninstall:
        options:
          foo:
            help: "Install/Uninstall an app"
            short: -n
            long: --app-name
            type: choice
            var: app_name
            required: True
```

<a name="simple-templating"></a>

## Simple Templating

As of version 1.1.5, simple templating is available to the following objects:

- Help messages
- Examples
- Options
- Options values

What this means is that we expose a limited set of internal variables to the above.

As an example:

```
        help:
          message: |
            Invoke the 'run' command 
          epilog: |
            This line will be displayed at the end of the help text message
          examples:
            - example1: |
                tasks -f $tf_path --foo foo --bar bar
            - example2: |
                tasks -f $tf_path --foo foo --baz baz
```            

In the above strings, `$tf_path` will expand to the internal variable tf_path,
which holds the relative path to the current tasks file.

Below is a list of available variables for your convenience:

```
Variable        | Description
-------------   | -------------
__command__     | The name of the current subcommand
cli_args        | The current command-line invocation
cli_args_short  | The current command-line invocation, minus the executable
sys_platform    | The OS Platform as detected by Python
tf_path         | The relative path to the specified Taskfile
```

Additionally, all **currently set environmental variables** are available for templating.

[Back To Top](#top)
<a name="single-executable-releases"></a>

## Single-Executable Releases

This script also ships as a zipapp executable (similar to a windows .exe).

Head over to the [releases page](https://github.com/berttejeda/ansible-taskrunner/releases) for release downloads.

You can also build your own single-executable zipapp, as follows:

1. Make sure you have the [make-zipapp](https://github.com/berttejeda/make-zipapp) executable in your path
1. Invoking build tasks
  - Build zipapp: `python ansible_taskrunner/cli.py -f Makefile.yaml run ---make zipapp`

Read More on zipapps: [zipapp — Manage executable Python zip archives — Python 3.7.4rc2 documentation](https://docs.python.org/3/library/zipapp.html)

<a name="unit-testing"></a>

## Unit Testing

To run all tests, simply call the test script, as with:

`python tests/test_ansible_taskrunner.py`

# TODO - Add more tests!

[Back To Top](#top)
<a name="license-and-credits"></a>

# License and Credits

This project adopts the the MIT distribution License.

[Releases](https://github.com/berttejeda/ansible-taskrunner/releases) come bundled with the following opensource python packages:

- [click](https://github.com/pallets/click), licensed under BSD-3-Clause
- [pyYaml](https://github.com/yaml/pyyaml), licensed under MIT

Lastly, this package was created with Cookiecutter and the `audreyr/cookiecutter-pypackage` project template.

- Cookiecutter: https://github.com/audreyr/cookiecutter
- audreyr/cookiecutter-pypackage: https://github.com/audreyr/cookiecutter-pypackage

            

Raw data

            {
    "_id": null,
    "home_page": "https://github.com/berttejeda/ansible-taskrunner.git",
    "name": "ansible-taskrunner",
    "maintainer": null,
    "docs_url": null,
    "requires_python": ">=2.7",
    "maintainer_email": null,
    "keywords": "ansible, playbook, wrapper, bash, python, click, task-runner, subprocess, yaml, cli, options",
    "author": "Engelbert Tejeda",
    "author_email": "etejeda@tecknicos.com",
    "download_url": "https://files.pythonhosted.org/packages/56/59/eedb61fafb4f3d9d150469f8fc68c4994f3c5157c4c7c5f1e8bf68d934bd/ansible_taskrunner-2.11.0.tar.gz",
    "platform": null,
    "description": "<!-- START doctoc generated TOC please keep comment here to allow auto update -->\n<!-- DON'T EDIT THIS SECTION, INSTEAD RE-RUN doctoc TO UPDATE -->\n**Table of Contents**  *generated with [DocToc](https://github.com/thlorenz/doctoc)*\n\n- [Overview](#overview)\n- [TL;DR](#tldr)\n- [Use case and example](#use-case-and-example)\n    - [Given](#given)\n    - [The Challenge](#the-challenge)\n    - [Investigation](#investigation)\n    - [Assessment](#assessment)\n    - [Proposed Solution](#proposed-solution)\n- [Technical Details](#technical-details)\n- [Creating a task manifest file](#creating-a-task-manifest-file)\n    - [Add the hosts block](#add-the-hosts-block)\n    - [Add the vars block](#add-the-vars-block)\n    - [Populate the vars block - defaults](#populate-the-vars-block---defaults)\n    - [Populate the vars block - define global options](#populate-the-vars-block---define-global-options)\n    - [Populate the vars block - define subcommands](#populate-the-vars-block---define-subcommands)\n      - [Populate the vars block - cli options - mapped variables](#populate-the-vars-block---cli-options---mapped-variables)\n    - [Populate the vars block - help/message](#populate-the-vars-block---helpmessage)\n    - [Populate the vars block - embedded shell functions](#populate-the-vars-block---embedded-shell-functions)\n      - [More about embedded shell functions](#more-about-embedded-shell-functions)\n        - [Bash example:](#bash-example)\n        - [Python example:](#python-example)\n        - [Ruby example:](#ruby-example)\n    - [Populate the vars block - dynamic inventory expression](#populate-the-vars-block---dynamic-inventory-expression)\n    - [Populate the vars block - inventory file](#populate-the-vars-block---inventory-file)\n    - [Add tasks](#add-tasks)\n- [Usage Examples](#usage-examples)\n- [Installation](#installation)\n    - [More Examples](#more-examples)\n- [Appendix](#appendix)\n    - [The Options Separator](#the-options-separator)\n    - [Bastion Mode](#bastion-mode)\n    - [Special Variables](#special-variables)\n      - [ansible_playbook_command](#ansible_playbook_command)\n      - [pre_execution](#pre_execution)\n      - [post_execution](#post_execution)\n      - [environment_vars](#environment_vars)\n        - [ANSIBLE_ Variables](#ansible_-variables)\n      - [cli_provider](#cli_provider)\n      - [__ansible_run_flags__](#__ansible_run_flags__)\n      - [__tasks_file__](#__tasks_file__)\n      - [__command__](#__command__)\n    - [Mutually Exclusive Options](#mutually-exclusive-options)\n    - [Cloned subcommands](#cloned-subcommands)\n    - [Simple Templating](#simple-templating)\n    - [Single-Executable Releases](#single-executable-releases)\n    - [Unit Testing](#unit-testing)\n- [TODO - Add more tests!](#todo---add-more-tests)\n- [License and Credits](#license-and-credits)\n\n<!-- END doctoc generated TOC please keep comment here to allow auto update -->\n\n\n<a name=\"top\"></a>\n<a name=\"overview\"></a>\n\n# Overview\n\n*ansible-taskrunner* is a cli app that is essentially an ansible wrapper.\n\nIt reads an ansible playbook file as its input, which serves as a _task manifest_.\n\nIf no task manifest is specified, the app will search for 'Taskfile.yaml' in the current working directory.\n\nThe inspiration for the tool comes from the gnu make command, which operates in similar fashion, i.e.\n\n- A Makefile defines available build steps\n- The make command consumes the Makefile at runtime and exposes these steps as command-line options\n\nIf you are running this tool from Windows, please read the section on [Bastion Mode](#bastion_mode)\n\n# TL;DR\n\n- Ever wanted to add custom switches to the `ansible-playbook` command? Something like this:<br /> \n`ansible-playbook -i myinventory.txt -d dbhost1 -w webhost1 -t value1 myplaybook.yaml`\n- Well, you can through the use of an ansible-playbook wrapper\n- That's where `tasks` comes in:<br />\n`tasks -s -b bar -f foo1`<br />\ntranslates to:<br />\n`ansible-playbook -i /tmp/ansible-inventory16xdkrjd.tmp.ini \\\n-e \"{'some_foo_variable':'foo1'}\" -e \"{'some_bar_variable':'bar'}\" -e \"{'playbook_targets':'local'}\" Taskfile.yaml`\n\n1. Jump down to the [usage examples](#usage-examples) to see this in action\n2. Review the [installation](#installation) instructions if you want to test-drive it\n3. Read on if you want to dig deeper into the tool\n\n<a name=\"use-case-and-example\"></a>\n\n# Use case and example\n\n<a name=\"given\"></a>\n\n## Given\n\n1. An enterprise-grade application named contoso-app\n2. Multiple teams:\n    - Development\n    - Engineering\n    - DBA\n    - Operations\n    - QA\n3. Ansible is the primary means of invoking business and operational processes across the numerous environments\n\n<a name=\"the-challenge\"></a>\n\n## The Challenge\n\nYou must ensure all teams adopt a standardized approach to running ansible workloads\n\n<a name=\"investigation\"></a>\n\n## Investigation\n\nUpon investigating the current approach, you observe the following: \n\n- Users tend to create wrapper scripts that call the ansible-playbook command\n- These scripts don't follow any naming convention, as you've noted:\n  - run.sh\n  - start.sh\n  - playbook.sh\n- These shell scripts have common attributes:\n  - Dynamically populate ansible-playbook variables via the --extra-vars option\n  - Dynamically creating ansible inventories\n  - Performing pre/post-flight tasks\n  - Providing a command-line interface\n\n<a name=\"assessment\"></a>\n\n## Assessment\n\nAdvantages to the above approach:\n- Quick-n-dirty, anyone can get started relatively quickly with writing ansible automation\n\nDisadvantages:\n- Lack of standards: \n- Leads to difficulty in collaboration and code refactoring\n- Decreased re-usability of codebase\n  - This design encourages standalone playbooks\n  - Makes it more difficult to package actions as roles\n  - Duplicate efforts across codebase\n\n<a name=\"proposed-solution\"></a>\n\n## Proposed Solution\n\nEmploy a pre-execution script that operates at a layer above the `ansible-playbook` command:\n\n- Accomplishes the same as the above, but in more uniform manner\n- Support for custom command-line parameters/flags\n- Dynamic inventory expression\n- Embedded shell functions\n\nAdvantages to this approach:\n\n- Easier to manage\n  - If you know YAML and Ansible, you can get started relatively quickly with writing ansible automation\n- Single executable (/usr/local/bin/tasks)\n\nDisadvantages:\n\n- Target ansible controller needs to have the `tasks` command installed\n\n[Back To Top](#top)\n<a name=\"technical-details\"></a>\n\n# Technical Details\n\nAs stated in the [overview](#overview), this tool functions much like the *make* command in that \nit accepts an input file that essentially extends its cli options.\n\nWe create a specially formatted ansible-playbook that serves as a task manifest file (by default, Taskfile.yaml).\n\nThis task manifest file:\n\n- Extends the `tasks` command\n- Is a valid ansible playbook (Taskfile.yaml), and can thus be launched with the `ansible-playbook` command\n- Variables available to the pre-execution phase are also available to the ansible execution phase\n\n<a name=\"creating-a-task-manifest-file\"></a>\n\n# Creating a task manifest file\n\nIn the following sections, we'll be building a sample manifest/playbook.\n\nStart by opening up your favorite text/IDE/editor and creating \na new _task manifest file_ named *Taskfile.yaml*.\n\n<a name=\"add-the-hosts-block\"></a>\n\n## Add the hosts block\n\nAdd hosts, gather_facts, etc:\n\n<details>\n  <summary>Click to Expand</summary>\n\n*Taskfile.yaml*\n\n```\n### The hosts block\n- hosts: myhosts\n  gather_facts: true\n  become: true\n```\n\n</details>\n\n<a name=\"add-the-vars-block\"></a>\n\n## Add the vars block\n\nRemember, the task runner will ultimately be calling the `ansible-playbook` command against \nthis very same file, <br />so it must be a valid ansible playbook.\n\nLet's add the 'vars' block, which allows us to populate some default values:\n\n<details>\n  <summary>Click to Expand</summary>\n\n*Taskfile.yaml*\n\n```\n### The hosts block\n- hosts: myhosts\n  gather_facts: true\n  become: true\n### The vars block\n  vars:\n```\n\n</details>\n\n<a name=\"populate-the-vars-block---defaults\"></a>\n\n## Populate the vars block - defaults\n\nLet's add some default variables to the playbook:\n\n<details>\n  <summary>Click to Expand</summary>\n\n*Taskfile.yaml*\n\n```\n### The hosts block\n- hosts: myhosts\n  gather_facts: true\n  become: true\n  ### The vars block  \n  vars:\n    var1: value1\n    var2: value2\n    var3: value3\n    var4: |-\n      This is a multi-line value\n      of type string\n    var5:\n      - listvalue1\n      - listvalue2\n      - listvalue3\n      - listvalue4\n    var6:\n      some_key:\n        some_child_key: dictvalue1\n    var7: $(echo some_value)\n    var8: 8000\n    dbhosts:\n      - dbhost1\n      - dbhost2\n      - dbhost3\n    webhosts:\n      - webhost1\n      - webhost2\n      - webhost3\n```\n</details>\n\n<br />As you can see, we've defined a number of variables holding different values.\n\nThe rules for evaluation of these are as follows:<br /><br />\n\n\n```\nVariable                                     | Ansible Evaluation      | Shell Function Evaluation\n-------------------------------------------- | ----------------------- | -----------------------\nstr_var: value1                              | String                  | String\nbool_var: True                               | Boolean                 | String\nnum_var: 3                                   | Integer                 | Integer\nmultiline_var: |                             | Multiline String        | String (heredoc)\n  This is a multi-line value                 |                         |\n  of type string                             |                         |\nlist_var:                                    | List Object             | String (heredoc)\n  - item1                                    |                         |\n  - item2                                    |                         |\ndict_var:                                    | Dictionary Object       | JSON String (heredoc)\n  key1: somevalue1                           |                         |\n  key2: somevalue2                           |                         |\nshell_var: $(grep somestring /some/file.txt) | String                  | Depends on output\n```\n\nAdditionally, arguments supplied from the command-line conform<br />\nto the type specified in the options definition, with \"string\" being the default type.\n\n[Back To Top](#top)\n\n<a name=\"populate-the-vars-block---define-global-options\"></a>\n## Populate the vars block - define global options\n\nGlobal options are available to all subcommands.\n\nThese are defined under the `vars.globals.options` key.\n\nLet's add a simple example:\n\n<details>\n  <summary>Click to Expand</summary>\n\n*Taskfile.yaml*\n\n```\n### The hosts block\n- hosts: myhosts\n  gather_facts: true\n  become: true\n  ### The vars block  \n  vars:\n    var1: value1\n    var2: value2\n    var3: value3\n    var4: |-\n      This is a multi-line value\n      of type string\n    var5:\n      - listvalue1\n      - listvalue2\n      - listvalue3\n      - listvalue4\n    var6:\n      some_key:\n        some_child_key: dictvalue1\n    var7: $(echo some_value)\n    var8: 8000\n    dbhosts:\n      - dbhost1\n      - dbhost2\n      - dbhost3\n    webhosts:\n      - webhost1\n      - webhost2\n      - webhost3\n    globals:\n      options:\n          my_global_option:\n            help: \"This is my global option\"\n            short: -g\n            long: --global\n            var: some_global_variable\n\n```  \n</details>\n\n<a name=\"populate-the-vars-block---define-subcommands\"></a>\n## Populate the vars block - define subcommands\n\nNext, we define the available subcommands and their options.\n\nLet's add a sub-command named `run` along with its command-line options:\n\n<details>\n  <summary>Click to Expand</summary>\n\n*Taskfile.yaml*\n\n```\n### The hosts block\n- hosts: myhosts\n  gather_facts: true\n  become: true\n  ### The vars block  \n  vars:\n    var1: value1\n    var2: value2\n    var3: value3\n    var4: |-\n      This is a multi-line value\n      of type string\n    var5:\n      - listvalue1\n      - listvalue2\n      - listvalue3\n      - listvalue4\n    var6:\n      some_key:\n        some_child_key: dictvalue1\n    var7: $(echo some_value)\n    var8: 8000\n    dbhosts:\n      - dbhost1\n      - dbhost2\n      - dbhost3\n    webhosts:\n      - webhost1\n      - webhost2\n      - webhost3\n    ### Global Options Block\n    globals:\n      options:\n          my_global_option:\n            help: \"This is my global option\"\n            short: -g\n            long: --global\n            var: some_global_variable    \n    ### The commands block\n    commands:\n      run:\n        options:\n          foo:\n            help: \"This is some foo option\"\n            short: -f\n            long: --foo\n            type: choice\n            var: some_foo_variable\n            required: True\n            not_required_if: \n              - some_bar_variable\n            options:\n              - foo1\n              - foo2\n          bar:\n            help: \"This is some bar option\"\n            short: -b\n            long: --bar \n            type: str\n            var: some_bar_variable\n            required: False\n            required_if: \n              - hello\n              - some_baz_variable\n          baz:\n            help: \"This is some baz option\"\n            short: -z\n            long: --baz\n            type: str\n            var: some_baz_variable\n            required: False\n            mutually_exclusive_with: \n              - some_bar_variable\n              - some_foo_variable\n          envvar:\n            help: \"The value for this argument can be derived from an Environmental Variable\"\n            short: -E\n            long: --env-var\n            type: str\n            var: env_var\n            env_var: SOME_ENVIRONMENT_VARIABLE\n            env_var_show: True\n          num:\n            help: \"This is a numeric argument\"\n            short: -n\n            long: --number\n            var: some_num_variable\n            type: int\n            required: False \n            env_var_show: True\n          targets:\n            help: \"Playbook targets\"\n            short: -t\n            long: --targets\n            type: str\n            var: playbook_targets\n            required: True\n          multiple:\n            help: |-\n              This option can be specified multiple times\n            short: -m\n            long: --multiple\n            type: str\n            var: multiple_arg\n            allow_multiple: True\n          some_switch:\n            help: |-\n              This is some boolean option, behaves like Click's switches,\n              holds the value of True if specified\n              see: https://github.com/pallets/click\n            short: -s\n            long: --some-switch\n            is_flag: true\n            var: some_switch\n            required: True\n          say_hello:\n            help: \"Invoke the 'hello' embedded shell function\"\n            short: -hello\n            long: --say-hello\n            type: str\n            var: hello\n            is_flag: True\n          say_goodbye:\n            help: \"Invoke the 'goodbye' embedded shell function\"\n            short: -goodbye\n            long: --say-goodbye\n            type: str\n            var: goodbye\n            is_flag: True\n          hidden_option:\n            help: \"This is a hidden option\"\n            short: -O\n            long: --hidden-option\n            is_hidden: True\n            type: str\n            var: hidden\n            is_flag: True            \n          verbose:\n            help: |-\n              This is a sample paramter that supports counting, as with:\n              -v, -vv, -vvv, which would evaluate to 1, 2, and 3, respectively\n            short: -v\n            allow_counting: True\n            var: verbosity \n```   \n</details>\n\n<br />\n\nAs you can see, commands are defined via YAML, and the syntax is mostly self-explanatory.\n\nCurrently, the parameters available to any given option are <br />\nconsistent with click version 8.1.x, see [API \u2014 Click Documentation (8.1.x)](https://click.palletsprojects.com/en/8.1.x/api/)\n\n**Important Notes**: \n\n- An option's _var_ key:\n    - In the case of standard options, this variable holds the value of the arguments passed to the option\n    - In the case of flags/switches, this variable is a Boolean\n    - The variable is available during the entire runtime\n- In the above example, the `-hello` and `-goodbye` options point to special mapped<br />\n  variables that themselves map to corresponding shell functions defined in the subcommand's<br />\n  functions directive. We'll discuss this more in section [embedded-shell-functions](#embedded-shell-functions).\n\n<a name=\"populate-the-vars-block---cli-options---mapped-variables\"></a>\n\n### Populate the vars block - cli options - mapped variables\n\nAs I mentioned before, the above mapped variables can be used **during runtime**.<br />\nThat is, they can be referenced in any defined shell functions, <br />\ndynamic inventory expression logic, as well as during ansible execution.\n\nConsider the `-f|-foo` from the example.\n\nWhatever argument you pass to this option becomes the value for the mapped variable `some_foo_variable`.\n\nAgain, this variable is made available to the underlying subprocess call, and thus to ansible.\n\nSo when we call the tasks command like so `tasks run -f foo1`, the value for the `some_foo_variable` becomes `foo`.\n\n<a name=\"populate-the-vars-block---helpmessage\"></a>\n\n## Populate the vars block - help/message\n\nNext, we add the help/message section:\n\n<details>\n  <summary>Click to Expand</summary>\n\n*Taskfile.yaml*\n\n```\n### The hosts block\n- hosts: myhosts\n  gather_facts: true\n  become: true\n  ### The vars block  \n  vars:\n    var1: value1\n    var2: value2\n    var3: value3\n    var4: |-\n      This is a multi-line value\n      of type string\n    var5:\n      - listvalue1\n      - listvalue2\n      - listvalue3\n      - listvalue4\n    var6:\n      some_key:\n        some_child_key: dictvalue1\n    var7: $(echo some_value)\n    var8: 8000\n    dbhosts:\n      - dbhost1\n      - dbhost2\n      - dbhost3\n    webhosts:\n      - webhost1\n      - webhost2\n      - webhost3\n    ### Global Options Block\n    globals:\n      options:\n          my_global_option:\n            help: \"This is my global option\"\n            short: -g\n            long: --global\n            var: some_global_variable \n    ### The commands block\n    commands:\n      run:\n        options:\n          foo:\n            help: \"This is some foo option\"\n            short: -f\n            long: --foo\n            type: choice\n            var: some_foo_variable\n            required: True\n            not_required_if: \n              - some_bar_variable\n            options:\n              - foo1\n              - foo2\n          bar:\n            help: \"This is some bar option\"\n            short: -b\n            long: --bar \n            type: str\n            var: some_bar_variable\n            required: False\n            required_if: \n              - hello\n              - some_baz_variable\n          baz:\n            help: \"This is some baz option\"\n            short: -z\n            long: --baz\n            type: str\n            var: some_baz_variable\n            required: False\n            mutually_exclusive_with: \n              - some_bar_variable\n              - some_foo_variable\n          envvar:\n            help: \"The value for this argument can be derived from an Environmental Variable\"\n            short: -E\n            long: --env-var\n            type: str\n            var: env_var\n            env_var: SOME_ENVIRONMENT_VARIABLE\n            env_var_show: True\n          num:\n            help: \"This is a numeric argument\"\n            short: -n\n            long: --number\n            var: some_num_variable\n            type: int\n            required: False \n            env_var_show: True\n          targets:\n            help: \"Playbook targets\"\n            short: -t\n            long: --targets\n            type: str\n            var: playbook_targets\n            required: True\n          multiple:\n            help: |-\n              This option can be specified multiple times\n            short: -m\n            long: --multiple\n            type: str\n            var: multiple_arg\n            allow_multiple: True\n          some_switch:\n            help: |-\n              This is some boolean option, behaves like Click's switches,\n              holds the value of True if specified\n              see: https://github.com/pallets/click\n            short: -s\n            long: --some-switch\n            is_flag: true\n            var: some_switch\n            required: True\n          say_hello:\n            help: \"Invoke the 'hello' embedded shell function\"\n            short: -hello\n            long: --say-hello\n            type: str\n            var: hello\n            is_flag: True\n          say_goodbye:\n            help: \"Invoke the 'goodbye' embedded shell function\"\n            short: -goodbye\n            long: --say-goodbye\n            type: str\n            var: goodbye\n            is_flag: True\n          hidden_option:\n            help: \"This is a hidden option\"\n            short: -O\n            long: --hidden-option\n            is_hidden: True\n            type: str\n            var: hidden\n            is_flag: True            \n          verbose:\n            help: |-\n              This is a sample paramter that supports counting, as with:\n              -v, -vv, -vvv, which would evaluate to 1, 2, and 3, respectively\n            short: -v\n            allow_counting: True\n            var: verbosity\n        help:\n          message: |\n            Invoke the 'run' command \n          epilog: |\n            This line will be displayed at the end of the help text message\n          examples:\n            - example1: |\n                tasks $command\n            - example2: |\n                Usage example 2\n```\n\n</details>\n\nRunning `tasks run --help` should return the list of parameters along with the help message you defined.\n\n<a name=\"populate-the-vars-block---embedded-shell-functions\"></a>\n\n## Populate the vars block - embedded shell functions\n\n<details>\n  <summary>Add embedded shell functions: </summary>\n\n*Taskfile.yaml*\n\n```\n### The hosts block\n- hosts: myhosts\n  gather_facts: true\n  become: true\n  ### The vars block  \n  vars:\n    var1: value1\n    var2: value2\n    var3: value3\n    var4: |-\n      This is a multi-line value\n      of type string\n    var5:\n      - listvalue1\n      - listvalue2\n      - listvalue3\n      - listvalue4\n    var6:\n      some_key:\n        some_child_key: dictvalue1\n    var7: $(echo some_value)\n    var8: 8000\n    dbhosts:\n      - dbhost1\n      - dbhost2\n      - dbhost3\n    webhosts:\n      - webhost1\n      - webhost2\n      - webhost3\n    ### Global Options Block\n    globals:\n      options:\n          my_global_option:\n            help: \"This is my global option\"\n            short: -g\n            long: --global\n            var: some_global_variable \n    ### The commands block\n    commands:\n      run:\n        options:\n          foo:\n            help: \"This is some foo option\"\n            short: -f\n            long: --foo\n            type: choice\n            var: some_foo_variable\n            required: True\n            not_required_if: \n              - some_bar_variable\n            options:\n              - foo1\n              - foo2\n          bar:\n            help: \"This is some bar option\"\n            short: -b\n            long: --bar \n            type: str\n            var: some_bar_variable\n            required: False\n            required_if: \n              - hello\n              - some_baz_variable\n          baz:\n            help: \"This is some baz option\"\n            short: -z\n            long: --baz\n            type: str\n            var: some_baz_variable\n            required: False\n            mutually_exclusive_with: \n              - some_bar_variable\n              - some_foo_variable\n          envvar:\n            help: \"The value for this argument can be derived from an Environmental Variable\"\n            short: -E\n            long: --env-var\n            type: str\n            var: env_var\n            env_var: SOME_ENVIRONMENT_VARIABLE\n            env_var_show: True\n          num:\n            help: \"This is a numeric argument\"\n            short: -n\n            long: --number\n            var: some_num_variable\n            type: int\n            required: False \n            env_var_show: True\n          targets:\n            help: \"Playbook targets\"\n            short: -t\n            long: --targets\n            type: str\n            var: playbook_targets\n            required: True\n          multiple:\n            help: |-\n              This option can be specified multiple times\n            short: -m\n            long: --multiple\n            type: str\n            var: multiple_arg\n            allow_multiple: True\n          some_switch:\n            help: |-\n              This is some boolean option, behaves like Click's switches,\n              holds the value of True if specified\n              see: https://github.com/pallets/click\n            short: -s\n            long: --some-switch\n            is_flag: true\n            var: some_switch\n            required: True\n          say_hello:\n            help: \"Invoke the 'hello' embedded shell function\"\n            short: -hello\n            long: --say-hello\n            type: str\n            var: hello\n            is_flag: True\n          say_goodbye:\n            help: \"Invoke the 'goodbye' embedded shell function\"\n            short: -goodbye\n            long: --say-goodbye\n            type: str\n            var: goodbye\n            is_flag: True\n          hidden_option:\n            help: \"This is a hidden option\"\n            short: -O\n            long: --hidden-option\n            is_hidden: True\n            type: str\n            var: hidden\n            is_flag: True            \n          verbose:\n            help: |-\n              This is a sample paramter that supports counting, as with:\n              -v, -vv, -vvv, which would evaluate to 1, 2, and 3, respectively\n            short: -v\n            allow_counting: True\n            var: verbosity\n        ### The help message\n        help:\n          message: |\n            Invoke the 'run' command \n          epilog: |\n            This line will be displayed at the end of the help text message\n          examples:\n            - example1: |\n                tasks $command\n            - example2: |\n                Usage example 2\n        ### Embedded shell functions\n        functions:\n          hello:\n            shell: bash\n            help: Say hello\n            source: |-\n              echo Hello! The value for var1 is $var1\n          goodbye:\n            shell: bash\n            help: Say goodbye\n            source: |-\n              echo The value for var1 is $var1. Goodbye!\n```\n\n</details>\n\n<a name=\"embedded-shell-functions\"></a>\n\nAgain, notice the two switches `-hello` and `-goodbye`, with mapped variables _hello_ and _goodbye_, respectively.\n\nThese mapped variables correspond to keys in the `functions` block with matching names.\n\nAs such, specifying either or both `-hello` and `-goodbye` in your `tasks run` invocation<br />\nwill short-circuit normal operation and execute the corresponding functions<br /> \n**in the order in which you call them**.\n\nTry it yourself by running:\n\n- `tasks run -t local -s -b bar -m one -m two -vvv -O -hello -goodbye`\n- `tasks run -t local -s -b bar -m one -m two -vvv -O -goodbye -hello`\n\nThere is also a special flag `---invoke-function` that is globally available to all subcommands.\n\nInvocation is as follows: `tasks <subcommand> ---invoke-function <function_name>`.\n\nIn our example, we would run: `tasks run -t local -s -b bar -m one -m two -vvv -O ---invoke-function hello`\n\nFor more usage examples, see the [appendix](#usage-examples).\n\n<a name=\"more-about-embedded-shell-functions\"></a>\n\n### More about embedded shell functions\n\nLet's briefly side-step into embedded shell functions.\n\nThe syntax for nesting these under the _functions_ key is as follows:\n\n```\n          name_of_function:\n            shell: bash, ruby, or python\n            help: Help Text to Display\n            hidden: false/true\n            source: |-\n              {{ code }}\n```\n\n[Back To Top](#top)\n<a name=\"bash-example\"></a>\n\n#### Bash example:\n\n```\n          hello:\n            shell: bash\n            help: Hello World in Bash\n            hidden: false\n            source: |-\n              echo 'Hello World!'\n```\n\n<a name=\"python-example\"></a>\n\n#### Python example:\n\n```\n          hello:\n            shell: python\n            help: Hello World in Python\n            hidden: false\n            source: |-\n              print('Hello World!')\n```\n\n<a name=\"ruby-example\"></a>\n\n#### Ruby example:\n\n```\n          hello:\n            shell: ruby\n            help: Hello World in Ruby\n            hidden: false\n            source: |-\n              puts 'Hello World!'\n```\n\n[Back To Top](#top)\n<a name=\"populate-the-vars-block---dynamic-inventory-expression\"></a>\n\n## Populate the vars block - dynamic inventory expression\n\nA useful feature of this tool is the ability to define your ansible<br />\ninventory as a dynamic expression in the Taskfile itself.\n\nTo do so, we populate the with the _inventory_expression_ key.\n\nWhen the inventory is defined in this manner, the logic is as follows:\n\n- The inventory expression is evaluated\n- An ephemeral inventory file is created with the<br />\n  contents of this file being the output, or result, of that expression\n- The fully qualified path to the ephemeral inventory file is specified as the<br />\n  argument to the `ansible-playbook` inventory parameter `-i`\n\nLet's define our inventory expression:\n\n<details>\n  <summary>Click to Expand</summary>\n\n*Taskfile.yaml*\n\n```\n### The hosts block\n- hosts: myhosts\n  gather_facts: true\n  become: true\n  ### The vars block  \n  vars:\n    var1: value1\n    var2: value2\n    var3: value3\n    var4: |-\n      This is a multi-line value\n      of type string\n    var5:\n      - listvalue1\n      - listvalue2\n      - listvalue3\n      - listvalue4\n    var6:\n      some_key:\n        some_child_key: dictvalue1\n    var7: $(echo some_value)\n    var8: 8000\n    dbhosts:\n      - dbhost1\n      - dbhost2\n      - dbhost3\n    webhosts:\n      - webhost1\n      - webhost2\n      - webhost3\n    ### Global Options Block\n    globals:\n      options:\n          my_global_option:\n            help: \"This is my global option\"\n            short: -g\n            long: --global\n            var: some_global_variable \n    ### The commands block\n    commands:\n      run:\n        options:\n          foo:\n            help: \"This is some foo option\"\n            short: -f\n            long: --foo\n            type: choice\n            var: some_foo_variable\n            required: True\n            not_required_if: \n              - some_bar_variable\n            options:\n              - foo1\n              - foo2\n          bar:\n            help: \"This is some bar option\"\n            short: -b\n            long: --bar \n            type: str\n            var: some_bar_variable\n            required: False\n            required_if: \n              - hello\n              - some_baz_variable\n          baz:\n            help: \"This is some baz option\"\n            short: -z\n            long: --baz\n            type: str\n            var: some_baz_variable\n            required: False\n            mutually_exclusive_with: \n              - some_bar_variable\n              - some_foo_variable\n          envvar:\n            help: \"The value for this argument can be derived from an Environmental Variable\"\n            short: -E\n            long: --env-var\n            type: str\n            var: env_var\n            env_var: SOME_ENVIRONMENT_VARIABLE\n            env_var_show: True\n          num:\n            help: \"This is a numeric argument\"\n            short: -n\n            long: --number\n            var: some_num_variable\n            type: int\n            required: False \n            env_var_show: True\n          targets:\n            help: \"Playbook targets\"\n            short: -t\n            long: --targets\n            type: str\n            var: playbook_targets\n            required: True\n          multiple:\n            help: |-\n              This option can be specified multiple times\n            short: -m\n            long: --multiple\n            type: str\n            var: multiple_arg\n            allow_multiple: True\n          some_switch:\n            help: |-\n              This is some boolean option, behaves like Click's switches,\n              holds the value of True if specified\n              see: https://github.com/pallets/click\n            short: -s\n            long: --some-switch\n            is_flag: true\n            var: some_switch\n            required: True\n          say_hello:\n            help: \"Invoke the 'hello' embedded shell function\"\n            short: -hello\n            long: --say-hello\n            type: str\n            var: hello\n            is_flag: True\n          say_goodbye:\n            help: \"Invoke the 'goodbye' embedded shell function\"\n            short: -goodbye\n            long: --say-goodbye\n            type: str\n            var: goodbye\n            is_flag: True\n          hidden_option:\n            help: \"This is a hidden option\"\n            short: -O\n            long: --hidden-option\n            is_hidden: True\n            type: str\n            var: hidden\n            is_flag: True            \n          verbose:\n            help: |-\n              This is a sample paramter that supports counting, as with:\n              -v, -vv, -vvv, which would evaluate to 1, 2, and 3, respectively\n            short: -v\n            allow_counting: True\n            var: verbosity\n        ### The help message\n        help:\n          message: |\n            Invoke the 'run' command \n          epilog: |\n            This line will be displayed at the end of the help text message\n          examples:\n            - example1: |\n                tasks $command\n            - example2: |\n                Usage example 2\n        ### Embedded shell functions\n        functions:\n          hello:\n            shell: bash\n            help: Say hello\n            source: |-\n              echo Hello! The value for var1 is $var1\n          goodbye:\n            shell: bash\n            help: Say goodbye\n            source: |-\n              echo The value for var1 is $var1. Goodbye!\n    ### The inventory expression              \n    inventory_expression: |\n      [local]\n      localhost ansible_connection=local\n      [web_hosts]\n      $(echo -e \"${webhosts}\" | tr ',' '\\n')\n      [db_hosts]\n      $(echo -e \"${dbhosts}\" | tr ',' '\\n')\n      [myhosts:children]\n      web_hosts\n      db_hosts\n```\n\n</details>\n\nAs you can see, the inventory expression is dynamic, as<br />\nit gets evaluated based on the output of inline shell commands.\n\nLet's focus on the variable _$webhosts_.\n\nAs per the logic described [above](#populate-the-vars-block---defaults), the variable $webhosts is a heredoc with a value of:\n\n```\nwebhosts='\nwebhost1\nwebhost2\nwebhost3\n'\n```\n\nAs such, the _web_hosts_ group in the inventory expression ...\n```\n      [web_hosts]\n      $(echo -e \"${webhosts}\" | tr ',' '\\n')\n```\n\n... will evaluate to:\n\n```\n[web_hosts]\nwebhost1\nwebhost2\nwebhost3\n```\n\nAlso, notice how the inline shell command transforms commas into newline characters by way of the transform (`tr`) command.\n\nThis makes it so that if we were to have defined the _webhosts_ variable<br />\nin the Tasksfile as `webhosts: webhost1,webhost2,webhost3`, it would have had the same outcome<br />\nas defining it as a list object in the _vars_ block.\n\n<a name=\"populate-the-vars-block---inventory-file\"></a>\n\n## Populate the vars block - inventory file\n\nWe can specify an inventory file instead of an inventory expression with the _inventory_file_ key.\n\n<details>\n  <summary>Click to Expand</summary>\n\n*Taskfile.yaml*\n\n```\n### The hosts block\n- hosts: myhosts\n  gather_facts: true\n  become: true\n  ### The vars block  \n  vars:\n    var1: value1\n    var2: value2\n    var3: value3\n    var4: |-\n      This is a multi-line value\n      of type string\n    var5:\n      - listvalue1\n      - listvalue2\n      - listvalue3\n      - listvalue4\n    var6:\n      some_key:\n        some_child_key: dictvalue1\n    var7: $(echo some_value)\n    var8: 8000\n    dbhosts:\n      - dbhost1\n      - dbhost2\n      - dbhost3\n    webhosts:\n      - webhost1\n      - webhost2\n      - webhost3\n    ### Global Options Block\n    globals:\n      options:\n          my_global_option:\n            help: \"This is my global option\"\n            short: -g\n            long: --global\n            var: some_global_variable \n    ### The commands block\n    commands:\n      run:\n        options:\n          foo:\n            help: \"This is some foo option\"\n            short: -f\n            long: --foo\n            type: choice\n            var: some_foo_variable\n            required: True\n            not_required_if: \n              - some_bar_variable\n            options:\n              - foo1\n              - foo2\n          bar:\n            help: \"This is some bar option\"\n            short: -b\n            long: --bar \n            type: str\n            var: some_bar_variable\n            required: False\n            required_if: \n              - hello\n              - some_baz_variable\n          baz:\n            help: \"This is some baz option\"\n            short: -z\n            long: --baz\n            type: str\n            var: some_baz_variable\n            required: False\n            mutually_exclusive_with: \n              - some_bar_variable\n              - some_foo_variable\n          envvar:\n            help: \"The value for this argument can be derived from an Environmental Variable\"\n            short: -E\n            long: --env-var\n            type: str\n            var: env_var\n            env_var: SOME_ENVIRONMENT_VARIABLE\n            env_var_show: True\n          num:\n            help: \"This is a numeric argument\"\n            short: -n\n            long: --number\n            var: some_num_variable\n            type: int\n            required: False \n            env_var_show: True\n          targets:\n            help: \"Playbook targets\"\n            short: -t\n            long: --targets\n            type: str\n            var: playbook_targets\n            required: True\n          multiple:\n            help: |-\n              This option can be specified multiple times\n            short: -m\n            long: --multiple\n            type: str\n            var: multiple_arg\n            allow_multiple: True\n          some_switch:\n            help: |-\n              This is some boolean option, behaves like Click's switches,\n              holds the value of True if specified\n              see: https://github.com/pallets/click\n            short: -s\n            long: --some-switch\n            is_flag: true\n            var: some_switch\n            required: True\n          say_hello:\n            help: \"Invoke the 'hello' embedded shell function\"\n            short: -hello\n            long: --say-hello\n            type: str\n            var: hello\n            is_flag: True\n          say_goodbye:\n            help: \"Invoke the 'goodbye' embedded shell function\"\n            short: -goodbye\n            long: --say-goodbye\n            type: str\n            var: goodbye\n            is_flag: True\n          hidden_option:\n            help: \"This is a hidden option\"\n            short: -O\n            long: --hidden-option\n            is_hidden: True\n            type: str\n            var: hidden\n            is_flag: True            \n          verbose:\n            help: |-\n              This is a sample paramter that supports counting, as with:\n              -v, -vv, -vvv, which would evaluate to 1, 2, and 3, respectively\n            short: -v\n            allow_counting: True\n            var: verbosity\n        ### The help message\n        help:\n          message: |\n            Invoke the 'run' command \n          epilog: |\n            This line will be displayed at the end of the help text message\n          examples:\n            - example1: |\n                tasks $command\n            - example2: |\n                Usage example 2\n        ### Embedded shell functions\n        functions:\n          hello:\n            shell: bash\n            help: Say hello\n            source: |-\n              echo Hello! The value for var1 is $var1\n          goodbye:\n            shell: bash\n            help: Say goodbye\n            source: |-\n              echo The value for var1 is $var1. Goodbye!\n    ### Inventory file\n    inventory_file: '/some/path/some/inventory.yaml'\n```\n\n</details>\n\n**Notes of Importance**:\n\n- The value you provide to the _inventory_file_ key supports templating\n    - That is, any of the variables available runtime variables can be used, for example:\n        - `inventory_file: '/some/path/some/inventory_$foo_variable.yaml'`\n        - `inventory_file: '/some/path/some/inventory_$var1.yaml'`\n- You should not be specifying both an _inventory_file_ and an _inventory_expression_, as you get unexpected results.\n\n[Back To Top](#top)\n<a name=\"add-tasks\"></a>\n\n## Add tasks\n\nFinally, let's add some proper ansible tasks!\n\n<details>\n  <summary>Click to Expand</summary>\n\n*Taskfile.yaml*\n\n```\n### The hosts block\n- hosts: myhosts\n  gather_facts: true\n  become: true\n  ### The vars block  \n  vars:\n    var1: value1\n    var2: value2\n    var3: value3\n    var4: |-\n      This is a multi-line value\n      of type string\n    var5:\n      - listvalue1\n      - listvalue2\n      - listvalue3\n      - listvalue4\n    var6:\n      some_key:\n        some_child_key: dictvalue1\n    var7: $(echo some_value)\n    var8: 8000\n    dbhosts:\n      - dbhost1\n      - dbhost2\n      - dbhost3\n    webhosts:\n      - webhost1\n      - webhost2\n      - webhost3\n    ### Global Options Block\n    globals:\n      options:\n          my_global_option:\n            help: \"This is my global option\"\n            short: -g\n            long: --global\n            var: some_global_variable \n    ### The commands block\n    commands:\n      run:\n        options:\n          foo:\n            help: \"This is some foo option\"\n            short: -f\n            long: --foo\n            type: choice\n            var: some_foo_variable\n            required: True\n            not_required_if: \n              - some_bar_variable\n            options:\n              - foo1\n              - foo2\n          bar:\n            help: \"This is some bar option\"\n            short: -b\n            long: --bar \n            type: str\n            var: some_bar_variable\n            required: False\n            required_if: \n              - hello\n              - some_baz_variable\n          baz:\n            help: \"This is some baz option\"\n            short: -z\n            long: --baz\n            type: str\n            var: some_baz_variable\n            required: False\n            mutually_exclusive_with: \n              - some_bar_variable\n              - some_foo_variable\n          envvar:\n            help: \"The value for this argument can be derived from an Environmental Variable\"\n            short: -E\n            long: --env-var\n            type: str\n            var: env_var\n            env_var: SOME_ENVIRONMENT_VARIABLE\n            env_var_show: True\n          num:\n            help: \"This is a numeric argument\"\n            short: -n\n            long: --number\n            var: some_num_variable\n            type: int\n            required: False \n            env_var_show: True\n          targets:\n            help: \"Playbook targets\"\n            short: -t\n            long: --targets\n            type: str\n            var: playbook_targets\n            required: True\n          multiple:\n            help: |-\n              This option can be specified multiple times\n            short: -m\n            long: --multiple\n            type: str\n            var: multiple_arg\n            allow_multiple: True\n          some_switch:\n            help: |-\n              This is some boolean option, behaves like Click's switches,\n              holds the value of True if specified\n              see: https://github.com/pallets/click\n            short: -s\n            long: --some-switch\n            is_flag: true\n            var: some_switch\n            required: True\n          say_hello:\n            help: \"Invoke the 'hello' embedded shell function\"\n            short: -hello\n            long: --say-hello\n            type: str\n            var: hello\n            is_flag: True\n          say_goodbye:\n            help: \"Invoke the 'goodbye' embedded shell function\"\n            short: -goodbye\n            long: --say-goodbye\n            type: str\n            var: goodbye\n            is_flag: True\n          hidden_option:\n            help: \"This is a hidden option\"\n            short: -O\n            long: --hidden-option\n            is_hidden: True\n            type: str\n            var: hidden\n            is_flag: True            \n          verbose:\n            help: |-\n              This is a sample paramter that supports counting, as with:\n              -v, -vv, -vvv, which would evaluate to 1, 2, and 3, respectively\n            short: -v\n            allow_counting: True\n            var: verbosity\n        ### The help message\n        help:\n          message: |\n            Invoke the 'run' command \n          epilog: |\n            This line will be displayed at the end of the help text message\n          examples:\n            - example1: |\n                tasks $command\n            - example2: |\n                Usage example 2\n        ### Embedded shell functions\n        functions:\n          hello:\n            shell: bash\n            help: Say hello\n            source: |-\n              echo Hello! The value for var1 is $var1\n          goodbye:\n            shell: bash\n            help: Say goodbye\n            source: |-\n              echo The value for var1 is $var1. Goodbye!\n    ### The inventory expression\n    inventory_expression: |\n      [local]\n      localhost ansible_connection=local\n      [web_hosts]\n      $(echo -e \"${webhosts}\" | tr ',' '\\n')\n      [db_hosts]\n      $(echo -e \"${dbhosts}\" | tr ',' '\\n')\n      [myhosts:children]\n      web_hosts\n      db_hosts            \n  tasks:\n    - name: Show Variables\n      debug:\n        msg: |-\n          {{ hostvars[inventory_hostname] | to_nice_json }}\n```\n\n</details>\n\n<br />\nThe task above will display all available host variables.\n\n<a name=\"usage-examples\"></a>\n\n# Usage Examples\n\nQuick usage examples:\n\n* Display help for main command<br />\n  `tasks --help`\n* Display help for the *run* subcommand<br />\n  `tasks run --help`\n* Initialize your workspace<br />\n  `tasks init`<br />\n* Run the Taskfile.yaml playbook, passing in additional options to the underlying subprocess<br />\n  `tasks run -t local -s -b bar -m one -m two`</br>\n* Don't do anything, just echo the underlying shell command<br />\n  `tasks run -t local -s -b bar -m one -m two -O ---echo`<br />\n  Result should be similar to:<br />\n  `ansible-playbook -i /var/folders/5f/4g4xnnv958q52108qxd2rj_r0000gn/T/ansible-inventorytlmz2hpz.tmp.ini \\\n  -e \"{'var1':'${var1}'}\" ... Taskfile.yaml`\n* Run the embedded function `hello`<br />\n  `tasks run -t local -s -b bar -m one -m two -hello`\n* Run the embedded functions `hello` and `goodbye`<br />\n  `run -t local -s -b bar -m one -m two -hello -goodbye`\n\n[Back To Top](#top)\n<a name=\"installation\"></a>\n\n# Installation\n\nAnsible-taskrunner consists of the `tasks` command.\n\nIt can be installed in a few ways:\n\n1. pip install ansible-taskrunner\n2. pip install git+https://github.com/berttejeda/ansible-taskrunner.git\n3. Obtaining a [release](#single-executable-releases) (these lag behind the pip distributions)\n\nNote: You'll need to pre-install a python distribution for the Windows MSI release.\nNot yet sure if I am doing something wrong or if that's by design.\nI lean toward the former :|\n\n<a name=\"more-examples\"></a>\n\n## More Examples\n\nReview the [examples](examples) directory for more hands-on usage samples.\n\n<a name=\"appendix\"></a>\n\n# Appendix\n\n\n<a name=\"the-options-separator\"></a>\n## The Options Separator\n\nWhen you pass the `---` options separator to any subcommand, anything\nafter the separator is passed directly to the ansible subprocess.\n\n<a name=\"bastion-mode\"></a>\n## Bastion Mode\n\nIf you're launching the `tasks` command from a Windows host, this tool will automatically execute in _Bastion Mode_\n\nUnder Bastion Mode, the `tasks` command will:\n- Execute the `ansible-playbook` subprocess via a _bastion host_, i.e. a remote machine that _should have_ `ansible` installed\n- This is done via ssh using the [paramiko](http://www.paramiko.org/) module\n\nRunning in Bastion Mode requires a configuration file containing the ssh connection settings.\n\nTo initialize this configuration file, you can simply run `tasks init`.\n\nFor full usage options, enter in `tasks init --help`.\n\nOnce you've initialized the configuration file, you should see *sftp-config.json* in your workspace.\n\nThis configuration file is fashioned after the [sftp](https://packagecontrol.io/packages/SFTP)<br />\nplugin for [Sublime Text](https://www.sublimetext.com/) and is thus compatible.\n\n<a name=\"special-variables\"></a>\n\n## Special Variables\n\n<a name=\"ansible_playbook_command\"></a>\n\n### ansible_playbook_command\n\nIf you define the playbook variable *ansible_playbook_command*, this will override the underlying ansible-playbook command invocation.\n\nAs an example, suppose I define this variable in the above *Taskfile.yaml*, as follows:\n\n```\n- hosts: myhosts\n  gather_facts: true\n  become: true\n  vars:\n    ansible_playbook_command: 'python ${HOME}/ansible_2.7.8/ansible-playbook'\n    var1: value1\n    var2: value2\n    var3: value3\n    # ...\n```\nUpon invoking the `tasks` command with the `---echo` flag:\n\n- The temporary inventory would be revealed as:<br />\n\n```\ninventory_is_ephemeral=True\nif [[ \"$inventory_is_ephemeral\" == \"True\" ]];then\necho -e \"\"\"${inventory_expression}\"\"\"| while read line;do\n eval \"echo -e ${line}\" >> \"/var/folders/some/path/ansible-inventoryo4fw4ttc.tmp.ini\";\ndone\nfi;\n```\n\n\\*_The above inventory file path will differ, of course_\n\n- And the underlying shell command would be revealed as:<br />\n\n```\npython ${HOME}/ansible_2.7.8/ansible-playbook \\\n-i /var/folders/some/path/ansible-inventoryo4fw4ttc.tmp.ini \\\n-e \"{'var1':'${var1}'}\" \\\n-e \"{'var2':'${var2}'}\" \\\n-e \"{'var3':'${var3}'}\" \\\n... Taskfile.yaml\n```\n\n[Back To Top](#top)\n<a name=\"pre_execution\"></a>\n\n### pre_execution\n\nAnything defined under the *pre_execution* variable will be evaluated\n**before** all other statements in the underlying shell expression.\n\nAs an example, suppose I define the pre_execution variable in the above *Taskfile.yaml*, as follows:\n\n```\n- hosts: myhosts\n  gather_facts: true\n  become: true\n  vars:\n    pre_execution: |-\n      export pxe_var=some_value\n      touch /tmp/.run.lock\n```\n\nUpon invoking the `tasks` command with the `---echo` flag:\n\n- The underlying shell expression would be revealed as:<br />\n\n```\n...\nexport pxe_var=some_value\ntouch /tmp/.run.lock\n...\n```\n\nThe commands above are always placed **before**<br />\nall variables declarations in the underlying shell expresison.\n\n<a name=\"post_execution\"></a>\n\n### post_execution\n\nThis is similar to *pre_execution*, except that anything defined under the *post_execution* variable will be evaluated\n**after** all other statements in the underlying shell expression.\n\n<a name=\"environment_vars\"></a>\n\n### environment_vars\n\nBy defining the playbook dictionary variable *environment_vars*,<br />\nthe following occurs:\n\n- For each dictionary `key: value` pair:\n    - A corresponding `export` statement is defined in the underlying shell expression\n\nAs an example, suppose I define this variable in the above *Taskfile.yaml*, as follows:\n\n```\n- hosts: myhosts\n  gather_facts: true\n  become: true\n  vars:\n    ansible_playbook_command: 'python ${HOME}/ansible_2.7.8/ansible-playbook'\n    var1: value1\n    var2: value2\n    var3: value3\n    some_path: /some/path\n    environment_vars:\n      MY_ENV_VAR1: \"${some_path}/${var1}\"    \n      MY_ENV_VAR2: \"${some_path}/${var2}\"    \n    # ...\n```\n\nUpon invoking the `tasks` command with the `---echo` flag:\n\n- The underlying shell expression would be revealed as:<br />\n\n```\nvar1=\"value1\"\nvar2=\"value2\"\nexport MY_ENV_VAR1=\"${some_path}/${var1}\"\nexport MY_ENV_VAR2=\"${some_path}/${var2}\"\n```\n\nThese export statements are always placed **after**<br />\nall variables declarations in the underlying shell expresison.\n\n#### ANSIBLE_ Variables\n\nAny variables matching ANSIBLE_.* will automatically be expressed as export statements.\n\nAs an example, suppose I define such variables in the above *Taskfile.yaml*, as follows:\n\n```\n- hosts: myhosts\n  gather_facts: true\n  become: true\n  vars:\n    var1: value1\n    var2: value2\n    ANSIBLE_VAULT_PASSWORD_FILE: /some/path/some/password_file\n    ANSIBLE_CALLBACK_PLUGINS: /some/other/path/some/plugins\n    # ...\n```\n\nUpon invoking the `tasks` command with the `---echo` flag:\n\n- The underlying shell expression would be revealed as:<br />\n\n```\nvar1=\"value1\"\nvar2=\"value2\"\nexport ANSIBLE_VAULT_PASSWORD_FILE=\"/some/path/some/password_file\"\nexport ANSIBLE_CALLBACK_PLUGINS=\"/some/other/path/some/plugins\"\n```\n\n<a name=\"cli_provider\"></a>\n\n### cli_provider\n\nYou can override the underlying command-line provider in two ways:\n\n- Via the tasks config file (see [examples](#examples))\n- By defining the variable *cli_provider* in the specified Taskfile\n\nAs an example, suppose I define this variable in the above *Taskfile.yaml*, as follows:\n\n```\n- hosts: myhosts\n  gather_facts: true\n  become: true\n  vars:\n    cli_provider: bash\n    # ...\n```\n\nUpon invoking the `tasks` command, you will note that the app no longer operates in an **ansible-playbook** mode, but rather as yaml-abstracted bash-script.\n\nThere are three cli-providers built in to the tasks command:\n\n- ansible\n- bash\n- vagrant\n\n<a name=\"__ansible_run_flags__\"></a>\n\n### __ansible_run_flags__\n\nApart from utilizing the `---` options separator, you can specify additional options to pass to the underlying `ansible-playbook` subprocess by setting an appropriate value for **\\_\\_ansible_run_flags\\_\\_** \nvia Environmental variable or Taskfile, as with:\n\n`__ansible_run_flags__: --diff`\n\nor \n\n`export __ansible_run_flags__='--diff'`\n\n<a name=\"__tasks_file__\"></a>\n\n### __tasks_file__\n\nThe **\\_\\_tasks_file\\_\\_** variable points to the current Taskfile.\n\nIt is available to the underlying subprocess shell.\n\n### __command__\n\nThe **\\_\\_command\\_\\_** variable points to the name of the invoked subcommand.\n\nIt is available to the underlying subprocess shell.\n\n[Back To Top](#top)\n<a name=\"advanced-options\"></a>\n\n## Mutually Exclusive Options\n\nThis tool supports the following advanced options:\n\n  - Mutually Exclusive, see [Mutually exclusive option groups in python Click - Stack Overflow](https://stackoverflow.com/questions/37310718/mutually-exclusive-option-groups-in-python-click).\n  - Mutually Inclusive\n  - Conditionally required\n\nSuppose you want a set of options such that:\n- You want to accept one option but only if another, related option has not been specified\n\nYou can accomplish this by defining your option with the following parameters:\n\n```\n  - required: False\n  - mutually_exclusive_with: \n    - some_bar_variable\n    - some_foo_variable\n```\n\nIn the above configuration, calling this option along with options<br /> \n`-f|-foo` and `-b|-bar` will trigger an illegal usage error, since you've<br />\nmarked the option as mutually exclusive with either of these two options.\n\nFeel free to review the [Taskfile.yaml](Taskfile.yaml), as you'll find an example of:\n\n- mutually exclusive\n- mutually inclusive\n- conditionally required\n\n<a name=\"cloned-subcommands\"></a>\n\n## Cloned subcommands\n\nThere might exist edge cases in which you need two different\nsubcommands to be defined with identical commandline options.\n\nThis could be useful for, say, subcommands like `install` or `uninstall`,\nwhere the only differentiating factor between the two would be the\nname of the subcommand being invoked.\n\nTo enable this, simply declare your subcommand with a pipe `|` delimiter, as with:\n\n```\n    commands:\n      install|uninstall:\n        options:\n          foo:\n            help: \"Install/Uninstall an app\"\n            short: -n\n            long: --app-name\n            type: choice\n            var: app_name\n            required: True\n```\n\n<a name=\"simple-templating\"></a>\n\n## Simple Templating\n\nAs of version 1.1.5, simple templating is available to the following objects:\n\n- Help messages\n- Examples\n- Options\n- Options values\n\nWhat this means is that we expose a limited set of internal variables to the above.\n\nAs an example:\n\n```\n        help:\n          message: |\n            Invoke the 'run' command \n          epilog: |\n            This line will be displayed at the end of the help text message\n          examples:\n            - example1: |\n                tasks -f $tf_path --foo foo --bar bar\n            - example2: |\n                tasks -f $tf_path --foo foo --baz baz\n```            \n\nIn the above strings, `$tf_path` will expand to the internal variable tf_path,\nwhich holds the relative path to the current tasks file.\n\nBelow is a list of available variables for your convenience:\n\n```\nVariable        | Description\n-------------   | -------------\n__command__     | The name of the current subcommand\ncli_args        | The current command-line invocation\ncli_args_short  | The current command-line invocation, minus the executable\nsys_platform    | The OS Platform as detected by Python\ntf_path         | The relative path to the specified Taskfile\n```\n\nAdditionally, all **currently set environmental variables** are available for templating.\n\n[Back To Top](#top)\n<a name=\"single-executable-releases\"></a>\n\n## Single-Executable Releases\n\nThis script also ships as a zipapp executable (similar to a windows .exe).\n\nHead over to the [releases page](https://github.com/berttejeda/ansible-taskrunner/releases) for release downloads.\n\nYou can also build your own single-executable zipapp, as follows:\n\n1. Make sure you have the [make-zipapp](https://github.com/berttejeda/make-zipapp) executable in your path\n1. Invoking build tasks\n  - Build zipapp: `python ansible_taskrunner/cli.py -f Makefile.yaml run ---make zipapp`\n\nRead More on zipapps: [zipapp \u2014 Manage executable Python zip archives \u2014 Python 3.7.4rc2 documentation](https://docs.python.org/3/library/zipapp.html)\n\n<a name=\"unit-testing\"></a>\n\n## Unit Testing\n\nTo run all tests, simply call the test script, as with:\n\n`python tests/test_ansible_taskrunner.py`\n\n# TODO - Add more tests!\n\n[Back To Top](#top)\n<a name=\"license-and-credits\"></a>\n\n# License and Credits\n\nThis project adopts the the MIT distribution License.\n\n[Releases](https://github.com/berttejeda/ansible-taskrunner/releases) come bundled with the following opensource python packages:\n\n- [click](https://github.com/pallets/click), licensed under BSD-3-Clause\n- [pyYaml](https://github.com/yaml/pyyaml), licensed under MIT\n\nLastly, this package was created with Cookiecutter and the `audreyr/cookiecutter-pypackage` project template.\n\n- Cookiecutter: https://github.com/audreyr/cookiecutter\n- audreyr/cookiecutter-pypackage: https://github.com/audreyr/cookiecutter-pypackage\n",
    "bugtrack_url": null,
    "license": null,
    "summary": "Ansible Task Runner",
    "version": "2.11.0",
    "project_urls": {
        "Homepage": "https://github.com/berttejeda/ansible-taskrunner.git"
    },
    "split_keywords": [
        "ansible",
        " playbook",
        " wrapper",
        " bash",
        " python",
        " click",
        " task-runner",
        " subprocess",
        " yaml",
        " cli",
        " options"
    ],
    "urls": [
        {
            "comment_text": "",
            "digests": {
                "blake2b_256": "6848c028770971f48b1c7c53ce538450cb0ecdb6ebaec4ffc6ae544b74539b84",
                "md5": "94ceec4e1c94494a62bd58f1fe28a86d",
                "sha256": "8949a14099487acb542998a21b6ea6f104135c82cd1d375c831c3ee8b1839f14"
            },
            "downloads": -1,
            "filename": "ansible_taskrunner-2.11.0-py3-none-any.whl",
            "has_sig": false,
            "md5_digest": "94ceec4e1c94494a62bd58f1fe28a86d",
            "packagetype": "bdist_wheel",
            "python_version": "py3",
            "requires_python": ">=2.7",
            "size": 58516,
            "upload_time": "2024-05-26T19:25:06",
            "upload_time_iso_8601": "2024-05-26T19:25:06.370487Z",
            "url": "https://files.pythonhosted.org/packages/68/48/c028770971f48b1c7c53ce538450cb0ecdb6ebaec4ffc6ae544b74539b84/ansible_taskrunner-2.11.0-py3-none-any.whl",
            "yanked": false,
            "yanked_reason": null
        },
        {
            "comment_text": "",
            "digests": {
                "blake2b_256": "5659eedb61fafb4f3d9d150469f8fc68c4994f3c5157c4c7c5f1e8bf68d934bd",
                "md5": "703d23a7f9ea84a128ca6456881af883",
                "sha256": "3ab43a06bb7f32871a6131d654785f5552ab90fee386344a6f5cf690eb239106"
            },
            "downloads": -1,
            "filename": "ansible_taskrunner-2.11.0.tar.gz",
            "has_sig": false,
            "md5_digest": "703d23a7f9ea84a128ca6456881af883",
            "packagetype": "sdist",
            "python_version": "source",
            "requires_python": ">=2.7",
            "size": 98529,
            "upload_time": "2024-05-26T19:25:08",
            "upload_time_iso_8601": "2024-05-26T19:25:08.918242Z",
            "url": "https://files.pythonhosted.org/packages/56/59/eedb61fafb4f3d9d150469f8fc68c4994f3c5157c4c7c5f1e8bf68d934bd/ansible_taskrunner-2.11.0.tar.gz",
            "yanked": false,
            "yanked_reason": null
        }
    ],
    "upload_time": "2024-05-26 19:25:08",
    "github": true,
    "gitlab": false,
    "bitbucket": false,
    "codeberg": false,
    "github_user": "berttejeda",
    "github_project": "ansible-taskrunner",
    "travis_ci": true,
    "coveralls": false,
    "github_actions": false,
    "tox": true,
    "lcname": "ansible-taskrunner"
}
        
Elapsed time: 9.65154s