Skip to content

Merge pull request #1178 from yosemite01/snyk-fix-559836ce68e4fdcb4b1… #598

Merge pull request #1178 from yosemite01/snyk-fix-559836ce68e4fdcb4b1…

Merge pull request #1178 from yosemite01/snyk-fix-559836ce68e4fdcb4b1… #598

Workflow file for this run

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