deps(dev)(deps-dev): bump the eslint-ecosystem group across 1 directory with 2 updates #366
Workflow file for this run
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| name: Continuous integration | |
| permissions: | |
| contents: read | |
| actions: write | |
| pull-requests: write | |
| issues: write | |
| concurrency: | |
| group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.ref }} | |
| cancel-in-progress: true | |
| on: | |
| pull_request: | |
| branches: [main, master, develop] | |
| paths-ignore: | |
| - '**.md' | |
| - 'issue/**' | |
| push: | |
| branches: [main, master, develop] | |
| paths-ignore: | |
| - '**.md' | |
| - 'issue/**' | |
| merge_group: | |
| types: [checks_requested] | |
| workflow_dispatch: | |
| env: | |
| NODE_VERSION: '24' | |
| NEXT_PUBLIC_SITE_URL: https://www.sortvision.com | |
| FORCE_JAVASCRIPT_ACTIONS_TO_NODE24: true | |
| defaults: | |
| run: | |
| working-directory: ./SortVision | |
| jobs: | |
| format: | |
| name: Format | |
| runs-on: ubuntu-latest | |
| timeout-minutes: 10 | |
| steps: | |
| - name: Checkout repository | |
| uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 | |
| with: | |
| fetch-depth: 1 | |
| - name: Setup SortVision | |
| uses: ./.github/actions/setup-sortvision | |
| with: | |
| node-version: ${{ env.NODE_VERSION }} | |
| - name: Prettier check | |
| run: pnpm run format:check | |
| lint: | |
| name: Lint | |
| runs-on: ubuntu-latest | |
| timeout-minutes: 15 | |
| steps: | |
| - name: Checkout repository | |
| uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 | |
| with: | |
| fetch-depth: 1 | |
| - name: Setup SortVision | |
| uses: ./.github/actions/setup-sortvision | |
| with: | |
| node-version: ${{ env.NODE_VERSION }} | |
| - name: ESLint | |
| run: pnpm run lint | |
| - name: Unit tests | |
| run: pnpm run test:unit | |
| hygiene: | |
| name: Hygiene | |
| runs-on: ubuntu-latest | |
| timeout-minutes: 10 | |
| steps: | |
| - name: Checkout repository | |
| uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 | |
| with: | |
| fetch-depth: 1 | |
| - name: Lockfile policy (pnpm only) | |
| run: | | |
| set -euo pipefail | |
| ROOT="${GITHUB_WORKSPACE}/SortVision" | |
| test -f "${ROOT}/pnpm-lock.yaml" | |
| if [ -f "${ROOT}/package-lock.json" ] || [ -f "${ROOT}/yarn.lock" ]; then | |
| echo "::error::SortVision must use pnpm only β remove package-lock.json / yarn.lock under SortVision/." | |
| exit 1 | |
| fi | |
| - name: Setup SortVision | |
| uses: ./.github/actions/setup-sortvision | |
| with: | |
| node-version: ${{ env.NODE_VERSION }} | |
| - name: pnpm audit (production high/critical) | |
| run: pnpm audit --production --audit-level=high | |
| build: | |
| name: Build | |
| needs: [format, lint, hygiene] | |
| runs-on: ubuntu-latest | |
| timeout-minutes: 20 | |
| steps: | |
| - name: Checkout repository | |
| uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 | |
| with: | |
| fetch-depth: 1 | |
| - name: Cache Next.js build | |
| uses: actions/cache@27d5ce7f107fe9357f9df03efb73ab90386fccae | |
| with: | |
| path: SortVision/.next/cache | |
| key: nextjs-${{ runner.os }}-${{ hashFiles('SortVision/pnpm-lock.yaml', 'SortVision/package.json') }} | |
| restore-keys: nextjs-${{ runner.os }}- | |
| - name: Setup SortVision | |
| uses: ./.github/actions/setup-sortvision | |
| with: | |
| node-version: ${{ env.NODE_VERSION }} | |
| - name: Build application | |
| run: pnpm run build | |
| env: | |
| NODE_ENV: production | |
| - name: Analyze bundle size | |
| run: | | |
| echo "=== Bundle Size Analysis ===" | |
| du -sh .next/static/chunks/* 2>/dev/null | sort -h | tail -10 || echo "No chunks found" | |
| TOTAL_SIZE=$(du -sb .next 2>/dev/null | awk '{print $1}') | |
| TOTAL_SIZE_MB=$((TOTAL_SIZE / 1024 / 1024)) | |
| echo "Total build size: ${TOTAL_SIZE_MB}MB" | |
| if [ "$TOTAL_SIZE_MB" -gt 50 ]; then | |
| echo "WARNING: Build size exceeds 50MB" | |
| fi | |
| - name: Validate dynamic sitemap configuration | |
| run: | | |
| pnpm run generate-sitemap | |
| test -f src/app/sitemap.xml/route.ts | |
| test -f src/app/sitemap-index.xml/route.ts | |
| echo "Dynamic sitemap routes detected:" | |
| echo "- src/app/sitemap.xml/route.ts" | |
| echo "- src/app/sitemap-index.xml/route.ts" | |
| - name: Pack Next.js build for CI artifact | |
| working-directory: ${{ github.workspace }} | |
| run: | | |
| set -euo pipefail | |
| test -f SortVision/.next/BUILD_ID | |
| tar czf "${RUNNER_TEMP}/next-build.tar.gz" -C SortVision .next | |
| - name: Upload Next.js build output | |
| uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a | |
| with: | |
| name: next-build | |
| path: ${{ runner.temp }}/next-build.tar.gz | |
| if-no-files-found: error | |
| retention-days: 7 | |
| test: | |
| name: Test | |
| needs: [build] | |
| runs-on: ubuntu-latest | |
| timeout-minutes: 35 | |
| steps: | |
| - name: Checkout repository | |
| uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 | |
| with: | |
| fetch-depth: ${{ github.event_name == 'pull_request' && '0' || '1' }} | |
| - name: Setup SortVision | |
| uses: ./.github/actions/setup-sortvision | |
| with: | |
| node-version: ${{ env.NODE_VERSION }} | |
| - name: Restore Next.js build from artifact | |
| uses: ./.github/actions/restore-next-build | |
| - name: Log scan context | |
| run: | | |
| echo "Commit: ${{ github.sha }}" | |
| echo "Branch: ${{ github.ref_name }}" | |
| - name: Start production server | |
| run: | | |
| pnpm run start & | |
| echo $! > .server-pid | |
| pnpm exec wait-on http://localhost:3000 --timeout 120000 | |
| sleep 2 | |
| - name: Run complete test suite | |
| id: qa_tests | |
| run: pnpm run test:ci | |
| env: | |
| CI: 'true' | |
| GITHUB_ACTIONS: 'true' | |
| QA_COMMENT_DIR: ${{ github.event.pull_request && format('{0}/SortVision/.qa-pr-comment', github.workspace) || '' }} | |
| PR_NUMBER: ${{ github.event.pull_request.number || '' }} | |
| QA_METRICS_FILE: ${{ (github.event_name == 'push' || github.event_name == 'pull_request') && format('{0}/SortVision/qa-metrics.json', github.workspace) || '' }} | |
| - name: Prepare QA PR comment artifact | |
| if: github.event_name == 'pull_request' && always() | |
| run: | | |
| DIR="${GITHUB_WORKSPACE}/SortVision/.qa-pr-comment" | |
| mkdir -p "$DIR" | |
| if [ ! -s "$DIR/comment.md" ]; then | |
| echo "No QA report on disk; writing minimal placeholder for PR comment workflow." | |
| { | |
| echo '<!-- sortvision-qa-report -->' | |
| echo '### QA suite report' | |
| echo '' | |
| echo '**Result:** No report file was written (early failure or env issue). See **Test** job logs.' | |
| echo '' | |
| echo "[View run](${GITHUB_SERVER_URL}/${GITHUB_REPOSITORY}/actions/runs/${GITHUB_RUN_ID})" | |
| } > "$DIR/comment.md" | |
| fi | |
| if [ ! -s "$DIR/pr_number.txt" ]; then | |
| echo '${{ github.event.pull_request.number }}' > "$DIR/pr_number.txt" | |
| fi | |
| - name: Download base branch QA metrics (PR) | |
| if: github.event_name == 'pull_request' && always() | |
| env: | |
| GH_TOKEN: ${{ github.token }} | |
| working-directory: ${{ github.workspace }} | |
| run: | | |
| set +e | |
| BASE_REF="${{ github.event.pull_request.base.ref }}" | |
| PREV_RUN_ID=$(gh run list --repo "${GITHUB_REPOSITORY}" \ | |
| --workflow="continuous-integration.yml" \ | |
| --branch="${BASE_REF}" \ | |
| --status=success \ | |
| --json databaseId \ | |
| --limit 25 | jq -r --argjson rid "${GITHUB_RUN_ID}" '[.[] | select(.databaseId != $rid) | .databaseId] | first // empty') | |
| set -e | |
| if [ -z "${PREV_RUN_ID}" ]; then | |
| echo "No successful CI run on ${BASE_REF} with downloadable metrics yet." | |
| exit 0 | |
| fi | |
| mkdir -p "${GITHUB_WORKSPACE}/qa-base-dl" | |
| if gh run download "${PREV_RUN_ID}" -n qa-metrics -D "${GITHUB_WORKSPACE}/qa-base-dl"; then | |
| FOUND=$(find "${GITHUB_WORKSPACE}/qa-base-dl" -name qa-metrics.json -print -quit) | |
| if [ -n "${FOUND}" ]; then | |
| cp "${FOUND}" "${GITHUB_WORKSPACE}/SortVision/qa-metrics-base.json" | |
| echo "Base metrics from workflow run ${PREV_RUN_ID} (${BASE_REF})" | |
| fi | |
| else | |
| echo "Could not download qa-metrics from run ${PREV_RUN_ID}." | |
| fi | |
| - name: Append QA vs base branch to PR comment | |
| if: github.event_name == 'pull_request' && always() | |
| env: | |
| BASE_REF: ${{ github.event.pull_request.base.ref }} | |
| BASE_SHA: ${{ github.event.pull_request.base.sha }} | |
| QA_COMMENT_FILE: ${{ github.workspace }}/SortVision/.qa-pr-comment/comment.md | |
| QA_METRICS_CURRENT: ${{ github.workspace }}/SortVision/qa-metrics.json | |
| QA_METRICS_BASE: ${{ github.workspace }}/SortVision/qa-metrics-base.json | |
| run: node scripts/ci/ci-qa-pr-vs-base.cjs | |
| - name: Upload QA metrics artifact | |
| if: (github.event_name == 'push' || github.event_name == 'pull_request') && always() | |
| uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a | |
| with: | |
| name: qa-metrics | |
| path: SortVision/qa-metrics.json | |
| if-no-files-found: ignore | |
| retention-days: 30 | |
| - name: Find previous QA comment | |
| id: find_qa_comment | |
| if: github.event_name == 'pull_request' && always() | |
| uses: peter-evans/find-comment@b30e6a3c0ed37e7c023ccd3f1db5c6c0b0c23aad | |
| with: | |
| issue-number: ${{ github.event.pull_request.number }} | |
| body-includes: '<!-- sortvision-qa-report -->' | |
| - name: Post QA report to pull request | |
| if: github.event_name == 'pull_request' && always() | |
| uses: peter-evans/create-or-update-comment@e8674b075228eee787fea43ef493e45ece1004c9 | |
| with: | |
| comment-id: ${{ steps.find_qa_comment.outputs.comment-id }} | |
| issue-number: ${{ github.event.pull_request.number }} | |
| body-path: SortVision/.qa-pr-comment/comment.md | |
| edit-mode: replace | |
| - name: Upload QA PR comment artifact | |
| if: github.event_name == 'pull_request' && always() | |
| uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a | |
| with: | |
| name: qa-pr-comment | |
| path: SortVision/.qa-pr-comment/ | |
| include-hidden-files: true | |
| doctor: | |
| name: Doctor | |
| needs: [build] | |
| runs-on: ubuntu-latest | |
| timeout-minutes: 15 | |
| steps: | |
| - name: Checkout repository | |
| uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 | |
| with: | |
| # fetch-depth: 0 required for react-doctor diff comparison | |
| fetch-depth: 0 | |
| - name: Setup SortVision | |
| uses: ./.github/actions/setup-sortvision | |
| with: | |
| node-version: ${{ env.NODE_VERSION }} | |
| - name: Log scan context | |
| run: | | |
| echo "Scanning commit: ${{ github.sha }}" | |
| echo "Branch: ${{ github.ref_name }}" | |
| - name: Run React Doctor | |
| run: | | |
| npx -y react-doctor@latest . \ | |
| --diff main \ | |
| --fail-on none \ | |
| --score 2>&1 | tee doctor-output.txt | |
| continue-on-error: true | |
| - name: Extract score | |
| id: score | |
| run: | | |
| SCORE=$(cat doctor-output.txt | grep -oP '\d+\s*/\s*100' | head -1 | grep -oP '\d+' || echo "0") | |
| echo "score=$SCORE" >> $GITHUB_OUTPUT | |
| - name: Write metrics | |
| run: | | |
| echo "{\"score\": ${{ steps.score.outputs.score }}}" > doctor-metrics.json | |
| - name: Download base Doctor metrics (PR) | |
| if: github.event_name == 'pull_request' | |
| env: | |
| GH_TOKEN: ${{ github.token }} | |
| working-directory: ${{ github.workspace }} | |
| run: | | |
| set +e | |
| BASE_REF="${{ github.event.pull_request.base.ref }}" | |
| PREV_RUN_ID=$(gh run list --repo "${GITHUB_REPOSITORY}" \ | |
| --workflow="continuous-integration.yml" \ | |
| --branch="${BASE_REF}" \ | |
| --status=success \ | |
| --json databaseId \ | |
| --limit 25 | jq -r --argjson rid "${GITHUB_RUN_ID}" '[.[] | select(.databaseId != $rid) | .databaseId] | first // empty') | |
| set -e | |
| if [ -z "${PREV_RUN_ID}" ]; then | |
| echo "No previous successful CI on ${BASE_REF} for Doctor baseline." | |
| exit 0 | |
| fi | |
| mkdir -p "${GITHUB_WORKSPACE}/doctor-base-dl" | |
| if gh run download "${PREV_RUN_ID}" -n doctor-metrics -D "${GITHUB_WORKSPACE}/doctor-base-dl"; then | |
| FOUND=$(find "${GITHUB_WORKSPACE}/doctor-base-dl" -name doctor-metrics.json -print -quit) | |
| if [ -n "${FOUND}" ]; then | |
| cp "${FOUND}" "${GITHUB_WORKSPACE}/doctor-metrics-base.json" | |
| echo "Base Doctor metrics from run ${PREV_RUN_ID}" | |
| fi | |
| fi | |
| - name: Update PR comment | |
| env: | |
| DOCTOR_COMMENT_DIR: ${{ github.workspace }}/.doctor-pr-comment | |
| DOCTOR_METRICS_CURRENT: ${{ github.workspace }}/doctor-metrics.json | |
| DOCTOR_METRICS_BASE: ${{ github.workspace }}/doctor-metrics-base.json | |
| run: node scripts/ci/ci-doctor-pr-vs-base.mjs | |
| - name: Upload Doctor metrics artifact | |
| uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a | |
| with: | |
| name: doctor-metrics | |
| path: doctor-metrics.json | |
| if-no-files-found: ignore | |
| retention-days: 30 | |
| - name: Upload Doctor PR comment artifact | |
| if: github.event_name == 'pull_request' | |
| uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a | |
| with: | |
| name: doctor-pr-comment | |
| path: .doctor-pr-comment/ | |
| include-hidden-files: true | |
| lighthouse: | |
| name: Lighthouse (${{ matrix.variant }}) | |
| needs: [build] | |
| runs-on: ubuntu-latest | |
| timeout-minutes: 20 | |
| strategy: | |
| fail-fast: false | |
| matrix: | |
| include: | |
| - variant: mobile | |
| config: lighthouserc.json | |
| artifact: lighthouse-results-mobile | |
| - variant: desktop | |
| config: lighthouserc.desktop.json | |
| artifact: lighthouse-results-desktop | |
| steps: | |
| - name: Checkout repository | |
| uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 | |
| with: | |
| fetch-depth: 1 | |
| - name: Setup SortVision | |
| uses: ./.github/actions/setup-sortvision | |
| with: | |
| node-version: ${{ env.NODE_VERSION }} | |
| - name: Restore Next.js build from artifact | |
| uses: ./.github/actions/restore-next-build | |
| - name: Log scan context | |
| run: | | |
| echo "Commit: ${{ github.sha }}" | |
| echo "Branch: ${{ github.ref_name }}" | |
| - name: Start production server | |
| run: | | |
| pnpm run start & | |
| echo $! > .server-pid | |
| pnpm exec wait-on http://localhost:3000 --timeout 120000 | |
| sleep 2 | |
| - name: Lighthouse (${{ matrix.variant }}) | |
| uses: treosh/lighthouse-ci-action@3e7e23fb74242897f95c0ba9cabad3d0227b9b18 | |
| with: | |
| configPath: ./SortVision/${{ matrix.config }} | |
| runs: 1 | |
| urls: | | |
| http://localhost:3000 | |
| http://localhost:3000/algorithms/config/bubble | |
| http://localhost:3000/es | |
| http://localhost:3000/contributions/overview | |
| uploadArtifacts: true | |
| artifactName: ${{ matrix.artifact }} | |
| temporaryPublicStorage: true | |
| - name: Upload Lighthouse manifest (for summary) | |
| uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a | |
| with: | |
| name: lighthouse-manifest-${{ matrix.variant }} | |
| path: .lighthouseci/manifest.json | |
| if-no-files-found: error | |
| retention-days: 7 | |
| summary: | |
| name: Summary | |
| runs-on: ubuntu-latest | |
| timeout-minutes: 10 | |
| if: ${{ !cancelled() }} | |
| defaults: | |
| run: | |
| working-directory: ${{ github.workspace }} | |
| steps: | |
| - name: Log CI status | |
| run: | | |
| echo "## CI Status Summary" >> $GITHUB_STEP_SUMMARY | |
| echo "" >> $GITHUB_STEP_SUMMARY | |
| echo "| Job | Status |" >> $GITHUB_STEP_SUMMARY | |
| echo "|-----|--------|" >> $GITHUB_STEP_SUMMARY | |
| echo "| Format | \${{ needs.format.result }} |" 2>/dev/null || true | |
| echo "| Lint | \${{ needs.lint.result }} |" 2>/dev/null || true | |
| echo "| Hygiene | \${{ needs.hygiene.result }} |" 2>/dev/null || true | |
| echo "| Build | \${{ needs.build.result }} |" 2>/dev/null || true | |
| echo "| Test | \${{ needs.test.result }} |" 2>/dev/null || true | |
| echo "| Doctor | \${{ needs.doctor.result }} |" 2>/dev/null || true | |
| echo "| Lighthouse | \${{ needs.lighthouse.result }} |" 2>/dev/null || true | |
| echo "" >> $GITHUB_STEP_SUMMARY | |
| - name: Fail on critical job failures | |
| run: | | |
| FAILED="" | |
| for job in test doctor lighthouse; do | |
| result="\${needs.$job.result:-skipped}" | |
| if [ "$result" = "failure" ]; then | |
| FAILED="$FAILED $job" | |
| fi | |
| done | |
| if [ -n "$FAILED" ]; then | |
| echo "::error::Critical jobs failed:$FAILED" | |
| exit 1 | |
| fi | |
| validate: | |
| name: Validate | |
| needs: [summary] | |
| runs-on: ubuntu-latest | |
| timeout-minutes: 10 | |
| if: github.ref == 'refs/heads/main' || github.ref == 'refs/heads/master' | |
| defaults: | |
| run: | |
| working-directory: ./SortVision | |
| steps: | |
| - name: Checkout repository | |
| uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 | |
| with: | |
| fetch-depth: 1 | |
| - name: Setup Node.js | |
| uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 | |
| with: | |
| node-version: ${{ env.NODE_VERSION }} | |
| - name: Test production site | |
| run: node tests/qa/quality-assurance.ts --production | |
| - name: Validate production performance | |
| run: | | |
| echo "=== Production Site Health Check ===" | |
| RESPONSE_TIME=$(curl -o /dev/null -s -w '%{time_total}' https://www.sortvision.com) | |
| HTTP_CODE=$(curl -o /dev/null -s -w '%{http_code}' https://www.sortvision.com) | |
| echo "HTTP Status: $HTTP_CODE" | |
| echo "Response Time: ${RESPONSE_TIME}s" | |
| if [ "$HTTP_CODE" != "200" ]; then | |
| echo "::error::Production site returned $HTTP_CODE" | |
| exit 1 | |
| fi | |
| RESPONSE_MS=$(echo "$RESPONSE_TIME * 1000" | bc) | |
| if [ "$(echo "$RESPONSE_MS > 3000" | bc)" -eq 1 ]; then | |
| echo "WARNING: Response time exceeds 3 seconds" | |
| fi | |
| - name: Generate production report | |
| if: always() | |
| run: | | |
| echo "## Production Validation Results" >> $GITHUB_STEP_SUMMARY | |
| echo "" >> $GITHUB_STEP_SUMMARY | |
| echo "Production site: https://www.sortvision.com" >> $GITHUB_STEP_SUMMARY | |
| echo "" >> $GITHUB_STEP_SUMMARY | |
| echo "| Check | Result |" >> $GITHUB_STEP_SUMMARY | |
| echo "|-------|--------|" >> $GITHUB_STEP_SUMMARY | |
| echo "| Site Accessibility | Passed |" >> $GITHUB_STEP_SUMMARY | |
| echo "| Production Tests | Passed |" >> $GITHUB_STEP_SUMMARY | |
| echo "| Response Time | Under 3s |" >> $GITHUB_STEP_SUMMARY |