Skip to content

chore(deps): update all pnpm dependencies to latest (Vite 7 pinned) (… #1105

chore(deps): update all pnpm dependencies to latest (Vite 7 pinned) (…

chore(deps): update all pnpm dependencies to latest (Vite 7 pinned) (… #1105

Workflow file for this run

name: CI
on:
push:
branches: ["main"]
pull_request:
branches: ["main"]
concurrency:
group: ${{ github.workflow }}-${{ github.ref }}
cancel-in-progress: true
# Least-privilege: restrict GITHUB_TOKEN to read-only by default.
# Jobs that need more (e.g. pull-requests: write) declare their own block.
permissions:
contents: read
# Suppress deprecation warnings from upstream GitHub Actions dependencies
env:
NODE_OPTIONS: "--no-deprecation"
jobs:
ci-setup:
name: "🚀 Initialize CI Report"
runs-on: ubuntu-latest
permissions:
contents: read
pull-requests: write
steps:
- uses: actions/checkout@v6
- name: Post Initial Comment
uses: actions/github-script@v9
with:
script: |
const { default: script } = await import(`file://${process.env.GITHUB_WORKSPACE}/scripts/ci/update-ci-comment.mjs`)
await script({github, context, step: 'init'})
build:
name: Build Artifact
runs-on: ubuntu-latest
timeout-minutes: 15
needs: ci-setup
steps:
- uses: actions/checkout@v6
- name: Cache Playwright Browsers
id: playwright-cache
uses: actions/cache@v5
with:
path: ~/.cache/ms-playwright
key: playwright-browsers-${{ runner.os }}-${{ hashFiles('pnpm-lock.yaml') }}
- name: Cache Optimized Images
uses: actions/cache@v5
with:
path: .cache/optimized-images
key: optimized-images-${{ runner.os }}-${{ hashFiles('astro.config.mjs', 'pnpm-lock.yaml', 'src/**/*.{png,jpg,jpeg,webp,avif,gif,svg}', 'public/**/*.{png,jpg,jpeg,webp,avif,gif,svg}') }}
restore-keys: |
optimized-images-${{ runner.os }}-
- uses: pnpm/action-setup@v6
- uses: actions/setup-node@v6
with:
node-version: 22
cache: "pnpm"
- run: pnpm install --frozen-lockfile --prefer-offline
- name: Install Playwright Browsers
if: steps.playwright-cache.outputs.cache-hit != 'true'
run: pnpm exec playwright install --with-deps chromium
- name: Build Project (Production)
run: pnpm run build
- name: Upload build artifact
uses: actions/upload-artifact@v6
with:
name: dist-build
path: dist/
retention-days: 1
# --- Stage: Quality & Security Checks (Parallel) ---
sa-astro:
name: "SA: Astro Check"
runs-on: ubuntu-latest
timeout-minutes: 5
needs: build
steps:
- uses: actions/checkout@v6
- uses: pnpm/action-setup@v6
- uses: actions/setup-node@v6
with:
node-version: 22
cache: "pnpm"
- run: pnpm install --frozen-lockfile --prefer-offline
- name: Run Astro Check
run: pnpm exec astro check 2>&1 | tee astro-check.log
- name: Upload Astro Check log
if: always()
uses: actions/upload-artifact@v6
with:
name: log-astro
path: astro-check.log
retention-days: 1
sa-prettier:
name: "SA: Prettier"
runs-on: ubuntu-latest
timeout-minutes: 5
needs: build
steps:
- uses: actions/checkout@v6
- uses: pnpm/action-setup@v6
- uses: actions/setup-node@v6
with:
node-version: 22
cache: "pnpm"
- run: pnpm install --frozen-lockfile --prefer-offline
- name: Run Prettier check
run: pnpm exec prettier --check . 2>&1 | tee prettier.log
- name: Upload Prettier log
if: always()
uses: actions/upload-artifact@v6
with:
name: log-prettier
path: prettier.log
retention-days: 1
sa-eslint:
name: "SA: ESLint"
runs-on: ubuntu-latest
timeout-minutes: 5
needs: build
steps:
- uses: actions/checkout@v6
- uses: pnpm/action-setup@v6
- uses: actions/setup-node@v6
with:
node-version: 22
cache: "pnpm"
- run: pnpm install --frozen-lockfile --prefer-offline
- name: Run ESLint
run: pnpm exec eslint . --max-warnings=0 2>&1 | tee eslint.log
- name: Upload ESLint log
if: always()
uses: actions/upload-artifact@v6
with:
name: log-eslint
path: eslint.log
retention-days: 1
sa-audit:
name: "SA: Security Audit"
runs-on: ubuntu-latest
timeout-minutes: 5
needs: build
steps:
- uses: actions/checkout@v6
- uses: pnpm/action-setup@v6
- uses: actions/setup-node@v6
with:
node-version: 22
cache: "pnpm"
- run: pnpm install --frozen-lockfile --prefer-offline
- name: Run Security Audit
run: pnpm audit --audit-level=high 2>&1 | tee security-audit.log
- name: Upload Audit log
if: always()
uses: actions/upload-artifact@v6
with:
name: log-audit
path: security-audit.log
retention-days: 1
sa-stylelint:
name: "SA: Stylelint"
runs-on: ubuntu-latest
timeout-minutes: 5
needs: build
steps:
- uses: actions/checkout@v6
- uses: pnpm/action-setup@v6
- uses: actions/setup-node@v6
with:
node-version: 22
cache: "pnpm"
- run: pnpm install --frozen-lockfile --prefer-offline
- name: Run Stylelint
run: pnpm exec stylelint "src/**/*.{astro,css}" 2>&1 | tee stylelint.log
- name: Upload Stylelint log
if: always()
uses: actions/upload-artifact@v6
with:
name: log-stylelint
path: stylelint.log
retention-days: 1
sa-jsdoc:
name: "SA: JSDoc Coverage"
runs-on: ubuntu-latest
timeout-minutes: 5
needs: build
outputs:
coverage: ${{ steps.jsdoc_calc.outputs.coverage }}
steps:
- uses: actions/checkout@v6
- uses: pnpm/action-setup@v6
- uses: actions/setup-node@v6
with:
node-version: 22
cache: "pnpm"
- run: pnpm install --frozen-lockfile --prefer-offline
- name: Calculate JSDoc Coverage
id: jsdoc_calc
run: |
OUTPUT=$(node scripts/ci/calculate-jsdoc-coverage.mjs 2>&1 | tee jsdoc-coverage.log)
echo "$OUTPUT"
COVERAGE=$(echo "$OUTPUT" | sed -nE 's/.*Coverage: ([0-9.]+).*/\1/p')
if [ -z "$COVERAGE" ]; then
echo "::error::Failed to parse JSDoc coverage from output."
exit 1
fi
echo "coverage=${COVERAGE}%" >> $GITHUB_OUTPUT
- name: Upload JSDoc log
if: always()
uses: actions/upload-artifact@v6
with:
name: log-jsdoc
path: jsdoc-coverage.log
retention-days: 1
sa-lychee:
name: "SA: Link Checker (Dist)"
runs-on: ubuntu-latest
timeout-minutes: 5
needs: build
steps:
- uses: actions/checkout@v6
- name: Restore lychee cache
uses: actions/cache@v5
with:
path: .lycheecache
key: cache-lychee-source-${{ github.sha }}
restore-keys: cache-lychee-source-
- name: Download build artifact
uses: actions/download-artifact@v7
with:
name: dist-build
path: dist
- uses: lycheeverse/lychee-action@v2.7.0
with:
args: --config lychee.toml --root-dir ${{ github.workspace }}/dist --format markdown ./dist/**/*.html
output: lychee-report.md
fail: true
env:
GITHUB_TOKEN: ${{ secrets.TOKEN_GITHUB }}
- name: Generate Lychee HTML Report
if: always()
run: |
set -e -o pipefail
node scripts/ci/generate-lychee-report.mjs
- name: Upload Lychee reports
if: always()
uses: actions/upload-artifact@v6
with:
name: lychee-report
path: |
lychee-report.md
lychee-report.html
retention-days: 1
sa-cspell:
name: "SA: Spell Checker"
runs-on: ubuntu-latest
timeout-minutes: 5
needs: build
steps:
- uses: actions/checkout@v6
- uses: pnpm/action-setup@v6
- uses: actions/setup-node@v6
with:
node-version-file: package.json
cache: pnpm
- run: pnpm install --frozen-lockfile --prefer-offline
- name: Run CSpell
run: pnpm exec cspell lint "." --no-progress 2>&1 | tee cspell.log
- name: Upload CSpell log
if: always()
uses: actions/upload-artifact@v6
with:
name: log-cspell
path: cspell.log
retention-days: 1
sa-sonar:
name: "SA: SonarQube"
runs-on: ubuntu-latest
timeout-minutes: 5
needs: build
steps:
- uses: actions/checkout@v6
with:
fetch-depth: 0
- name: SonarQube Scan
# Only run when SONAR_TOKEN is configured
if: env.SONAR_TOKEN != ''
uses: SonarSource/sonarqube-scan-action@v7
continue-on-error: true
env:
SONAR_TOKEN: ${{ secrets.SONAR_TOKEN }}
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
bundle-size:
name: Bundle Size Check
runs-on: ubuntu-latest
timeout-minutes: 5
needs: build
steps:
- uses: actions/checkout@v6
- name: Download build artifact
uses: actions/download-artifact@v7
with:
name: dist-build
path: dist
- uses: pnpm/action-setup@v6
- uses: actions/setup-node@v6
with:
node-version: 22
cache: "pnpm"
- run: pnpm install --frozen-lockfile --prefer-offline
- name: Analyze bundle size
run: node scripts/ci/analyze-bundle-size.mjs 2>&1 | tee bundle-size.log
- name: Upload bundle log
if: always()
uses: actions/upload-artifact@v6
with:
name: log-bundle
path: bundle-size.log
retention-days: 1
- name: Upload bundle analysis
uses: actions/upload-artifact@v6
with:
name: bundle-analysis
path: bundle-analysis.json
retention-days: 1
html-validator:
name: HTML Validation
runs-on: ubuntu-latest
timeout-minutes: 5
needs: build
steps:
- uses: actions/checkout@v6
- name: Download build artifact
uses: actions/download-artifact@v7
with:
name: dist-build
path: dist
- uses: pnpm/action-setup@v6
- uses: actions/setup-node@v6
with:
node-version: 22
cache: "pnpm"
- run: pnpm install --frozen-lockfile --prefer-offline
- name: Validate HTML5
# Two html-validate runs:
# 1. JSON formatter with || true: Always produce a best-effort JSON report for dashboard
# 2. Stylish formatter (no || true): This run determines workflow pass/fail
run: |
pnpm exec html-validate -c .htmlvalidate.json --formatter json dist > html-validation.json 2>&1 || true
pnpm exec html-validate -c .htmlvalidate.json --formatter stylish dist 2>&1 | tee html-validation.log
node scripts/ci/generate-html-validation-report.mjs
- name: Upload HTML log
if: always()
uses: actions/upload-artifact@v6
with:
name: log-html
path: html-validation.log
retention-days: 1
- name: Upload HTML validation files
uses: actions/upload-artifact@v6
with:
name: html-validation-report
path: |
html-validation.json
html-validation-report.html
retention-days: 1
rss-validation:
name: RSS Feed Validation
runs-on: ubuntu-latest
timeout-minutes: 5
needs: build
steps:
- uses: actions/checkout@v6
- name: Download build artifact
uses: actions/download-artifact@v7
with:
name: dist-build
path: dist
- uses: pnpm/action-setup@v6
- uses: actions/setup-node@v6
with:
node-version: 22
cache: "pnpm"
- run: pnpm install --frozen-lockfile --prefer-offline
- name: Validate RSS feed
run: |
set -e -o pipefail
node scripts/ci/validate-rss.mjs dist 2>&1 | tee rss-validation.log
- name: Generate RSS preview
run: |
set -e -o pipefail
node scripts/preview-rss.mjs
- name: Upload RSS log
if: always()
uses: actions/upload-artifact@v6
with:
name: log-rss
path: rss-validation.log
retention-days: 1
- name: Upload RSS validation files
uses: actions/upload-artifact@v6
with:
name: rss-validation-report
path: |
rss-validation.json
dist/rss-preview.html
retention-days: 1
schema-validation:
name: Schema.org JSON-LD Validation
runs-on: ubuntu-latest
timeout-minutes: 5
needs: build
steps:
- uses: actions/checkout@v6
- name: Download build artifact
uses: actions/download-artifact@v7
with:
name: dist-build
path: dist
- uses: pnpm/action-setup@v6
- uses: actions/setup-node@v6
with:
node-version: 22
cache: "pnpm"
- run: pnpm install --frozen-lockfile --prefer-offline
- name: Validate Schema.org structured data
run: node scripts/ci/validate-schema.mjs dist 2>&1 | tee schema-validation.log
- name: Generate Schema Report
if: always()
run: |
if [ -f schema-report.json ]; then
node scripts/ci/generate-schema-report.mjs
fi
- name: Upload Schema log
if: always()
uses: actions/upload-artifact@v6
with:
name: log-schema
path: schema-validation.log
retention-days: 1
- name: Upload Schema validation files
uses: actions/upload-artifact@v6
with:
name: schema-validation-report
path: |
schema-report.json
schema-report.html
retention-days: 1
image-optimization:
name: Image Optimization Check
runs-on: ubuntu-latest
timeout-minutes: 5
needs: build
steps:
- uses: actions/checkout@v6
- name: Download build artifact
uses: actions/download-artifact@v7
with:
name: dist-build
path: dist
- uses: pnpm/action-setup@v6
- uses: actions/setup-node@v6
with:
node-version: 22
cache: "pnpm"
- run: pnpm install --frozen-lockfile --prefer-offline
- name: Analyze images
run: |
echo "## 🖼️ Image Optimization Analysis" >> $GITHUB_STEP_SUMMARY
WEBP_COUNT=$(find dist -type f -name "*.webp" | wc -l)
echo "- WebP images: $WEBP_COUNT ✅" >> $GITHUB_STEP_SUMMARY
- name: Generate Image HTML Report
run: node scripts/ci/generate-image-report.mjs
- name: Upload image report
uses: actions/upload-artifact@v6
with:
name: image-optimization-report
path: image-report.html
retention-days: 1
functional-tests:
name: Functional Tests
runs-on: ubuntu-latest
timeout-minutes: 30
needs: build
steps:
- uses: actions/checkout@v6
- name: Download build artifact
uses: actions/download-artifact@v7
with:
name: dist-build
path: dist
- uses: pnpm/action-setup@v6
- uses: actions/setup-node@v6
with:
node-version: 22
cache: "pnpm"
- run: pnpm install --frozen-lockfile --prefer-offline
- name: Cache Playwright Browsers
id: playwright-cache
uses: actions/cache@v5
with:
path: ~/.cache/ms-playwright
key: playwright-browsers-${{ runner.os }}-${{ hashFiles('pnpm-lock.yaml') }}
- name: Install Playwright Browsers
if: steps.playwright-cache.outputs.cache-hit != 'true'
run: pnpm exec playwright install --with-deps chromium
- name: Run Playwright tests
run: pnpm exec playwright test --project=functional --project=mobile-functional
- name: Upload functional report
uses: actions/upload-artifact@v6
with:
name: functional-report
path: playwright-report/
retention-days: 1
accessibility:
name: Accessibility Tests (${{ matrix.theme }} - ${{ matrix.locale }})
runs-on: ubuntu-latest
timeout-minutes: 30
needs: build
strategy:
matrix:
theme: [light, dark]
locale: [en, es]
fail-fast: false
steps:
- uses: actions/checkout@v6
- name: Download build artifact
uses: actions/download-artifact@v7
with:
name: dist-build
path: dist
- uses: pnpm/action-setup@v6
- uses: actions/setup-node@v6
with:
node-version: 22
cache: "pnpm"
- run: pnpm install --frozen-lockfile --prefer-offline
- name: Cache Playwright Browsers
id: playwright-cache
uses: actions/cache@v5
with:
path: ~/.cache/ms-playwright
key: playwright-browsers-${{ runner.os }}-${{ hashFiles('pnpm-lock.yaml') }}
- name: Install Playwright Browsers
if: steps.playwright-cache.outputs.cache-hit != 'true'
run: pnpm exec playwright install --with-deps chromium
- name: Run Axe accessibility tests
run: THEME=${{ matrix.theme }} LOCALE_FILTER=${{ matrix.locale }} pnpm exec playwright test --project=accessibility
- name: Upload Axe Results
if: always()
uses: actions/upload-artifact@v6
with:
name: axe-accessibility-report-${{ matrix.theme }}-${{ matrix.locale }}
path: accessibility-report/
retention-days: 7
lighthouse-audit:
name: LH Audit (${{ matrix.theme }} - ${{ matrix.formFactor }} - ${{ matrix.locale }})
runs-on: ubuntu-latest
timeout-minutes: 45
needs: build
strategy:
matrix:
theme: [light, dark]
formFactor: [mobile, desktop]
locale: [en, es]
fail-fast: false
steps:
- uses: actions/checkout@v6
- name: Download build artifact
uses: actions/download-artifact@v7
with:
name: dist-build
path: dist
- uses: pnpm/action-setup@v6
- uses: actions/setup-node@v6
with:
node-version: 22
cache: "pnpm"
- run: pnpm install --frozen-lockfile --prefer-offline
- name: Run Lighthouse CI
shell: bash
env:
FORM_FACTOR: ${{ matrix.formFactor }}
LOCALE_FILTER: ${{ matrix.locale }}
LHCI_GITHUB_TOKEN: ${{ secrets.TOKEN_GITHUB }}
run: |
if [ "${{ matrix.theme }}" = "dark" ]; then export LHCI_CHROME_FLAGS="--force-dark-mode --force-prefers-color-scheme=dark"; fi
pnpm exec lhci autorun
- name: Upload Lighthouse Results
if: always()
uses: actions/upload-artifact@v6
with:
name: lh-results-${{ matrix.theme }}-${{ matrix.formFactor }}-${{ matrix.locale }}
path: lighthouse-results/
retention-days: 1
# --- Stage: Report Aggregation ---
static-analysis-report:
name: Static Analysis Report
runs-on: ubuntu-latest
timeout-minutes: 5
if: always()
needs:
[
sa-astro,
sa-prettier,
sa-eslint,
sa-audit,
sa-jsdoc,
sa-lychee,
sa-cspell,
sa-sonar,
sa-stylelint,
ci-setup,
]
permissions:
contents: read
pull-requests: write
steps:
- uses: actions/checkout@v6
- name: Update CI Comment (SA Results)
if: github.event_name == 'pull_request'
uses: actions/github-script@v9
env:
OUTCOME_ASTRO: ${{ needs.sa-astro.result }}
OUTCOME_PRETTIER: ${{ needs.sa-prettier.result }}
OUTCOME_ESLINT: ${{ needs.sa-eslint.result }}
OUTCOME_LYCHEE: ${{ needs.sa-lychee.result }}
OUTCOME_CSPELL: ${{ needs.sa-cspell.result }}
OUTCOME_STYLELINT: ${{ needs.sa-stylelint.result }}
OUTCOME_SECURITY: ${{ needs.sa-audit.result }}
OUTCOME_SONAR: ${{ needs.sa-sonar.result }}
OUTCOME_JSDOC: ${{ needs.sa-jsdoc.result }}
JSDOC_COVERAGE: ${{ needs.sa-jsdoc.outputs.coverage }}
with:
script: |
const { default: script } = await import(`file://${process.env.GITHUB_WORKSPACE}/scripts/ci/update-ci-comment.mjs`)
await script({github, context, step: 'sa'})
accessibility-report:
name: Accessibility Report
runs-on: ubuntu-latest
timeout-minutes: 5
needs: accessibility
if: always()
steps:
- uses: actions/checkout@v6
- uses: pnpm/action-setup@v6
- uses: actions/setup-node@v6
with:
node-version: 22
cache: "pnpm"
- run: pnpm install --frozen-lockfile --prefer-offline
# Download each locale separately to avoid summary JSON overwrites
- name: Download Axe Results (Light EN)
uses: actions/download-artifact@v7
continue-on-error: true
with:
name: axe-accessibility-report-light-en
path: a11y-deploy/light/en
- name: Download Axe Results (Light ES)
uses: actions/download-artifact@v7
continue-on-error: true
with:
name: axe-accessibility-report-light-es
path: a11y-deploy/light/es
- name: Download Axe Results (Dark EN)
uses: actions/download-artifact@v7
continue-on-error: true
with:
name: axe-accessibility-report-dark-en
path: a11y-deploy/dark/en
- name: Download Axe Results (Dark ES)
uses: actions/download-artifact@v7
continue-on-error: true
with:
name: axe-accessibility-report-dark-es
path: a11y-deploy/dark/es
- name: Merge locale results per theme
run: |
for theme in light dark; do
# Move HTML reports from locale subdirs to theme dir
for locale in en es; do
src="a11y-deploy/${theme}/${locale}"
if [ -d "$src" ]; then
# Move HTML reports (skip summary JSONs and index.html)
find "$src" -maxdepth 1 -name '*.html' ! -name 'index.html' \
-exec mv {} "a11y-deploy/${theme}/" \;
fi
done
# Merge summary JSONs per theme using Node.js
node -e "
const fs = require('fs');
const path = require('path');
const theme = '${theme}';
const base = 'a11y-deploy/${theme}';
const summaries = [];
for (const locale of ['en', 'es']) {
const f = path.join(base, locale, 'accessibility-summary-' + theme + '.json');
if (fs.existsSync(f)) summaries.push(JSON.parse(fs.readFileSync(f, 'utf-8')));
}
if (summaries.length === 0) { console.log('No summaries for ' + theme); process.exit(0); }
// Merge pages arrays
const pages = summaries.flatMap(s => s.pages || []);
// Merge violations/incomplete by rule id, summing nodes
function mergeRules(arrays) {
const map = new Map();
for (const arr of arrays) {
for (const item of (arr || [])) {
const existing = map.get(item.id);
if (existing) { existing.nodes += item.nodes || 0; }
else { map.set(item.id, { ...item }); }
}
}
return [...map.values()];
}
const merged = {
theme,
totalPages: pages.length,
passed: pages.filter(p => p.violations === 0).length,
failed: pages.filter(p => p.violations > 0).length,
incomplete: pages.filter(p => p.incomplete > 0).length,
violations: mergeRules(summaries.map(s => s.violations)),
incompleteList: mergeRules(summaries.map(s => s.incompleteList)),
pages,
};
fs.writeFileSync('a11y-deploy/accessibility-summary-' + theme + '.json', JSON.stringify(merged, null, 2));
console.log('Merged ' + theme + ' summary: ' + merged.totalPages + ' pages');
"
# Cleanup locale subdirs
rm -rf "a11y-deploy/${theme}/en" "a11y-deploy/${theme}/es"
done
- name: Generate Dashboard
run: node scripts/ci/generate-accessibility-index.mjs a11y-deploy
- name: Upload consolidated assets
uses: actions/upload-artifact@v6
with:
name: a11y-dashboard
path: a11y-deploy/
retention-days: 1
lighthouse-report:
name: Lighthouse Report
runs-on: ubuntu-latest
timeout-minutes: 5
needs: lighthouse-audit
if: always()
steps:
- uses: actions/checkout@v6
- uses: pnpm/action-setup@v6
- uses: actions/setup-node@v6
with:
node-version: 22
cache: "pnpm"
- run: pnpm install --frozen-lockfile --prefer-offline
- name: Download LH Results (Light - Mobile - EN)
uses: actions/download-artifact@v7
continue-on-error: true
with:
name: lh-results-light-mobile-en
path: lh-deploy/light/mobile/en
- name: Download LH Results (Light - Mobile - ES)
uses: actions/download-artifact@v7
continue-on-error: true
with:
name: lh-results-light-mobile-es
path: lh-deploy/light/mobile/es
- name: Download LH Results (Light - Desktop - EN)
uses: actions/download-artifact@v7
continue-on-error: true
with:
name: lh-results-light-desktop-en
path: lh-deploy/light/desktop/en
- name: Download LH Results (Light - Desktop - ES)
uses: actions/download-artifact@v7
continue-on-error: true
with:
name: lh-results-light-desktop-es
path: lh-deploy/light/desktop/es
- name: Download LH Results (Dark - Mobile - EN)
uses: actions/download-artifact@v7
continue-on-error: true
with:
name: lh-results-dark-mobile-en
path: lh-deploy/dark/mobile/en
- name: Download LH Results (Dark - Mobile - ES)
uses: actions/download-artifact@v7
continue-on-error: true
with:
name: lh-results-dark-mobile-es
path: lh-deploy/dark/mobile/es
- name: Download LH Results (Dark - Desktop - EN)
uses: actions/download-artifact@v7
continue-on-error: true
with:
name: lh-results-dark-desktop-en
path: lh-deploy/dark/desktop/en
- name: Download LH Results (Dark - Desktop - ES)
uses: actions/download-artifact@v7
continue-on-error: true
with:
name: lh-results-dark-desktop-es
path: lh-deploy/dark/desktop/es
- name: Merge Lighthouse manifests per combo
run: |
for dir in lh-deploy/light/mobile lh-deploy/light/desktop lh-deploy/dark/mobile lh-deploy/dark/desktop; do
merged="[]"
for locale_dir in "$dir"/en "$dir"/es; do
if [ -f "$locale_dir/manifest.json" ]; then
merged=$(node -e "
const a = JSON.parse(process.argv[1]);
const b = JSON.parse(require('fs').readFileSync('$locale_dir/manifest.json','utf-8'));
console.log(JSON.stringify([...a, ...b]));
" "$merged")
fi
done
echo "$merged" > "$dir/manifest.json"
echo "Merged manifest for $dir ($(node -e "console.log(JSON.parse(process.argv[1]).length)" "$merged") entries)"
done
- name: Generate Dashboard
run: node scripts/ci/generate-lighthouse-index.mjs lh-deploy
- name: Upload LH Dashboard
uses: actions/upload-artifact@v6
with:
name: lh-dashboard
path: lh-deploy/
retention-days: 1
deploy-reports:
name: "🚀 Deploy CI Dashboard"
runs-on: ubuntu-latest
timeout-minutes: 10
needs:
[
ci-setup,
sa-astro,
sa-prettier,
sa-eslint,
sa-audit,
sa-jsdoc,
sa-lychee,
sa-cspell,
sa-sonar,
sa-stylelint,
accessibility-report,
bundle-size,
html-validator,
rss-validation,
schema-validation,
image-optimization,
lighthouse-report,
functional-tests,
]
if: always() && (github.event_name == 'pull_request' || github.ref == 'refs/heads/main')
permissions:
contents: read
pull-requests: write
steps:
- uses: actions/checkout@v6
- name: Download Accessibility Dashboard
uses: actions/download-artifact@v7
continue-on-error: true
with:
name: a11y-dashboard
path: a11y-deploy
- name: Download Lighthouse Dashboard
uses: actions/download-artifact@v7
continue-on-error: true
with:
name: lh-dashboard
path: lh-deploy
- name: Download HTML Validation
uses: actions/download-artifact@v7
continue-on-error: true
with:
name: html-validation-report
path: .
- name: Download RSS Preview
uses: actions/download-artifact@v7
continue-on-error: true
with:
name: rss-validation-report
path: .
- name: Download Schema Report
uses: actions/download-artifact@v7
continue-on-error: true
with:
name: schema-validation-report
path: .
- name: Download Image Report
uses: actions/download-artifact@v7
continue-on-error: true
with:
name: image-optimization-report
path: .
- name: Download Bundle Stats
uses: actions/download-artifact@v7
continue-on-error: true
with:
name: bundle-analysis
path: .
- name: Download Lychee Report
uses: actions/download-artifact@v7
continue-on-error: true
with:
name: lychee-report
path: .
- uses: pnpm/action-setup@v6
- uses: actions/setup-node@v6
with:
node-version: 22
cache: "pnpm"
- name: Install dependencies
run: pnpm install --frozen-lockfile --prefer-offline
- name: Download Logs
uses: actions/download-artifact@v7
continue-on-error: true
with:
pattern: log-*
path: logs
merge-multiple: true
- name: Build Dashboard
run: node scripts/ci/build-report-dashboard.mjs
env:
GITHUB_RUN_NUMBER: ${{ github.run_number }}
GITHUB_RUN_ID: ${{ github.run_id }}
GITHUB_REPOSITORY: ${{ github.repository }}
OUTCOME_ASTRO: ${{ needs.sa-astro.result }}
OUTCOME_PRETTIER: ${{ needs.sa-prettier.result }}
OUTCOME_ESLINT: ${{ needs.sa-eslint.result }}
OUTCOME_LYCHEE: ${{ needs.sa-lychee.result }}
OUTCOME_CSPELL: ${{ needs.sa-cspell.result }}
OUTCOME_SECURITY: ${{ needs.sa-audit.result }}
OUTCOME_SONAR: ${{ needs.sa-sonar.result }}
OUTCOME_JSDOC: ${{ needs.sa-jsdoc.result }}
OUTCOME_STYLELINT: ${{ needs.sa-stylelint.result }}
OUTCOME_A11Y: ${{ needs.accessibility-report.result }}
OUTCOME_HTML: ${{ needs.html-validator.result }}
OUTCOME_BUNDLE: ${{ needs.bundle-size.result }}
OUTCOME_RSS: ${{ needs.rss-validation.result }}
OUTCOME_SCHEMA: ${{ needs.schema-validation.result }}
OUTCOME_IMAGE: ${{ needs.image-optimization.result }}
OUTCOME_FUNCTIONAL: ${{ needs.functional-tests.result }}
- name: Deploy to Vercel
id: deploy
env:
VERCEL_TOKEN: ${{ secrets.VERCEL_TOKEN }}
run: |
# Deploy to production domain if on main branch, otherwise preview
IS_PROD=${{ github.ref == 'refs/heads/main' && '--prod' || '' }}
# NOTE: To view PR reports without Vercel authentication, you must disable
# 'Deployment Protection' for this project in your Vercel Dashboard.
PR_ID=${{ github.event.pull_request.number || '0' }}
DEPLOY_URL=$(npx vercel deploy ./dist-reports --archive=tgz --name jmrp-ci-reports --token="$VERCEL_TOKEN" --yes --public $IS_PROD --meta prid=$PR_ID)
echo "url=$DEPLOY_URL" >> $GITHUB_OUTPUT
- name: Update CI Comment (Final Results)
uses: actions/github-script@v9
env:
VERCEL_URL: ${{ steps.deploy.outputs.url }}
OUTCOME_ASTRO: ${{ needs.sa-astro.result }}
OUTCOME_PRETTIER: ${{ needs.sa-prettier.result }}
OUTCOME_ESLINT: ${{ needs.sa-eslint.result }}
OUTCOME_LYCHEE: ${{ needs.sa-lychee.result }}
OUTCOME_CSPELL: ${{ needs.sa-cspell.result }}
OUTCOME_SECURITY: ${{ needs.sa-audit.result }}
OUTCOME_SONAR: ${{ needs.sa-sonar.result }}
OUTCOME_JSDOC: ${{ needs.sa-jsdoc.result }}
OUTCOME_STYLELINT: ${{ needs.sa-stylelint.result }}
JSDOC_COVERAGE: ${{ needs.sa-jsdoc.outputs.coverage }}
OUTCOME_A11Y: ${{ needs.accessibility-report.result }}
OUTCOME_HTML: ${{ needs.html-validator.result }}
OUTCOME_BUNDLE: ${{ needs.bundle-size.result }}
OUTCOME_RSS: ${{ needs.rss-validation.result }}
OUTCOME_SCHEMA: ${{ needs.schema-validation.result }}
OUTCOME_IMAGE: ${{ needs.image-optimization.result }}
OUTCOME_FUNCTIONAL: ${{ needs.functional-tests.result }}
with:
script: |
const { default: script } = await import(`file://${process.env.GITHUB_WORKSPACE}/scripts/ci/update-ci-comment.mjs`)
await script({github, context, step: 'final'})