Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
16 changes: 14 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down Expand Up @@ -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
Expand All @@ -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

Expand All @@ -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
Expand Down
24 changes: 19 additions & 5 deletions build-gradle/action.yml
Original file line number Diff line number Diff line change
Expand Up @@ -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:
Expand Down Expand Up @@ -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;
Expand Down Expand Up @@ -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 }}
Expand Down
65 changes: 50 additions & 15 deletions build-gradle/build.sh
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -99,28 +110,27 @@ build_gradle_args() {

if [[ "$SKIP_TESTS" == "true" ]]; then
args+=("-x" "test")
echo "Skipping tests as requested"
fi

# SonarQube analysis
# 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")
args+=("-Dsonar.token=$SONAR_TOKEN")
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
Expand All @@ -135,7 +145,6 @@ build_gradle_args() {
fi
fi

# Artifactory deployment
if should_deploy; then
args+=("artifactoryPublish")
fi
Expand All @@ -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" ]]
Expand Down Expand Up @@ -202,28 +216,49 @@ 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() {
# 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: $(basename "$SONAR_HOST_URL")"


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")"
}

gradle_build() {
local build_type
build_type=$(get_build_type)

set_gradle_cmd

echo "Starting $build_type build..."
echo "Gradle command: $GRADLE_CMD ${gradle_args[*]}"

"$GRADLE_CMD" "${gradle_args[@]}"
echo "Sonar Platform: ${SONAR_PLATFORM}"
echo "Run Shadow Scans: ${RUN_SHADOW_SCANS}"

# 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() {
Expand Down
Loading
Loading