diff --git a/README.md b/README.md index 3f4ec8fa..14043e5f 100644 --- a/README.md +++ b/README.md @@ -359,6 +359,7 @@ See also [`config-maven`](#config-maven) input environment variables. | `artifactory-deployer-role` | Suffix for the Artifactory deployer role in Vault | `qa-deployer` for private repos, `public-deployer` for public repos | | `deploy` | Whether to deploy on master, maintenance, dogfood and long-lived branches | `true` | | `deploy-pull-request` | Whether to also deploy for pull requests. If deploy is false, this has no effect. | `false` | +| `skip-build` | If `true`, skip compile/test/deploy and only run Sonar analysis against restored `target/` output. Requires `deploy: false`. | `false` | | `maven-args` | Additional arguments to pass to Maven | (optional) | | `scanner-java-opts` | Additional Java options for the Sonar scanner (`SONAR_SCANNER_JAVA_OPTS`) | `-Xmx512m` | | `repox-url` | URL for Repox | `https://repox.jfrog.io` | diff --git a/build-maven/action.yml b/build-maven/action.yml index 8757c1e5..c7576569 100644 --- a/build-maven/action.yml +++ b/build-maven/action.yml @@ -16,6 +16,13 @@ inputs: deploy-pull-request: description: Whether to also deploy pull request artifacts. If deploy is `false`, this has no effect. default: 'false' + skip-build: + description: >- + If `true`, skip compiling/testing/deploying and only run Sonar analysis against target/ output + already restored on disk (e.g. downloaded from a prior build job's uploaded artifact). Lets a + scanner-only job retry independently without repeating an expensive build. Combine with + `deploy: false`. + default: 'false' maven-args: description: Additional Maven arguments to pass to the build script. default: '' @@ -187,6 +194,7 @@ runs: # Action inputs DEPLOY: ${{ inputs.deploy }} DEPLOY_PULL_REQUEST: ${{ inputs.deploy-pull-request }} + SKIP_BUILD: ${{ inputs.skip-build }} USER_MAVEN_ARGS: ${{ steps.params.outputs.USER_MAVEN_ARGS }} SONAR_SCANNER_JAVA_OPTS: ${{ inputs.scanner-java-opts }} SONAR_PLATFORM: ${{ inputs.sonar-platform }} diff --git a/build-maven/build.sh b/build-maven/build.sh index 1f7fbb12..a6d9454a 100755 --- a/build-maven/build.sh +++ b/build-maven/build.sh @@ -35,6 +35,8 @@ # Optional user customization: # - DEPLOY: Whether to deploy (default: true) # - DEPLOY_PULL_REQUEST: Whether to deploy pull request artifacts (default: false) +# - SKIP_BUILD: If true, skip compile/test/deploy and only run Sonar analysis against target/ output +# already restored on disk (default: false) # - SONAR_SCANNER_JAVA_OPTS: JVM options for SonarQube scanner (e.g. -Xmx512m) # - SCANNER_VERSION: SonarQube Maven plugin version (default: 5.6.0.6792) # - USER_MAVEN_ARGS: Additional arguments to pass to Maven @@ -55,7 +57,13 @@ if [[ "${SONAR_PLATFORM:?}" != "none" || "$RUN_SHADOW_SCANS" == "true" ]]; then : "${NEXT_URL:?}" "${NEXT_TOKEN:?}" "${SQC_US_URL:?}" "${SQC_US_TOKEN:?}" "${SQC_EU_URL:?}" "${SQC_EU_TOKEN:?}" fi : "${USER_MAVEN_ARGS:=}" -export DEPLOY DEPLOY_PULL_REQUEST USER_MAVEN_ARGS +: "${SKIP_BUILD:=false}" +if [[ "$SKIP_BUILD" == "true" && "$DEPLOY" != "false" ]]; then + echo "::error title=Invalid configuration::skip-build requires deploy: false - skip-build never deploys, but deploy" \ + "defaults to true and was not disabled." >&2 + exit 1 +fi +export DEPLOY DEPLOY_PULL_REQUEST USER_MAVEN_ARGS SKIP_BUILD readonly DEPLOYED_OUTPUT_KEY="deployed" # FIXME Workaround for SonarSource parent POM; it can be removed after releases of parent 73+ and parent-oss 84+ @@ -134,20 +142,7 @@ should_scan() { return $? } -build_maven() { - echo "::group::Check tools" - check_tool mvn --version - check_settings_xml - echo "::endgroup::" - - if should_scan; then - echo "::group::Fetch Git history" - git_fetch_unshallow - echo "::endgroup::" - else - echo "Skipping git fetch (Sonar analysis disabled)" - fi - +build_and_deploy() { local maven_command_args mvn_output if should_deploy; then maven_command_args=("deploy" "-Pdeploy-sonarsource") @@ -189,9 +184,44 @@ build_maven() { echo "$DEPLOYED_OUTPUT_KEY=true" >> "$GITHUB_OUTPUT" export_built_artifacts fi +} + +# Sanity check for skip-build: the caller is responsible for restoring a prior build's target/ +# output before this runs; if none is present, fail loudly instead of letting the Sonar scanner +# silently produce a degraded analysis (e.g. missing bytecode-based issues/coverage). +check_build_output_restored() { + if ! find . -mindepth 1 -maxdepth 4 -type d -path '*/target/classes' 2>/dev/null | grep -q .; then + echo "::error title=Missing build output::skip-build is enabled but no target/classes directories were found under" \ + "$(pwd) - was the prior build job's output actually restored before this step ran?" >&2 + exit 1 + fi +} + +build_maven() { + echo "::group::Check tools" + check_tool mvn --version + check_settings_xml + echo "::endgroup::" + + if should_scan; then + echo "::group::Fetch Git history" + git_fetch_unshallow + echo "::endgroup::" + else + echo "Skipping git fetch (Sonar analysis disabled)" + fi + + if [[ "$SKIP_BUILD" != "true" ]]; then + build_and_deploy "$@" + else + echo "Skipping Maven compile/test/deploy (skip-build enabled) - analyzing previously built output restored on disk." + fi # Execute SonarQube analysis if enabled if should_scan; then + if [[ "$SKIP_BUILD" == "true" ]]; then + check_build_output_restored + fi local sonar_args=() if is_pull_request; then sonar_args+=("-Dsonar.pullrequest.key=$PULL_REQUEST") diff --git a/spec/build-maven_spec.sh b/spec/build-maven_spec.sh index 9fc56c79..1b9cae0c 100755 --- a/spec/build-maven_spec.sh +++ b/spec/build-maven_spec.sh @@ -73,6 +73,12 @@ Describe 'build-maven/build.sh' The output should include "Maven command: mvn" rm -f "$GITHUB_OUTPUT" End + It 'fails when skip-build is true and deploy is not false' + export SKIP_BUILD="true" + When run script build-maven/build.sh + The status should be failure + The stderr should include "skip-build requires deploy: false" + End End Include build-maven/build.sh @@ -339,6 +345,33 @@ Describe 'check_settings_xml()' End End +Describe 'check_build_output_restored()' + It 'succeeds when a target/classes directory exists' + temp_dir=$(mktemp -d) + pushd "$temp_dir" > /dev/null || exit + mkdir -p some-module/target/classes + + When call check_build_output_restored + The status should be success + The output should be blank + + popd > /dev/null || exit + rm -rf "$temp_dir" + End + + It 'fails when no target/classes directory exists' + temp_dir=$(mktemp -d) + pushd "$temp_dir" > /dev/null || exit + + When run check_build_output_restored + The status should be failure + The stderr should include "Missing build output::skip-build is enabled but no target/classes directories were found" + + popd > /dev/null || exit + rm -rf "$temp_dir" + End +End + Describe 'git_fetch_unshallow()' It 'fetches unshallow repository' When call git_fetch_unshallow @@ -424,6 +457,36 @@ Describe 'build_maven()' The output should not include "release" The output should not include "sign" End + + Describe 'skip-build' + Mock check_build_output_restored + true + End + + It 'skips compile/test/deploy but still analyzes' + export SKIP_BUILD="true" + export DEPLOY="false" + + When call build_maven + The status should be success + The output should include "Skipping Maven compile/test/deploy (skip-build enabled)" + The output should not include "Maven command: mvn install" + The output should not include "Maven command: mvn deploy" + The output should include "orchestrate_sonar_platforms" + End + + It 'skips the build output check when analysis is disabled' + export SKIP_BUILD="true" + export DEPLOY="false" + export SONAR_PLATFORM="none" + export RUN_SHADOW_SCANS="false" + + When call build_maven + The status should be success + The output should include "Skipping Maven compile/test/deploy (skip-build enabled)" + The output should not include "orchestrate_sonar_platforms" + End + End End Describe 'is_maintenance_branch'