Merge pull request #1178 from yosemite01/snyk-fix-559836ce68e4fdcb4b1… #598
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: CI | |
| on: | |
| push: | |
| branches: [ main, dev ] | |
| pull_request: | |
| branches: [ main, dev ] | |
| jobs: | |
| frontend: | |
| runs-on: ubuntu-latest | |
| steps: | |
| - uses: actions/checkout@v6 | |
| - uses: pnpm/action-setup@v6 | |
| with: | |
| version: 8 | |
| - uses: actions/setup-node@v6 | |
| with: | |
| node-version: '20' | |
| cache: 'pnpm' | |
| - name: Install dependencies | |
| run: pnpm install | |
| - name: Lint | |
| run: pnpm run lint | |
| - name: Build | |
| run: pnpm run build | |
| - name: Upload build artifact | |
| uses: actions/upload-artifact@v4 | |
| with: | |
| name: next-build-${{ github.sha }} | |
| path: .next/ | |
| if-no-files-found: error | |
| migration-health: | |
| runs-on: ubuntu-latest | |
| steps: | |
| - uses: actions/checkout@v6 | |
| - name: Check no duplicate migration prefixes | |
| run: | | |
| cd backend/migrations | |
| duplicates=$(ls *.sql 2>/dev/null | sed 's/_.*//' | sort | uniq -d) | |
| if [ -n "$duplicates" ]; then | |
| echo "Duplicate migration prefixes found: $duplicates" | |
| exit 1 | |
| fi | |
| echo "Migration prefixes are unique" | |
| backend: | |
| runs-on: ubuntu-latest | |
| steps: | |
| - uses: actions/checkout@v6 | |
| - name: Install Rust | |
| uses: dtolnay/rust-toolchain@stable | |
| with: | |
| components: clippy | |
| - name: Rust Cache | |
| uses: Swatinem/rust-cache@v2 | |
| with: | |
| workspaces: backend | |
| - name: Clippy | |
| run: cd backend && cargo clippy --workspace --all-targets --all-features -- -D warnings | |
| - name: Test | |
| run: cd backend && cargo test | |
| contracts: | |
| runs-on: ubuntu-latest | |
| steps: | |
| - uses: actions/checkout@v6 | |
| - name: Install Rust & Soroban | |
| uses: dtolnay/rust-toolchain@stable | |
| - name: Test Contracts | |
| run: cd backend && cargo test --all-features | |
| reproducible-build: | |
| name: Verify Reproducible Contract Hashes | |
| runs-on: ubuntu-latest | |
| steps: | |
| - uses: actions/checkout@v6 | |
| - name: Build & verify hashes | |
| run: ./scripts/verify.sh | |
| - name: Upload WASM artifacts | |
| uses: actions/upload-artifact@v7 | |
| with: | |
| name: soroban-contracts-${{ github.sha }} | |
| path: artifacts/ | |
| if-no-files-found: error | |
| lighthouse: | |
| name: Lighthouse CI | |
| runs-on: ubuntu-latest | |
| needs: frontend | |
| steps: | |
| - uses: actions/checkout@v6 | |
| - uses: pnpm/action-setup@v6 | |
| with: | |
| version: 8 | |
| - uses: actions/setup-node@v6 | |
| with: | |
| node-version: '20' | |
| cache: 'pnpm' | |
| - name: Install dependencies | |
| run: pnpm install | |
| - name: Download build artifact | |
| uses: actions/download-artifact@v4 | |
| with: | |
| name: next-build-${{ github.sha }} | |
| path: .next/ | |
| - name: Run Lighthouse CI | |
| run: | | |
| pnpm exec lhci autorun | |
| env: | |
| LHCI_GITHUB_APP_TOKEN: ${{ secrets.LHCI_GITHUB_APP_TOKEN }} | |
| - name: Comment PR with Lighthouse scores | |
| if: github.event_name == 'pull_request' | |
| uses: actions/github-script@v7 | |
| with: | |
| script: | | |
| const fs = require('fs'); | |
| const resultsPath = '.lighthouseci'; | |
| if (!fs.existsSync(resultsPath)) return; | |
| const files = fs.readdirSync(resultsPath).filter(f => f.endsWith('.json') && f.startsWith('lhr-')); | |
| if (files.length === 0) return; | |
| let body = '## Lighthouse CI Results\n\n| URL | Performance | Accessibility | Best Practices | SEO |\n|-----|------------|---------------|----------------|-----|\n'; | |
| for (const file of files) { | |
| const report = JSON.parse(fs.readFileSync(`${resultsPath}/${file}`, 'utf8')); | |
| const url = new URL(report.requestedUrl).pathname || '/'; | |
| const perf = Math.round(report.categories.performance.score * 100); | |
| const a11y = Math.round(report.categories.accessibility.score * 100); | |
| const bp = Math.round(report.categories['best-practices'].score * 100); | |
| const seo = Math.round(report.categories.seo.score * 100); | |
| const perfIcon = perf >= 90 ? '🟢' : perf >= 50 ? '🟠' : '🔴'; | |
| const a11yIcon = a11y >= 90 ? '🟢' : a11y >= 50 ? '🟠' : '🔴'; | |
| body += `| \`${url}\` | ${perfIcon} ${perf} | ${a11yIcon} ${a11y} | ${bp} | ${seo} |\n`; | |
| } | |
| body += '\n_Scores from Lighthouse CI — [detailed report](https://storage.googleapis.com/lighthouse-infrastructure.appspot.com/reports)_'; | |
| await github.rest.issues.createComment({ | |
| owner: context.repo.owner, | |
| repo: context.repo.repo, | |
| issue_number: context.issue.number, | |
| body, | |
| }); | |
| playwright: | |
| name: Playwright E2E Tests | |
| runs-on: ubuntu-latest | |
| needs: frontend | |
| steps: | |
| - uses: actions/checkout@v6 | |
| - uses: pnpm/action-setup@v6 | |
| with: | |
| version: 8 | |
| - uses: actions/setup-node@v6 | |
| with: | |
| node-version: '20' | |
| cache: 'pnpm' | |
| - name: Install dependencies | |
| run: pnpm install | |
| - name: Install Playwright browsers | |
| run: pnpm exec playwright install --with-deps | |
| - name: Run Playwright tests | |
| run: pnpm run test:e2e | |
| - name: Upload Playwright report | |
| if: always() | |
| uses: actions/upload-artifact@v7 | |
| with: | |
| name: playwright-report-${{ github.sha }} | |
| path: playwright-report/ | |
| if-no-files-found: ignore | |
| docker-app: | |
| name: Build Production Docker Image | |
| runs-on: ubuntu-latest | |
| steps: | |
| - uses: actions/checkout@v6 | |
| - name: Build Docker image | |
| run: docker build -f Dockerfile.app -t stellar-creator-portfolio:${{ github.sha }} . | |
| - name: Verify image size under 300MB | |
| run: | | |
| SIZE=$(docker image inspect stellar-creator-portfolio:${{ github.sha }} --format='{{.Size}}') | |
| echo "Image size: $SIZE bytes" | |
| if [ "$SIZE" -gt 314572800 ]; then | |
| echo "Image exceeds 300MB limit" | |
| exit 1 | |
| fi | |
| - name: Verify container starts and responds | |
| run: | | |
| docker run -d \ | |
| --name smoke-test \ | |
| -p 3000:3000 \ | |
| -e NODE_ENV=production \ | |
| stellar-creator-portfolio:${{ github.sha }} | |
| echo "Waiting for container to become ready..." | |
| for i in $(seq 1 30); do | |
| if curl -sf http://localhost:3000/api/health > /dev/null 2>&1; then | |
| echo "Container is healthy after ${i}s" | |
| break | |
| fi | |
| if [ "$i" -eq 30 ]; then | |
| echo "Container did not become ready within 30 seconds" | |
| docker logs smoke-test | |
| docker rm -f smoke-test | |
| exit 1 | |
| fi | |
| sleep 1 | |
| done | |
| docker rm -f smoke-test |