name: License Report Workflow on: push: branches: - main pull_request: branches: - main concurrency: group: ${{ github.workflow }}-${{ github.event_name }}-${{ github.ref_name || github.ref }} cancel-in-progress: true permissions: contents: read jobs: files-changed: name: detect what files changed runs-on: ubuntu-latest timeout-minutes: 3 outputs: licenses-frontend: ${{ steps.changes.outputs.licenses-frontend }} licenses-backend: ${{ steps.changes.outputs.licenses-backend }} steps: - name: Harden the runner (Audit all outbound calls) uses: step-security/harden-runner@20cf305ff2072d973412fa9b1e3a4f227bda3c76 # v2.14.0 with: egress-policy: audit - name: Checkout repository uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 - name: Check for file changes uses: dorny/paths-filter@de90cc6fb38fc0963ad72b210f1f284cd68cea36 # v3.0.2 id: changes with: filters: .github/config/.files.yaml generate-frontend-license-report: if: needs.files-changed.outputs.licenses-frontend == 'true' name: Generate Frontend License Report needs: files-changed runs-on: ubuntu-latest permissions: contents: write pull-requests: write repository-projects: write # Required for enabling automerge steps: - name: Harden Runner uses: step-security/harden-runner@20cf305ff2072d973412fa9b1e3a4f227bda3c76 # v2.14.0 with: egress-policy: audit - name: Checkout PR head (default) uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 with: fetch-depth: 0 persist-credentials: false - name: Setup GitHub App Bot if: (github.event_name == 'push' || (github.event_name == 'pull_request' && github.event.pull_request.head.repo.fork == false)) && github.actor != 'dependabot[bot]' id: setup-bot uses: ./.github/actions/setup-bot with: app-id: ${{ secrets.GH_APP_ID }} private-key: ${{ secrets.GH_APP_PRIVATE_KEY }} - name: Checkout BASE branch (safe script) if: github.event_name == 'pull_request' uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 with: ref: ${{ github.event.pull_request.base.sha }} path: base fetch-depth: 1 persist-credentials: false - name: Set up Node.js uses: actions/setup-node@395ad3262231945c25e8478fd5baf05154b1d79f # v6.1.0 with: node-version: "22" cache: "npm" cache-dependency-path: frontend/package-lock.json - name: Install frontend dependencies working-directory: frontend env: NPM_CONFIG_IGNORE_SCRIPTS: "true" run: npm ci --ignore-scripts --audit=false --fund=false - name: Generate frontend license report (internal PR) if: github.event_name == 'pull_request' && github.event.pull_request.head.repo.fork == false working-directory: frontend env: PR_IS_FORK: "false" run: npm run generate-licenses - name: Generate frontend license report (fork PRs, pinned) if: github.event_name == 'pull_request' && github.event.pull_request.head.repo.fork == true env: NPM_CONFIG_IGNORE_SCRIPTS: "true" working-directory: frontend run: | mkdir -p src/assets npx --yes license-report --only=prod --output=json > src/assets/3rdPartyLicenses.json - name: Postprocess with project script (BASE version) if: github.event_name == 'pull_request' && github.event.pull_request.head.repo.fork == true env: PR_IS_FORK: "true" run: | node base/frontend/scripts/generate-licenses.js \ --input frontend/src/assets/3rdPartyLicenses.json - name: Copy postprocessed artifacts back (fork PRs) if: github.event_name == 'pull_request' && github.event.pull_request.head.repo.fork == true run: | mkdir -p frontend/src/assets if [ -f "base/frontend/src/assets/3rdPartyLicenses.json" ]; then cp base/frontend/src/assets/3rdPartyLicenses.json frontend/src/assets/3rdPartyLicenses.json fi if [ -f "base/frontend/src/assets/license-warnings.json" ]; then cp base/frontend/src/assets/license-warnings.json frontend/src/assets/license-warnings.json fi - name: Check for license warnings run: | if [ -f "frontend/src/assets/license-warnings.json" ]; then echo "LICENSE_WARNINGS_EXIST=true" >> $GITHUB_ENV else echo "LICENSE_WARNINGS_EXIST=false" >> $GITHUB_ENV fi # PR Event: Check licenses and comment on PR - name: Delete previous license check comments if: (github.event_name == 'pull_request' && github.event.pull_request.head.repo.fork == false) && github.actor != 'dependabot[bot]' uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8.0.0 with: github-token: ${{ steps.setup-bot.outputs.token }} script: | const { owner, repo } = context.repo; const prNumber = context.issue.number; // Get all comments on the PR const { data: comments } = await github.rest.issues.listComments({ owner, repo, issue_number: prNumber, per_page: 100 }); // Filter for license check comments const licenseComments = comments.filter(comment => comment.body.includes('## ✅ Frontend License Check Passed') || comment.body.includes('## ❌ Frontend License Check Failed') ); // Delete old license check comments for (const comment of licenseComments) { console.log(`Deleting old license check comment: ${comment.id}`); await github.rest.issues.deleteComment({ owner, repo, comment_id: comment.id }); } - name: Summarize results (fork PRs) if: (github.event_name == 'pull_request' && github.event.pull_request.head.repo.fork == true) || github.actor == 'dependabot[bot]' run: | { echo "## Frontend License Check" echo "" if [ "${LICENSE_WARNINGS_EXIST}" = "true" ]; then echo "❌ **Failed** – incompatible or unknown licenses found." if [ -f "frontend/src/assets/license-warnings.json" ]; then echo "" echo "### Warnings" jq -r '.warnings[] | "- \(.message)"' frontend/src/assets/license-warnings.json || true fi else echo "✅ **Passed** – no license warnings detected." fi echo "" echo "_Note: This is a fork PR. PR comments are disabled; use this summary._" } >> "$GITHUB_STEP_SUMMARY" - name: Comment on PR - License Check Results if: (github.event_name == 'pull_request' && github.event.pull_request.head.repo.fork == false) && github.actor != 'dependabot[bot]' uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8.0.0 with: github-token: ${{ steps.setup-bot.outputs.token }} script: | const { owner, repo } = context.repo; const prNumber = context.issue.number; const hasWarnings = process.env.LICENSE_WARNINGS_EXIST === 'true'; let commentBody; if (hasWarnings) { // Read warnings file to get specific issues const fs = require('fs'); let warningDetails = ''; try { const warnings = JSON.parse(fs.readFileSync('frontend/src/assets/license-warnings.json', 'utf8')); warningDetails = warnings.warnings.map(w => `- ${w.message}`).join('\n'); } catch (e) { warningDetails = 'Unable to read warning details'; } commentBody = `## ❌ Frontend License Check Failed The frontend license check has detected compatibility warnings that require review: ${warningDetails} **Action Required:** Please review these licenses to ensure they are acceptable for your use case before merging. _This check will fail the PR until license issues are resolved._`; } else { commentBody = `## ✅ Frontend License Check Passed All frontend licenses have been validated and no compatibility warnings were detected. The frontend license report has been updated successfully.`; } await github.rest.issues.createComment({ owner, repo, issue_number: prNumber, body: commentBody }); - name: Fail workflow if license warnings exist (PR only) if: github.event_name == 'pull_request' && env.LICENSE_WARNINGS_EXIST == 'true' run: | echo "❌ License warnings detected. Failing the workflow." exit 1 # Push Event: Commit license files and create PR - name: Commit changes (Push only) if: github.event_name == 'push' run: | git add frontend/src/assets/3rdPartyLicenses.json # Note: Do NOT commit license-warnings.json - it's only for PR review git diff --staged --quiet || echo "CHANGES_DETECTED=true" >> $GITHUB_ENV - name: Prepare PR body (Push only) if: github.event_name == 'push' run: | PR_BODY="Auto-generated by ${{ steps.setup-bot.outputs.app-slug }}[bot] This PR updates the frontend license report based on changes to package.json dependencies." if [ "${{ env.LICENSE_WARNINGS_EXIST }}" = "true" ]; then PR_BODY="$PR_BODY ## ⚠️ License Compatibility Warnings The following licenses may require review for corporate compatibility: $(cat frontend/src/assets/license-warnings.json | jq -r '.warnings[].message') Please review these licenses to ensure they are acceptable for your use case." fi echo "PR_BODY<> $GITHUB_ENV echo "$PR_BODY" >> $GITHUB_ENV echo "EOF" >> $GITHUB_ENV - name: Create Pull Request (Push only) id: cpr if: github.event_name == 'push' && env.CHANGES_DETECTED == 'true' uses: peter-evans/create-pull-request@c0f553fe549906ede9cf27b5156039d195d2ece0 # v8.1.0 with: token: ${{ steps.setup-bot.outputs.token }} commit-message: "Update Frontend 3rd Party Licenses" committer: ${{ steps.setup-bot.outputs.committer }} author: ${{ steps.setup-bot.outputs.committer }} signoff: true branch: update-frontend-3rd-party-licenses base: main title: "Update Frontend 3rd Party Licenses" body: ${{ env.PR_BODY }} labels: Licenses,github-actions,frontend draft: false delete-branch: true sign-commits: true - name: Enable Pull Request Automerge (Push only) if: github.event_name == 'push' && steps.cpr.outputs.pull-request-operation == 'created' && env.LICENSE_WARNINGS_EXIST == 'false' run: gh pr merge --squash --auto "${{ steps.cpr.outputs.pull-request-number }}" env: GH_TOKEN: ${{ steps.setup-bot.outputs.token }} - name: Add review required label (Push only) if: github.event_name == 'push' && steps.cpr.outputs.pull-request-operation == 'created' && env.LICENSE_WARNINGS_EXIST == 'true' run: gh pr edit "${{ steps.cpr.outputs.pull-request-number }}" --add-label "license-review-required" env: GH_TOKEN: ${{ steps.setup-bot.outputs.token }} generate-backend-license-report: if: needs.files-changed.outputs.licenses-backend == 'true' needs: files-changed name: Generate Backend License Report runs-on: ubuntu-latest permissions: contents: write pull-requests: write repository-projects: write # Required for enabling automerge steps: - name: Harden Runner uses: step-security/harden-runner@20cf305ff2072d973412fa9b1e3a4f227bda3c76 # v2.14.0 with: egress-policy: audit - name: Checkout repository uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 with: fetch-depth: 0 persist-credentials: false - name: Setup GitHub App Bot if: (github.event_name == 'push' || (github.event_name == 'pull_request' && github.event.pull_request.head.repo.fork == false)) && github.actor != 'dependabot[bot]' id: setup-bot uses: ./.github/actions/setup-bot with: app-id: ${{ secrets.GH_APP_ID }} private-key: ${{ secrets.GH_APP_PRIVATE_KEY }} - name: Set up JDK 21 uses: actions/setup-java@be666c2fcd27ec809703dec50e508c2fdc7f6654 # v5.2.0 with: java-version: "21" distribution: "temurin" - name: Setup Gradle uses: gradle/actions/setup-gradle@f29f5a9d7b09a7c6b29859002d29d24e1674c884 # v5.0.1 with: gradle-version: 8.14 - name: Check licenses and generate report id: license-check run: | ./gradlew checkLicense generateLicenseReport || echo "LICENSE_CHECK_FAILED=true" >> $GITHUB_ENV env: MAVEN_USER: ${{ secrets.MAVEN_USER }} MAVEN_PASSWORD: ${{ secrets.MAVEN_PASSWORD }} MAVEN_PUBLIC_URL: ${{ secrets.MAVEN_PUBLIC_URL }} DISABLE_ADDITIONAL_FEATURES: false STIRLING_PDF_DESKTOP_UI: true - name: Check for license compatibility issues run: | if [ -f build/reports/dependency-license/dependencies-without-allowed-license.json ] && \ jq '.dependenciesWithoutAllowedLicenses | length > 0' build/reports/dependency-license/dependencies-without-allowed-license.json | grep -q true; then echo "LICENSE_WARNINGS_EXIST=true" >> $GITHUB_ENV else echo "LICENSE_WARNINGS_EXIST=false" >> $GITHUB_ENV fi if: always() - name: Upload artifact on license issues if: env.LICENSE_WARNINGS_EXIST == 'true' uses: actions/upload-artifact@b7c566a772e6b6bfb58ed0dc250532a479d7789f # v6.0.0 with: name: backend-dependencies-without-allowed-license.json path: build/reports/dependency-license/dependencies-without-allowed-license.json - name: Move license file if: env.LICENSE_CHECK_FAILED != 'true' && env.LICENSE_WARNINGS_EXIST == 'false' run: | mkdir -p app/core/src/main/resources/static cp build/reports/dependency-license/index.json app/core/src/main/resources/static/3rdPartyLicenses.json - name: Delete previous backend license check comments if: (github.event_name == 'pull_request' && github.event.pull_request.head.repo.fork == false) && github.actor != 'dependabot[bot]' uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8.0.0 with: github-token: ${{ steps.setup-bot.outputs.token }} script: | const { owner, repo } = context.repo; const prNumber = context.issue.number; const { data: comments } = await github.rest.issues.listComments({ owner, repo, issue_number: prNumber, per_page: 100 }); const backendLicenseComments = comments.filter(comment => comment.body.includes('## ✅ Backend License Check Passed') || comment.body.includes('## ❌ Backend License Check Failed') ); for (const comment of backendLicenseComments) { console.log(`Deleting old backend license comment: ${comment.id}`); await github.rest.issues.deleteComment({ owner, repo, comment_id: comment.id }); } - name: Comment on PR - Backend License Check Results if: (github.event_name == 'pull_request' && github.event.pull_request.head.repo.fork == false) && github.actor != 'dependabot[bot]' uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8.0.0 with: github-token: ${{ steps.setup-bot.outputs.token }} script: | const hasWarnings = process.env.LICENSE_WARNINGS_EXIST === 'true'; const fs = require('fs'); let warningDetails = ''; if (hasWarnings) { try { const warningsFile = 'build/reports/dependency-license/dependencies-without-allowed-license.json'; if (fs.existsSync(warningsFile)) { const data = JSON.parse(fs.readFileSync(warningsFile, 'utf8')); if (data.length > 0) { warningDetails = data.map(dep => `- **${dep.moduleName}@${dep.moduleVersion}** – ${dep.moduleLicenses.map(l => l.licenseName).join(', ')}`).join('\n'); } } } catch (e) { warningDetails = 'Unable to parse warning details.'; } } let commentBody; if (hasWarnings) { commentBody = `## ❌ Backend License Check Failed The backend license check has detected dependencies with incompatible or unallowed licenses: ${warningDetails || 'See uploaded artifact for details.'} **Action Required:** Please review these licenses and resolve before merging. _This check will fail the PR until license issues are resolved._`; } else { commentBody = `## ✅ Backend License Check Passed All backend dependencies have valid and allowed licenses. The backend license report has been updated successfully.`; } await github.rest.issues.createComment({ owner: context.repo.owner, repo: context.repo.repo, issue_number: context.issue.number, body: commentBody }); - name: Fail workflow if license warnings exist (PR only) if: github.event_name == 'pull_request' && env.LICENSE_WARNINGS_EXIST == 'true' run: | echo "❌ Backend license warnings detected. Failing the workflow." exit 1 - name: Commit changes (push only) if: github.event_name == 'push' && env.LICENSE_WARNINGS_EXIST == 'false' run: | git config user.name "${{ steps.setup-bot.outputs.committer }}" git config user.email "${{ steps.setup-bot.outputs.committer-email || 'bot@github.com' }}" git add app/core/src/main/resources/static/3rdPartyLicenses.json git diff --staged --quiet || echo "CHANGES_DETECTED=true" >> $GITHUB_ENV - name: Prepare PR body (push only) if: github.event_name == 'push' && env.CHANGES_DETECTED == 'true' run: | PR_BODY="Auto-generated by ${{ steps.setup-bot.outputs.app-slug }}[bot] This PR updates the backend license report based on dependency changes." if [ "${{ env.LICENSE_WARNINGS_EXIST }}" = "true" ]; then PR_BODY="$PR_BODY ## ⚠️ License Compatibility Warnings Incompatible licenses detected – manual review required before merge." fi echo "PR_BODY<> $GITHUB_ENV echo "$PR_BODY" >> $GITHUB_ENV echo "EOF" >> $GITHUB_ENV - name: Create Pull Request (push only) if: github.event_name == 'push' && env.CHANGES_DETECTED == 'true' id: cpr uses: peter-evans/create-pull-request@c0f553fe549906ede9cf27b5156039d195d2ece0 # v8.1.0 with: token: ${{ steps.setup-bot.outputs.token }} commit-message: "Update Backend 3rd Party Licenses" committer: ${{ steps.setup-bot.outputs.committer }} author: ${{ steps.setup-bot.outputs.committer }} signoff: true branch: update-backend-3rd-party-licenses base: main title: "Update Backend 3rd Party Licenses" body: ${{ env.PR_BODY }} labels: Licenses,github-actions,backend delete-branch: true sign-commits: true - name: Enable Pull Request Automerge (push only, no warnings) if: github.event_name == 'push' && steps.cpr.outputs.pull-request-operation == 'created' && env.LICENSE_WARNINGS_EXIST == 'false' run: gh pr merge --squash --auto "${{ steps.cpr.outputs.pull-request-number }}" env: GH_TOKEN: ${{ steps.setup-bot.outputs.token }} - name: Add review required label (push only, with warnings) if: github.event_name == 'push' && steps.cpr.outputs.pull-request-operation == 'created' && env.LICENSE_WARNINGS_EXIST == 'true' run: gh pr edit "${{ steps.cpr.outputs.pull-request-number }}" --add-label "license-review-required" env: GH_TOKEN: ${{ steps.setup-bot.outputs.token }}