From 8f962bf53da6f748dc1618bfa3fdbfe6ee12af72 Mon Sep 17 00:00:00 2001 From: Benny Rahmanim Date: Thu, 4 Jun 2026 09:58:32 +0300 Subject: [PATCH 1/7] [ACA-5022]Add covarage tests for SonarQube cloud --- .github/workflows/sonarcloud.yml | 276 +++++++++++++++++++++++++++++-- CI.md | 9 + README.md | 23 ++- SONARCLOUD.md | 30 ++++ sonar-project.properties | 4 +- 5 files changed, 309 insertions(+), 33 deletions(-) create mode 100644 SONARCLOUD.md diff --git a/.github/workflows/sonarcloud.yml b/.github/workflows/sonarcloud.yml index 824e7c43..bbd19cd3 100644 --- a/.github/workflows/sonarcloud.yml +++ b/.github/workflows/sonarcloud.yml @@ -1,41 +1,281 @@ ---- -# SonarCloud analysis for cloud.aws_ops +## SonarCloud: integration coverage (separate from integration.yml) + scanner. +# +# Coverage jobs mirror Integration PR gates (safe to test, splitter, AWS) but run ansible-test with +# --coverage only here. integration.yml runs the same targets without coverage. finalize scans after coverage. # -# Uses the same-repo + default-branch push model: GitHub does not expose org secrets to workflows -# from fork PRs (see https://docs.github.com/en/actions/security-guides/using-secrets-in-github-actions). -# This job is gated so the Sonar token is never available in untrusted fork contexts. A follow-up -# workflow triggered by workflow_run + artifacts is an alternative if the org later requires Sonar -# with coverage on fork PRs (see README.md and https://docs.github.com/en/actions/using-workflows/events-that-trigger-workflows#workflow_run). +# ORG NOTE: Secret name ANSIBLE_COLLECTIONS_ORG_SONAR_TOKEN_CICD_BOT is shared across ansible-collections repos. + +--- name: SonarCloud on: - push: - branches: - - main - - stable-* - pull_request: + pull_request_target: + types: + - opened + - reopened + - labeled + - unlabeled + - synchronize branches: - main - stable-* - workflow_dispatch: permissions: contents: read pull-requests: read jobs: - sonarqube: - name: SonarCloud Scan + safe-to-test: + if: ${{ github.event.label.name == 'safe to test' }} || ${{ github.event.action != 'labeled' }} + uses: ansible-network/github_actions/.github/workflows/safe-to-test.yml@main + + splitter: + needs: + - safe-to-test + runs-on: ubuntu-latest + env: + source_dir: "cloud_awsops" + outputs: + test_targets: ${{ steps.splitter.outputs.test_targets }} + test_targets_json: ${{ steps.splitter.outputs.test_targets_json }} + test_jobs: ${{ steps.splitter.outputs.test_jobs }} + steps: + - name: Checkout collection + uses: actions/checkout@v3 + with: + path: ${{ env.source_dir }} + ref: ${{ github.event.pull_request.head.sha }} + fetch-depth: "0" + + - name: List changes for pull request + id: splitter + uses: ansible-network/github_actions/.github/actions/ansible_test_splitter@main + with: + collections_to_test: ${{ env.source_dir }} + total_jobs: 2 + + coverage-test: + if: ${{ needs.splitter.outputs.test_targets != '' }} + runs-on: ubuntu-latest + needs: + - splitter + env: + source: "./source" + ansible_version: "milestone" + python_version: "3.12" + strategy: + fail-fast: false + matrix: + workflow-id: ${{ fromJson(needs.splitter.outputs.test_jobs) }} + name: "coverage-integration-${{ matrix.workflow-id }}" + steps: + - name: Read target + id: read-targets + run: | + import json, os + with open(os.environ.get('GITHUB_OUTPUT'), "a", encoding="utf-8") as fh: + fh.write(f'ansible_test_targets={json.loads(os.environ.get("ALL_TEST_TARGETS")).get(os.environ.get("WORKFLOW_ID"))}\n') + shell: python + env: + ALL_TEST_TARGETS: ${{ needs.splitter.outputs.test_targets_json }} + WORKFLOW_ID: ${{ matrix.workflow-id }} + + - name: Checkout collection + uses: actions/checkout@v3 + with: + path: ${{ env.source }} + ref: ${{ github.event.pull_request.head.sha }} + + - name: Set up Python ${{ env.python_version }} + uses: actions/setup-python@v4 + with: + python-version: ${{ env.python_version }} + + - name: Install ansible-core (${{ env.ansible_version }}) + run: >- + python3 -m pip install + https://github.com/ansible/ansible/archive/${{ env.ansible_version }}.tar.gz + --disable-pip-version-check + shell: bash + + - name: Pre install collections dependencies first so the collection install does not + run: ansible-galaxy collection install --pre '-r${{ env.source }}/tests/integration/requirements.yml' -p /home/runner/collections/ + + - name: Build and install collection + id: install + uses: ansible-network/github_actions/.github/actions/build_install_collection@main + with: + install_python_dependencies: false + source_path: ${{ env.source }} + + - name: Create AWS/sts session credentials + uses: ansible-network/github_actions/.github/actions/ansible_aws_test_provider@main + with: + collection_path: ${{ steps.install.outputs.collection_path }} + ansible_core_ci_key: ${{ secrets.ANSIBLE_CORE_CI_KEY }} + + - name: Set up git + run: | + git config --global user.email gha@localhost + git config --global user.name "Github Actions" + shell: bash + + - name: Install collection python requirements + run: python3 -m pip install -r test-requirements.txt --disable-pip-version-check + working-directory: ${{ steps.install.outputs.collection_path }} + shell: bash + + - name: Run integration tests with coverage + run: >- + ansible-test integration + --diff + --no-temp-workdir + --color + --skip-tags False + --retry-on-error + --continue-on-error + --coverage + --requirements + --python ${{ env.python_version }} + -v + ${{ steps.read-targets.outputs.ansible_test_targets }} + working-directory: ${{ steps.install.outputs.collection_path }} + shell: bash + + - name: Upload integration coverage data + if: success() || failure() + uses: actions/upload-artifact@v4 + with: + name: coverage-raw-${{ matrix.workflow-id }} + path: ${{ steps.install.outputs.collection_path }}/tests/output/ + if-no-files-found: ignore + retention-days: 5 + + coverage: + name: Integration test coverage + if: >- + needs.splitter.outputs.test_targets != '' + && needs.coverage-test.result != 'skipped' + && needs.coverage-test.result != 'cancelled' + runs-on: ubuntu-latest + needs: + - splitter + - coverage-test + env: + source: "./source" + ansible_version: "milestone" + python_version: "3.12" + steps: + - name: Checkout collection + uses: actions/checkout@v3 + with: + path: ${{ env.source }} + ref: ${{ github.event.pull_request.head.sha }} + + - name: Set up Python ${{ env.python_version }} + uses: actions/setup-python@v4 + with: + python-version: ${{ env.python_version }} + + - name: Install ansible-core (${{ env.ansible_version }}) + run: >- + python3 -m pip install + https://github.com/ansible/ansible/archive/${{ env.ansible_version }}.tar.gz + --disable-pip-version-check + shell: bash + + - name: Pre install collections dependencies first so the collection install does not + run: ansible-galaxy collection install --pre '-r${{ env.source }}/tests/integration/requirements.yml' -p /home/runner/collections/ + + - name: Build and install collection + id: install + uses: ansible-network/github_actions/.github/actions/build_install_collection@main + with: + install_python_dependencies: false + source_path: ${{ env.source }} + + - name: Install collection python requirements + run: python3 -m pip install -r test-requirements.txt --disable-pip-version-check + working-directory: ${{ steps.install.outputs.collection_path }} + shell: bash + + - name: Download integration coverage data + uses: actions/download-artifact@v4 + with: + pattern: coverage-raw-* + merge-multiple: true + path: ${{ steps.install.outputs.collection_path }}/tests/output + + - name: Combine and emit coverage XML + working-directory: ${{ steps.install.outputs.collection_path }} + run: | + ansible-test coverage combine --requirements --python "${{ env.python_version }}" + ansible-test coverage xml --requirements --python "${{ env.python_version }}" + + - name: Prepare coverage.xml for SonarCloud + run: | + set -euo pipefail + collection_path="${{ steps.install.outputs.collection_path }}" + xml=$(find "${collection_path}/tests/output/reports" -maxdepth 1 -name '*.xml' ! -name '*powershell*' | head -1) + test -n "${xml}" + cp "${xml}" "${GITHUB_WORKSPACE}/coverage.xml" + sed -i "s#${GITHUB_WORKSPACE}/##g" "${GITHUB_WORKSPACE}/coverage.xml" + sed -i "s#${collection_path}/##g" "${GITHUB_WORKSPACE}/coverage.xml" + + - name: Upload coverage artifact + uses: actions/upload-artifact@v4 + with: + name: coverage + path: ${{ github.workspace }}/coverage.xml + retention-days: 5 + + finalize: + name: finalize + needs: + - coverage + if: >- + needs.coverage.result == 'success' + && github.event.pull_request.head.repo.full_name == github.repository + && secrets.ANSIBLE_COLLECTIONS_ORG_SONAR_TOKEN_CICD_BOT != '' runs-on: ubuntu-latest - if: github.event_name != 'pull_request' || github.event.pull_request.head.repo.full_name == github.repository steps: - - name: Checkout + - name: Checkout repository uses: actions/checkout@v4 with: + ref: ${{ github.event.pull_request.head.sha }} fetch-depth: 0 + - name: Download coverage artifact + uses: actions/download-artifact@v4 + with: + name: coverage + path: . + + - name: Set coverage report paths + run: | + coverage_files=$(find . -name "coverage*.xml" -type f 2>/dev/null | tr '\n' ',' | sed 's/,$//') + echo "Found coverage files: ${coverage_files:-none}" + echo "COVERAGE_PATHS=${coverage_files}" >> "$GITHUB_ENV" + + - name: Prepare SonarCloud args + env: + COMMIT_SHA: ${{ github.event.pull_request.head.sha }} + PR_NUMBER: ${{ github.event.pull_request.number }} + PR_HEAD: ${{ github.event.pull_request.head.ref }} + PR_BASE: ${{ github.event.pull_request.base.ref }} + run: | + SONAR_ARGS="-Dsonar.scm.revision=\"${COMMIT_SHA}\"" + SONAR_ARGS="${SONAR_ARGS} -Dsonar.pullrequest.key=${PR_NUMBER}" + SONAR_ARGS="${SONAR_ARGS} -Dsonar.pullrequest.branch=${PR_HEAD}" + SONAR_ARGS="${SONAR_ARGS} -Dsonar.pullrequest.base=${PR_BASE}" + if [[ -n "${COVERAGE_PATHS:-}" ]]; then + SONAR_ARGS="${SONAR_ARGS} -Dsonar.python.coverage.reportPaths=${COVERAGE_PATHS}" + fi + echo "SONAR_ARGS=${SONAR_ARGS}" >> "$GITHUB_ENV" + - name: SonarCloud Scan - # Same pinned version as ansible-collections/amazon.aws sonarcloud.yml uses: SonarSource/sonarqube-scan-action@a31c9398be7ace6bbfaf30c0bd5d415f843d45e9 env: SONAR_TOKEN: ${{ secrets.ANSIBLE_COLLECTIONS_ORG_SONAR_TOKEN_CICD_BOT }} + with: + args: ${{ env.SONAR_ARGS }} diff --git a/CI.md b/CI.md index 7a75e36a..d4ccfe36 100644 --- a/CI.md +++ b/CI.md @@ -49,3 +49,12 @@ Integration tests run on real AWS infrastructure and require the "safe to test" - Integration targets are automatically split across 2 parallel jobs - Split is determined by `ansible_test_splitter` action based on changed files - Each job runs the subset of tests relevant to the PR's changes + +### SonarCloud and coverage + +| Workflow | Description | +| -------- | ----------- | +| [Integration](.github/workflows/integration.yml) | Integration tests only (no coverage) | +| [SonarCloud](.github/workflows/sonarcloud.yml) | Separate integration run with `--coverage`, **`coverage`** job, then **`finalize`** Sonar scan | + +This collection does not use an **`all_green`** gate. Coverage for Sonar is **not** produced by the Integration workflow. Details: [SONARCLOUD.md](SONARCLOUD.md). diff --git a/README.md b/README.md index 9821e71f..e8390ed7 100644 --- a/README.md +++ b/README.md @@ -1,23 +1,20 @@ +[![Quality Gate Status](https://sonarcloud.io/api/project_badges/measure?project=redhat-cop_cloud.aws_ops&metric=alert_status)](https://sonarcloud.io/summary/new_code?id=redhat-cop_cloud.aws_ops) +[![Coverage](https://sonarcloud.io/api/project_badges/measure?project=redhat-cop_cloud.aws_ops&metric=coverage)](https://sonarcloud.io/summary/new_code?id=redhat-cop_cloud.aws_ops) +[![Vulnerabilities](https://sonarcloud.io/api/project_badges/measure?project=redhat-cop_cloud.aws_ops&metric=vulnerabilities)](https://sonarcloud.io/summary/new_code?id=redhat-cop_cloud.aws_ops) +[![Security Rating](https://sonarcloud.io/api/project_badges/measure?project=redhat-cop_cloud.aws_ops&metric=security_rating)](https://sonarcloud.io/summary/new_code?id=redhat-cop_cloud.aws_ops) +[![Bugs](https://sonarcloud.io/api/project_badges/measure?project=redhat-cop_cloud.aws_ops&metric=bugs)](https://sonarcloud.io/summary/new_code?id=redhat-cop_cloud.aws_ops) +[![Code Smells](https://sonarcloud.io/api/project_badges/measure?project=redhat-cop_cloud.aws_ops&metric=code_smells)](https://sonarcloud.io/summary/new_code?id=redhat-cop_cloud.aws_ops) +[![Maintainability Rating](https://sonarcloud.io/api/project_badges/measure?project=redhat-cop_cloud.aws_ops&metric=sqale_rating)](https://sonarcloud.io/summary/new_code?id=redhat-cop_cloud.aws_ops) + # cloud.aws_ops Validated Content Collection This repository hosts the `cloud.aws_ops` Ansible Collection. ## SonarCloud (code quality) -Static analysis runs on [SonarCloud](https://sonarcloud.io) using `sonar-project.properties` and -`.github/workflows/sonarcloud.yml`. **Note:** Coverage in SonarCloud is not wired yet. - -The SonarCloud project key must match `sonar.projectKey` (`ansible-collections_cloud.aws_ops`). Adding -or renaming the project is coordinated via Ansible Collections maintainers. - -GitHub does not expose organization secrets to workflows for pull requests opened from forks. The -Sonar job therefore only runs on pushes to this repository's branches and on pull requests where the -head branch is on `redhat-cop/cloud.aws_ops` (not from forks). That matches GitHub's -documented behavior for [secrets in Actions](https://docs.github.com/en/actions/security-guides/using-secrets-in-github-actions). +Static analysis and **integration-test coverage** are reported on [SonarCloud](https://sonarcloud.io/project/overview?id=redhat-cop_cloud.aws_ops). Coverage is collected in [.github/workflows/sonarcloud.yml](.github/workflows/sonarcloud.yml), separate from [.github/workflows/integration.yml](.github/workflows/integration.yml). See [SONARCLOUD.md](SONARCLOUD.md). -If the project later needs Sonar with coverage on **fork** PRs, maintainers typically add a separate -trusted job after a workflow that uploads coverage artifacts, using GitHub's `workflow_run` event. -See [workflow_run (GitHub Docs)](https://docs.github.com/en/actions/using-workflows/events-that-trigger-workflows#workflow_run). +The SonarCloud project key must match `sonar.projectKey` in [sonar-project.properties](sonar-project.properties) (`redhat-cop_cloud.aws_ops`). ## Description diff --git a/SONARCLOUD.md b/SONARCLOUD.md new file mode 100644 index 00000000..27d49fbb --- /dev/null +++ b/SONARCLOUD.md @@ -0,0 +1,30 @@ +# SonarCloud + +Dashboard: + +[SonarCloud project overview](https://sonarcloud.io/project/overview?id=redhat-cop_cloud.aws_ops) + +## CI integration + +Sonar analysis and **coverage collection** live in **[.github/workflows/sonarcloud.yml](.github/workflows/sonarcloud.yml)** only. They are **not** part of **[.github/workflows/integration.yml](.github/workflows/integration.yml)** or an **`all_green`** aggregator. + +| Workflow | Role | +| -------- | ---- | +| [integration.yml](.github/workflows/integration.yml) | Standard integration tests (`ansible_test_integration`, no coverage) | +| [sonarcloud.yml](.github/workflows/sonarcloud.yml) | Same PR gates and targets, but `ansible-test integration --coverage`; **`coverage`** job emits `coverage.xml`; **`finalize`** runs the Sonar scanner | + +Both workflows use **`pull_request_target`**, the **safe to test** label, and the same splitter/AWS setup. A labeled PR therefore runs integration tests twice (once per workflow); only the SonarCloud workflow produces coverage for Sonar. + +### SonarCloud workflow jobs + +- **`coverage-test`** (matrix) — integration tests with `--coverage`; uploads **`coverage-raw-*`** artifacts +- **`coverage`** — `ansible-test coverage combine` / `coverage xml`, path rewrite, upload artifact **`coverage`** +- **`finalize`** — downloads **`coverage`**, sets **`sonar.python.coverage.reportPaths`**, runs **`SonarSource/sonarqube-scan-action`** (same-repo PRs only, when org secret is set) + +Scanner configuration: [sonar-project.properties](sonar-project.properties) (`sonar.projectKey=redhat-cop_cloud.aws_ops`, `sonar.tests=tests/integration`). + +**`finalize`** uses org secret **`ANSIBLE_COLLECTIONS_ORG_SONAR_TOKEN_CICD_BOT`** and is gated so the token is not used for fork-head PRs. See GitHub [secrets in Actions](https://docs.github.com/en/actions/security-guides/using-secrets-in-github-actions). + +## Branch protection (repository settings) + +If **SonarCloud** / **finalize** should block merges, add the check under **Settings** > **Branches** > **Required status checks**. That is not configured in YAML. diff --git a/sonar-project.properties b/sonar-project.properties index 183f938c..252cd3f2 100644 --- a/sonar-project.properties +++ b/sonar-project.properties @@ -1,8 +1,8 @@ # SonarCloud project configuration for cloud.aws_ops # Complete documentation: https://docs.sonarqube.org/latest/analysis/analysis-parameters/ -sonar.projectKey=ansible-collections_cloud.aws_ops -sonar.organization=ansible-collections +sonar.projectKey=redhat-cop_cloud.aws_ops +sonar.organization=redhat-cop sonar.sources=. sonar.projectName=cloud.aws_ops sonar.python.coverage.reportPaths=coverage.xml From 89ac144a9bd7f917b3851060f8ecfb8adde88b8e Mon Sep 17 00:00:00 2001 From: Benny Rahmanim Date: Sun, 14 Jun 2026 15:21:41 +0300 Subject: [PATCH 2/7] Configure SonarQube coverage --- .github/workflows/sonarcloud.yml | 165 +++++++++---------------------- 1 file changed, 49 insertions(+), 116 deletions(-) diff --git a/.github/workflows/sonarcloud.yml b/.github/workflows/sonarcloud.yml index bbd19cd3..4f53c7f6 100644 --- a/.github/workflows/sonarcloud.yml +++ b/.github/workflows/sonarcloud.yml @@ -1,21 +1,13 @@ -## SonarCloud: integration coverage (separate from integration.yml) + scanner. +## SonarCloud Analysis Workflow (redhat-cop canonical) # -# Coverage jobs mirror Integration PR gates (safe to test, splitter, AWS) but run ansible-test with -# --coverage only here. integration.yml runs the same targets without coverage. finalize scans after coverage. -# -# ORG NOTE: Secret name ANSIBLE_COLLECTIONS_ORG_SONAR_TOKEN_CICD_BOT is shared across ansible-collections repos. +# Coverage jobs run Integration tests all targets with ansible-test with --coverage, then finalize scans after coverage. +# Downloads coverage* artifacts from that run and passes sonar.python.coverage.reportPaths to the scanner. --- name: SonarCloud on: - pull_request_target: - types: - - opened - - reopened - - labeled - - unlabeled - - synchronize + push: branches: - main - stable-* @@ -23,11 +15,12 @@ on: permissions: contents: read pull-requests: read + actions: read jobs: safe-to-test: if: ${{ github.event.label.name == 'safe to test' }} || ${{ github.event.action != 'labeled' }} - uses: ansible-network/github_actions/.github/workflows/safe-to-test.yml@main + uses: ansible-network/github_actions/.github/workflows/safe-to-test.yml@d6170859194bb9ca227655c03c9048455364a822 # main, committed on Jul 26, 2023 splitter: needs: @@ -35,13 +28,13 @@ jobs: runs-on: ubuntu-latest env: source_dir: "cloud_awsops" + SPLITTER_TEST_ALL_THE_TARGETS: "true" outputs: - test_targets: ${{ steps.splitter.outputs.test_targets }} - test_targets_json: ${{ steps.splitter.outputs.test_targets_json }} - test_jobs: ${{ steps.splitter.outputs.test_jobs }} + targets: ${{ steps.splitter.outputs.targets }} + test_jobs: ${{ steps.splitter.outputs.jobs }} steps: - name: Checkout collection - uses: actions/checkout@v3 + uses: actions/checkout@v6 with: path: ${{ env.source_dir }} ref: ${{ github.event.pull_request.head.sha }} @@ -49,13 +42,20 @@ jobs: - name: List changes for pull request id: splitter - uses: ansible-network/github_actions/.github/actions/ansible_test_splitter@main + uses: ansible/cloud-content-ci-automation/.github/actions/ansible_test_splitter@a93c78e1d842d4ee8961769110d88ac16bcc217a # main, Commits on May 15, 2026 with: collections_to_test: ${{ env.source_dir }} - total_jobs: 2 + total_jobs: 1 + base_ref: "${{ github.event.pull_request.base.ref }}" + + - name: Display splitter output + run: | + echo "test_targets=${{ steps.splitter.outputs.targets }}" + echo "test_jobs=${{ steps.splitter.outputs.jobs }}" + shell: bash coverage-test: - if: ${{ needs.splitter.outputs.test_targets != '' }} + if: ${{ needs.splitter.outputs.targets != '' }} runs-on: ubuntu-latest needs: - splitter @@ -63,98 +63,58 @@ jobs: source: "./source" ansible_version: "milestone" python_version: "3.12" + ansible_test_splitter_targets: "${{ needs.splitter.outputs.targets }}" strategy: fail-fast: false matrix: workflow-id: ${{ fromJson(needs.splitter.outputs.test_jobs) }} name: "coverage-integration-${{ matrix.workflow-id }}" steps: - - name: Read target + - name: Read job targets id: read-targets - run: | - import json, os - with open(os.environ.get('GITHUB_OUTPUT'), "a", encoding="utf-8") as fh: - fh.write(f'ansible_test_targets={json.loads(os.environ.get("ALL_TEST_TARGETS")).get(os.environ.get("WORKFLOW_ID"))}\n') - shell: python - env: - ALL_TEST_TARGETS: ${{ needs.splitter.outputs.test_targets_json }} - WORKFLOW_ID: ${{ matrix.workflow-id }} - - - name: Checkout collection - uses: actions/checkout@v3 + uses: ansible/cloud-content-ci-automation/.github/actions/ansible_read_targets@0b891487c4170ef856f02313c66cb1c776814c42 # main, committedon Apr 1, 2026 with: - path: ${{ env.source }} - ref: ${{ github.event.pull_request.head.sha }} + job_id: "${{ matrix.workflow-id }}" + all_targets: "${{ env.ansible_test_splitter_targets }}" - - name: Set up Python ${{ env.python_version }} - uses: actions/setup-python@v4 - with: - python-version: ${{ env.python_version }} - - - name: Install ansible-core (${{ env.ansible_version }}) - run: >- - python3 -m pip install - https://github.com/ansible/ansible/archive/${{ env.ansible_version }}.tar.gz - --disable-pip-version-check - shell: bash - - - name: Pre install collections dependencies first so the collection install does not - run: ansible-galaxy collection install --pre '-r${{ env.source }}/tests/integration/requirements.yml' -p /home/runner/collections/ - - - name: Build and install collection - id: install - uses: ansible-network/github_actions/.github/actions/build_install_collection@main - with: - install_python_dependencies: false - source_path: ${{ env.source }} - - - name: Create AWS/sts session credentials - uses: ansible-network/github_actions/.github/actions/ansible_aws_test_provider@main - with: - collection_path: ${{ steps.install.outputs.collection_path }} - ansible_core_ci_key: ${{ secrets.ANSIBLE_CORE_CI_KEY }} - - - name: Set up git + - name: Display ansible test targets run: | - git config --global user.email gha@localhost - git config --global user.name "Github Actions" + echo "ansible_test_targets -> ${{ steps.read-targets.outputs.job_targets }}" shell: bash - - name: Install collection python requirements - run: python3 -m pip install -r test-requirements.txt --disable-pip-version-check - working-directory: ${{ steps.install.outputs.collection_path }} - shell: bash + - name: Create AWS/sts session credentials + uses: ansible/ansible-test-auth@008750a5bf07124c3ab86d30d73d7319d7518f36 # v1.0.0, committed on Mar 13, 2026 - name: Run integration tests with coverage - run: >- - ansible-test integration - --diff - --no-temp-workdir - --color - --skip-tags False - --retry-on-error - --continue-on-error - --coverage - --requirements - --python ${{ env.python_version }} - -v - ${{ steps.read-targets.outputs.ansible_test_targets }} - working-directory: ${{ steps.install.outputs.collection_path }} - shell: bash + id: integration + uses: ansible-community/ansible-test-gh-action@d3a8ec7a59694e25e210fcd44738910149537f0e # v1.17.0, committed on Aug 6, 2025 + with: + ansible-core-version: ${{ env.ansible_version }} + origin-python-version: ${{ env.python_version }} + testing-type: integration + target-python-version: ${{ env.python_version }} + target: ${{ steps.read-targets.outputs.job_targets }} + git-checkout-ref: ${{ github.event.pull_request.head.sha }} + coverage: always + test-deps: >- + git+https://github.com/ansible-collections/amazon.aws.git,main + git+https://github.com/ansible-collections/community.aws.git,main + community.crypto + pre-test-cmd: python -m pip install -r tests/integration/constraints.txt -r tests/integration/requirements.txt - name: Upload integration coverage data if: success() || failure() uses: actions/upload-artifact@v4 with: name: coverage-raw-${{ matrix.workflow-id }} - path: ${{ steps.install.outputs.collection_path }}/tests/output/ + path: ${{ steps.integration.outputs.checkout-directory }}/tests/output/ if-no-files-found: ignore retention-days: 5 coverage: name: Integration test coverage if: >- - needs.splitter.outputs.test_targets != '' + needs.splitter.outputs.targets != '' && needs.coverage-test.result != 'skipped' && needs.coverage-test.result != 'cancelled' runs-on: ubuntu-latest @@ -167,37 +127,11 @@ jobs: python_version: "3.12" steps: - name: Checkout collection - uses: actions/checkout@v3 + uses: actions/checkout@v6 with: path: ${{ env.source }} ref: ${{ github.event.pull_request.head.sha }} - - - name: Set up Python ${{ env.python_version }} - uses: actions/setup-python@v4 - with: - python-version: ${{ env.python_version }} - - - name: Install ansible-core (${{ env.ansible_version }}) - run: >- - python3 -m pip install - https://github.com/ansible/ansible/archive/${{ env.ansible_version }}.tar.gz - --disable-pip-version-check - shell: bash - - - name: Pre install collections dependencies first so the collection install does not - run: ansible-galaxy collection install --pre '-r${{ env.source }}/tests/integration/requirements.yml' -p /home/runner/collections/ - - - name: Build and install collection - id: install - uses: ansible-network/github_actions/.github/actions/build_install_collection@main - with: - install_python_dependencies: false - source_path: ${{ env.source }} - - - name: Install collection python requirements - run: python3 -m pip install -r test-requirements.txt --disable-pip-version-check - working-directory: ${{ steps.install.outputs.collection_path }} - shell: bash + fetch-depth: "0" - name: Download integration coverage data uses: actions/download-artifact@v4 @@ -236,11 +170,10 @@ jobs: if: >- needs.coverage.result == 'success' && github.event.pull_request.head.repo.full_name == github.repository - && secrets.ANSIBLE_COLLECTIONS_ORG_SONAR_TOKEN_CICD_BOT != '' runs-on: ubuntu-latest steps: - name: Checkout repository - uses: actions/checkout@v4 + uses: actions/checkout@v6 with: ref: ${{ github.event.pull_request.head.sha }} fetch-depth: 0 From 44705283de8651ef8832a058720e5d3fdb84eab8 Mon Sep 17 00:00:00 2001 From: Benny Rahmanim Date: Sun, 14 Jun 2026 15:43:36 +0300 Subject: [PATCH 3/7] fix sonarcloud issue --- .github/workflows/sonarcloud.yml | 13 ++++++------- 1 file changed, 6 insertions(+), 7 deletions(-) diff --git a/.github/workflows/sonarcloud.yml b/.github/workflows/sonarcloud.yml index 4f53c7f6..8cf5aab1 100644 --- a/.github/workflows/sonarcloud.yml +++ b/.github/workflows/sonarcloud.yml @@ -11,6 +11,11 @@ on: branches: - main - stable-* + pull_request: + branches: + - main + - stable-* + workflow_dispatch: permissions: contents: read @@ -18,17 +23,10 @@ permissions: actions: read jobs: - safe-to-test: - if: ${{ github.event.label.name == 'safe to test' }} || ${{ github.event.action != 'labeled' }} - uses: ansible-network/github_actions/.github/workflows/safe-to-test.yml@d6170859194bb9ca227655c03c9048455364a822 # main, committed on Jul 26, 2023 - splitter: - needs: - - safe-to-test runs-on: ubuntu-latest env: source_dir: "cloud_awsops" - SPLITTER_TEST_ALL_THE_TARGETS: "true" outputs: targets: ${{ steps.splitter.outputs.targets }} test_jobs: ${{ steps.splitter.outputs.jobs }} @@ -47,6 +45,7 @@ jobs: collections_to_test: ${{ env.source_dir }} total_jobs: 1 base_ref: "${{ github.event.pull_request.base.ref }}" + splitter_test_all_the_targets: 'true' - name: Display splitter output run: | From 6ec6dfd815bba11245ec7b4f26e48cfc0b621337 Mon Sep 17 00:00:00 2001 From: Benny Rahmanim Date: Sun, 14 Jun 2026 19:23:10 +0300 Subject: [PATCH 4/7] Run integration coverage tests with ansible-test --- .github/workflows/coverage_test.yml | 116 ++++++++++++++++ .github/workflows/sonarcloud.yml | 203 +++++----------------------- 2 files changed, 153 insertions(+), 166 deletions(-) create mode 100644 .github/workflows/coverage_test.yml diff --git a/.github/workflows/coverage_test.yml b/.github/workflows/coverage_test.yml new file mode 100644 index 00000000..37b652f3 --- /dev/null +++ b/.github/workflows/coverage_test.yml @@ -0,0 +1,116 @@ +--- + name: coverage_tests + + concurrency: + group: ${{ github.workflow }}-${{ github.head_ref || github.ref }} + cancel-in-progress: true + + on: # yamllint disable-line rule:truthy + pull_request: + branches: + - main + - stable-* + push: + branches: + - main + - stable-* + workflow_dispatch: + + jobs: + coverage: + name: Integration test coverage + runs-on: ubuntu-latest + env: + ANSIBLE_CORE_REF: "milestone" + steps: + - name: Checkout repository + uses: actions/checkout@v4 + with: + # ansible-test requires cwd under .../ansible_collections/{namespace}/{name}/ + path: ansible_collections/cloud/aws_ops + + - name: Checkout amazon.aws dependency + uses: ansible-network/github_actions/.github/actions/checkout_dependency@47822b51aee957080e811cc434443c4e3bb9569a + with: + repository: ansible-collections/amazon.aws + path: ansible_collections/amazon/aws + ref: main + + - name: Checkout community.aws dependency + uses: ansible-network/github_actions/.github/actions/checkout_dependency@47822b51aee957080e811cc434443c4e3bb9569a + with: + repository: ansible-collections/community.aws + path: ansible_collections/community/aws + ref: main + + - name: Checkout community.crypto dependency + uses: ansible-network/github_actions/.github/actions/checkout_dependency@47822b51aee957080e811cc434443c4e3bb9569a + with: + repository: ansible-collections/community.crypto + path: ansible_collections/community/crypto + ref: main + + - name: Set up Python + uses: actions/setup-python@v5 + with: + python-version: "3.12" + + - name: Install Ansible (ansible-test) + run: | + python -m pip install --upgrade pip + python -m pip install "https://github.com/ansible/ansible/archive/${ANSIBLE_CORE_REF}.tar.gz" + + - name: Install integration test dependencies + run: | + python -m pip install -r ansible_collections/cloud/aws_ops/tests/integration/constraints.txt -r ansible_collections/cloud/aws_ops/tests/integration/requirements.txt + + - name: Create AWS/sts session credentials + uses: ansible/ansible-test-auth@008750a5bf07124c3ab86d30d73d7319d7518f36 # v1.0.0, committed on Mar 13, 2026 + + - name: Run integration tests with coverage + working-directory: ansible_collections/cloud/aws_ops + run: ansible-test integration --venv --coverage --python 3.12 --requirements + + - name: Combine and emit coverage XML + working-directory: ansible_collections/cloud/aws_ops + run: | + ansible-test coverage combine --venv --python 3.12 --requirements + ansible-test coverage xml --venv --python 3.12 --requirements + + - name: Prepare coverage.xml for SonarCloud + env: + COLLECTION_DIR: ${{ github.workspace }}/ansible_collections/cloud/aws_ops + run: | + set -euo pipefail + xml=$(find "${COLLECTION_DIR}/tests/output/reports" -maxdepth 1 -name '*.xml' ! -name '*powershell*' | head -1) + if [[ -z "${xml}" ]]; then + echo "::error::coverage XML not found under tests/output/reports" + exit 1 + fi + cp "${xml}" "${GITHUB_WORKSPACE}/coverage.xml" + sed -i "s#${GITHUB_WORKSPACE}/##g" "${GITHUB_WORKSPACE}/coverage.xml" + sed -i 's#ansible_collections/cloud/aws_ops/##g' "${GITHUB_WORKSPACE}/coverage.xml" + if ! grep -q '- + python -c "assert 'failure' not in + set([ + '${{ needs.coverage.result }}' + ])" \ No newline at end of file diff --git a/.github/workflows/sonarcloud.yml b/.github/workflows/sonarcloud.yml index 8cf5aab1..764a067c 100644 --- a/.github/workflows/sonarcloud.yml +++ b/.github/workflows/sonarcloud.yml @@ -7,15 +7,11 @@ name: SonarCloud on: - push: - branches: - - main - - stable-* - pull_request: - branches: - - main - - stable-* - workflow_dispatch: + workflow_run: + workflows: + - coverage_tests + types: + - completed permissions: contents: read @@ -23,165 +19,25 @@ permissions: actions: read jobs: - splitter: - runs-on: ubuntu-latest - env: - source_dir: "cloud_awsops" - outputs: - targets: ${{ steps.splitter.outputs.targets }} - test_jobs: ${{ steps.splitter.outputs.jobs }} - steps: - - name: Checkout collection - uses: actions/checkout@v6 - with: - path: ${{ env.source_dir }} - ref: ${{ github.event.pull_request.head.sha }} - fetch-depth: "0" - - - name: List changes for pull request - id: splitter - uses: ansible/cloud-content-ci-automation/.github/actions/ansible_test_splitter@a93c78e1d842d4ee8961769110d88ac16bcc217a # main, Commits on May 15, 2026 - with: - collections_to_test: ${{ env.source_dir }} - total_jobs: 1 - base_ref: "${{ github.event.pull_request.base.ref }}" - splitter_test_all_the_targets: 'true' - - - name: Display splitter output - run: | - echo "test_targets=${{ steps.splitter.outputs.targets }}" - echo "test_jobs=${{ steps.splitter.outputs.jobs }}" - shell: bash - - coverage-test: - if: ${{ needs.splitter.outputs.targets != '' }} - runs-on: ubuntu-latest - needs: - - splitter - env: - source: "./source" - ansible_version: "milestone" - python_version: "3.12" - ansible_test_splitter_targets: "${{ needs.splitter.outputs.targets }}" - strategy: - fail-fast: false - matrix: - workflow-id: ${{ fromJson(needs.splitter.outputs.test_jobs) }} - name: "coverage-integration-${{ matrix.workflow-id }}" - steps: - - name: Read job targets - id: read-targets - uses: ansible/cloud-content-ci-automation/.github/actions/ansible_read_targets@0b891487c4170ef856f02313c66cb1c776814c42 # main, committedon Apr 1, 2026 - with: - job_id: "${{ matrix.workflow-id }}" - all_targets: "${{ env.ansible_test_splitter_targets }}" - - - name: Display ansible test targets - run: | - echo "ansible_test_targets -> ${{ steps.read-targets.outputs.job_targets }}" - shell: bash - - - name: Create AWS/sts session credentials - uses: ansible/ansible-test-auth@008750a5bf07124c3ab86d30d73d7319d7518f36 # v1.0.0, committed on Mar 13, 2026 - - - name: Run integration tests with coverage - id: integration - uses: ansible-community/ansible-test-gh-action@d3a8ec7a59694e25e210fcd44738910149537f0e # v1.17.0, committed on Aug 6, 2025 - with: - ansible-core-version: ${{ env.ansible_version }} - origin-python-version: ${{ env.python_version }} - testing-type: integration - target-python-version: ${{ env.python_version }} - target: ${{ steps.read-targets.outputs.job_targets }} - git-checkout-ref: ${{ github.event.pull_request.head.sha }} - coverage: always - test-deps: >- - git+https://github.com/ansible-collections/amazon.aws.git,main - git+https://github.com/ansible-collections/community.aws.git,main - community.crypto - pre-test-cmd: python -m pip install -r tests/integration/constraints.txt -r tests/integration/requirements.txt - - - name: Upload integration coverage data - if: success() || failure() - uses: actions/upload-artifact@v4 - with: - name: coverage-raw-${{ matrix.workflow-id }} - path: ${{ steps.integration.outputs.checkout-directory }}/tests/output/ - if-no-files-found: ignore - retention-days: 5 - - coverage: - name: Integration test coverage - if: >- - needs.splitter.outputs.targets != '' - && needs.coverage-test.result != 'skipped' - && needs.coverage-test.result != 'cancelled' - runs-on: ubuntu-latest - needs: - - splitter - - coverage-test - env: - source: "./source" - ansible_version: "milestone" - python_version: "3.12" - steps: - - name: Checkout collection - uses: actions/checkout@v6 - with: - path: ${{ env.source }} - ref: ${{ github.event.pull_request.head.sha }} - fetch-depth: "0" - - - name: Download integration coverage data - uses: actions/download-artifact@v4 - with: - pattern: coverage-raw-* - merge-multiple: true - path: ${{ steps.install.outputs.collection_path }}/tests/output - - - name: Combine and emit coverage XML - working-directory: ${{ steps.install.outputs.collection_path }} - run: | - ansible-test coverage combine --requirements --python "${{ env.python_version }}" - ansible-test coverage xml --requirements --python "${{ env.python_version }}" - - - name: Prepare coverage.xml for SonarCloud - run: | - set -euo pipefail - collection_path="${{ steps.install.outputs.collection_path }}" - xml=$(find "${collection_path}/tests/output/reports" -maxdepth 1 -name '*.xml' ! -name '*powershell*' | head -1) - test -n "${xml}" - cp "${xml}" "${GITHUB_WORKSPACE}/coverage.xml" - sed -i "s#${GITHUB_WORKSPACE}/##g" "${GITHUB_WORKSPACE}/coverage.xml" - sed -i "s#${collection_path}/##g" "${GITHUB_WORKSPACE}/coverage.xml" - - - name: Upload coverage artifact - uses: actions/upload-artifact@v4 - with: - name: coverage - path: ${{ github.workspace }}/coverage.xml - retention-days: 5 - finalize: name: finalize - needs: - - coverage - if: >- - needs.coverage.result == 'success' - && github.event.pull_request.head.repo.full_name == github.repository runs-on: ubuntu-latest + if: github.event.workflow_run.conclusion == 'success' steps: - name: Checkout repository - uses: actions/checkout@v6 + uses: actions/checkout@v4 with: - ref: ${{ github.event.pull_request.head.sha }} + ref: ${{ github.event.workflow_run.head_sha }} fetch-depth: 0 + show-progress: false - - name: Download coverage artifact - uses: actions/download-artifact@v4 + - name: Download coverage artifacts + uses: dawidd6/action-download-artifact@2536c51d3d126276eb39f74d6bc9c72ac6ef30d3 # v16 with: - name: coverage - path: . + github_token: ${{ secrets.GITHUB_TOKEN }} + workflow: all_green + run_id: ${{ github.event.workflow_run.id }} + pattern: coverage* - name: Set coverage report paths run: | @@ -189,17 +45,32 @@ jobs: echo "Found coverage files: ${coverage_files:-none}" echo "COVERAGE_PATHS=${coverage_files}" >> "$GITHUB_ENV" + - name: Get PR number and info + if: github.event.workflow_run.event == 'pull_request' + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + HEAD_BRANCH: ${{ github.event.workflow_run.head_branch }} + REPO: ${{ github.repository }} + run: | + PR_NUMBER=$(gh pr list --head "$HEAD_BRANCH" --repo "$REPO" --json number -q '.[0].number') + echo "PR_NUMBER=$PR_NUMBER" >> "$GITHUB_ENV" + if [ -n "$PR_NUMBER" ]; then + PR_DATA=$(gh api "repos/$REPO/pulls/$PR_NUMBER") + echo "PR_BASE=$(echo "$PR_DATA" | jq -r '.base.ref')" >> "$GITHUB_ENV" + echo "PR_HEAD=$(echo "$PR_DATA" | jq -r '.head.ref')" >> "$GITHUB_ENV" + fi + - name: Prepare SonarCloud args env: - COMMIT_SHA: ${{ github.event.pull_request.head.sha }} - PR_NUMBER: ${{ github.event.pull_request.number }} - PR_HEAD: ${{ github.event.pull_request.head.ref }} - PR_BASE: ${{ github.event.pull_request.base.ref }} + COMMIT_SHA: ${{ github.event.workflow_run.head_sha }} + EVENT_TYPE: ${{ github.event.workflow_run.event }} run: | SONAR_ARGS="-Dsonar.scm.revision=\"${COMMIT_SHA}\"" - SONAR_ARGS="${SONAR_ARGS} -Dsonar.pullrequest.key=${PR_NUMBER}" - SONAR_ARGS="${SONAR_ARGS} -Dsonar.pullrequest.branch=${PR_HEAD}" - SONAR_ARGS="${SONAR_ARGS} -Dsonar.pullrequest.base=${PR_BASE}" + if [[ "${EVENT_TYPE}" == "pull_request" && -n "${PR_NUMBER:-}" ]]; then + SONAR_ARGS="${SONAR_ARGS} -Dsonar.pullrequest.key=${PR_NUMBER}" + SONAR_ARGS="${SONAR_ARGS} -Dsonar.pullrequest.branch=${PR_HEAD}" + SONAR_ARGS="${SONAR_ARGS} -Dsonar.pullrequest.base=${PR_BASE}" + fi if [[ -n "${COVERAGE_PATHS:-}" ]]; then SONAR_ARGS="${SONAR_ARGS} -Dsonar.python.coverage.reportPaths=${COVERAGE_PATHS}" fi From cf066f8fd4885ec3ebdc08676ba4b3f7d4e99153 Mon Sep 17 00:00:00 2001 From: Benny Rahmanim Date: Tue, 16 Jun 2026 09:48:36 +0300 Subject: [PATCH 5/7] Fix linter issues --- .github/workflows/coverage_test.yml | 222 ++++++++++++++-------------- 1 file changed, 111 insertions(+), 111 deletions(-) diff --git a/.github/workflows/coverage_test.yml b/.github/workflows/coverage_test.yml index 37b652f3..1ebff0de 100644 --- a/.github/workflows/coverage_test.yml +++ b/.github/workflows/coverage_test.yml @@ -1,116 +1,116 @@ --- - name: coverage_tests - - concurrency: - group: ${{ github.workflow }}-${{ github.head_ref || github.ref }} - cancel-in-progress: true - - on: # yamllint disable-line rule:truthy - pull_request: - branches: - - main - - stable-* - push: - branches: - - main - - stable-* - workflow_dispatch: - - jobs: - coverage: - name: Integration test coverage - runs-on: ubuntu-latest - env: - ANSIBLE_CORE_REF: "milestone" - steps: - - name: Checkout repository - uses: actions/checkout@v4 - with: - # ansible-test requires cwd under .../ansible_collections/{namespace}/{name}/ - path: ansible_collections/cloud/aws_ops - - - name: Checkout amazon.aws dependency - uses: ansible-network/github_actions/.github/actions/checkout_dependency@47822b51aee957080e811cc434443c4e3bb9569a - with: - repository: ansible-collections/amazon.aws - path: ansible_collections/amazon/aws - ref: main +name: coverage_tests + +concurrency: + group: ${{ github.workflow }}-${{ github.head_ref || github.ref }} + cancel-in-progress: true + +on: # yamllint disable-line rule:truthy + pull_request: + branches: + - main + - stable-* + push: + branches: + - main + - stable-* + workflow_dispatch: + +jobs: + coverage: + name: Integration test coverage + runs-on: ubuntu-latest + env: + ANSIBLE_CORE_REF: "milestone" + steps: + - name: Checkout repository + uses: actions/checkout@v4 + with: + # ansible-test requires cwd under .../ansible_collections/{namespace}/{name}/ + path: ansible_collections/cloud/aws_ops + + - name: Checkout amazon.aws dependency + uses: ansible-network/github_actions/.github/actions/checkout_dependency@47822b51aee957080e811cc434443c4e3bb9569a + with: + repository: ansible-collections/amazon.aws + path: ansible_collections/amazon/aws + ref: main + + - name: Checkout community.aws dependency + uses: ansible-network/github_actions/.github/actions/checkout_dependency@47822b51aee957080e811cc434443c4e3bb9569a + with: + repository: ansible-collections/community.aws + path: ansible_collections/community/aws + ref: main + + - name: Checkout community.crypto dependency + uses: ansible-network/github_actions/.github/actions/checkout_dependency@47822b51aee957080e811cc434443c4e3bb9569a + with: + repository: ansible-collections/community.crypto + path: ansible_collections/community/crypto + ref: main - - name: Checkout community.aws dependency - uses: ansible-network/github_actions/.github/actions/checkout_dependency@47822b51aee957080e811cc434443c4e3bb9569a - with: - repository: ansible-collections/community.aws - path: ansible_collections/community/aws - ref: main + - name: Set up Python + uses: actions/setup-python@v5 + with: + python-version: "3.12" - - name: Checkout community.crypto dependency - uses: ansible-network/github_actions/.github/actions/checkout_dependency@47822b51aee957080e811cc434443c4e3bb9569a - with: - repository: ansible-collections/community.crypto - path: ansible_collections/community/crypto - ref: main - - - name: Set up Python - uses: actions/setup-python@v5 - with: - python-version: "3.12" - - - name: Install Ansible (ansible-test) - run: | - python -m pip install --upgrade pip - python -m pip install "https://github.com/ansible/ansible/archive/${ANSIBLE_CORE_REF}.tar.gz" + - name: Install Ansible (ansible-test) + run: | + python -m pip install --upgrade pip + python -m pip install "https://github.com/ansible/ansible/archive/${ANSIBLE_CORE_REF}.tar.gz" + + - name: Install integration test dependencies + run: | + python -m pip install -r ansible_collections/cloud/aws_ops/tests/integration/constraints.txt -r ansible_collections/cloud/aws_ops/tests/integration/requirements.txt + + - name: Create AWS/sts session credentials + uses: ansible/ansible-test-auth@008750a5bf07124c3ab86d30d73d7319d7518f36 # v1.0.0, committed on Mar 13, 2026 + + - name: Run integration tests with coverage + working-directory: ansible_collections/cloud/aws_ops + run: ansible-test integration --venv --coverage --python 3.12 --requirements + + - name: Combine and emit coverage XML + working-directory: ansible_collections/cloud/aws_ops + run: | + ansible-test coverage combine --venv --python 3.12 --requirements + ansible-test coverage xml --venv --python 3.12 --requirements + + - name: Prepare coverage.xml for SonarCloud + env: + COLLECTION_DIR: ${{ github.workspace }}/ansible_collections/cloud/aws_ops + run: | + set -euo pipefail + xml=$(find "${COLLECTION_DIR}/tests/output/reports" -maxdepth 1 -name '*.xml' ! -name '*powershell*' | head -1) + if [[ -z "${xml}" ]]; then + echo "::error::coverage XML not found under tests/output/reports" + exit 1 + fi + cp "${xml}" "${GITHUB_WORKSPACE}/coverage.xml" + sed -i "s#${GITHUB_WORKSPACE}/##g" "${GITHUB_WORKSPACE}/coverage.xml" + sed -i 's#ansible_collections/cloud/aws_ops/##g' "${GITHUB_WORKSPACE}/coverage.xml" + if ! grep -q '- - python -c "assert 'failure' not in - set([ - '${{ needs.coverage.result }}' - ])" \ No newline at end of file + coverage_tests: + name: SonarCloud Coverage tests + needs: + - coverage + runs-on: ubuntu-latest + if: ${{ always() }} + steps: + - run: >- + python -c "assert 'failure' not in + set([ + '${{ needs.coverage.result }}' + ])" From 748c9a88af7fe8dd776f206cc68c45fda7f68ae8 Mon Sep 17 00:00:00 2001 From: Benny Rahmanim Date: Tue, 16 Jun 2026 12:31:49 +0300 Subject: [PATCH 6/7] add workflow_call by sonar scan job --- .github/workflows/coverage_test.yml | 18 +++---- .github/workflows/sonarcloud.yml | 80 ++++++++++++----------------- 2 files changed, 41 insertions(+), 57 deletions(-) diff --git a/.github/workflows/coverage_test.yml b/.github/workflows/coverage_test.yml index 1ebff0de..ee4bea15 100644 --- a/.github/workflows/coverage_test.yml +++ b/.github/workflows/coverage_test.yml @@ -103,14 +103,14 @@ jobs: path: ${{ github.workspace }}/coverage.xml coverage_tests: - name: SonarCloud Coverage tests + name: SonarCloud scan needs: - coverage - runs-on: ubuntu-latest - if: ${{ always() }} - steps: - - run: >- - python -c "assert 'failure' not in - set([ - '${{ needs.coverage.result }}' - ])" + if: >- + ${{ needs.coverage.result == 'success' + && (github.event_name == 'push' + || (github.event_name == 'pull_request' + && github.event.pull_request.head.repo.full_name == github.repository)) }} + uses: ./.github/workflows/sonarcloud.yml + secrets: + ANSIBLE_COLLECTIONS_ORG_SONAR_TOKEN_CICD_BOT: ${{ secrets.ANSIBLE_COLLECTIONS_ORG_SONAR_TOKEN_CICD_BOT }} diff --git a/.github/workflows/sonarcloud.yml b/.github/workflows/sonarcloud.yml index 764a067c..ea72f5a4 100644 --- a/.github/workflows/sonarcloud.yml +++ b/.github/workflows/sonarcloud.yml @@ -1,43 +1,39 @@ -## SonarCloud Analysis Workflow (redhat-cop canonical) +## SonarCloud scan (reusable) # -# Coverage jobs run Integration tests all targets with ansible-test with --coverage, then finalize scans after coverage. -# Downloads coverage* artifacts from that run and passes sonar.python.coverage.reportPaths to the scanner. +# Invoked from **coverage_test** after the aggregate gate and **coverage** succeed. Uses the **caller's** +# **pull_request** / **push** event so **actions/checkout** can use **github.event.pull_request.head.sha** +# on PRs (Sonar-compliant). Not triggered by **workflow_run** + **workflow_run.head_sha** checkout. --- name: SonarCloud on: - workflow_run: - workflows: - - coverage_tests - types: - - completed + workflow_call: + secrets: + ANSIBLE_COLLECTIONS_ORG_SONAR_TOKEN_CICD_BOT: + required: true permissions: contents: read pull-requests: read - actions: read jobs: - finalize: - name: finalize + scan: + name: SonarCloud scan runs-on: ubuntu-latest - if: github.event.workflow_run.conclusion == 'success' steps: - name: Checkout repository uses: actions/checkout@v4 with: - ref: ${{ github.event.workflow_run.head_sha }} + ref: ${{ github.event_name == 'pull_request' && github.event.pull_request.head.sha || github.sha }} fetch-depth: 0 show-progress: false - name: Download coverage artifacts - uses: dawidd6/action-download-artifact@2536c51d3d126276eb39f74d6bc9c72ac6ef30d3 # v16 + uses: actions/download-artifact@v4 with: - github_token: ${{ secrets.GITHUB_TOKEN }} - workflow: all_green - run_id: ${{ github.event.workflow_run.id }} - pattern: coverage* + name: coverage + path: . - name: Set coverage report paths run: | @@ -45,36 +41,24 @@ jobs: echo "Found coverage files: ${coverage_files:-none}" echo "COVERAGE_PATHS=${coverage_files}" >> "$GITHUB_ENV" - - name: Get PR number and info - if: github.event.workflow_run.event == 'pull_request' - env: - GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} - HEAD_BRANCH: ${{ github.event.workflow_run.head_branch }} - REPO: ${{ github.repository }} - run: | - PR_NUMBER=$(gh pr list --head "$HEAD_BRANCH" --repo "$REPO" --json number -q '.[0].number') - echo "PR_NUMBER=$PR_NUMBER" >> "$GITHUB_ENV" - if [ -n "$PR_NUMBER" ]; then - PR_DATA=$(gh api "repos/$REPO/pulls/$PR_NUMBER") - echo "PR_BASE=$(echo "$PR_DATA" | jq -r '.base.ref')" >> "$GITHUB_ENV" - echo "PR_HEAD=$(echo "$PR_DATA" | jq -r '.head.ref')" >> "$GITHUB_ENV" - fi - - - name: Prepare SonarCloud args - env: - COMMIT_SHA: ${{ github.event.workflow_run.head_sha }} - EVENT_TYPE: ${{ github.event.workflow_run.event }} - run: | - SONAR_ARGS="-Dsonar.scm.revision=\"${COMMIT_SHA}\"" - if [[ "${EVENT_TYPE}" == "pull_request" && -n "${PR_NUMBER:-}" ]]; then - SONAR_ARGS="${SONAR_ARGS} -Dsonar.pullrequest.key=${PR_NUMBER}" - SONAR_ARGS="${SONAR_ARGS} -Dsonar.pullrequest.branch=${PR_HEAD}" - SONAR_ARGS="${SONAR_ARGS} -Dsonar.pullrequest.base=${PR_BASE}" - fi - if [[ -n "${COVERAGE_PATHS:-}" ]]; then - SONAR_ARGS="${SONAR_ARGS} -Dsonar.python.coverage.reportPaths=${COVERAGE_PATHS}" - fi - echo "SONAR_ARGS=${SONAR_ARGS}" >> "$GITHUB_ENV" + - name: Prepare SonarCloud args + env: + COMMIT_SHA: ${{ github.event_name == 'pull_request' && github.event.pull_request.head.sha || github.sha }} + EVENT_NAME: ${{ github.event_name }} + PR_NUMBER: ${{ github.event_name == 'pull_request' && github.event.pull_request.number || '' }} + PR_HEAD_REF: ${{ github.event_name == 'pull_request' && github.event.pull_request.head.ref || '' }} + PR_BASE_REF: ${{ github.event_name == 'pull_request' && github.event.pull_request.base.ref || '' }} + run: | + SONAR_ARGS="-Dsonar.scm.revision=\"${COMMIT_SHA}\"" + if [[ "${EVENT_NAME}" == "pull_request" ]]; then + SONAR_ARGS="${SONAR_ARGS} -Dsonar.pullrequest.key=${PR_NUMBER}" + SONAR_ARGS="${SONAR_ARGS} -Dsonar.pullrequest.branch=${PR_HEAD_REF}" + SONAR_ARGS="${SONAR_ARGS} -Dsonar.pullrequest.base=${PR_BASE_REF}" + fi + if [[ -n "${COVERAGE_PATHS:-}" ]]; then + SONAR_ARGS="${SONAR_ARGS} -Dsonar.python.coverage.reportPaths=${COVERAGE_PATHS}" + fi + echo "SONAR_ARGS=${SONAR_ARGS}" >> "$GITHUB_ENV" - name: SonarCloud Scan uses: SonarSource/sonarqube-scan-action@a31c9398be7ace6bbfaf30c0bd5d415f843d45e9 From e38e5486f762d28899656b09aab534a1a495d7f2 Mon Sep 17 00:00:00 2001 From: Benny Rahmanim Date: Tue, 16 Jun 2026 13:11:45 +0300 Subject: [PATCH 7/7] Fix sonar scan permission issues --- .github/workflows/coverage_test.yml | 3 +++ .github/workflows/sonarcloud.yml | 36 ++++++++++++++--------------- 2 files changed, 21 insertions(+), 18 deletions(-) diff --git a/.github/workflows/coverage_test.yml b/.github/workflows/coverage_test.yml index ee4bea15..db58a772 100644 --- a/.github/workflows/coverage_test.yml +++ b/.github/workflows/coverage_test.yml @@ -104,6 +104,9 @@ jobs: coverage_tests: name: SonarCloud scan + permissions: + contents: read + pull-requests: read needs: - coverage if: >- diff --git a/.github/workflows/sonarcloud.yml b/.github/workflows/sonarcloud.yml index ea72f5a4..54f25dcc 100644 --- a/.github/workflows/sonarcloud.yml +++ b/.github/workflows/sonarcloud.yml @@ -41,24 +41,24 @@ jobs: echo "Found coverage files: ${coverage_files:-none}" echo "COVERAGE_PATHS=${coverage_files}" >> "$GITHUB_ENV" - - name: Prepare SonarCloud args - env: - COMMIT_SHA: ${{ github.event_name == 'pull_request' && github.event.pull_request.head.sha || github.sha }} - EVENT_NAME: ${{ github.event_name }} - PR_NUMBER: ${{ github.event_name == 'pull_request' && github.event.pull_request.number || '' }} - PR_HEAD_REF: ${{ github.event_name == 'pull_request' && github.event.pull_request.head.ref || '' }} - PR_BASE_REF: ${{ github.event_name == 'pull_request' && github.event.pull_request.base.ref || '' }} - run: | - SONAR_ARGS="-Dsonar.scm.revision=\"${COMMIT_SHA}\"" - if [[ "${EVENT_NAME}" == "pull_request" ]]; then - SONAR_ARGS="${SONAR_ARGS} -Dsonar.pullrequest.key=${PR_NUMBER}" - SONAR_ARGS="${SONAR_ARGS} -Dsonar.pullrequest.branch=${PR_HEAD_REF}" - SONAR_ARGS="${SONAR_ARGS} -Dsonar.pullrequest.base=${PR_BASE_REF}" - fi - if [[ -n "${COVERAGE_PATHS:-}" ]]; then - SONAR_ARGS="${SONAR_ARGS} -Dsonar.python.coverage.reportPaths=${COVERAGE_PATHS}" - fi - echo "SONAR_ARGS=${SONAR_ARGS}" >> "$GITHUB_ENV" + - name: Prepare SonarCloud args + env: + COMMIT_SHA: ${{ github.event_name == 'pull_request' && github.event.pull_request.head.sha || github.sha }} + EVENT_NAME: ${{ github.event_name }} + PR_NUMBER: ${{ github.event_name == 'pull_request' && github.event.pull_request.number || '' }} + PR_HEAD_REF: ${{ github.event_name == 'pull_request' && github.event.pull_request.head.ref || '' }} + PR_BASE_REF: ${{ github.event_name == 'pull_request' && github.event.pull_request.base.ref || '' }} + run: | + SONAR_ARGS="-Dsonar.scm.revision=\"${COMMIT_SHA}\"" + if [[ "${EVENT_NAME}" == "pull_request" ]]; then + SONAR_ARGS="${SONAR_ARGS} -Dsonar.pullrequest.key=${PR_NUMBER}" + SONAR_ARGS="${SONAR_ARGS} -Dsonar.pullrequest.branch=${PR_HEAD_REF}" + SONAR_ARGS="${SONAR_ARGS} -Dsonar.pullrequest.base=${PR_BASE_REF}" + fi + if [[ -n "${COVERAGE_PATHS:-}" ]]; then + SONAR_ARGS="${SONAR_ARGS} -Dsonar.python.coverage.reportPaths=${COVERAGE_PATHS}" + fi + echo "SONAR_ARGS=${SONAR_ARGS}" >> "$GITHUB_ENV" - name: SonarCloud Scan uses: SonarSource/sonarqube-scan-action@a31c9398be7ace6bbfaf30c0bd5d415f843d45e9