From 7ad71a54a80e58b301cc42f637e0b088500a1a0b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Miko=C5=82aj=20Matuszny?= Date: Wed, 20 Aug 2025 13:45:37 +0200 Subject: [PATCH 1/2] BUILD-8079 Gradle shadow scans --- README.md | 16 ++++- build-gradle/action.yml | 24 +++++-- build-gradle/build.sh | 80 +++++++++++++++++---- spec/build-gradle_spec.sh | 143 +++++++++++++++++++++++++++++++------- 4 files changed, 219 insertions(+), 44 deletions(-) diff --git a/README.md b/README.md index e9528b4f..5373cfa4 100644 --- a/README.md +++ b/README.md @@ -261,12 +261,16 @@ Build and publish a Gradle project with SonarQube analysis and Artifactory deplo #### Required Vault Permissions -- `development/kv/data/next`, `development/kv/data/sonarcloud`, or `development/kv/data/sonarqube-us`: SonarQube credentials (based on sonar-platform) +- `development/kv/data/next`: SonarQube credentials for next platform +- `development/kv/data/sonarcloud`: SonarQube credentials for sqc-eu platform +- `development/kv/data/sonarqube-us`: SonarQube credentials for sqc-us platform - `development/kv/data/sign`: Artifact signing credentials (key, passphrase, and key_id) - `development/kv/data/develocity`: Develocity access token if `use-develocity: true` - `public-reader` or `private-reader`: Artifactory role for reading dependencies - `public-deployer` or `qa-deployer`: Artifactory role for deployment +**Note**: Credentials for all three SonarQube platforms are always required, regardless of the `run-shadow-scans` setting. + #### Other Dependencies **Java**: Not pre-installed in the runner image. We recommend using `mise` to install and manage Java versions. @@ -302,6 +306,11 @@ jobs: steps: - uses: actions/checkout@08eba0b27e820071cde6df949e0beb9ba4906955 # v4.3.0 - uses: SonarSource/ci-github-actions/build-gradle@v1 + with: + # Enable shadow scans for unified platform dogfooding (optional) + run-shadow-scans: 'true' + # Primary platform when shadow scans disabled (optional) + sonar-platform: 'next' ``` ### Inputs @@ -319,6 +328,7 @@ jobs: | `develocity-url` | URL for Develocity | `https://develocity.sonar.build/` | | `repox-url` | URL for Repox | `https://repox.jfrog.io` | | `sonar-platform` | SonarQube variant - 'next', 'sqc-eu', or 'sqc-us' | `next` | +| `run-shadow-scans` | Enable analysis across all 3 SonarQube platforms (unified platform dogfooding) | `false` | ### Outputs @@ -330,7 +340,9 @@ jobs: - Uses the gradle wrapper (`./gradlew`) by default and falls back to the `gradle` binary in case it is not found - Automated version management with build numbers -- SonarQube analysis for code quality +- SonarQube analysis for code quality with multi-platform support +- Unified platform dogfooding - analyze across all 3 SonarQube platforms (next, sqc-eu, sqc-us) +- Automatic deployment prevention during shadow scans to avoid duplicate artifacts - Conditional deployment based on branch patterns - Automatic artifact signing with credentials from Vault - Pull request support with optional deployment diff --git a/build-gradle/action.yml b/build-gradle/action.yml index 28f6c62b..18b5d2ff 100644 --- a/build-gradle/action.yml +++ b/build-gradle/action.yml @@ -36,6 +36,10 @@ inputs: sonar-platform: description: SonarQube variant (next, sqc-eu, or sqc-us) default: next + run-shadow-scans: + description: If true, run sonar scanner on all 3 platforms using the provided URL and token. + If false, run on the platform provided by SONAR_PLATFORM. + default: 'false' outputs: project-version: @@ -64,8 +68,12 @@ runs: with: # yamllint disable rule:line-length secrets: | - development/kv/data/${{ inputs.sonar-platform == 'sqc-eu' && 'sonarcloud' || (inputs.sonar-platform == 'sqc-us' && 'sonarqube-us' || 'next') }} url | SONAR_HOST_URL; - development/kv/data/${{ inputs.sonar-platform == 'sqc-eu' && 'sonarcloud' || (inputs.sonar-platform == 'sqc-us' && 'sonarqube-us' || 'next') }} token | SONAR_TOKEN; + development/kv/data/next url | NEXT_URL; + development/kv/data/next token | NEXT_TOKEN; + development/kv/data/sonarqube-us url | SQC_US_URL; + development/kv/data/sonarqube-us token | SQC_US_TOKEN; + development/kv/data/sonarcloud url | SQC_EU_URL; + development/kv/data/sonarcloud token | SQC_EU_TOKEN; development/artifactory/token/{REPO_OWNER_NAME_DASH}-${{ env.ARTIFACTORY_DEPLOYER_ROLE }} username | ARTIFACTORY_DEPLOY_USERNAME; development/artifactory/token/{REPO_OWNER_NAME_DASH}-${{ env.ARTIFACTORY_DEPLOYER_ROLE }} access_token | ARTIFACTORY_DEPLOY_PASSWORD; development/artifactory/token/{REPO_OWNER_NAME_DASH}-${{ env.ARTIFACTORY_READER_ROLE }} username | ARTIFACTORY_READER_USERNAME; @@ -106,9 +114,15 @@ runs: SKIP_TESTS: ${{ inputs.skip-tests }} GRADLE_ARGS: ${{ inputs.gradle-args }} - # Vault secrets - SONAR_HOST_URL: ${{ fromJSON(steps.secrets.outputs.vault).SONAR_HOST_URL }} - SONAR_TOKEN: ${{ fromJSON(steps.secrets.outputs.vault).SONAR_TOKEN }} + # Vault secrets - always fetch all platforms + NEXT_URL: ${{ fromJSON(steps.secrets.outputs.vault).NEXT_URL }} + NEXT_TOKEN: ${{ fromJSON(steps.secrets.outputs.vault).NEXT_TOKEN }} + SQC_US_URL: ${{ fromJSON(steps.secrets.outputs.vault).SQC_US_URL }} + SQC_US_TOKEN: ${{ fromJSON(steps.secrets.outputs.vault).SQC_US_TOKEN }} + SQC_EU_URL: ${{ fromJSON(steps.secrets.outputs.vault).SQC_EU_URL }} + SQC_EU_TOKEN: ${{ fromJSON(steps.secrets.outputs.vault).SQC_EU_TOKEN }} + SONAR_PLATFORM: ${{ inputs.sonar-platform }} + RUN_SHADOW_SCANS: ${{ inputs.run-shadow-scans }} ORG_GRADLE_PROJECT_signingKey: ${{ fromJSON(steps.secrets.outputs.vault).SIGN_KEY }} ORG_GRADLE_PROJECT_signingPassword: ${{ fromJSON(steps.secrets.outputs.vault).PGP_PASSPHRASE }} ORG_GRADLE_PROJECT_signingKeyId: ${{ fromJSON(steps.secrets.outputs.vault).SIGN_KEY_ID }} diff --git a/build-gradle/build.sh b/build-gradle/build.sh index 4f1e9a7c..dd3b4075 100755 --- a/build-gradle/build.sh +++ b/build-gradle/build.sh @@ -4,8 +4,14 @@ # # Required inputs (must be explicitly provided): # - BUILD_NUMBER: Build number for versioning -# - SONAR_HOST_URL: URL of SonarQube server -# - SONAR_TOKEN: Access token to send analysis reports to SonarQube +# - SONAR_PLATFORM: SonarQube primary platform (next, sqc-eu, or sqc-us) +# - NEXT_URL: URL of SonarQube server for next platform +# - NEXT_TOKEN: Access token to send analysis reports to SonarQube for next platform +# - SQC_US_URL: URL of SonarQube server for sqc-us platform +# - SQC_US_TOKEN: Access token to send analysis reports to SonarQube for sqc-us platform +# - SQC_EU_URL: URL of SonarQube server for sqc-eu platform +# - SQC_EU_TOKEN: Access token to send analysis reports to SonarQube for sqc-eu platform +# - RUN_SHADOW_SCANS: If true, run sonar scanner on all 3 platforms. If false, run on the platform provided by SONAR_PLATFORM. # - ARTIFACTORY_URL: URL to Artifactory repository # - ARTIFACTORY_DEPLOY_REPO: Name of deployment repository # - ARTIFACTORY_DEPLOY_USERNAME: Username to deploy to Artifactory @@ -37,12 +43,17 @@ set -euo pipefail +# Source common functions shared across build scripts +# shellcheck source=../shared/common-functions.sh +source "$(dirname "${BASH_SOURCE[0]}")/../shared/common-functions.sh" + : "${ARTIFACTORY_URL:?}" : "${ARTIFACTORY_DEPLOY_REPO:?}" "${ARTIFACTORY_DEPLOY_USERNAME:?}" "${ARTIFACTORY_DEPLOY_PASSWORD:?}" : "${GITHUB_REF_NAME:?}" "${BUILD_NUMBER:?}" "${GITHUB_RUN_ID:?}" "${GITHUB_REPOSITORY:?}" "${GITHUB_EVENT_NAME:?}" "${GITHUB_SHA:?}" : "${GITHUB_OUTPUT:?}" : "${PULL_REQUEST?}" "${DEFAULT_BRANCH:?}" -: "${SONAR_HOST_URL:?}" "${SONAR_TOKEN:?}" +: "${SONAR_PLATFORM:?}" "${RUN_SHADOW_SCANS:?}" +: "${NEXT_URL:?}" "${NEXT_TOKEN:?}" "${SQC_US_URL:?}" "${SQC_US_TOKEN:?}" "${SQC_EU_URL:?}" "${SQC_EU_TOKEN:?}" : "${ORG_GRADLE_PROJECT_signingKey:?}" "${ORG_GRADLE_PROJECT_signingPassword:?}" "${ORG_GRADLE_PROJECT_signingKeyId:?}" : "${DEPLOY_PULL_REQUEST:=false}" "${SKIP_TESTS:=false}" export ARTIFACTORY_URL DEPLOY_PULL_REQUEST @@ -102,7 +113,6 @@ build_gradle_args() { echo "Skipping tests as requested" fi - # SonarQube analysis if [[ -n "${SONAR_HOST_URL:-}" && -n "${SONAR_TOKEN:-}" ]]; then args+=("sonar") args+=("-Dsonar.host.url=$SONAR_HOST_URL") @@ -110,17 +120,17 @@ build_gradle_args() { args+=("-Dsonar.analysis.buildNumber=$BUILD_NUMBER") args+=("-Dsonar.analysis.pipeline=$GITHUB_RUN_ID") args+=("-Dsonar.analysis.repository=$GITHUB_REPOSITORY") + args+=("-Dsonar.projectVersion=$PROJECT_VERSION") + args+=("-Dsonar.scm.revision=$GITHUB_SHA") # Add branch-specific sonar arguments if is_default_branch && ! is_pull_request; then # Master branch analysis - args+=("-Dsonar.projectVersion=$PROJECT_VERSION") args+=("-Dsonar.analysis.sha1=$GITHUB_SHA") elif is_maintenance_branch && ! is_pull_request; then # Maintenance branch analysis args+=("-Dsonar.branch.name=$GITHUB_REF_NAME") - args+=("-Dsonar.projectVersion=$PROJECT_VERSION") args+=("-Dsonar.analysis.sha1=$GITHUB_SHA") elif is_pull_request; then @@ -135,7 +145,6 @@ build_gradle_args() { fi fi - # Artifactory deployment if should_deploy; then args+=("artifactoryPublish") fi @@ -153,6 +162,11 @@ build_gradle_args() { } should_deploy() { + # Disable deployment when shadow scans are enabled to prevent duplicate artifacts + if [[ "${RUN_SHADOW_SCANS}" == "true" ]]; then + return 1 + fi + if is_pull_request; then # For pull requests, deploy only if explicitly enabled [[ "$DEPLOY_PULL_REQUEST" == "true" ]] @@ -202,28 +216,68 @@ is_long_lived_feature_branch() { set_gradle_cmd() { if [[ -f "./gradlew" ]]; then - GRADLE_CMD="./gradlew" + export GRADLE_CMD="./gradlew" elif command_exists gradle; then - GRADLE_CMD="gradle" + export GRADLE_CMD="gradle" else echo "Neither ./gradlew nor gradle command found!" >&2 exit 1 fi } -gradle_build() { + +# Run Gradle build with SonarQube analysis +run_gradle_with_sonar() { + local platform="$1" local gradle_args + + echo "Running Gradle build with SonarQube analysis for platform: $platform" + set_sonar_platform_vars "$platform" + read -ra gradle_args <<< "$(build_gradle_args)" + echo "Gradle command: $GRADLE_CMD ${gradle_args[*]}" + "$GRADLE_CMD" "${gradle_args[@]}" + + echo "SonarQube analysis finished for platform: $(basename "$SONAR_HOST_URL")" +} + +# Run SonarQube analysis with shadow scan support +run_sonar_analysis_gradle() { + if [[ "${RUN_SHADOW_SCANS}" == "true" ]]; then + echo "=== Running Sonar analysis on all platforms (shadow scan enabled) ===" + local platforms=("next" "sqc-us" "sqc-eu") + for platform in "${platforms[@]}"; do + echo "--- Analyzing with platform: $platform ---" + run_gradle_with_sonar "$platform" + done + + echo "=== Completed Sonar analysis on all platforms ===" + else + echo "=== Running Sonar analysis on selected platform: $SONAR_PLATFORM ===" + run_gradle_with_sonar "$SONAR_PLATFORM" + fi +} + +gradle_build() { + local gradle_args local build_type build_type=$(get_build_type) - set_gradle_cmd echo "Starting $build_type build..." - echo "Gradle command: $GRADLE_CMD ${gradle_args[*]}" + echo "Sonar Platform: ${SONAR_PLATFORM}" + echo "Run Shadow Scans: ${RUN_SHADOW_SCANS}" - "$GRADLE_CMD" "${gradle_args[@]}" + # Always run SonarQube analysis - credentials are always available + if [[ "${RUN_SHADOW_SCANS}" == "true" ]]; then + run_sonar_analysis_gradle + else + set_sonar_platform_vars "${SONAR_PLATFORM}" + read -ra gradle_args <<< "$(build_gradle_args)" + echo "Gradle command: $GRADLE_CMD ${gradle_args[*]}" + "$GRADLE_CMD" "${gradle_args[@]}" + fi } main() { diff --git a/spec/build-gradle_spec.sh b/spec/build-gradle_spec.sh index f92bd5dc..dcf569a8 100644 --- a/spec/build-gradle_spec.sh +++ b/spec/build-gradle_spec.sh @@ -51,6 +51,15 @@ export SKIP_TESTS="false" export GRADLE_ARGS="" export GITHUB_EVENT_NAME="push" export GITHUB_OUTPUT=/dev/null +# Required SonarQube platform variables +export SONAR_PLATFORM="next" +export RUN_SHADOW_SCANS="false" +export NEXT_URL="https://next.sonarqube.com" +export NEXT_TOKEN="next-token" +export SQC_US_URL="https://sonarcloud.io" +export SQC_US_TOKEN="sqc-us-token" +export SQC_EU_URL="https://sonarcloud.io" +export SQC_EU_TOKEN="sqc-eu-token" GITHUB_EVENT_PATH=$(mktemp) export GITHUB_EVENT_PATH echo '{}' > "$GITHUB_EVENT_PATH" @@ -162,6 +171,14 @@ Describe 'should_deploy' When call should_deploy The status should be success End + + It 'does not deploy when shadow scans are enabled' + export GITHUB_REF_NAME="master" + export GITHUB_EVENT_NAME="push" + export RUN_SHADOW_SCANS="true" + When call should_deploy + The status should be failure + End End Describe 'build_gradle_args' @@ -289,20 +306,106 @@ Describe 'get_build_type' End End +Describe 'run_gradle_with_sonar' + It 'runs gradle with sonar for a platform' + export PROJECT_VERSION="1.0.0.42" + export GRADLE_ARGS="" + Mock set_sonar_platform_vars + echo "Platform set to $1" + End + Mock build_gradle_args + echo "--no-daemon build sonar" + End + Mock gradle + echo "gradle executed with args: $*" + End + export GRADLE_CMD="gradle" + + When call run_gradle_with_sonar "next" + The output should include "Running Gradle build with SonarQube analysis for platform: next" + The output should include "Platform set to next" + The output should include "gradle executed with args: --no-daemon build sonar" + End +End + +Describe 'run_sonar_analysis_gradle' + It 'runs analysis on single platform when shadow scans disabled' + export RUN_SHADOW_SCANS="false" + export SONAR_PLATFORM="next" + Mock run_gradle_with_sonar + echo "Analysis for $1" + End + + When call run_sonar_analysis_gradle + The output should include "Running Sonar analysis on selected platform: next" + The output should include "Analysis for next" + End + + It 'runs analysis on all platforms when shadow scans enabled' + export RUN_SHADOW_SCANS="true" + Mock run_gradle_with_sonar + echo "Analysis for $1" + End + + When call run_sonar_analysis_gradle + The output should include "Running Sonar analysis on all platforms (shadow scan enabled)" + The output should include "Analysis for next" + The output should include "Analysis for sqc-us" + The output should include "Analysis for sqc-eu" + The output should include "Completed Sonar analysis on all platforms" + End +End + Describe 'gradle_build' It 'executes gradle build successfully' export PROJECT_VERSION="1.0.0.42" export GRADLE_ARGS="" + export RUN_SHADOW_SCANS="false" + export SONAR_PLATFORM="next" + export GRADLE_CMD="gradle" Mock command_exists echo "gradle $*" End Mock gradle echo "gradle executed" End + Mock set_sonar_platform_vars + echo "Platform set" + End + Mock build_gradle_args + echo "--no-daemon build" + End + Mock set_gradle_cmd + true + End + Mock get_build_type + echo "default branch" + End When call gradle_build + The output should include "Starting default branch build" The output should include "gradle executed" End + + It 'runs shadow scans when enabled' + export PROJECT_VERSION="1.0.0.42" + export RUN_SHADOW_SCANS="true" + export GRADLE_CMD="gradle" + Mock run_sonar_analysis_gradle + echo "Shadow scan analysis executed" + End + Mock set_gradle_cmd + true + End + Mock get_build_type + echo "default branch" + End + + When call gradle_build + The output should include "Starting default branch build" + The output should include "Run Shadow Scans: true" + The output should include "Shadow scan analysis executed" + End End Describe 'set_gradle_cmd' @@ -345,32 +448,24 @@ Describe 'set_gradle_cmd' End It 'fails when neither gradle nor gradlew are available' - # Create a test wrapper function that handles the exit - test_set_gradle_cmd_failure() { - # Mock command_exists within the function - # shellcheck disable=SC2317 # Function is called indirectly - command_exists() { - if [[ "$1" == "gradle" && $# -eq 1 ]]; then - # This is the availability check - fail with error message - echo "gradle is not installed." >&2 - false - else - # Any other call should also fail - echo "$1 is not installed." >&2 - false - fi - } - - # Ensure no gradlew exists - rm -f ./gradlew - - # Call set_gradle_cmd in a subshell to contain the exit - ( set_gradle_cmd ) - } - - When call test_set_gradle_cmd_failure + rm -f ./gradlew + + # Mock command_exists to fail for gradle + Mock command_exists + if [[ "$1" == "gradle" ]]; then + echo "gradle is not installed." >&2 + false + else + echo "$1 is not installed." >&2 + false + fi + End + + When run set_gradle_cmd The status should be failure The stderr should include "Neither ./gradlew nor gradle command found!" + + rm -f ./gradlew End End From 41b6789bbfe048ad8d53c9d0b83625c17b8b749a Mon Sep 17 00:00:00 2001 From: Mate Molnar Date: Fri, 22 Aug 2025 12:08:02 +0200 Subject: [PATCH 2/2] BUILD-8079 Gradle shadow scans and fix bug with skip tests --- build-gradle/build.sh | 51 +++++--------- spec/build-gradle_spec.sh | 145 +++++++++++++++++++------------------- 2 files changed, 88 insertions(+), 108 deletions(-) diff --git a/build-gradle/build.sh b/build-gradle/build.sh index dd3b4075..b290517a 100755 --- a/build-gradle/build.sh +++ b/build-gradle/build.sh @@ -110,9 +110,9 @@ build_gradle_args() { if [[ "$SKIP_TESTS" == "true" ]]; then args+=("-x" "test") - echo "Skipping tests as requested" fi + # SonarQube analysis (orchestrator will provide SONAR_HOST_URL and SONAR_TOKEN) if [[ -n "${SONAR_HOST_URL:-}" && -n "${SONAR_TOKEN:-}" ]]; then args+=("sonar") args+=("-Dsonar.host.url=$SONAR_HOST_URL") @@ -225,14 +225,18 @@ set_gradle_cmd() { fi } - -# Run Gradle build with SonarQube analysis -run_gradle_with_sonar() { - local platform="$1" +# CALLBACK IMPLEMENTATION: SonarQube scanner execution +# +# This function is called BY THE ORCHESTRATOR (orchestrate_sonar_platforms) +# The orchestrator will: +# 1. Set SONAR_HOST_URL and SONAR_TOKEN for the current platform +# 2. Call this function to execute the actual scanner +# 3. Repeat for each platform (if shadow scanning enabled) +sonar_scanner_implementation() { local gradle_args - echo "Running Gradle build with SonarQube analysis for platform: $platform" - set_sonar_platform_vars "$platform" + echo "Running Gradle build with SonarQube analysis for platform: $(basename "$SONAR_HOST_URL")" + read -ra gradle_args <<< "$(build_gradle_args)" echo "Gradle command: $GRADLE_CMD ${gradle_args[*]}" @@ -241,26 +245,7 @@ run_gradle_with_sonar() { echo "SonarQube analysis finished for platform: $(basename "$SONAR_HOST_URL")" } -# Run SonarQube analysis with shadow scan support -run_sonar_analysis_gradle() { - if [[ "${RUN_SHADOW_SCANS}" == "true" ]]; then - echo "=== Running Sonar analysis on all platforms (shadow scan enabled) ===" - local platforms=("next" "sqc-us" "sqc-eu") - - for platform in "${platforms[@]}"; do - echo "--- Analyzing with platform: $platform ---" - run_gradle_with_sonar "$platform" - done - - echo "=== Completed Sonar analysis on all platforms ===" - else - echo "=== Running Sonar analysis on selected platform: $SONAR_PLATFORM ===" - run_gradle_with_sonar "$SONAR_PLATFORM" - fi -} - gradle_build() { - local gradle_args local build_type build_type=$(get_build_type) set_gradle_cmd @@ -269,15 +254,11 @@ gradle_build() { echo "Sonar Platform: ${SONAR_PLATFORM}" echo "Run Shadow Scans: ${RUN_SHADOW_SCANS}" - # Always run SonarQube analysis - credentials are always available - if [[ "${RUN_SHADOW_SCANS}" == "true" ]]; then - run_sonar_analysis_gradle - else - set_sonar_platform_vars "${SONAR_PLATFORM}" - read -ra gradle_args <<< "$(build_gradle_args)" - echo "Gradle command: $GRADLE_CMD ${gradle_args[*]}" - "$GRADLE_CMD" "${gradle_args[@]}" - fi + # This will call back to sonar_scanner_implementation() function + # No additional arguments needed as branch-specific args are handled in build_gradle_args() + # TODO: Add support for sonar-platform=none to skip sonar analysis entirely + # shellcheck disable=SC2119 + orchestrate_sonar_platforms } main() { diff --git a/spec/build-gradle_spec.sh b/spec/build-gradle_spec.sh index dcf569a8..f2e69f69 100644 --- a/spec/build-gradle_spec.sh +++ b/spec/build-gradle_spec.sh @@ -41,8 +41,14 @@ export GITHUB_REPOSITORY="my-org/my-repo" export ARTIFACTORY_DEPLOY_REPO="deploy-repo" export ARTIFACTORY_DEPLOY_USERNAME="deploy-user" export ARTIFACTORY_DEPLOY_PASSWORD="deploy-pass" -export SONAR_HOST_URL="https://sonar.example.com" -export SONAR_TOKEN="sonar-token" +export SONAR_PLATFORM="next" +export RUN_SHADOW_SCANS="false" +export NEXT_URL="https://next.sonarqube.com" +export NEXT_TOKEN="next-token" +export SQC_US_URL="https://sonarcloud.io" +export SQC_US_TOKEN="sqc-us-token" +export SQC_EU_URL="https://sonarcloud.io" +export SQC_EU_TOKEN="sqc-eu-token" export ORG_GRADLE_PROJECT_signingKey="signing-key" export ORG_GRADLE_PROJECT_signingPassword="signing-pass" export ORG_GRADLE_PROJECT_signingKeyId="signing-id" @@ -172,10 +178,10 @@ Describe 'should_deploy' The status should be success End - It 'does not deploy when shadow scans are enabled' - export GITHUB_REF_NAME="master" - export GITHUB_EVENT_NAME="push" + It 'does not deploy when shadow scans enabled' export RUN_SHADOW_SCANS="true" + export GITHUB_EVENT_NAME="push" + export GITHUB_REF_NAME="master" When call should_deploy The status should be failure End @@ -193,6 +199,8 @@ Describe 'build_gradle_args' It 'includes sonar when configured' export GRADLE_ARGS="" export PROJECT_VERSION="1.0.0.42" + export SONAR_HOST_URL="https://sonar.example.com" + export SONAR_TOKEN="sonar-token" When call build_gradle_args The output should include "sonar" The output should include "-Dsonar.host.url=https://sonar.example.com" @@ -220,6 +228,8 @@ Describe 'build_gradle_args' export PROJECT_VERSION="1.0.0.42" export GITHUB_REF_NAME="master" export GITHUB_EVENT_NAME="push" + export SONAR_HOST_URL="https://sonar.example.com" + export SONAR_TOKEN="sonar-token" When call build_gradle_args The output should include "-Dsonar.projectVersion=1.0.0.42" The output should include "-Dsonar.analysis.sha1=abc123def456" @@ -230,6 +240,8 @@ Describe 'build_gradle_args' export PROJECT_VERSION="1.0.0.42" export GITHUB_REF_NAME="branch-1.0" export GITHUB_EVENT_NAME="push" + export SONAR_HOST_URL="https://sonar.example.com" + export SONAR_TOKEN="sonar-token" When call build_gradle_args The output should include "-Dsonar.branch.name=branch-1.0" End @@ -240,6 +252,8 @@ Describe 'build_gradle_args' export GITHUB_EVENT_NAME="pull_request" export PULL_REQUEST="123" export PULL_REQUEST_SHA="base123" + export SONAR_HOST_URL="https://sonar.example.com" + export SONAR_TOKEN="sonar-token" When call build_gradle_args The output should include "-Dsonar.analysis.prNumber=123" End @@ -249,6 +263,8 @@ Describe 'build_gradle_args' export PROJECT_VERSION="1.0.0.42" export GITHUB_REF_NAME="feature/long/my-feature" export GITHUB_EVENT_NAME="push" + export SONAR_HOST_URL="https://sonar.example.com" + export SONAR_TOKEN="sonar-token" When call build_gradle_args The output should include "-Dsonar.branch.name=feature/long/my-feature" The output should include "-Dsonar.analysis.sha1=abc123def456" @@ -306,105 +322,88 @@ Describe 'get_build_type' End End -Describe 'run_gradle_with_sonar' - It 'runs gradle with sonar for a platform' + +Describe 'gradle_build' + It 'executes gradle build successfully' export PROJECT_VERSION="1.0.0.42" export GRADLE_ARGS="" - Mock set_sonar_platform_vars - echo "Platform set to $1" + export GRADLE_CMD="gradle" + Mock orchestrate_sonar_platforms + echo "orchestrator executed" End + Mock set_gradle_cmd + true + End + Mock get_build_type + echo "default branch" + End + + When call gradle_build + The output should include "Starting default branch build" + The output should include "Sonar Platform: next" + The output should include "Run Shadow Scans: false" + The output should include "orchestrator executed" + End +End + +Describe 'sonar_scanner_implementation' + It 'runs gradle with sonar for current platform' + export PROJECT_VERSION="1.0.0.42" + export GRADLE_ARGS="" + export SONAR_HOST_URL="https://next.sonarqube.com" + export GRADLE_CMD="gradle" Mock build_gradle_args echo "--no-daemon build sonar" End Mock gradle echo "gradle executed with args: $*" End - export GRADLE_CMD="gradle" - When call run_gradle_with_sonar "next" - The output should include "Running Gradle build with SonarQube analysis for platform: next" - The output should include "Platform set to next" + When call sonar_scanner_implementation + The output should include "Running Gradle build with SonarQube analysis for platform: next.sonarqube.com" The output should include "gradle executed with args: --no-daemon build sonar" + The output should include "SonarQube analysis finished for platform: next.sonarqube.com" End End -Describe 'run_sonar_analysis_gradle' +Describe 'orchestrate_sonar_platforms integration' It 'runs analysis on single platform when shadow scans disabled' export RUN_SHADOW_SCANS="false" export SONAR_PLATFORM="next" - Mock run_gradle_with_sonar - echo "Analysis for $1" - End - - When call run_sonar_analysis_gradle - The output should include "Running Sonar analysis on selected platform: next" - The output should include "Analysis for next" - End - - It 'runs analysis on all platforms when shadow scans enabled' - export RUN_SHADOW_SCANS="true" - Mock run_gradle_with_sonar - echo "Analysis for $1" - End - - When call run_sonar_analysis_gradle - The output should include "Running Sonar analysis on all platforms (shadow scan enabled)" - The output should include "Analysis for next" - The output should include "Analysis for sqc-us" - The output should include "Analysis for sqc-eu" - The output should include "Completed Sonar analysis on all platforms" - End -End - -Describe 'gradle_build' - It 'executes gradle build successfully' export PROJECT_VERSION="1.0.0.42" export GRADLE_ARGS="" - export RUN_SHADOW_SCANS="false" - export SONAR_PLATFORM="next" export GRADLE_CMD="gradle" - Mock command_exists - echo "gradle $*" - End - Mock gradle - echo "gradle executed" - End - Mock set_sonar_platform_vars - echo "Platform set" - End Mock build_gradle_args - echo "--no-daemon build" - End - Mock set_gradle_cmd - true + echo "--no-daemon build sonar" End - Mock get_build_type - echo "default branch" + Mock gradle + echo "gradle executed with args: $*" End - When call gradle_build - The output should include "Starting default branch build" - The output should include "gradle executed" + When call orchestrate_sonar_platforms + The output should include "=== ORCHESTRATOR: Running Sonar analysis on selected platform: next ===" + The output should include "Running Gradle build with SonarQube analysis for platform: next.sonarqube.com" End - It 'runs shadow scans when enabled' - export PROJECT_VERSION="1.0.0.42" + It 'runs analysis on all platforms when shadow scans enabled' export RUN_SHADOW_SCANS="true" + export SONAR_PLATFORM="next" + export PROJECT_VERSION="1.0.0.42" + export GRADLE_ARGS="" export GRADLE_CMD="gradle" - Mock run_sonar_analysis_gradle - echo "Shadow scan analysis executed" - End - Mock set_gradle_cmd - true + Mock build_gradle_args + echo "--no-daemon build sonar" End - Mock get_build_type - echo "default branch" + Mock gradle + echo "gradle executed with args: $*" End - When call gradle_build - The output should include "Starting default branch build" - The output should include "Run Shadow Scans: true" - The output should include "Shadow scan analysis executed" + When call orchestrate_sonar_platforms + The output should include "=== ORCHESTRATOR: Running Sonar analysis on all platforms (shadow scan enabled) ===" + The output should include "--- ORCHESTRATOR: Analyzing with platform: next ---" + The output should include "--- ORCHESTRATOR: Analyzing with platform: sqc-us ---" + The output should include "--- ORCHESTRATOR: Analyzing with platform: sqc-eu ---" + The output should include "=== ORCHESTRATOR: Completed Sonar analysis on all platforms ===" End End