From bd25ac2f336d69ad1c13e670c33e04f6fc40fd20 Mon Sep 17 00:00:00 2001 From: JohT <7671054+JohT@users.noreply.github.com> Date: Sun, 30 Nov 2025 11:39:19 +0100 Subject: [PATCH] Add input parameter for maven artifacts to public workflow --- .../workflows/internal-java-code-analysis.yml | 8 + .../workflows/public-analyze-code-graph.yml | 19 ++- INTEGRATION.md | 1 + scripts/downloadMavenArtifacts.sh | 86 ++++++++++ scripts/testDownloadMavenArtifacts.sh | 150 ++++++++++++++++++ 5 files changed, 261 insertions(+), 3 deletions(-) create mode 100755 scripts/downloadMavenArtifacts.sh create mode 100755 scripts/testDownloadMavenArtifacts.sh diff --git a/.github/workflows/internal-java-code-analysis.yml b/.github/workflows/internal-java-code-analysis.yml index ce7bc2494..4f42d9079 100644 --- a/.github/workflows/internal-java-code-analysis.yml +++ b/.github/workflows/internal-java-code-analysis.yml @@ -48,6 +48,7 @@ jobs: analysis-name: ${{ steps.set-analysis-name.outputs.analysis-name }} sources-upload-name: ${{ steps.set-sources-upload-name.outputs.sources-upload-name }} artifacts-upload-name: ${{ steps.set-artifacts-upload-name.outputs.artifacts-upload-name }} + additional-maven-artifacts: ${{ steps.set-additional-maven-artifacts.outputs.additional-maven-artifacts }} env: PROJECT_NAME: AxonFramework @@ -94,6 +95,10 @@ jobs: id: set-artifacts-upload-name run: echo "artifacts-upload-name=${{ steps.set-analysis-name.outputs.analysis-name }}-analysis-artifacts-input-${{ env.ARTIFACT_UPLOAD_ID }}" >> "$GITHUB_OUTPUT" + - name: (Prepare Code to Analyze) Set output variable 'additional-maven-artifacts' + id: set-additional-maven-artifacts + run: echo "additional-maven-artifacts=org.axonframework:axon-messaging:${{ env.AXON_FRAMEWORK_VERSION }},org.axonframework:axon-modelling:${{ env.AXON_FRAMEWORK_VERSION }}" >> "$GITHUB_OUTPUT" + - name: (Prepare Code to Analyze) Upload sources to analyze if: success() uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4 @@ -120,6 +125,9 @@ jobs: uses: ./.github/workflows/public-analyze-code-graph.yml with: analysis-name: ${{ needs.prepare-code-to-analyze.outputs.analysis-name }} + # All necessary artifacts are contained in the uploaded artifacts: artifacts-upload-name: ${{ needs.prepare-code-to-analyze.outputs.artifacts-upload-name }} + # Additional (duplicate) artifacts are not needed for analysis but only used here to test maven artifact download: + maven-artifacts: ${{needs.prepare-code-to-analyze.outputs.additional-maven-artifacts}} sources-upload-name: ${{ needs.prepare-code-to-analyze.outputs.sources-upload-name }} jupyter-pdf: "false" \ No newline at end of file diff --git a/.github/workflows/public-analyze-code-graph.yml b/.github/workflows/public-analyze-code-graph.yml index 659aa04a1..a9d19a029 100644 --- a/.github/workflows/public-analyze-code-graph.yml +++ b/.github/workflows/public-analyze-code-graph.yml @@ -18,6 +18,14 @@ on: required: false type: string default: '' + maven-artifacts: + description: > + Comma-separated list of Maven coordinates (groupId:artifactId:version) + to download from Maven Central for the analysis. + Example: 'org.apache.commons:commons-lang3:3.12.0,com.google.guava:guava:31.1-jre' + required: false + type: string + default: '' sources-upload-name: description: > The name of the sources uploaded with 'actions/upload-artifact' @@ -87,9 +95,9 @@ jobs: python: 3.12 miniforge: 24.9.0-0 steps: - - name: Assure that either artifacts-upload-name or sources-upload-name is set - if: inputs.artifacts-upload-name == '' && inputs.sources-upload-name == '' - run: echo "Please specify either the input parameter 'artifacts-upload-name' or 'sources-upload-name'."; exit 1 + - name: Assure that either artifacts-upload-name or maven-artifacts or sources-upload-name is set + if: inputs.artifacts-upload-name == '' && inputs.maven-artifacts == '' && inputs.sources-upload-name == '' + run: echo "Please specify either the input parameter 'artifacts-upload-name' or 'maven-artifacts' or 'sources-upload-name'."; exit 1 - name: Assemble ENVIRONMENT_INFO run: echo "ENVIRONMENT_INFO=java-${{ matrix.java }}-python-${{ matrix.python }}-miniforge-${{ matrix.miniforge }}" >> $GITHUB_ENV @@ -170,6 +178,11 @@ jobs: name: ${{ inputs.artifacts-upload-name }} path: temp/${{ inputs.analysis-name }}/artifacts + - name: (Code Analysis Setup) Download Maven artifacts for analysis + if: inputs.maven-artifacts != '' + working-directory: temp/${{ inputs.analysis-name }} + run: ./../../scripts/downloadMavenArtifacts.sh "${{ inputs.maven-artifacts }}" + - name: (Debug) Log folder structure of temp directory if: runner.debug == '1' working-directory: temp diff --git a/INTEGRATION.md b/INTEGRATION.md index 4276aef60..e34bd281d 100644 --- a/INTEGRATION.md +++ b/INTEGRATION.md @@ -33,6 +33,7 @@ The workflow parameters are as follows: - **analysis-name**: The name of the project to analyze. Example: MyProject-1.0.0. This parameter is required and should be a string. - **artifacts-upload-name**: The name of the artifacts uploaded with [actions/upload-artifact](https://github.com/actions/upload-artifact/tree/65c4c4a1ddee5b72f698fdd19549f0f0fb45cf08) containing the content of the 'artifacts' directory for the analysis. This is used to analyze Java JARs, WARs, EARs, etc. This parameter is optional and defaults to an empty string. +- **maven-artifacts**: Comma separated list of Maven artifact coordinates (groupId:artifactId:version) to download from Maven Central for the analysis. This is used to analyze Java artifacts without having to upload them as build artifacts. This parameter is optional and defaults to an empty string. - **sources-upload-name**: The name of the sources uploaded with [actions/upload-artifact](https://github.com/actions/upload-artifact/tree/65c4c4a1ddee5b72f698fdd19549f0f0fb45cf08) containing the content of the 'source' directory for the analysis. It also supports sub-folders for multiple source code bases. This parameter is optional and defaults to an empty string. Please use 'include-hidden-files: true' if you also want to upload the git history. - **ref**: The branch, tag, or SHA of the code-graph-analysis-pipeline to checkout. This parameter is optional and defaults to "main". diff --git a/scripts/downloadMavenArtifacts.sh b/scripts/downloadMavenArtifacts.sh new file mode 100755 index 000000000..996c58379 --- /dev/null +++ b/scripts/downloadMavenArtifacts.sh @@ -0,0 +1,86 @@ +#!/usr/bin/env bash + +# Uses Maven to download specified Maven artifacts from Maven Central. +# The artifacts are specified in the first argument as comma separated Maven coordinates. +# Details on the Maven coordinates format: https://maven.apache.org/guides/mini/guide-naming-conventions.html +# The downloaded files are written into the "artifacts" directory of the current analysis directory. + +# This script is used inside .github/workflows/public-analyze-code-graph.yml (November 2025) + +# Fail on any error ("-e" = exit on first error, "-o pipefail" exist on errors within piped commands) +set -o errexit -o pipefail + +# Overrideable Constants (defaults also defined in sub scripts) +LOG_GROUP_START=${LOG_GROUP_START:-"::group::"} +LOG_GROUP_END=${LOG_GROUP_END:-"::endgroup::"} +ARTIFACTS_DIRECTORY=${ARTIFACTS_DIRECTORY:-"artifacts"} + +# Local constants +SCRIPT_NAME=$(basename "${0}") + +fail() { + local ERROR_COLOR='\033[0;31m' # red + local DEFAULT_COLOR='\033[0m' + local errorMessage="${1}" + echo -e "${ERROR_COLOR}${SCRIPT_NAME}: Error: ${errorMessage}${DEFAULT_COLOR}" >&2 + exit 1 +} + +maven_artifacts="" +dry_run=false + +# Input Arguments: Parse the command-line arguments +while [[ $# -gt 0 ]]; do + arg="$1" + case $arg in + --dry-run) + dry_run=true + shift + ;; + *) + if [ -z "${maven_artifacts}" ]; then + # The first unnamed input argument contains the comma separated Maven artifact coordinates to download + maven_artifacts="${arg}" + #echo "${SCRIPT_NAME}: maven_artifacts: ${maven_artifacts}" + else + fail "Unknown argument: ${arg}" + fi + shift + ;; + esac +done + +if [ -z "${maven_artifacts}" ]; then + fail "No Maven artifacts specified to download. Please provide a comma-separated list of Maven coordinates (groupId:artifactId:version)." +fi + +if [ ! -d "./${ARTIFACTS_DIRECTORY}" ]; then + fail "This script needs to run inside the analysis directory with an already existing artifacts directory in it. Change into that directory or use ./init.sh to set up an analysis." +fi + +if ! command -v "mvn" &> /dev/null ; then + fail "Command mvn (Maven) not found. It's needed to download Maven artifacts from Maven Central." +fi + +dry_run_info="" +if [ "${dry_run}" = true ] ; then + echo "${SCRIPT_NAME}: Info: Dry run mode enabled." + dry_run_info=" (dry run)" +fi + +# Process each Maven artifact coordinate +echo "${maven_artifacts}" | tr ',' '\n' | while read -r maven_artifact; do + maven_artifact=$(echo "$maven_artifact" | xargs) + + # Check if the maven artifact "coordinate" contains exactly two colons + colon_count=$(echo "${maven_artifact}" | tr -cd ':' | wc -c) + if [ "${colon_count}" -ne 2 ]; then + fail "Invalid Maven artifact coordinates: '${maven_artifact}'. It should be in the format 'groupId:artifactId:version'." + fi + + echo "${LOG_GROUP_START}Downloading Maven artifact ${maven_artifact}${dry_run_info}" + if [ "${dry_run}" = false ] ; then + mvn --quiet dependency:copy -Dartifact="${maven_artifact}" -DoutputDirectory="./${ARTIFACTS_DIRECTORY}" -Dtransitive=false -Dsilent=true + fi + echo "${LOG_GROUP_END}" +done \ No newline at end of file diff --git a/scripts/testDownloadMavenArtifacts.sh b/scripts/testDownloadMavenArtifacts.sh new file mode 100755 index 000000000..c923c9ab2 --- /dev/null +++ b/scripts/testDownloadMavenArtifacts.sh @@ -0,0 +1,150 @@ +#!/usr/bin/env bash + +# Tests "downloadMavenArtifacts.sh". + +# Fail on any error ("-e" = exit on first error, "-o pipefail" exist on errors within piped commands) +set -o errexit -o pipefail + +# Local constants +SCRIPT_NAME=$(basename "${0}") +COLOR_ERROR='\033[0;31m' # red +COLOR_DE_EMPHASIZED='\033[0;90m' # dark gray +COLOR_SUCCESSFUL="\033[0;32m" # green +COLOR_DEFAULT='\033[0m' + +## Get this "scripts" directory if not already set +# Even if $BASH_SOURCE is made for Bourne-like shells it is also supported by others and therefore here the preferred solution. +# CDPATH reduces the scope of the cd command to potentially prevent unintended directory changes. +# This way non-standard tools like readlink aren't needed. +SCRIPTS_DIR=${SCRIPTS_DIR:-$( CDPATH=. cd -- "$(dirname -- "${BASH_SOURCE[0]}")" && pwd -P )} # Repository directory containing the shell scripts + +tearDown() { + # echo "${SCRIPT_NAME}: Tear down tests...." + rm -rf "${temporaryTestDirectory}" +} + +successful() { + echo "" + echo -e "${COLOR_DE_EMPHASIZED}${SCRIPT_NAME}:${COLOR_DEFAULT} ${COLOR_SUCCESSFUL}✅ Tests finished successfully.${COLOR_DEFAULT}" + tearDown +} + +info() { + local infoMessage="${1}" + echo -e "${COLOR_DE_EMPHASIZED}${SCRIPT_NAME}:${COLOR_DEFAULT} ${infoMessage}" +} + +fail() { + local errorMessage="${1}" + echo -e "${COLOR_DE_EMPHASIZED}${SCRIPT_NAME}: ${COLOR_ERROR}${errorMessage}${COLOR_DEFAULT}" + tearDown + return 1 +} + +printTestLogFileContent() { + local logFileContent=$( cat "${temporaryTestDirectory}/${SCRIPT_NAME}-${test_case_number}.log" ) + # Remove color codes from the output for better readability in test logs + logFileContent=$(echo -e "${logFileContent}" | sed -r "s/\x1B\[[0-9;]*[mK]//g") + echo -e "${COLOR_DE_EMPHASIZED}${logFileContent}${COLOR_DEFAULT}" +} + +downloadMavenArtifactsExpectingSuccessUnderTest() { + local COLOR_DE_EMPHASIZED='\033[0;90m' # dark gray + ( + cd "${temporaryTestDirectory}"; + source "${SCRIPTS_DIR}/downloadMavenArtifacts.sh" "$@" >"${temporaryTestDirectory}/${SCRIPT_NAME}-${test_case_number}.log" 2>&1 + ) + exitCode=$? + if [ ${exitCode} -ne 0 ]; then + fail "❌ Test failed: Script exited with non-zero exit code ${exitCode}." + fi + printTestLogFileContent +} + +downloadMavenArtifactsExpectingFailureUnderTest() { + set +o errexit + ( + cd "${temporaryTestDirectory}"; + source "${SCRIPTS_DIR}/downloadMavenArtifacts.sh" "$@" >"${temporaryTestDirectory}/${SCRIPT_NAME}-${test_case_number}.log" 2>&1 + exitCode=$? + if [ ${exitCode} -eq 0 ]; then + fail "❌ Test failed: Script exited with zero exit code but was expected to fail." + fi + ) + set -o errexit + printTestLogFileContent +} + +info "Starting tests...." + +# Create testing resources +temporaryTestDirectory=$(mktemp -d 2>/dev/null || mktemp -d -t 'temporaryTestDirectory_${SCRIPT_NAME}') +mkdir -p "${temporaryTestDirectory}/artifacts" + +# ------- Integration Test Case +test_case_number=1 +echo "" +info "${test_case_number}.) Should download existing Maven artifacts with correct coordinates successfully (real-run/integration)." + +output=$(downloadMavenArtifactsExpectingSuccessUnderTest "org.apache.commons:commons-lang3:3.12.0,com.google.guava:guava:31.1-jre") +if [ ! -f "${temporaryTestDirectory}/artifacts/commons-lang3-3.12.0.jar" ]; then + fail "${test_case_number}.) Test failed: Expected artifact 'commons-lang3-3.12.0.jar' not found in artifacts directory." +fi +if [ ! -f "${temporaryTestDirectory}/artifacts/guava-31.1-jre.jar" ]; then + fail "${test_case_number}.) Test failed: Expected artifact 'guava-31.1-jre.jar' not found in artifacts directory." +fi + +# ------- Integration Test Case +test_case_number=2 +echo "" +info "${test_case_number}.) Should download a single Maven artifact successfully even if it had already been downloaded (real-run/integration)." + +output=$(downloadMavenArtifactsExpectingSuccessUnderTest "org.apache.commons:commons-lang3:3.12.0") +if [ ! -f "${temporaryTestDirectory}/artifacts/commons-lang3-3.12.0.jar" ]; then + fail "${test_case_number}.) Test failed: Expected artifact 'commons-lang3-3.12.0.jar' not found in artifacts directory." +fi + +# ------- Integration Test Case +test_case_number=3 +echo "" +info "${test_case_number}.) Should fail when downloading non-existing Maven artifact (real-run/integration)." +downloadMavenArtifactsExpectingFailureUnderTest "org.nonexistent:nonexistent-artifact:0.0.1" + +# ------- Unit Test Case +test_case_number=4 +echo "" +info "${test_case_number}.) Should fail when no input is specified (dry-run)." +downloadMavenArtifactsExpectingFailureUnderTest "--dry-run" + +# ------- Unit Test Case +test_case_number=5 +echo "" +info "${test_case_number}.) Should fail when input is empty (dry-run)." +downloadMavenArtifactsExpectingFailureUnderTest "--dry-run" + +# ------- Unit Test Case +test_case_number=6 +echo "" +info "${test_case_number}.) Should fail on unknown arguments." +downloadMavenArtifactsExpectingFailureUnderTest "--dry-run --unknown-argument" + +# ------- Unit Test Case +test_case_number=7 +echo "" +info "${test_case_number}.) Should fail when artifacts directory is missing (dry-run)." +# Rename artifacts directory to simulate missing directory +mv "${temporaryTestDirectory}/artifacts" "${temporaryTestDirectory}/artifacts_backup" + +downloadMavenArtifactsExpectingFailureUnderTest "--dry-run" + +# Restore artifacts directory +mv "${temporaryTestDirectory}/artifacts_backup" "${temporaryTestDirectory}/artifacts" + +# ------- Unit Test Case +test_case_number=8 +echo "" +info "${test_case_number}.) Should fail when the artifact coordinate has a wrong format (dry-run)." +downloadMavenArtifactsExpectingFailureUnderTest "--dry-run" "org.apache.commons:commons-lang3-3.12.0" + +successful +return 0 \ No newline at end of file