From 2160f1c2176d00a9216e7a9f0f57efc00fc75675 Mon Sep 17 00:00:00 2001 From: Vucomir Ianculov Date: Wed, 4 Feb 2026 21:22:07 +0200 Subject: [PATCH 1/3] DEVOPS-1274 updated workflow for node app --- ...ication.yml => check-node-application.yml} | 135 +++++++++++++++--- 1 file changed, 116 insertions(+), 19 deletions(-) rename .github/workflows/{check-npm-application.yml => check-node-application.yml} (69%) diff --git a/.github/workflows/check-npm-application.yml b/.github/workflows/check-node-application.yml similarity index 69% rename from .github/workflows/check-npm-application.yml rename to .github/workflows/check-node-application.yml index 22bd420..4a364b7 100644 --- a/.github/workflows/check-npm-application.yml +++ b/.github/workflows/check-node-application.yml @@ -1,4 +1,4 @@ -name: Reusable - Check NPM App +name: Reusable - Check Node App on: workflow_call: @@ -30,8 +30,8 @@ on: cache: required: false type: string - description: "Cache type (npm, yarn, or empty string to disable). Defaults to npm if package-lock.json exists" - default: "npm" + description: "Cache type (npm, yarn, pnpm or empty string to disable). Defaults to npm if package-lock.json exists" + default: "pnpm" install_dependencies: required: false type: boolean @@ -55,7 +55,7 @@ on: lint_command: required: false type: string - default: "npm run lint" + default: "" run_audit: required: false type: boolean @@ -90,15 +90,28 @@ jobs: ref: ${{ inputs.checkout_ref }} repository: ${{ inputs.checkout_repository }} + - name: Setup pnpm + if: ${{ inputs.cache == 'pnpm' }} + uses: pnpm/action-setup@v4 + with: + version: latest + - uses: actions/setup-node@v6 with: node-version: ${{ inputs.node_version }} cache: ${{ inputs.cache }} - cache-dependency-path: ${{ inputs.working_directory != '.' && format('{0}/package-lock.json', inputs.working_directory) || '' }} + cache-dependency-path: ${{ inputs.cache == 'pnpm' && (inputs.working_directory != '.' && format('{0}/pnpm-lock.yaml', inputs.working_directory) || 'pnpm-lock.yaml') || inputs.cache == 'yarn' && (inputs.working_directory != '.' && format('{0}/yarn.lock', inputs.working_directory) || 'yarn.lock') || inputs.working_directory != '.' && format('{0}/package-lock.json', inputs.working_directory) || 'package-lock.json' }} - name: Install dependencies if: ${{ inputs.install_dependencies }} - run: npm ci + run: | + if [ "${{ inputs.cache }}" == "pnpm" ]; then + pnpm install --frozen-lockfile + elif [ "${{ inputs.cache }}" == "yarn" ]; then + yarn install --frozen-lockfile + else + npm ci + fi - name: Build packages if: ${{ inputs.build_packages }} @@ -107,32 +120,72 @@ jobs: - name: Run linting if: ${{ inputs.run_lint }} - run: ${{ inputs.lint_command }} + run: | + if [ "${{ inputs.lint_command }}" != "" ]; then + ${{ inputs.lint_command }} + else + if [ "${{ inputs.cache }}" == "pnpm" ]; then + pnpm run lint + elif [ "${{ inputs.cache }}" == "yarn" ]; then + yarn lint + else + npm run lint + fi + fi - name: Run typescript checks if: ${{ inputs.run_checks }} - run: npm run types:check + run: | + if [ "${{ inputs.cache }}" == "pnpm" ]; then + pnpm run types:check + elif [ "${{ inputs.cache }}" == "yarn" ]; then + yarn types:check + else + npm run types:check + fi - - name: Run npm audit (JSON) + - name: Run audit (JSON) if: ${{ inputs.run_audit }} id: npm-audit-json continue-on-error: true run: | - npm audit --audit-level=${{ inputs.audit_level }} --json > audit.json 2>&1 || AUDIT_EXIT_CODE=$? + if [ "${{ inputs.cache }}" == "pnpm" ]; then + pnpm audit --audit-level=${{ inputs.audit_level }} --json > audit.json 2>&1 || AUDIT_EXIT_CODE=$? + elif [ "${{ inputs.cache }}" == "yarn" ]; then + yarn audit --level ${{ inputs.audit_level }} --json > audit.json 2>&1 || AUDIT_EXIT_CODE=$? + else + npm audit --audit-level=${{ inputs.audit_level }} --json > audit.json 2>&1 || AUDIT_EXIT_CODE=$? + fi echo "exit_code=${AUDIT_EXIT_CODE:-0}" >> $GITHUB_OUTPUT - - name: Convert npm audit JSON to Markdown + - name: Convert audit JSON to Markdown if: ${{ inputs.run_audit && steps.npm-audit-json.outcome != 'skipped' }} id: npm-audit + env: + PACKAGE_MANAGER: ${{ inputs.cache }} run: | REPORT_FILE=$(mktemp) # Check if audit.json exists and is valid JSON if [ ! -f audit.json ] || ! node -e "JSON.parse(require('fs').readFileSync('audit.json','utf8'))" 2>/dev/null; then # Fallback to text output if JSON parsing fails - TEXT_OUTPUT=$(npm audit --audit-level=${{ inputs.audit_level }} 2>&1 || true) + if [ "${{ inputs.cache }}" == "pnpm" ]; then + TEXT_OUTPUT=$(pnpm audit --audit-level=${{ inputs.audit_level }} 2>&1 || true) + elif [ "${{ inputs.cache }}" == "yarn" ]; then + TEXT_OUTPUT=$(yarn audit --level ${{ inputs.audit_level }} 2>&1 || true) + else + TEXT_OUTPUT=$(npm audit --audit-level=${{ inputs.audit_level }} 2>&1 || true) + fi { - echo "## ⚠️ npm audit error" + PM_NAME="${{ inputs.cache }}" + if [ "$PM_NAME" == "pnpm" ]; then + PM_DISPLAY="pnpm" + elif [ "$PM_NAME" == "yarn" ]; then + PM_DISPLAY="yarn" + else + PM_DISPLAY="npm" + fi + echo "## ⚠️ ${PM_DISPLAY} audit error" echo "" echo "Failed to generate audit report:" echo "" @@ -180,8 +233,11 @@ jobs: const totalVulns = sevOrder.reduce((s,k)=>s+countOrZero(k),0); + const pm = process.env.PACKAGE_MANAGER || 'npm'; + const pmName = pm === 'pnpm' ? 'pnpm' : pm === 'yarn' ? 'yarn' : 'npm'; + const md = []; - md.push(`## 📋 npm audit report`); + md.push(`## 📋 ${pmName} audit report`); md.push(''); md.push(`Generated: **${new Date().toISOString()}**`); md.push(''); @@ -247,11 +303,29 @@ jobs: let fixCell = '❌'; if (v.fixAvailable) { if (v.fixAvailable === true) { - fixCell = '✅ \`npm audit fix\`'; + if (pm === 'pnpm') { + fixCell = '✅ \`pnpm audit --fix\`'; + } else if (pm === 'yarn') { + fixCell = '✅ \`yarn audit --fix\`'; + } else { + fixCell = '✅ \`npm audit fix\`'; + } } else if (v.fixAvailable.isSemVerMajor) { - fixCell = '✅ \`npm audit fix --force\`
⚠️ **Breaking change**'; + if (pm === 'pnpm') { + fixCell = '✅ \`pnpm update\`
⚠️ **Breaking change**'; + } else if (pm === 'yarn') { + fixCell = '✅ Update manually
⚠️ **Breaking change**'; + } else { + fixCell = '✅ \`npm audit fix --force\`
⚠️ **Breaking change**'; + } } else { - fixCell = '✅ \`npm audit fix\`'; + if (pm === 'pnpm') { + fixCell = '✅ \`pnpm audit --fix\`'; + } else if (pm === 'yarn') { + fixCell = '✅ \`yarn audit --fix\`'; + } else { + fixCell = '✅ \`npm audit fix\`'; + } } } @@ -287,7 +361,13 @@ jobs: // Add fix instructions if vulnerabilities found if (totalVulns > 0) { - md.push(`> 💡 To fix these issues, run: \`npm audit fix\` or \`npm audit fix --force\` (for breaking changes)`); + if (pm === 'pnpm') { + md.push(`> 💡 To fix these issues, run: \`pnpm audit --fix\` or \`pnpm update\``); + } else if (pm === 'yarn') { + md.push(`> 💡 To fix these issues, run: \`yarn audit --fix\` or update packages manually`); + } else { + md.push(`> 💡 To fix these issues, run: \`npm audit fix\` or \`npm audit fix --force\` (for breaking changes)`); + } } else { md.push(`> ✅ No vulnerabilities found.`); } @@ -320,13 +400,30 @@ jobs: # Cleanup rm -f audit.json + - name: Generate unique comment tag + if: ${{ inputs.run_audit && github.event_name == 'pull_request' }} + id: comment-tag + run: | + WORK_DIR="${{ inputs.working_directory }}" + CACHE="${{ inputs.cache }}" + + # Sanitize working directory for use in tag (replace / with -, remove leading/trailing dots and slashes) + if [ "$WORK_DIR" != "." ]; then + SANITIZED_DIR=$(echo "$WORK_DIR" | sed 's/\//-/g' | sed 's/^\.//g' | sed 's/\.$//g' | sed 's/^-\|-$//g') + TAG="${CACHE}-audit-report-${SANITIZED_DIR}" + else + TAG="${CACHE}-audit-report" + fi + + echo "tag=${TAG}" >> $GITHUB_OUTPUT + - name: Create or update PR comment with audit results if: ${{ inputs.run_audit && github.event_name == 'pull_request' && steps.npm-audit.outcome != 'skipped' }} uses: thollander/actions-comment-pull-request@v3 with: github-token: ${{ secrets.GITHUB_TOKEN }} file-path: ${{ steps.npm-audit.outputs.report_file }} - comment-tag: npm-audit-report + comment-tag: ${{ steps.comment-tag.outputs.tag }} mode: upsert create-if-not-exists: ${{ steps.npm-audit.outputs.has_vulnerabilities || 'false' }} From 5672513e5f7372b09dec6a8c8e2c82cc3605a070 Mon Sep 17 00:00:00 2001 From: Vucomir Ianculov Date: Wed, 4 Feb 2026 21:27:22 +0200 Subject: [PATCH 2/3] DEVOPS-1274 updated workflow for node app --- .github/workflows/check-node-application.yml | 16 ++++------------ 1 file changed, 4 insertions(+), 12 deletions(-) diff --git a/.github/workflows/check-node-application.yml b/.github/workflows/check-node-application.yml index 4a364b7..c6cbf64 100644 --- a/.github/workflows/check-node-application.yml +++ b/.github/workflows/check-node-application.yml @@ -52,10 +52,6 @@ on: required: false type: boolean default: true - lint_command: - required: false - type: string - default: "" run_audit: required: false type: boolean @@ -121,17 +117,13 @@ jobs: - name: Run linting if: ${{ inputs.run_lint }} run: | - if [ "${{ inputs.lint_command }}" != "" ]; then - ${{ inputs.lint_command }} - else - if [ "${{ inputs.cache }}" == "pnpm" ]; then + if [ "${{ inputs.cache }}" == "pnpm" ]; then pnpm run lint elif [ "${{ inputs.cache }}" == "yarn" ]; then yarn lint else npm run lint fi - fi - name: Run typescript checks if: ${{ inputs.run_checks }} @@ -235,7 +227,7 @@ jobs: const pm = process.env.PACKAGE_MANAGER || 'npm'; const pmName = pm === 'pnpm' ? 'pnpm' : pm === 'yarn' ? 'yarn' : 'npm'; - + const md = []; md.push(`## 📋 ${pmName} audit report`); md.push(''); @@ -406,7 +398,7 @@ jobs: run: | WORK_DIR="${{ inputs.working_directory }}" CACHE="${{ inputs.cache }}" - + # Sanitize working directory for use in tag (replace / with -, remove leading/trailing dots and slashes) if [ "$WORK_DIR" != "." ]; then SANITIZED_DIR=$(echo "$WORK_DIR" | sed 's/\//-/g' | sed 's/^\.//g' | sed 's/\.$//g' | sed 's/^-\|-$//g') @@ -414,7 +406,7 @@ jobs: else TAG="${CACHE}-audit-report" fi - + echo "tag=${TAG}" >> $GITHUB_OUTPUT - name: Create or update PR comment with audit results From a96341da4e3224f58c42ff2d21b1d411f29f3dde Mon Sep 17 00:00:00 2001 From: Vucomir Ianculov Date: Wed, 4 Feb 2026 21:46:57 +0200 Subject: [PATCH 3/3] DEVOPS-1274 updated workflow for node app --- .github/workflows/check-node-application.yml | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/.github/workflows/check-node-application.yml b/.github/workflows/check-node-application.yml index c6cbf64..e17f5a8 100644 --- a/.github/workflows/check-node-application.yml +++ b/.github/workflows/check-node-application.yml @@ -155,6 +155,7 @@ jobs: id: npm-audit env: PACKAGE_MANAGER: ${{ inputs.cache }} + WORKING_DIRECTORY: ${{ inputs.working_directory }} run: | REPORT_FILE=$(mktemp) @@ -227,9 +228,14 @@ jobs: const pm = process.env.PACKAGE_MANAGER || 'npm'; const pmName = pm === 'pnpm' ? 'pnpm' : pm === 'yarn' ? 'yarn' : 'npm'; + const workDir = process.env.WORKING_DIRECTORY || '.'; const md = []; - md.push(`## 📋 ${pmName} audit report`); + if (workDir !== '.') { + md.push(`## 📋 ${pmName} audit report - \`${workDir}\``); + } else { + md.push(`## 📋 ${pmName} audit report`); + } md.push(''); md.push(`Generated: **${new Date().toISOString()}**`); md.push('');