From 6f1f1500687113bb47eb2457d955ea452d78bf8c Mon Sep 17 00:00:00 2001 From: JohT <7671054+JohT@users.noreply.github.com> Date: Thu, 18 Dec 2025 08:02:55 +0100 Subject: [PATCH 1/3] Show command "jupyter" in compatibility checker as optional --- scripts/checkCompatibility.sh | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/scripts/checkCompatibility.sh b/scripts/checkCompatibility.sh index 891dc7ffc..085e0c6c1 100755 --- a/scripts/checkCompatibility.sh +++ b/scripts/checkCompatibility.sh @@ -158,7 +158,8 @@ checkRequiredCommand "pip" "Python package installer (https://pip.pypa.io/en/sta icon=$(allOf "python" "pip" "jupyter") echo "" echo "${icon} Python reports dependencies (for ./analyze.sh --report Jupyter):" -checkRequiredCommand "jupyter" "Jupyter Notebook (https://jupyter.org/) for interactive data analysis and visualization" +# Since "jupyter" might only be available when the Python environment is activated, check for it only optionally +checkOptionalCommand "jupyter" "Jupyter Notebook (https://jupyter.org/) for interactive data analysis and visualization (will be available when the Python environment is activated)" # Check dependencies for visualization reports icon=$(oneOf "npx" "dot") From 18b95c9496b041e1b041ec305b92e35617ea819c Mon Sep 17 00:00:00 2001 From: JohT <7671054+JohT@users.noreply.github.com> Date: Tue, 16 Dec 2025 21:15:02 +0100 Subject: [PATCH 2/3] Install JavaScript dependencies in cloned source --- .../workflows/public-analyze-code-graph.yml | 5 + scripts/installJavaScriptDependencies.sh | 122 +++++++++++++ scripts/testInstallJavaScriptDependencies.sh | 164 ++++++++++++++++++ 3 files changed, 291 insertions(+) create mode 100755 scripts/installJavaScriptDependencies.sh create mode 100755 scripts/testInstallJavaScriptDependencies.sh diff --git a/.github/workflows/public-analyze-code-graph.yml b/.github/workflows/public-analyze-code-graph.yml index 2c4300bbb..ff94d1c59 100644 --- a/.github/workflows/public-analyze-code-graph.yml +++ b/.github/workflows/public-analyze-code-graph.yml @@ -203,6 +203,11 @@ jobs: working-directory: temp/${{ inputs.analysis-name }} run: ./../../scripts/cloneGitRepository.sh --url "${{ inputs.source-repository }}" --branch "${{ inputs.source-repository-branch }}" --history-only "${{ inputs.source-repository-history-only }}" --target "source/${{ inputs.analysis-name }}" + - name: (Code Analysis Setup) Install JavaScript dependencies in cloned source repository if needed + if: inputs.source-repository != '' + working-directory: temp/${{ inputs.analysis-name }} + run: ./../../scripts/installJavaScriptDependencies.sh + - name: (Code Analysis Setup) Download artifacts for analysis if: inputs.artifacts-upload-name != '' uses: actions/download-artifact@634f93cb2916e3fdff6788551b99b062d0335ce0 # v5 diff --git a/scripts/installJavaScriptDependencies.sh b/scripts/installJavaScriptDependencies.sh new file mode 100755 index 000000000..13e64acb9 --- /dev/null +++ b/scripts/installJavaScriptDependencies.sh @@ -0,0 +1,122 @@ +#!/usr/bin/env bash + +# This script triggers the installation of dependencies for JavaScript projects in the source folder. +# It supports npm, pnpm and yarn and will skip other projects. + +# This script is used inside .github/workflows/public-analyze-code-graph.yml (December 2025) +# If you want to delete all node_modules directories in the source folder, run: +# find "./${SOURCE_DIRECTORY}" -type d -name node_modules -prune -exec rm -rf {} + + +# 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::"} +SOURCE_DIRECTORY=${SOURCE_DIRECTORY:-"source"} + +# 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 +} + +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 + ;; + *) + fail "Unknown argument: ${arg}" + esac +done + +if [ ! -d "./${SOURCE_DIRECTORY}" ]; then + fail "This script needs to run inside the analysis directory with an already existing source directory in it. Change into that directory or use ./init.sh to set up an analysis." +fi + +dry_run_info="" +if [ "${dry_run}" = true ] ; then + echo "${SCRIPT_NAME}: Info: Dry run mode enabled." + dry_run_info=" (dry run)" +fi + +# Returns 0 (success) if "private": true is set in package.json, otherwise 1 (false). +# Pass the directory of the package.json file as first argument (default=.=current directory). +is_private_package_json() { + directory=${1:-.} + node -e "process.exit(require(require('path').resolve('${directory}/package.json')).private ? 0 : 1)" +} + +# Install node package manager (npm) dependencies by finding all directories with package-lock.json files and running "npm ci" in them. +find "./${SOURCE_DIRECTORY}" -type d -name "node_modules" -prune -o -type f -name "package-lock.json" -print | sort | while read -r lock_file; do + lock_file_directory=$(dirname -- "${lock_file}") + if is_private_package_json "${lock_file_directory}" ; then + echo "${SCRIPT_NAME}: Info: Skipping npm install in private package ${lock_file_directory}." + continue + fi + ( + cd "${lock_file_directory}" + echo "${LOG_GROUP_START}$(date +'%Y-%m-%dT%H:%M:%S') Installing JavaScript dependencies with npm in ${lock_file_directory}${dry_run_info}"; + if [ "${dry_run}" = true ] ; then + echo "${SCRIPT_NAME}: Info: Dry run mode - skipping npm ci in ${lock_file_directory}." + else + if ! command -v "npm" &> /dev/null ; then + fail "Command npm (Node Package Manager) not found. It's needed to install JavaScript dependencies." + fi + npm ci --no-scripts || true + fi + echo "${LOG_GROUP_END}"; + ) +done + +# Install pnpm dependencies by finding all directories with pnpm-lock.yaml files and running "pnpm install --frozen-lockfile" in them. +find "./${SOURCE_DIRECTORY}" -type d -name "node_modules" -prune -o -type f -name "pnpm-lock.yaml" -print | sort | while read -r lock_file; do + lock_file_directory=$(dirname -- "${lock_file}") + # Run pnpm also in private packages since they might trigger the installation of dependencies in monorepos. + ( + cd "${lock_file_directory}" + echo "${LOG_GROUP_START}$(date +'%Y-%m-%dT%H:%M:%S') Installing JavaScript dependencies with pnpm in ${lock_file_directory}${dry_run_info}"; + if [ "${dry_run}" = true ] ; then + echo "${SCRIPT_NAME}: Info: Dry run mode - skipping pnpm install in ${lock_file_directory}." + else + if ! command -v "pnpm" &> /dev/null ; then + fail "Command pnpm (Performant Node.js Package Manager) not found. It's needed to install JavaScript dependencies." + fi + pnpm install --frozen-lockfile --ignore-scripts || true + fi + echo "${LOG_GROUP_END}"; + ) +done + +# Install yarn dependencies by finding all directories with yarn.lock files and running "yarn install --frozen-lockfile" in them. +find "./${SOURCE_DIRECTORY}" -type d -name "node_modules" -prune -o -type f -name "yarn.lock" -print | sort | while read -r lock_file; do + lock_file_directory=$(dirname -- "${lock_file}") + if is_private_package_json "${lock_file_directory}" ; then + echo "${SCRIPT_NAME}: Info: Skipping yarn install in private package ${lock_file_directory}." + continue + fi + ( + cd "${lock_file_directory}" + echo "${LOG_GROUP_START}$(date +'%Y-%m-%dT%H:%M:%S') Installing JavaScript dependencies with yarn in ${lock_file_directory}${dry_run_info}"; + if [ "${dry_run}" = true ] ; then + echo "${SCRIPT_NAME}: Info: Dry run mode - skipping yarn install in ${lock_file_directory}." + else + if ! command -v "yarn" &> /dev/null ; then + fail "Command yarn (Yet Another Resource Negotiator) not found. It's needed to install JavaScript dependencies." + fi + yarn install --frozen-lockfile --ignore-scripts || true + fi + echo "${LOG_GROUP_END}"; + ) +done \ No newline at end of file diff --git a/scripts/testInstallJavaScriptDependencies.sh b/scripts/testInstallJavaScriptDependencies.sh new file mode 100755 index 000000000..2f30d449d --- /dev/null +++ b/scripts/testInstallJavaScriptDependencies.sh @@ -0,0 +1,164 @@ +#!/usr/bin/env bash + +# Tests "installJavaScriptDependencies.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 + 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}" +} + +installJavaScriptDependenciesExpectingSuccessUnderTest() { + local COLOR_DE_EMPHASIZED='\033[0;90m' # dark gray + ( + cd "${temporaryTestDirectory}"; + source "${SCRIPTS_DIR}/installJavaScriptDependencies.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 +} + +installJavaScriptDependenciesExpectingFailureUnderTest() { + set +o errexit + ( + cd "${temporaryTestDirectory}"; + source "${SCRIPTS_DIR}/installJavaScriptDependencies.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}/source" + +# ------- Unit Test Case +test_case_number=1 +echo "" +info "${test_case_number}.) Should do nothing if the source folder is empty (dry-run)." +result=$(installJavaScriptDependenciesExpectingSuccessUnderTest "--dry-run") +echo "${result}" +if [[ "${result}" == *"Installing"* ]]; then + fail "❌ Test failed: Expected no installation attempts, but some were found:\n${result}" +fi + +# ------- Unit Test Case +test_case_number=2 +echo "" +info "${test_case_number}.) Should do nothing when the source folder contains no JavaScript projects (dry-run)." +mkdir -p "${temporaryTestDirectory}/source/non-javascript-project" +echo "This is a text file." > "${temporaryTestDirectory}/source/non-javascript-project/README.md" +result=$(installJavaScriptDependenciesExpectingSuccessUnderTest "--dry-run") +if [[ "${result}" == *"Installing"* ]]; then + fail "❌ Test failed: Expected no installation attempts, but some were found:\n${result}" +fi + +# ------- Unit Test Case +test_case_number=3 +echo "" +info "${test_case_number}.) Should do nothing when the source folder contains a private npm project (dry-run)." +mkdir -p "${temporaryTestDirectory}/source/private-npm-project" +echo "{ \"name\": \"private-npm-project\" }" > "${temporaryTestDirectory}/source/private-npm-project/package-lock.json" +echo "{ \"name\": \"private-npm-project\", \"private\": true }" > "${temporaryTestDirectory}/source/private-npm-project/package.json" +result=$(installJavaScriptDependenciesExpectingSuccessUnderTest "--dry-run") +if [[ "${result}" == *"Installing"* ]]; then + fail "❌ Test failed: Expected no installation attempts, but some were found:\n${result}" +fi + +# ------- Unit Test Case +test_case_number=4 +echo "" +info "${test_case_number}.) Should do nothing when the source folder contains a private yarn project (dry-run)." +mkdir -p "${temporaryTestDirectory}/source/private-yarn-project" +echo "{ \"name\": \"private-yarn-project\" }" > "${temporaryTestDirectory}/source/private-yarn-project/package-lock.json" +echo "{ \"name\": \"private-yarn-project\", \"private\": true }" > "${temporaryTestDirectory}/source/private-yarn-project/package.json" +result=$(installJavaScriptDependenciesExpectingSuccessUnderTest "--dry-run") +if [[ "${result}" == *"Installing"* ]]; then + fail "❌ Test failed: Expected no installation attempts, but some were found:\n${result}" +fi + +# ------- Unit Test Case +test_case_number=5 +echo "" +info "${test_case_number}.) Should install npm dependencies for a npm project (dry-run)." +mkdir -p "${temporaryTestDirectory}/source/npm-project" +echo "{ \"name\": \"npm-project\" }" > "${temporaryTestDirectory}/source/npm-project/package-lock.json" +result=$(installJavaScriptDependenciesExpectingSuccessUnderTest "--dry-run") +if [[ ! "${result}" == *"Installing JavaScript dependencies with npm in"* ]]; then + fail "❌ Test failed: Expected npm installation attempt, but none was found:\n${result}" +fi + +# ------- Unit Test Case +test_case_number=6 +echo "" +info "${test_case_number}.) Should install pnpm dependencies for a pnpm project (dry-run)." +mkdir -p "${temporaryTestDirectory}/source/pnpm-project" +echo "name: pnpm-project" > "${temporaryTestDirectory}/source/pnpm-project/pnpm-lock.yaml" +result=$(installJavaScriptDependenciesExpectingSuccessUnderTest "--dry-run") +if [[ ! "${result}" == *"Installing JavaScript dependencies with pnpm in"* ]]; then + fail "❌ Test failed: Expected pnpm installation attempt, but none was found:\n${result}" +fi + +# ------- Unit Test Case +test_case_number=7 +echo "" +info "${test_case_number}.) Should install yarn dependencies for a yarn project (dry-run)." +mkdir -p "${temporaryTestDirectory}/source/yarn-project" +echo "name: yarn-project" > "${temporaryTestDirectory}/source/yarn-project/yarn.lock" +result=$(installJavaScriptDependenciesExpectingSuccessUnderTest "--dry-run") +if [[ ! "${result}" == *"Installing JavaScript dependencies with yarn in"* ]]; then + fail "❌ Test failed: Expected yarn installation attempt, but none was found:\n${result}" +fi + +successful +return 0 \ No newline at end of file From 48f9a667b59e7a0f431c8f1135e41102775d565c Mon Sep 17 00:00:00 2001 From: JohT <7671054+JohT@users.noreply.github.com> Date: Wed, 17 Dec 2025 08:36:00 +0100 Subject: [PATCH 3/3] Support JavaScript dependency installation in reusable GitHub Workflow --- .../internal-typescript-code-analysis.yml | 63 +-------- ...nternal-typescript-upload-code-example.yml | 124 ++++++++++++++++++ .../workflows/public-analyze-code-graph.yml | 45 ++++++- 3 files changed, 169 insertions(+), 63 deletions(-) create mode 100644 .github/workflows/internal-typescript-upload-code-example.yml diff --git a/.github/workflows/internal-typescript-code-analysis.yml b/.github/workflows/internal-typescript-code-analysis.yml index 145b40d7e..b8a4d8287 100644 --- a/.github/workflows/internal-typescript-code-analysis.yml +++ b/.github/workflows/internal-typescript-code-analysis.yml @@ -46,78 +46,23 @@ jobs: runs-on: ubuntu-latest outputs: analysis-name: ${{ steps.set-analysis-name.outputs.analysis-name }} - sources-upload-name: ${{ steps.set-sources-upload-name.outputs.sources-upload-name }} + project-version: ${{ env.REACT_ROUTER_VERSION }} env: PROJECT_NAME: react-router # Version variable name matches renovate.json configuration entry REACT_ROUTER_VERSION: 6.30.2 - steps: - - name: (Prepare Code to Analyze) Checkout GIT repository - uses: actions/checkout@93cb6efe18208431cddfb8368fd83d5badbf9bfd # v5 - - + steps: - name: Set Set output variable 'analysis-name' id: set-analysis-name run: echo "analysis-name=${{ env.PROJECT_NAME }}-${{ env.REACT_ROUTER_VERSION }}" >> "$GITHUB_OUTPUT" - - - name: Setup temp directory if missing - run: mkdir -p ./temp - - - name: Setup Cache for "temp/downloads" folder - uses: actions/cache@v4 - with: - path: ./temp/downloads - key: - ${{ runner.os }}-${{ hashFiles('**/*.sh') }} - - - name: Download ${{ steps.set-analysis-name.outputs.analysis-name }} - working-directory: temp - run: | - mkdir -p ${{ steps.set-analysis-name.outputs.analysis-name }} - cd ${{ steps.set-analysis-name.outputs.analysis-name }} - echo "Working directory: $( pwd -P )" - ./../../scripts/downloader/downloadReactRouter.sh ${{ env.REACT_ROUTER_VERSION }} - - - name: (Prepare Code to Analyze) Setup pnpm for react-router - uses: pnpm/action-setup@a7487c7e89a18df4991f7f222e4898a00d66ddda # v4.1.0 - with: - package_json_file: temp/${{ steps.set-analysis-name.outputs.analysis-name }}/source/${{ steps.set-analysis-name.outputs.analysis-name }}/package.json - - - name: (Prepare Code to Analyze) Install dependencies with pnpm - working-directory: temp/${{ steps.set-analysis-name.outputs.analysis-name }}/source/${{ steps.set-analysis-name.outputs.analysis-name }} - run: pnpm install --frozen-lockfile --strict-peer-dependencies - - - name: Debug folder structure in temp directory - if: runner.debug == '1' - working-directory: temp - run: | - ls -R | grep ":$" | sed -e 's/:$//' -e 's/[^-][^\/]*\//--/g' -e 's/^/ /' -e 's/-/|/' - - - name: (Prepare Code to Analyze) Generate ARTIFACT_UPLOAD_ID - run: echo "ARTIFACT_UPLOAD_ID=$(LC_ALL=C tr -dc 'A-Za-z0-9' < /dev/urandom | head -c 10)" >> $GITHUB_ENV - - - name: (Prepare Code to Analyze) Set sources-upload-name - id: set-sources-upload-name - run: echo "sources-upload-name=${{ steps.set-analysis-name.outputs.analysis-name }}-analysis-sources_input-${{ env.ARTIFACT_UPLOAD_ID }}" >> "$GITHUB_OUTPUT" - - - name: (Prepare Code to Analyze) Upload code to analyze - if: success() - uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4 - with: - name: ${{ steps.set-sources-upload-name.outputs.sources-upload-name }} - path: ./temp/${{ steps.set-analysis-name.outputs.analysis-name }}/source - include-hidden-files: true - if-no-files-found: error - retention-days: 1 - - analyze-code-graph: needs: [prepare-code-to-analyze] uses: ./.github/workflows/public-analyze-code-graph.yml with: analysis-name: ${{ needs.prepare-code-to-analyze.outputs.analysis-name }} - sources-upload-name: ${{ needs.prepare-code-to-analyze.outputs.sources-upload-name }} + source-repository: https://github.com/remix-run/react-router.git + source-repository-branch: react-router@${{ needs.prepare-code-to-analyze.outputs.project-version }} jupyter-pdf: "false" \ No newline at end of file diff --git a/.github/workflows/internal-typescript-upload-code-example.yml b/.github/workflows/internal-typescript-upload-code-example.yml new file mode 100644 index 000000000..94a0736b6 --- /dev/null +++ b/.github/workflows/internal-typescript-upload-code-example.yml @@ -0,0 +1,124 @@ +name: Typescript Code Upload Example for Graph Analysis + +on: + push: + branches: + - main + # Ignore changes in documentation, general configuration and reports for push events + paths-ignore: + - '**/*.md' + - '**/*.txt' + - '!requirements.txt' + - '**/*.css' + - '**/*.html' + - '**/*.js' + - '.gitignore' + - '.gitattributes' + - 'renovate.json' + - 'renovate-presets/**' + - 'changelogTemplate.mustache' + - '**.code-workspace' + - '.github/workflows/internal-java-code-analysis.yml' + - '.github/workflows/*documentation.yml' + pull_request: + branches: + - main + # Ignore changes in documentation, general configuration and reports for pull request events + paths-ignore: + - '**/*.md' + - '**/*.txt' + - '!requirements.txt' + - '**/*.css' + - '**/*.html' + - '**/*.js' + - '.gitignore' + - '.gitattributes' + - 'renovate.json' + - 'renovate-presets/**' + - 'changelogTemplate.mustache' + - '**.code-workspace' + - '.github/workflows/internal-java-code-analysis.yml' + - '.github/workflows/*documentation.yml' + +jobs: + + prepare-code-to-analyze: + runs-on: ubuntu-latest + outputs: + analysis-name: ${{ steps.set-analysis-name.outputs.analysis-name }} + sources-upload-name: ${{ steps.set-sources-upload-name.outputs.sources-upload-name }} + + env: + PROJECT_NAME: react-router + # Version variable name matches renovate.json configuration entry + REACT_ROUTER_6_VERSION: 6.30.2 + + steps: + - name: (Prepare Code to Analyze) Checkout GIT repository + uses: actions/checkout@93cb6efe18208431cddfb8368fd83d5badbf9bfd # v5 + + + - name: Set Set output variable 'analysis-name' + id: set-analysis-name + run: echo "analysis-name=${{ env.PROJECT_NAME }}-${{ env.REACT_ROUTER_6_VERSION }}" >> "$GITHUB_OUTPUT" + + - name: Setup temp directory if missing + run: mkdir -p ./temp + + - name: Setup Cache for "temp/downloads" folder + uses: actions/cache@v4 + with: + path: ./temp/downloads + key: + ${{ runner.os }}-${{ hashFiles('**/*.sh') }} + + - name: Download ${{ steps.set-analysis-name.outputs.analysis-name }} + working-directory: temp + run: | + mkdir -p ${{ steps.set-analysis-name.outputs.analysis-name }} + cd ${{ steps.set-analysis-name.outputs.analysis-name }} + echo "Working directory: $( pwd -P )" + ./../../scripts/downloader/downloadReactRouter.sh ${{ env.REACT_ROUTER_6_VERSION }} + + - name: (Prepare Code to Analyze) Setup pnpm for react-router + uses: pnpm/action-setup@a7487c7e89a18df4991f7f222e4898a00d66ddda # v4.1.0 + with: + package_json_file: temp/${{ steps.set-analysis-name.outputs.analysis-name }}/source/${{ steps.set-analysis-name.outputs.analysis-name }}/package.json + + - name: (Prepare Code to Analyze) Install dependencies with pnpm + working-directory: temp/${{ steps.set-analysis-name.outputs.analysis-name }}/source/${{ steps.set-analysis-name.outputs.analysis-name }} + run: pnpm install --frozen-lockfile --strict-peer-dependencies + + - name: Debug folder structure in temp directory + if: runner.debug == '1' + working-directory: temp + run: | + ls -R | grep ":$" | sed -e 's/:$//' -e 's/[^-][^\/]*\//--/g' -e 's/^/ /' -e 's/-/|/' + + - name: (Prepare Code to Analyze) Generate ARTIFACT_UPLOAD_ID + run: echo "ARTIFACT_UPLOAD_ID=$(LC_ALL=C tr -dc 'A-Za-z0-9' < /dev/urandom | head -c 10)" >> $GITHUB_ENV + + - name: (Prepare Code to Analyze) Set sources-upload-name + id: set-sources-upload-name + run: echo "sources-upload-name=${{ steps.set-analysis-name.outputs.analysis-name }}-analysis-sources_input-${{ env.ARTIFACT_UPLOAD_ID }}" >> "$GITHUB_OUTPUT" + + - name: (Prepare Code to Analyze) Upload code to analyze + if: success() + uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4 + with: + name: ${{ steps.set-sources-upload-name.outputs.sources-upload-name }} + path: ./temp/${{ steps.set-analysis-name.outputs.analysis-name }}/source + include-hidden-files: true + if-no-files-found: error + retention-days: 1 + + + + explore-code-graph: + needs: [prepare-code-to-analyze] + uses: ./.github/workflows/public-analyze-code-graph.yml + with: + analysis-name: ${{ needs.prepare-code-to-analyze.outputs.analysis-name }} + sources-upload-name: ${{ needs.prepare-code-to-analyze.outputs.sources-upload-name }} + jupyter-pdf: "false" + analysis-arguments: "--explore" # Only setup the Graph, do not generate any reports \ 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 ff94d1c59..1c293452f 100644 --- a/.github/workflows/public-analyze-code-graph.yml +++ b/.github/workflows/public-analyze-code-graph.yml @@ -32,6 +32,7 @@ on: containing the content of the 'source' directory for the analysis. Also supports sub-folders for multiple source code bases. Please use 'include-hidden-files: true' if you also want to upload the git history. + Note: JavaScript dependencies will NOT be installed automatically when using this option. This needs to be done before uploading the sources. required: false type: string default: '' @@ -40,6 +41,7 @@ on: The URL of the source repository to analyze. For now, only GitHub repositories are supported. This can be used instead of 'sources-upload-name' to directly analyze a repository without uploading artifacts first. It can also be used in addition to 'sources-upload-name' to analyze both uploaded sources and a repository. + If specified, JavaScript dependencies will be installed automatically if a package.json file is found in the repository. required: false type: string default: '' @@ -143,7 +145,8 @@ jobs: distribution: "temurin" java-version: ${{ matrix.java }} - # "Setup Python" can be skipped if jupyter notebook analysis-results aren't needed + # "Setup Python" could be skipped if jupyter notebook analysis-results aren't needed or .venv is used. + # However, since this is a reuseable workflow, we always do it here. - name: (Python Setup) Use version ${{ matrix.python }} with Conda package manager Miniforge if: inputs.use-venv_virtual_python_environment == 'false' id: prepare-conda-environment @@ -203,7 +206,31 @@ jobs: working-directory: temp/${{ inputs.analysis-name }} run: ./../../scripts/cloneGitRepository.sh --url "${{ inputs.source-repository }}" --branch "${{ inputs.source-repository-branch }}" --history-only "${{ inputs.source-repository-history-only }}" --target "source/${{ inputs.analysis-name }}" - - name: (Code Analysis Setup) Install JavaScript dependencies in cloned source repository if needed + - name: (Code Analysis JavaScript Setup) Detect node version file .nvmrc in cloned source repository + if: inputs.source-repository != '' + working-directory: temp/${{ inputs.analysis-name }}/source/${{ inputs.analysis-name }} + run: echo "nodeVersionFileDetected=$(if [ -f ".nvmrc" ]; then echo "true"; else echo "false"; fi)" >> $GITHUB_ENV + - name: (Code Analysis JavaScript Setup) Detect pnpm project in cloned source repository + if: inputs.source-repository != '' + working-directory: temp/${{ inputs.analysis-name }}/source/${{ inputs.analysis-name }} + run: echo "pnpmDetected=$(if [ -f "pnpm-lock.yaml" ]; then echo "true"; else echo "false"; fi)" >> $GITHUB_ENV + - name: (Code Analysis JavaScript Setup) Setup Node.js with version in .nvmrc for cloned source repository + if: inputs.source-repository != '' && env.nodeVersionFileDetected == 'true' + uses: actions/setup-node@v6.1.0 + with: + node-version-file: temp/${{ inputs.analysis-name }}/source/${{ inputs.analysis-name }}/.nvmrc + - name: (Code Analysis JavaScript Setup) Setup Node.js (long-term support version fallback, no .nvmrc) for cloned source repository + if: inputs.source-repository != '' && env.nodeVersionFileDetected != 'true' + uses: actions/setup-node@v6.1.0 + with: + node-version: 'lts/*' + - name: (Code Analysis JavaScript Setup) Setup pnpm for cloned source repository + if: inputs.source-repository != '' && env.pnpmDetected == 'true' + uses: pnpm/action-setup@a7487c7e89a18df4991f7f222e4898a00d66ddda # v4.1.0 + with: + package_json_file: temp/${{ inputs.analysis-name }}/source/${{ inputs.analysis-name }}/package.json + run_install: false + - name: (Code Analysis JavaScript Setup) Install JavaScript dependencies in cloned source repository if needed if: inputs.source-repository != '' working-directory: temp/${{ inputs.analysis-name }} run: ./../../scripts/installJavaScriptDependencies.sh @@ -243,9 +270,9 @@ jobs: id: set-analysis-results-artifact-name run: echo "uploaded-analysis-results-artifact-name=code-analysis-results-${{ env.ENVIRONMENT_INFO }}" >> $GITHUB_OUTPUT - # Upload successful analysis-results in case they are needed for troubleshooting + # Upload successful analysis-results as the main output artifact - name: (Code Analysis Results) Archive successful analysis-results - if: success() + if: success() && !contains(inputs.analysis-arguments, '--explore') uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4 with: name: ${{ steps.set-analysis-results-artifact-name.outputs.uploaded-analysis-results-artifact-name }} @@ -253,6 +280,16 @@ jobs: if-no-files-found: error retention-days: ${{ inputs.retention-days }} + # Upload logs if analysis results had been skipped ("--explore" analysis option) + - name: (Code Analysis Results) Archive successful analysis-results + if: success() && contains(inputs.analysis-arguments, '--explore') + uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4 + with: + name: ${{ steps.set-analysis-results-artifact-name.outputs.uploaded-analysis-results-artifact-name }} + path: ./temp/${{ inputs.analysis-name }}/runtime/* + if-no-files-found: error + retention-days: ${{ inputs.retention-days }} + # Upload logs and unfinished analysis-results in case of an error for troubleshooting - name: (Code Analysis Results) Archive failed run with logs and unfinished analysis-results