diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 44da2839..c31eb8f9 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -186,11 +186,30 @@ jobs: - name: Run cppcheck run: | docker run --rm --volume $(pwd):/note-c/ --workdir /note-c/ --entrypoint ./scripts/run_cppcheck.sh ghcr.io/blues/note_c_ci:latest + + run_scancode: + runs-on: ubuntu-latest + if: ${{ always() }} + needs: [build_ci_docker_image] + + steps: + - name: Checkout code + uses: actions/checkout@v4 + + - name: Load CI Docker image + # Only load the Docker image artifact if build_ci_docker_image actually + # ran (e.g. it wasn't skipped and was successful). + if: ${{ needs.build_ci_docker_image.result == 'success' }} + uses: ./.github/actions/load-ci-image + + - name: Run license compliance check + run: | + docker run --rm --volume $(pwd):/note-c/ --workdir /note-c/ --entrypoint ./scripts/run_scancode.sh ghcr.io/blues/note_c_ci:latest publish_ci_image: runs-on: ubuntu-latest # Make sure unit tests unit tests passed before publishing. - needs: [build_ci_docker_image, run_unit_tests] + needs: [build_ci_docker_image, run_unit_tests, run_scancode] # Only publish the image if this is a push event and the Docker image was rebuilt if: ${{ github.event_name == 'push' && needs.build_ci_docker_image.result == 'success' }} diff --git a/.vscode/tasks.json b/.vscode/tasks.json index b00c48ce..d7a95d46 100644 --- a/.vscode/tasks.json +++ b/.vscode/tasks.json @@ -120,5 +120,19 @@ ], "group": "test" } + , + { + "label": "Note-C: Run License Compliance Check", + "type": "shell", + "command": "${workspaceFolder}/scripts/run_scancode.sh", + "options": { + "cwd": "${workspaceFolder}", + "env": { + "LC_ALL": "C" + } + }, + "problemMatcher": [], + "group": "test" + } ] } diff --git a/Dockerfile b/Dockerfile index 334df54f..b6a9c7d8 100644 --- a/Dockerfile +++ b/Dockerfile @@ -70,6 +70,7 @@ RUN ["dash", "-c", "\ && pip install --break-system-packages \ breathe \ sphinx-rtd-theme \ + scancode-toolkit \ && apt-get clean \ && apt-get purge \ && rm -rf /var/lib/apt/lists/* /tmp/* /var/tmp/* \ diff --git a/scripts/run_scancode.sh b/scripts/run_scancode.sh new file mode 100755 index 00000000..13df1e8f --- /dev/null +++ b/scripts/run_scancode.sh @@ -0,0 +1,103 @@ +#!/bin/bash +set -eo pipefail + +SCRIPT_DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" &> /dev/null && pwd )" +SRC_DIR="$SCRIPT_DIR/.." + +echo "Running License Compliance Analysis..." +echo + +# Create a function to generate the summary +generate_summary() { + { + # Initialize flag + has_critical_issues=false + + echo + + # Always generate and display summary regardless of exit code + echo "=== License Compliance Summary ===" + echo + + # Display license information + echo "License Information:" + echo "-------------------" + jq -r '.summary.declared_license_expression' scancode_output.json || echo "None found" + echo + + # Display license clarity score + echo "License Clarity Score:" + echo "---------------------" + jq -r '.summary.license_clarity_score.score' scancode_output.json || echo "Not available" + echo + + # Display other licenses found + echo "Other Licenses Found:" + echo "--------------------" + jq -r '.summary.other_license_expressions[] | select(.value != null) | "\(.value): \(.count) occurrences"' scancode_output.json || echo "None found" + echo + + # Display copyright holders + echo "Copyright Holders:" + echo "-----------------" + jq -r '.tallies.holders[] | select(.value != null) | "\(.value): \(.count) occurrences"' scancode_output.json || echo "None found" + echo + + # Check for non-compliant licenses + echo "License Compliance Issues:" + echo "-------------------------" + # List of non-compliant or problematic licenses + problematic_licenses=("gpl-2.0" "gpl-3.0" "agpl-3.0" "cc-by-nc" "proprietary") + + found_problematic=false + for license in "${problematic_licenses[@]}"; do + count=$(jq -r ".summary.other_license_expressions[] | select(.value == \"$license\") | .count" scancode_output.json 2>/dev/null || echo "0") + if [ "$count" != "0" ] && [ "$count" != "" ]; then + echo "WARNING: Found $count occurrences of $license license, which may have compliance implications." + found_problematic=true + has_critical_issues=true + fi + done + + if ! $found_problematic; then + echo "No problematic licenses found." + fi + echo + + # Display status and details + if $has_critical_issues; then + echo "Status: FAILED - License compliance issues found" + echo + echo "Review and fix license compliance issues before proceeding" + else + echo "Status: PASSED - No license compliance issues found" + echo + echo "Note: Review license information for potential improvements" + fi + } + + # Return 1 if critical issues found, 0 otherwise + if $has_critical_issues; then + return 1 + else + return 0 + fi +} + +# Run scancode and capture output and exit code +scancode \ + --license \ + --copyright \ + --classify \ + --summary \ + --license-clarity-score \ + --tallies \ + --json-pp scancode_output.json \ + --timeout 120 \ + --processes 2 \ + . 2>&1 | tee scancode_console_output.txt + +generate_summary + +# Exit with scancode's status code +exit $?