---
version: '3'

vars:
  MOLECULE_LOGS_PATH: molecule/.results/logs
  MOLECULE_TEST_OPTIONS: ANSIBLE_STDOUT_CALLBACK=community.general.yaml
    ANSIBLE_CALLBACKS_ENABLED="junit, ansible.posix.profile_tasks, ansible.posix.timer"
    JUNIT_OUTPUT_DIR="molecule/.results/junit" JUNIT_FAIL_ON_CHANGE=true JUNIT_HIDE_TASK_ARGUMENTS=true
  PYTHON_MOLECULE_HANDLE:
    sh: |
      if [ -n "$CI" ]; then
        echo ''
      else
        if command -v unbuffer > /dev/null; then
          echo 'unbuffer {{.PYTHON_HANDLE}}'
        else
          echo '{{.PYTHON_HANDLE}}'
        fi
      fi

tasks:
  allure:report:
    deps:
      - :install:software:allure
    log:
      error: Failed to generate and/or open the unit test report
      start: Generating and opening unit test report
      success: Successfully generated and opened unit test report
    cmds:
      - allure generate molecule/.results/junit --output allure-reports --clean
      - mkdir -p molecule/.results/junit
      - cp -rf allure-reports/history/ molecule/.results/junit/
      - .config/log info 'Opening JUnit results with Allure in the default browser'
      - allure open allure-reports

  ansifilter:
    deps:
      - :install:software:ansifilter
    cmds:
      - TMP="$(mktemp)" && cat {{.RESULTS_FILE}} | ansifilter > "$TMP" && mv "$TMP" {{.RESULTS_FILE}}
    status:
      - '[ -n "$CI" ]'

  default:
    log:
      start: Running default Ansible test
    cmds:
      - task: molecule:docker:matrix

  local:
    desc: Run the Ansible play on the local machine (or via WSL - see task summary)
    hide: '{{ne (print .REPOSITORY_TYPE "-" .REPOSITORY_SUBTYPE) "ansible-playbook"}}'
    summary: |
      # Run the Ansible play on the local machine

      This task will use the inventory stored in `test/<OS>/inventory`, the playbook
      file stored in `test/<OS>/test.yml`, and the Ansible configuration file stored in
      `test/<OS>/ansible.cfg` to run the play. At the beginning of the play, you will
      be prompted for the sudo password.
    cmds:
      - task: local:test

  local:test:
    cmds:
      - |
        if [ -n "$CI" ]; then
          .config/log info '`$CI` environment variable is present'
          task ansible:test:local:test:ci
        else
          task ansible:test:local:test:local
        fi

  local:test:ci:
    deps:
      - :symlink:{{.REPOSITORY_SUBTYPE}}
    cmds:
      - |
        if [ -n "$WINDOWS_ANSIBLE_TEST" ]; then
          pip3 install ansible 'pywinrm[credssp]'
        else
          pip3 install ansible
        fi
      - |
        PATH="$PATH:$HOME/.local/bin"
        ansible-galaxy install -r requirements.yml
      - task: local:test:logic

  local:test:local:
    deps:
      - :symlink:{{.REPOSITORY_SUBTYPE}}
      - task: :install:python:requirements
        env:
          INSTALL_OPTIONS: --no-dev
    cmds:
      - task: local:test:logic

  local:test:logic:
    vars:
      ANSIBLE_CFG: |-
        [winrm_connection]
        scheme = https
        server_cert_validation = ignore
        transport = credssp,ssl
      ROLE_NAME: '{{.GALAXY_NAMESPACE}}.{{.GALAXY_ROLE_NAME}}'
      SUDO_PASS_PARAM:
        sh: if [ -z "$CI" ]; then echo ' --ask-sudo-pass'; else echo ''; fi
      WINDOWS_INVENTORY: windows-ci ansible_host=localhost ansible_user=runneradmin
        ansible_password=AnsibleTest999 ansible_connection=winrm ansible_winrm_server_cert_validation=ignore
        ansible_winrm_transport=credssp
    log:
      error: Encountered error while testing the Ansible playbook locally
      start: Testing the Ansible playbook locally
      success: Successfully tested the Ansible playbook locally
    cmds:
      - |
        if [ -n "$WINDOWS_ANSIBLE_TEST" ]; then
          echo '{{.WINDOWS_INVENTORY}}' > inventory
          echo '{{.ANSIBLE_CFG}}' > ansible.cfg
        else
          echo 'localhost ansible_connection=local' > inventory
        fi
      - task: local:test:playbook
        vars:
          PLAY_HOSTS:
            sh: if [ -z "$WINDOWS_ANSIBLE_TEST" ]; then echo 'localhost'; else echo 'windows-ci'; fi
          PLAY_ROLE_NAME: '{{.ROLE_NAME}}'
      - |
        if [ -z "$CI" ]; then
          .config/log info 'Prompting for sudo password (required for non-CI environments)'
        fi
        if [ -n "$WINDOWS_ANSIBLE_TEST" ]; then
          export ANSIBLE_CONFIG="$PWD/ansible.cfg"
        fi
        PATH="$PATH:$HOME/.local/bin"
        {{.PYTHON_MOLECULE_HANDLE}} ansible-playbook --skip-tags "mas" -i inventory{{.SUDO_PASS_PARAM}} test/{{OS}}/test.yml 2>&1 | tee debug.log || EXIT_CODE=$?
      - task: post:molecule:log
        vars:
          RESULTS_FILE: debug.log

  local:test:playbook:
    vars:
      TEST_PLAY: |
        ---
        - hosts: {{.PLAY_HOSTS}}
          roles:
            - role: '{{.PLAY_ROLE_NAME}}'
    cmds:
      - echo '{{.TEST_PLAY}}' > test.yml

  molecule:ci:requirements:
    cmds:
      - |
        if [ -n "$WINDOWS_ANSIBLE_TEST" ]; then
          .config/log info 'Running `pip3 install ansible ansibler molecule pywinrm[credssp]`'
          pip3 install ansible ansibler molecule 'pywinrm[credssp]'
        else
          .config/log info 'Running `pip3 install ansible ansibler molecule`'
          pip3 install ansible ansibler molecule
        fi
      - |
        .config/log info 'Running `ansible-galaxy install --ignore-errors -r requirements.yml`'
        PATH="$PATH:$HOME/.local/bin"
        ansible-galaxy install --ignore-errors -r requirements.yml

  molecule:dependencies:
    run: once
    cmds:
      - |
        if [ -n "$CI" ]; then
          .config/log info '`$CI` environment is present'
          task ansible:test:molecule:dependencies:ci
        else
          task ansible:test:molecule:dependencies:local
        fi

  molecule:dependencies:ci:
    cmds:
      - task: molecule:ci:requirements
    status:
      - '[ -z "$CI" ]'

  molecule:dependencies:local:
    deps:
      - :install:python:requirements
      - :install:software:expect
      - :install:software:sshpass
    log:
      error: Encountered error while installing Ansible Galaxy requirements defined in `requirements.yml`
      start: Installing Ansible Galaxy requirements defined in `requirements.yml`
      success: Installed Ansible Galaxy requirements defined in `requirements.yml`
    cmds:
      - |
        PATH="$PATH:$HOME/.local/bin"
        if poetry &> /dev/null; then
          {{.PYTHON_HANDLE}} ansible-galaxy install --ignore-errors -r requirements.yml
        else
          .config/log info 'Current shell is already a Poetry virtual environment'
          ansible-galaxy install --ignore-errors -r requirements.yml
        fi
      - task: :symlink:{{.REPOSITORY_SUBTYPE}}
    status:
      - '[ -n "$CI" ]'

  molecule:docker:
    desc: Runs a Docker Molecule test
    hide: '{{ne .REPOSITORY_TYPE "ansible"}}'
    summary: |
      # Runs a Docker Molecule test

      This task runs the project's Molecule tests using Docker. It only tests against
      Linux systems.

      **Opens a prompt:**
      `task ansible:test:molecule:docker`

      **Runs the test against the "CentOS-8" group directly:**
      `task ansible:test:molecule:docker -- CentOS-8`

      **Save test results for use with auto-generating compatibility chart:**
      `task ansible:test:molecule:docker:matrix`
    cmds:
      - |
        if ! docker run --rm hello-world; then
        .config/log warn 'The command `docker run --rm hello-world` failed'
          if [ -f '/Applications/Docker.app' ]; then
            .config/log info 'Attempting to open `Applications/Docker.app` (Docker Desktop for macOS)'
            open /Applications/Docker.app
            sleep 30
          fi
        fi
      - task: molecule:docker:{{if .CLI_ARGS}}cli{{else}}prompt{{end}}

  molecule:docker:cli:
    deps:
      - molecule:dependencies
      - :install:software:docker
    log:
      error: The `{{.CLI_ARGS}}` Docker Molecule test finished with errors
      start: Running Docker Molecule test on containers in the `{{.CLI_ARGS}}` group
      success: Successfully ran the `{{.CLI_ARGS}}` Docker Molecule test
    cmds:
      - |
        set -o pipefail
        {{.MOLECULE_TEST_OPTIONS}} MOLECULE_GROUP="{{.CLI_ARGS}}" {{.PYTHON_MOLECULE_HANDLE}}molecule test -s docker \
        -- --skip-tags skipdockertest 2>&1 | tee debug.log || EXIT_CODE=$?
        if [ -n "$EXIT_CODE" ]; then
        fi
      - task: post:molecule:log
        vars:
          RESULTS_FILE: debug.log

  molecule:docker:matrix:
    deps:
      - molecule:dependencies
      - :install:software:docker
    vars:
      MOLECULE_DATE:
        sh: date '+%Y-%m-%d'
    log:
      error: There were errors while running the test (results were logged to `{{.MOLECULE_LOGS_PATH}}/{{.MOLECULE_DATE}}-$SCENARIO.txt`)
      start: Running Docker Molecule test with results teed to `{{.MOLECULE_LOGS_PATH}}/{{.MOLECULE_DATE}}-$SCENARIO.txt`
      success: Finished running the test (results were logged to `{{.MOLECULE_LOGS_PATH}}/{{.MOLECULE_DATE}}-$SCENARIO.txt`)
    cmds:
      - mkdir -p {{.MOLECULE_LOGS_PATH}}
      - |
        SCENARIO="Linux"
        if grep -Ril 'community.general.snap:' ./tasks; then
          SCENARIO="Snap"
          .config/log warn 'Running Docker Molecule tests on the Docker containers that are compatible with `snap` since the role has references to `snap`'
        fi
        set -o pipefail
        {{.MOLECULE_TEST_OPTIONS}} MOLECULE_GROUP="$SCENARIO" {{.PYTHON_MOLECULE_HANDLE}}molecule test -s docker -- --skip-tags skipdockertest 2>&1 | \
          tee '{{.MOLECULE_LOGS_PATH}}/{{.MOLECULE_DATE}}-$SCENARIO.txt' || EXIT_CODE=$?
        if [ -n "$EXIT_CODE" ]; then
        fi
      - task: post:molecule:log
        vars:
          RESULTS_FILE: '{{.MOLECULE_LOGS_PATH}}/{{.MOLECULE_DATE}}-$SCENARIO.txt'

  molecule:docker:prompt:
    env:
      MARKDOWN: |
        # Ansible Molecule Test via Docker

        Choose a container group from the options below to begin the Molecule test.
        The choices should be mostly self-explanatory. The `Snap` group is a special group
        that should be used to test roles that contain `snap` logic. Only recent versions of
        Debian and Ubuntu support snap installations inside a Docker container. Docker tests
        are a quick way to test Ansible plays without consuming a large amount of system
        resources. Granted, to fully test an Ansible play, a VirtualBox method should be used
        instead.
    cmds:
      - TMP="$(mktemp)" && echo "$MARKDOWN" > "$TMP" && .config/log md "$TMP"
      - task: molecule:docker:prompt:continue

  molecule:docker:prompt:continue:
    interactive: true
    deps:
      - :install:software:gum
      - :install:software:jq
      - :install:software:yq
    cmds:
      - |
        DOCKER_OPTIONS="$(yq eval -o=j '.groups' molecule/docker/molecule.yml | jq -r 'keys | join(" ")')"
        DOCKER_OPTIONS_LENGTH="$(yq eval -o=j '.groups' molecule/docker/molecule.yml | jq -r 'keys | length')"
        if [[ "$DOCKER_OPTIONS_LENGTH" == '0' ]]; then
          .config/log error 'There are no Molecule groups defined in `molecule/desktop/molecule.yml`' && exit 1
        else
          .config/log info 'Press SPACE to select an item and ENTER when you are done selecting test environments'
          .config/log prompt 'Which environment(s) would you like to run the test on?'
          CHOSEN_OPTIONS="$(gum choose --no-limit $(echo "$DOCKER_OPTIONS"))"
          COMBINED_OPTIONS=""
          CHOSEN_COUNT="$(echo "$CHOSEN_OPTIONS" | wc -l)"
          if [[ "$CHOSEN_COUNT" == '0' ]]; then
            .config/log error 'No items were selected!' && exit 1
          else
            while read CURRENT_OPTION; do
              COMBINED_OPTIONS="${COMBINED_OPTIONS}:${CURRENT_OPTION}"
            done< <(echo "$CHOSEN_OPTIONS")
            CHOSEN_OPTION="${COMBINED_OPTIONS:1}"
            export ANSIBLE_ENABLE_TASK_DEBUGGER=true
            .config/log info 'Running `task ansible:test:molecule:docker:cli -- '"$CHOSEN_OPTION"'`'
            task ansible:test:molecule:docker:cli -- "$CHOSEN_OPTION"
          fi
        fi
      - task: allure:report
    preconditions:
      - sh: test -f molecule/docker/molecule.yml
        msg: The `molecule/docker/molecule.yml` file must be present and in the proper format

  molecule:gcp:
    deps:
      - molecule:dependencies
      - :install:software:gcloud
    log:
      error: Encountered error(s) while running the Google Cloud Platform Molecule test
      start: Running Google Cloud Platform Molecule test
      success: Finished running Google Cloud Platform Molecule test
    cmds:
      - task: molecule:gcp:preconditions
      - |
        set -o pipefail
        .config/log 'Results will be available in the `debug.log` file'
        {{.PYTHON_MOLECULE_HANDLE}}molecule test -s gcp 2>&1 | tee debug.log || EXIT_CODE=$?
        if [ -n "$EXIT_CODE" ]; then
        fi
      - task: post:molecule:log
        vars:
          RESULTS_FILE: debug.log

  molecule:gcp:matrix:
    deps:
      - molecule:dependencies
      - :install:software:gcloud
      - :install:software:yq
    vars:
      MOLECULE_DATE:
        sh: date '+%Y-%m-%d'
    log:
      error: An error occurred while running the Google Cloud Platform Molecule test sequence
      start: Running Docker Molecule test with results teed to `{{.MOLECULE_LOGS_PATH}}/{{.MOLECULE_DATE}}-$SCENARIO.txt`
      success: Finished running and formatting the results of the Google Cloud Platform molecule test
    cmds:
      - task: molecule:gcp:preconditions
      - mkdir -p {{.MOLECULE_LOGS_PATH}}
      - |
        set -o pipefail
        {{.PYTHON_MOLECULE_HANDLE}}molecule test -s gcp 2>&1 | tee "{{.MOLECULE_LOGS_PATH}}/{{.MOLECULE_DATE}}-gcp.txt" || EXIT_CODE=$?
        if [ -n "$EXIT_CODE" ]; then
        else
          .config/log success 'Finished running the test (results were logged to `{{.MOLECULE_LOGS_PATH}}/{{.MOLECULE_DATE}}-gcp.txt`)'
        fi
      - task: post:molecule:log
        vars:
          RESULTS_FILE: '{{.MOLECULE_LOGS_PATH}}/{{.MOLECULE_DATE}}-gcp.txt'
      - |
        RESULTS="{{.MOLECULE_LOGS_PATH}}/{{.MOLECULE_DATE}}-gcp.txt"
        PLATFORM_LENGTH="$(yq e '.platforms | length' molecule/gcp/molecule.yml)"
        INDEX=0
        while [ $INDEX -lt $PLATFORM_LENGTH ]; do
          NAME="$(yq e '.platforms['$INDEX'].name' molecule/gcp/molecule.yml)"
          ALIAS="$(yq e '.platforms['$INDEX'].alias' molecule/gcp/molecule.yml)"
          sed -i -- 's/'"$NAME"'/'"$ALIAS"'/g' "$RESULTS"
          INDEX=$((INDEX+1))
        done

  molecule:gcp:preconditions:
    preconditions:
      - sh: '[ -n "$GCE_SERVICE_ACCOUNT_EMAIL" ]'
        msg: The GCE_SERVICE_ACCOUNT_EMAIL environment variable must be set (e.g. export
          GCE_SERVICE_ACCOUNT_EMAIL=molecule@megabyte-labs.iam.gserviceaccount.com).
      - sh: '[ -n "$GCE_CREDENTIALS_FILE" ]'
        msg: The GCE_CREDENTIALS_FILE environment variable must be set and pointing to the GCP
          service account JSON key (e.g. export GCE_CREDENTIALS_FILE=~/.config/gcp.json).
      - sh: test -f "$GCE_CREDENTIALS_FILE"
        msg: The GCE_CREDENTIALS_FILE environment variable is defined but is not pointing to a file that exists.
      - sh: '[ -n "$GCE_PROJECT_ID" ]'
        msg: The GCE_PROJECT_ID environment variable must be set (e.g. export GCE_PROJECT_ID=megabyte-labs)

  molecule:local:
    desc: Runs a Molecule test on the localhost
    hide: '{{ne .REPOSITORY_TYPE "ansible"}}'
    summary: |
      # Run a local Molecule test

      This option is the same as running the play on the localhost with the added
      benefit of incorporating Molecule's test for idempotency and other tests.

      **Opens a prompt:**
      `task ansible:test:local`
    cmds:
      - task: molecule:local:{{if .CLI_ARGS}}test{{else}}prompt{{end}}

  molecule:local:prompt:
    env:
      MARKDOWN: |
        # Run Molecule Locally

        This testing option is provided for cases where you would like to locally test the
        Ansible play with Molecule. This option assumes that the current user has sudo
        privileges.

        ## Sudo Password

        A sudo password is required for all roles because Molecule has a step where it
        ensures Python is installed with `become: true`. The sudo password could potentially
        be logged in clear text if logging is in verbose mode so be careful when using this
        method.

        ## Running Locally Without Molecule

        If you only want to install the play (without leveraging Molecule's features
        like testing for idempotency and running test cases), then a more secure method would
        be to run "ansible localhost --ask-sudo-pass -m include_role -a name=<role_name>" after
        installing the role and its dependencies with ansible-galaxy.
    cmds:
      - TMP="$(mktemp)" && echo "$MARKDOWN" > "$TMP" && .config/log md "$TMP"
      - task: molecule:local:prompt:continue

  molecule:local:prompt:continue:
    deps:
      - :install:software:gum
    interactive: true
    cmds:
      - .config/log prompt 'What is the sudo password for the current user?'
      - |
        SUDO_PASS="$(.config/log password 'Enter sudo password for local machine..')"
        export ANSIBLE_ENABLE_TASK_DEBUGGER=true
        export TEST_PASSWORD="$SUDO_PASS"
        task ansible:test:molecule:local:test
      - task: allure:report

  molecule:local:test:
    deps:
      - molecule:dependencies
    vars:
      MOLECULE_DATE:
        sh: date '+%Y-%m-%d'
    log:
      error: There was an error while running the Molecule test locally
      start: Running the Molecule test locally
      success: The local Molecule test was successfully run
    cmds:
      - |
        set -o pipefail
        if [ -z "$CI" ]; then
          export PATH="$(poetry env info | grep 'Python:   /' | sed 's/Python:   //' | sed 's/$/\/bin/'):$PATH"
        fi
        {{.MOLECULE_TEST_OPTIONS}} {{.PYTHON_MOLECULE_HANDLE}}molecule test -s local 2>&1 | \
        tee '{{.MOLECULE_LOGS_PATH}}/{{.MOLECULE_DATE}}-default.txt' || EXIT_CODE=$?
        if [ -n "$EXIT_CODE" ]; then
        fi
      - task: post:molecule:log
        vars:
          RESULTS_FILE: '{{.MOLECULE_LOGS_PATH}}/{{.MOLECULE_DATE}}-default.txt'

  molecule:ssh:
    desc: Runs a Molecule test over SSH
    hide: '{{ne .REPOSITORY_TYPE "ansible"}}'
    summary: |
      # Run an SSH Molecule test

      This option allows you to run the Molecule test against a single
      SSH host.

      **Opens a prompt:**
      `task ansible:test:molecule:ssh`
    cmds:
      - task: molecule:ssh:{{if .CLI_ARGS}}cli{{else}}prompt{{end}}

  molecule:ssh:cli:
    deps:
      - molecule:dependencies
    log:
      error: Errors encountered while running the SSH Molecule test
      start: Running the Molecule test over SSH
      success: Successfully ran the Molecule test over SSH
    cmds:
      - |
        set -o pipefail
        {{.MOLECULE_TEST_OPTIONS}} {{.PYTHON_MOLECULE_HANDLE}}molecule test -s remote 2>&1 | tee debug.log || EXIT_CODE=$?
        if [ -n "$EXIT_CODE" ]; then
        fi
      - task: post:molecule:log
        vars:
          RESULTS_FILE: debug.log

  molecule:ssh:prompt:
    env:
      MARKDOWN: |
        # Remote Ansible Molecule Test via SSH

        This testing option is provided for cases where you would like to remotely test
        the Ansible play on remote machines via SSH. The prompts will ask you for the
        host IP address or FQDN, user, and password. Before running this test, you should
        ensure that you can already connect to the machine via SSH (i.e. the ~/.ssh keys
        should already be set up). This test assumes that SSH does not require any passwords
        to establish the connection.
    cmds:
      - TMP="$(mktemp)" && echo "$MARKDOWN" > "$TMP" && .config/log md "$TMP"
      - task: molecule:ssh:prompt:continue

  molecule:ssh:prompt:continue:
    deps:
      - :install:software:gum
    interactive: true
    cmds:
      - |
        .config/log prompt 'What is the IP address or the FQDN of the target host?'
        IP_ANSWER="$(.config/log input 'Enter IP address or FQDN..')"
        if [[ "$IP_ANSWER" =~ ^(([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])\.){3}([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])$ ]] || \
          [[ "$IP_ANSWER" =~ ^(([a-zA-Z0-9]|[a-zA-Z0-9][a-zA-Z0-9\-]*[a-zA-Z0-9])\.)*([A-Za-z0-9]|[A-Za-z0-9][A-Za-z0-9\-]*[A-Za-z0-9])$ ]]; then
          .config/log prompt 'What port should the SSH connection use?'
          PORT_ANSWER="$(gum input --placeholder='Enter SSH port..' --value='22')"
          if [[ "$PORT_ANSWER" =~ ^[0-9]*$ ]]; then
            .config/log prompt 'What is the username of a user that has both sudo and SSH privileges?'
            USER_ANSWER="$(.config/log input 'SSH username..')"
            if [[ "$USER_ANSWER" ^[a-z_]([a-z0-9_-]{0,31}|[a-z0-9_-]{0,30}\$)$ ]]; then
              export ANSIBLE_ENABLE_TASK_DEBUGGER=true
              export TEST_HOST="$IP_ANSWER"
              export TEST_PORT="$PORT_ANSWER"
              export TEST_SSH_USER="$USER_ANSWER"
              export TEST_USER="$USER_ANSWER"
              if [[ "$USER_ANSWER" != 'root' ]]; then
                .config/log prompt 'What is the user's sudo password?'
                SUDO_PASS_ANSWER="$(.config/log password 'Enter sudo password..')"
                export TEST_PASSWORD="$SUDO_PASS_ANSWER"
                export TEST_BECOME_PASSWORD="$SUDO_PASS_ANSWER"
              fi
              task ansible:test:molecule:ssh:cli
            else
              .config/log error 'Username is invalid!' && exit 1
          else
            .config/log error 'That SSH port is not valid!' && exit 1
        else
          .config/log error 'An invalid IP address / FQDN was entered.' && exit 1
        fi

  molecule:virtualbox:
    desc: Runs a full E2E Molecule test for all supported operating systems
    hide: '{{ne .REPOSITORY_TYPE "ansible"}}'
    summary: |
      # Run a full E2E Molecule test for all supported operating systems

      This task uses VirtualBox to run tests for all of our supported operating
      systems in parallel. It is very RAM intensive so, if you want to run this,
      your computer should have _at least 32GB of RAM_.

      **Opens a prompt:**
      `task ansible:test:molecule:virtualbox`

      **Generate the compatibility matrix used in the README.md:**
      `task ansible:test:molecule:virtualbox:matrix`
    cmds:
      - task: molecule:virtualbox:{{if .CLI_ARGS}}cli{{else}}prompt{{end}}

  # yamllint disable rule:truthy
  molecule:virtualbox:cli:
    deps:
      - molecule:dependencies
      - :install:software:vagrant
      - :install:software:virtualbox
    env:
      OBJC_DISABLE_INITIALIZE_FORK_SAFETY: YES
    log:
      error: Errors encountered while running the `{{.CLI_ARGS}}` VirtualBox Molecule test
      start: Running a VirtualBox Molecule test on platforms in the `{{.CLI_ARGS}}` group
      success: Finished running the `{{.CLI_ARGS}}` VirtualBox Molecule test
    cmds:
      - |
        set -o pipefail
        {{.MOLECULE_TEST_OPTIONS}} MOLECULE_GROUP="{{.CLI_ARGS}}" {{.PYTHON_MOLECULE_HANDLE}}molecule test 2>&1 | tee debug.log || EXIT_CODE=$?
        if [ -n "$EXIT_CODE" ]; then
        fi
      - task: post:molecule:log
        vars:
          RESULTS_FILE: debug.log

  molecule:virtualbox:converge:
    desc: Provisions a desktop VirtualBox VM and then runs a Molecule test
    hide: '{{ne .REPOSITORY_TYPE "ansible"}}'
    summary: |
      # Provision a desktop VirtualBox VM and then run a Molecule test

      This task opens a VM with an operating system of your choosing and then tests
      the project's play against it. It then leaves the VM open for inspection.

      **Example with interactive prompt for VM type:**
      `task test:molecule`

      **Example usage bypassing prompt:**
      `task test:molecule -- ArchLinux`

      ## Available scenarios:

      * ArchLinux
      * CentOS
      * Debian
      * Fedora
      * macOS
      * Ubuntu
      * Windows
    cmds:
      - task: molecule:virtualbox:converge:{{if .CLI_ARGS}}cli{{else}}prompt{{end}}

  molecule:virtualbox:converge:cli:
    deps:
      - molecule:dependencies
      - :install:software:vagrant
      - :install:software:virtualbox
    env:
      OBJC_DISABLE_INITIALIZE_FORK_SAFETY: YES
    log:
      error: Errors were encountered while running the `{{.CLI_ARGS}}` VirtualBox Molecule converge play
      start: Running the `{{.CLI_ARGS}}` VirtualBox Molecule converge play (this will leave the VirtualBox instance open for inspection)
      success: Finished running the `{{.CLI_ARGS}}` VirtualBox Molecule converge play (you are encouraged to inspect the VM)
    cmds:
      - |
        set -o pipefail
        {{.MOLECULE_TEST_OPTIONS}} MOLECULE_GROUP={{.CLI_ARGS}} {{.PYTHON_MOLECULE_HANDLE}}molecule converge -s desktop 2>&1 | tee debug.log || EXIT_CODE=$?
        if [ -n "$EXIT_CODE" ]; then
        fi
      - task: post:molecule:log
        vars:
          RESULTS_FILE: debug.log

  molecule:virtualbox:converge:prompt:
    env:
      MARKDOWN: |
        # Desktop Ansible Molecule Test via VirtualBox

        Choose a desktop environment below to run the Ansible play on.
        After choosing, a VirtualBox VM will be created. Then, the Ansible play will run on the VM.
        After it is done, the VM will be left open for inspection. Please do get carried away
        ensuring everything is working as expected and looking for configuration optimizations that
        can be made. The operating systems should all be the latest stable release but might
        not always be the latest version.
    cmds:
      - TMP="$(mktemp)" && echo "$MARKDOWN" > "$TMP" && .config/log md "$TMP"
      - task: molecule:virtualbox:converge:prompt:continue

  molecule:virtualbox:converge:prompt:continue:
    interactive: true
    deps:
      - :install:software:gum
      - :install:software:jq
      - :install:software:yq
    vars:
      VIRTUALBOX_OPTIONS:
        sh: echo "\"$(yq eval -o=j '.groups' molecule/desktop/molecule.yml | jq -r 'keys | join("\" \"")')\""
      VIRTUALBOX_OPTIONS_LENGTH:
        sh: yq eval -o=j '.groups' molecule/desktop/molecule.yml | jq -r 'keys | length'
    cmds:
      - |
        if [[ '{{.VIRTUALBOX_OPTIONS_LENGTH}}' == '0' ]]; then
          .config/log error 'There are no Molecule groups defined in `molecule/desktop/molecule.yml`' && exit 1
        else
          .config/log prompt 'Which desktop operating system would you like to test the Ansible play against?'
          CHOSEN_OPTION="$(.config/log choose {{.VIRTUALBOX_OPTIONS}})"
          export ANSIBLE_ENABLE_TASK_DEBUGGER=true
          task ansible:test:molecule:virtualbox:converge:cli -- "$CHOSEN_OPTION"
        fi
      - task: allure:report
    preconditions:
      - sh: test -f molecule/desktop/molecule.yml
        msg: The `molecule/desktop/molecule.yml` file must be present and in the proper format

  molecule:virtualbox:matrix:
    deps:
      - molecule:dependencies
      - :install:software:vagrant
      - :install:software:virtualbox
    vars:
      MOLECULE_DATE:
        sh: date '+%Y-%m-%d'
    log:
      error: Errors were encountered while running the full E2E test (see `{{.MOLECULE_LOGS_PATH}}/{{.MOLECULE_DATE}}-default.txt` for details)
      start: Running a full E2E test with VirtualBox (results will be saved to `{{.MOLECULE_LOGS_PATH}}/{{.MOLECULE_DATE}}-default.txt`)
      success: Finished running the full E2E test (results are in `{{.MOLECULE_LOGS_PATH}}/{{.MOLECULE_DATE}}-default.txt`)
    cmds:
      - mkdir -p {{.MOLECULE_LOGS_PATH}}
      - |
        set -o pipefail
        {{.MOLECULE_TEST_OPTIONS}} {{.PYTHON_MOLECULE_HANDLE}}molecule test 2>&1 | \
        tee '{{.MOLECULE_LOGS_PATH}}/{{.MOLECULE_DATE}}-default.txt' || EXIT_CODE=$?
        if [ "$EXIT_CODE" ]; then
        fi
      - task: post:molecule:log
        vars:
          RESULTS_FILE: '{{.MOLECULE_LOGS_PATH}}/{{.MOLECULE_DATE}}-default.txt'

  # yamllint enable rule:truthy
  molecule:virtualbox:prompt:
    env:
      MARKDOWN: |
        # Ansible Molecule Test via Headless VirtualBox Instances

        This particular type of test is the best method for testing Ansible plays. It
        uses VirtualBox and utilizes headless images. Despite that, running the test across
        all the supported operating systems is RAM intensive. Ideally, you should have at
        least 16GB of RAM to run all the tests at once. This type of test is used to generate
        the compatibility chart so the results of this type of test have the final say.

        You do not need to run the tests on all instances at once. Use the prompt below to
        narrow your test scope.
    cmds:
      - TMP="$(mktemp)" && echo "$MARKDOWN" > "$TMP" && .config/log md "$TMP"
      - task: molecule:virtualbox:prompt:continue

  molecule:virtualbox:prompt:continue:
    interactive: true
    deps:
      - :install:software:gum
      - :install:software:jq
      - :install:software:yq
    vars:
      VIRTUALBOX_OPTIONS:
        sh: echo "\"$(yq eval -o=j '.groups' molecule/desktop/molecule.yml | jq -r 'keys | join("\" \"")')\""
      VIRTUALBOX_OPTIONS_LENGTH:
        sh: yq eval -o=j '.groups' molecule/desktop/molecule.yml | jq -r 'keys | length'
    cmds:
      - |
        if [[ '{{.VIRTUALBOX_OPTIONS_LENGTH}}' == '0' ]]; then
          .config/log error 'There are no Molecule groups defined in `molecule/desktop/molecule.yml`' && exit 1
        else
          .config/log prompt 'What environment(s) would you like to target with this test?'
          CHOSEN_OPTIONS="$(gum choose --no-limit {{.VIRTUALBOX_OPTIONS}})"
          COMBINED_OPTIONS=""
          CHOSEN_COUNT="$(echo "$CHOSEN_OPTIONS" | wc -l)"
          if [[ "$CHOSEN_COUNT" == '0' ]]; then
            .config/log error 'No items were selected!' && exit 1
          else
            while read CURRENT_OPTION; do
              COMBINED_OPTIONS="${COMBINED_OPTIONS}:${CURRENT_OPTION}"
            done< <(echo "$CHOSEN_OPTIONS")
            CHOSEN_OPTION="${COMBINED_OPTIONS:1}"
            export ANSIBLE_ENABLE_TASK_DEBUGGER=true
            task ansible:test:molecule:virtualbox:cli -- "$CHOSEN_OPTION"
          fi
        fi
      - task: allure:report
    preconditions:
      - sh: test -f molecule/default/molecule.yml
        msg: The `molecule/default/molecule.yml` file must be present and in the proper format

  post:molecule:log:
    deps:
      - :ci:commit:config
    cmds:
      - task: ansifilter
        vars:
          RESULTS_FILE: '{{.RESULTS_FILE}}'
      - task: post:molecule:log:commit:ci
      - cmd: |
          git pull --ff-only origin master
          task --list > /dev/null || (echo "ERROR: Invalid Taskfiles!" && exit 1)
          git add '{{.RESULTS_FILE}}'
          HUSKY=0 git commit -o ci.skip -m "📝 docs(molecule): New Molecule test results added." -n
          git push origin master
        ignore_error: true
    status:
      - '[ "{{.RESULTS_FILE}}" == "debug.log" ] || [ ! -d .git ]'

  post:molecule:log:commit:ci:
    cmds:
      - task: :ci:commit:config
    status:
      - '[ -z "$GITLAB_CI" ]'

  prompt:
    env:
      MARKDOWN: |
        # Molecule Test

        There are currently six different options for running Molecule tests.

        ## 1. VirtualBox Headless

        Runs tests using VirtualBox headless VMs. It is the test type used to generate
        the compatibility chart.

        ## 2. VirtualBox Desktop

        Runs tests using a VirtualBox desktop version VM. Use this type of test to run
        the Ansible play and then open the VirtualBox VM to smoke test the software.

        ## 3. Docker

        Utilizes Docker to test the Ansible play. It has some limitations such as not being
        able to test snap installations on all operating systems. It also can only run tests
        on Linux environments. This is, however, the fastest way to test roles and requires
        the least amount of RAM.

        ## 4. Local

        Runs the Ansible play on the local machine. Use this to run the Ansible play on your
        local machine. You might use this if you want to inspect the software after running
        the play.

        ## 5. SSH

        Runs the Ansible play on a remote machine after connecting via SSH. This requires
        that you already have the SSH credentials configured (i.e. ~/.ssh is setup).

        ## 6. Google Cloud Platform

        Provisions Google Cloud Platform instances and tests the Ansible play on them. This
        test requires that you have access to a GCP account and that the proper credentials
        are in place. For help, see
        [this guide](https://github.com/ProfessorManhattan/molecule-ansible-google-cloud/blob/master/README.md).
        Without the environment variables mentioned in the guide set, this task will fail.

        ## Note on Debugging

        All of the tests below (except GCP) enable the built-in Ansible debugger. If a task
        fails, the STDOUT will freeze and you will be able to enter a few different commands.
        For example, if you enter "r", then Ansible will run the task again. For more
        information on the Ansible debugger (including available commands), see
        https://docs.ansible.com/ansible/latest/user_guide/playbooks_debugger.html#available-debug-commands.
    cmds:
      - TMP="$(mktemp)" && echo "$MARKDOWN" > "$TMP" && .config/log md "$TMP"
      - task: prompt:continue

  prompt:continue:
    interactive: true
    cmds:
      - .config/log prompt 'What type of test would you like to perform?'
      - |
        CHOSEN_TEST="$(.config/log choose 'VirtualBox Headless' 'VirtualBox Desktop' 'Docker' 'Local' 'SSH' 'Google Cloud Platform')"
        if [[ "$CHOSEN_TEST" == 'VirtualBox Headless' ]]; then
          TEST_SLUG='virtualbox:prompt'
        elif [[ "$CHOSEN_TEST" == 'VirtualBox Desktop' ]]; then
          TEST_SLUG='virtualbox:converge:prompt'
        elif [[ "$CHOSEN_TEST" == 'Docker' ]]; then
          TEST_SLUG='docker:prompt'
        elif [ '{{.ANSWER}}' == 'Local' ]; then
          TEST_SLUG='local'
        elif [ '{{.ANSWER}}' == 'SSH' ]; then
          TEST_SLUG='ssh:prompt'
        elif [ '{{.ANSWER}}' == 'Google Cloud Platform' ]; then
          TEST_SLUG='gcp'
        fi
        export ANSIBLE_ENABLE_TASK_DEBUGGER=true
        task ansible:test:molecule:${TEST_SLUG}
      - task: allure:report

  vagrant:
    desc: Runs the playbook using Vagrant
    hide: '{{ne (print .REPOSITORY_TYPE "-" .REPOSITORY_SUBTYPE) "ansible-role"}}'
    summary: |
      # Run the playbook using Vagrant

      Using Vagrant, you can pick and choose which operating system and
      virtualization provider you want to use to test the playbook.

      ## Possible virtualization providers:

      * hyperv
      * libvirt
      * parallels
      * virtualbox
      * vmware_fusion
      * vmware_workstation

      ## Possible operating systems:

      * archlinux
      * centos
      * debian
      * fedora
      * macos
      * ubuntu
      * windows

      **Example opening interactive prompt:**
      `task test:vagrant`

      **Example bypassing interactive prompt:**
      `task test:vagrant -- --provider=vmware_workstation windows`
    cmds:
      - task: vagrant:{{if .CLI_ARGS}}cli{{else}}prompt{{end}}

  vagrant:cli:
    deps:
      - task: :install:python:requirements
        vars:
          INSTALL_OPTIONS: --no-dev
      - :install:software:vagrant
      - :install:software:virtualbox
    log:
      error: Encountered error when running `vagrant up {{.CLI_ARGS}}`
      start: Running `vagrant up {{.CLI_ARGS}}`
      success: Successfully ran `vagrant up {{.CLI_ARGS}}`
    cmds:
      - |
        set -o pipefail
        vagrant up {{.CLI_ARGS}} 2>&1 | tee debug.log || EXIT_CODE=$?
        if [ -n "$EXIT_CODE" ]; then
        fi
      - task: post:molecule:log
        vars:
          RESULTS_FILE: debug.log

  vagrant:prompt:
    env:
      MARKDOWN: |
        # Launch VM via Vagrant and Run Playbook

        Use the following prompts to select the type of operating system and
        the virtualization platform you wish to use with Vagrant. After you make your choice
        the corresponding environment will be provisioned with Vagrant.

        The options are generated by inspecting your system for which virtualization
        platforms are installed. The supported virtualization platforms are:

        * **KVM** - Shows if `qemu-system-x86_64` command is available
        * **Parallels** (macOS only) - Shows if `Parallels Desktop.app` is installed
        * **VirtualBox** - Shows if `vboxmanage` command is available
        * **VMWare Fusion** (macOS only) - Shows if `vmrun` command is available
        * **VMWare Workstation** (Linux only) - Shows if `vmware` command is available
    cmds:
      - TMP="$(mktemp)" && echo "$MARKDOWN" > "$TMP" && .config/log md "$TMP"
      - task: vagrant:prompt:continue

  vagrant:prompt:continue:
    deps:
      - :install:software:gum
      - :install:software:jq
    interactive: true
    vars:
      PROMPT_OPTIONS:
        sh: |
          TMP="$(mktemp)"
          if type qemu-system-x86_64 &> /dev/null; then
            echo 'KVM' > "$TMP"
          fi
          if [[ '{{OS}}' == 'darwin' ]] && mdfind -name 'Parallels Desktop.app' &> /dev/null; then
            echo 'Parallels' > "$TMP"
          fi
          if type vboxmanage &> /dev/null; then
            echo 'VirtualBox' > "$TMP"
          fi
          if [[ '{{OS}}' == 'linux' ]] && type vmware &> /dev/null; then
            echo 'VMWare Workstation' > "$TMP"
          fi
          if [[ '{{OS}}' == 'darwin' ]] && type vmrun &> /dev/null; then
            echo 'VMWare Fusion' > "$TMP"
          fi
          LIST_LENGTH="$(jq -R -s -c -r 'split("\n") | length' < "$TMP")"
          if [ "$LIST_LENGTH" != '0' ]; then
            echo "\""$(jq -R -s -c -r 'split("\n") | join("\" \"")' < "$TMP")"\""
          else
            echo "None"
          fi
    cmds:
      - |
        if [[ '{{.PROMPT_OPTIONS' == 'None' ]]; then
          .config/log error 'No virtualization platforms installed. Install a platform (e.g. VirtualBox, VMWare, QEMU) to continue.' && exit 1
        else
          .config/log prompt 'Which virtualization platform would you like to use?'
          PLATFORM_CHOICE="$(.config/log choose '{{.PROMPT_OPTIONS}}')"
          .config/log prompt 'Which desktop OS would you like to launch / provision?'
          OS_CHOICE="$(.config/log choose 'ArchLinux' 'CentOS' 'Debian' 'Fedora' 'macOS' 'Ubuntu' 'Windows')"
          task ansible:test:vagrant:cli -- --provider=\""$PLATFORM_CHOICE"\" "$OS_CHOICE"
        fi
