# SPDX-FileCopyrightText: 2026 Slavi Pantaleev # # SPDX-License-Identifier: AGPL-3.0-or-later --- name: Molecule # Unlike the MASH role repositories, where one repository holds one role, every # role here lives in the same repository. Running every scenario on every push # would be unaffordable, so a first job works out which roles the push actually # touched and the matrix is built from that. A push that changes documentation, # or a role with no scenario yet, runs nothing at all. on: # yamllint disable-line rule:truthy push: paths: - "roles/custom/**" - ".github/workflows/molecule.yml" pull_request: paths: - "roles/custom/**" - ".github/workflows/molecule.yml" workflow_dispatch: inputs: role: description: "Single role to test (directory name under roles/custom), or empty for all roles that have a scenario" required: false type: string permissions: contents: read jobs: detect: name: Work out which roles to test runs-on: ubuntu-latest # Same rule as the MASH repositories: a pull request from a branch of this # repository would otherwise run everything twice, once for the push and # once for the pull request. if: >- github.event_name != 'pull_request' || github.event.pull_request.head.repo.full_name != github.repository outputs: roles: ${{ steps.detect.outputs.roles }} steps: - name: Check out uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7 with: fetch-depth: 0 - name: Detect roles with a Molecule scenario that this change touches id: detect env: EVENT_NAME: ${{ github.event_name }} BASE_SHA: ${{ github.event.pull_request.base.sha }} BEFORE_SHA: ${{ github.event.before }} INPUT_ROLE: ${{ inputs.role }} run: | set -euo pipefail have_scenario() { [ -f "roles/custom/$1/molecule/default/molecule.yml" ] } # An explicit request through workflow_dispatch wins over detection. if [ -n "${INPUT_ROLE}" ]; then if have_scenario "${INPUT_ROLE}"; then printf 'roles=["%s"]\n' "${INPUT_ROLE}" >> "$GITHUB_OUTPUT" else echo "No scenario at roles/custom/${INPUT_ROLE}/molecule/default" >&2 exit 1 fi exit 0 fi # A hand-triggered run with no role named, and any run where the diff # base is unusable (a new branch, a force push, the very first commit), # falls back to every role that has a scenario. That is the safe # direction to fail in: too much testing rather than too little. base="" case "${EVENT_NAME}" in pull_request) base="${BASE_SHA}" ;; push) if [ -n "${BEFORE_SHA}" ] && [ "${BEFORE_SHA}" != "0000000000000000000000000000000000000000" ] \ && git cat-file -e "${BEFORE_SHA}^{commit}" 2>/dev/null; then base="${BEFORE_SHA}" fi ;; esac if [ -n "${base}" ]; then changed="$(git diff --name-only "${base}" HEAD -- 'roles/custom/*' || true)" candidates="$(printf '%s\n' "${changed}" | awk -F/ 'NF>2 {print $3}' | sort -u)" echo "Changed roles: ${candidates:-none}" else candidates="$(find roles/custom -mindepth 1 -maxdepth 1 -type d -printf '%f\n' | sort)" echo "No usable diff base; considering every role" fi selected="" for role in ${candidates}; do if have_scenario "${role}"; then selected="${selected} ${role}" fi done if [ -z "${selected}" ]; then echo "Nothing to test" echo 'roles=[]' >> "$GITHUB_OUTPUT" exit 0 fi echo "Testing:${selected}" json="$(printf '%s\n' ${selected} | jq -R . | jq -c -s .)" echo "roles=${json}" >> "$GITHUB_OUTPUT" molecule: name: "Molecule: ${{ matrix.role }}" runs-on: ubuntu-latest needs: detect if: needs.detect.outputs.roles != '[]' strategy: matrix: role: ${{ fromJson(needs.detect.outputs.roles) }} fail-fast: false env: MOLECULE_DISTRO: ubuntu2604 PY_COLORS: "1" ANSIBLE_FORCE_COLOR: "1" steps: - name: Check out uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7 - name: Set up Python uses: actions/setup-python@v6 with: python-version: "3.x" - name: Install test dependencies run: python3 -m pip install -r roles/custom/${{ matrix.role }}/molecule/requirements.txt - name: Run Molecule working-directory: roles/custom/${{ matrix.role }} run: molecule test --scenario-name default