Skip to content

Named conditions (and block directive) #325

@gnfzdz

Description

@gnfzdz

There's actually two requests here, but given the overlap I thought it suitable to just raise a single issue:

  1. Introduce a new type of plugin, condition plugins, that provide named, configurable conditions written in Python
  2. Introduce a new directive accepting: condition(s) to evaluate and a list of tasks/actions to execute when true

Motivation

There are currently 3 plugins linked from the official documentation that provide a condition and a container of tasks to be executed based on the outcome (if, ifarch, ifplatform). Dotbot users have already adopted this type of plugin so the interest is clearly there.

The most flexible (and similar to this request) is the if plugin which takes a single shell command. Personally, there are at least a couple other conditions that I'd like to share with the community that aren't well suited to a shell one-liner.

Notably, all three of the plugins also share the same issue. They don't propagate defaults from the parent context (and scope any new defaults set to the child context).

I think it would be great to provide improved support for this pattern in the core dotbot project itself.

Examples

First, you can see below a couple options for a hypothetical new directive. I don't actually have a strong preference here. Whatever we think is least likely to conflict with a custom plugin name.

- cond:
    if:
      tty: true
    then:
      - shell:
        - '<command_that_may_prompt_for_input>'
    else:
      - shell:
        - 'fallback_command'

- block:
    when:
      tty: true
    tasks:
      - shell:
        - '<command_that_may_prompt_for_input>'

Next you can see a few variants of the condition. I actually think it makes sense to support all of these.

# For a string, it is interpreted as a shell command (providing consistency with the link directive's current if option) 
- cond:
    if: "command -v ansible"
    then:
      - shell:
        - 'echo "I found ansible in $PATH"'

# For a dict, use each key to find the corresponding Condition plugin and pass the value as arguments
- cond:
    if:
      tty: true
      platform: 'arch'
    then:
      - shell:
        - 'echo "I am running in a TTY on ArchLinux!"'

# An array is also supported for the same reasons as with dotbot tasks/actions.
# It allows repeating a condition multiple times.
- cond:
    if:
      - decrypted: "./config/file/a"
      - decrypted: "./config/file/b"
      - 'command -v "program_requiring_sensitive_config"'
    then:
      - shell:
        - 'echo "I have everything i need to configure <program_requiring_sensitive_config>."'

I'm envisioning the condition plugins following the same pattern as those providing directives. A new test runner class would take the full condition and delegate to the appropriate condition plugins for evaluation.

import dotbot
import sys

class TtyCondition(dotbot.Condition):

    def can_handle(self, directive):
        return "tty" == self._directive

    def handle(self, directive, data=True):
        expected = data if data is not None else True
        return expected == (sys.stdin.isatty() and sys.stdout.isatty() and sys.stderr.isatty())

Other thoughts

  1. Unlike Allow if on tasks #225 the only potential for collision would be the single new directive. Behavior of tasks/actions remains the same as it is today.
  2. The if attribute in the current link plugin could also make use of the above conditions. As described above it would be backwards compatible.
  3. Why not just implement this as a new plugin? Configuring dotbot/dotbot-plugins as submodules seems to complicate import resolution. It's much easier if the new Condition abstract base class is in the dotbot repository itself. I'm fairly new to python, so maybe there's a pattern I'm missing that would actually make this easier.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions