Gitleaks needs explicit contents:read and pull-requests:read permissions to scan dependabot PR commits. Without them, the GitHub API returns 403. Ref: https://github.com/gitleaks/gitleaks/issues/1343#issuecomment-2690243151 Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
248 lines
7.5 KiB
YAML
248 lines
7.5 KiB
YAML
name: CI
|
|
|
|
on:
|
|
push:
|
|
branches: [main]
|
|
pull_request:
|
|
branches: [main]
|
|
|
|
concurrency:
|
|
group: ${{ github.workflow }}-${{ github.ref }}
|
|
cancel-in-progress: true
|
|
|
|
jobs:
|
|
security:
|
|
name: Security Scan
|
|
runs-on: ubuntu-latest
|
|
permissions:
|
|
contents: read
|
|
pull-requests: read
|
|
steps:
|
|
- uses: actions/checkout@v6
|
|
with:
|
|
fetch-depth: 0
|
|
- name: Gitleaks
|
|
uses: gitleaks/gitleaks-action@v2
|
|
env:
|
|
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
|
GITLEAKS_LICENSE: ${{ secrets.GITLEAKS_LICENSE }}
|
|
- uses: pnpm/action-setup@v4
|
|
- uses: actions/setup-node@v6
|
|
with:
|
|
node-version: 24
|
|
cache: pnpm
|
|
- run: pnpm install --frozen-lockfile
|
|
- name: Dependency audit
|
|
run: pnpm audit --audit-level=high
|
|
continue-on-error: true
|
|
|
|
typecheck:
|
|
name: Typecheck
|
|
runs-on: ubuntu-latest
|
|
steps:
|
|
- uses: actions/checkout@v6
|
|
- uses: pnpm/action-setup@v4
|
|
- uses: actions/setup-node@v6
|
|
with:
|
|
node-version: 24
|
|
cache: pnpm
|
|
- run: pnpm install --frozen-lockfile
|
|
- run: pnpm typecheck
|
|
|
|
lint:
|
|
name: Lint
|
|
runs-on: ubuntu-latest
|
|
steps:
|
|
- uses: actions/checkout@v6
|
|
- uses: pnpm/action-setup@v4
|
|
- uses: actions/setup-node@v6
|
|
with:
|
|
node-version: 24
|
|
cache: pnpm
|
|
- run: pnpm install --frozen-lockfile
|
|
- run: pnpm lint
|
|
|
|
test:
|
|
name: Unit Tests
|
|
runs-on: ubuntu-latest
|
|
steps:
|
|
- uses: actions/checkout@v6
|
|
- uses: pnpm/action-setup@v4
|
|
- uses: actions/setup-node@v6
|
|
with:
|
|
node-version: 24
|
|
cache: pnpm
|
|
- run: pnpm install --frozen-lockfile
|
|
- run: pnpm test
|
|
|
|
build:
|
|
name: Build
|
|
runs-on: ubuntu-latest
|
|
steps:
|
|
- uses: actions/checkout@v6
|
|
- uses: pnpm/action-setup@v4
|
|
- uses: actions/setup-node@v6
|
|
with:
|
|
node-version: 24
|
|
cache: pnpm
|
|
- run: pnpm install --frozen-lockfile
|
|
- run: pnpm build
|
|
|
|
e2e:
|
|
name: E2E Tests
|
|
needs: build
|
|
runs-on: ubuntu-latest
|
|
env:
|
|
DATABASE_URL: postgres://trails:trails@localhost:5432/trails
|
|
steps:
|
|
- uses: actions/checkout@v6
|
|
- uses: pnpm/action-setup@v4
|
|
- uses: actions/setup-node@v6
|
|
with:
|
|
node-version: 24
|
|
cache: pnpm
|
|
- run: pnpm install --frozen-lockfile
|
|
|
|
- name: Cache PostGIS Docker image
|
|
id: postgis-cache
|
|
uses: actions/cache@v4
|
|
with:
|
|
path: /tmp/postgis-image.tar
|
|
key: postgis-16-3.4
|
|
|
|
- name: Load or pull PostGIS image
|
|
run: |
|
|
if [ -f /tmp/postgis-image.tar ]; then
|
|
docker load < /tmp/postgis-image.tar
|
|
else
|
|
docker pull postgis/postgis:16-3.4
|
|
docker save postgis/postgis:16-3.4 > /tmp/postgis-image.tar
|
|
fi
|
|
|
|
- name: Start PostgreSQL
|
|
run: |
|
|
docker run -d --name postgres \
|
|
-e POSTGRES_USER=trails \
|
|
-e POSTGRES_PASSWORD=trails \
|
|
-e POSTGRES_DB=trails \
|
|
-p 5432:5432 \
|
|
postgis/postgis:16-3.4
|
|
# Wait for pg_isready
|
|
for i in $(seq 1 30); do
|
|
docker exec postgres pg_isready -U trails > /dev/null 2>&1 && break
|
|
sleep 1
|
|
done
|
|
# Wait for PostGIS extension to be ready
|
|
for i in $(seq 1 10); do
|
|
docker exec postgres psql -U trails -c "SELECT PostGIS_Version();" > /dev/null 2>&1 && break
|
|
sleep 1
|
|
done
|
|
|
|
- name: Push database schema
|
|
run: pnpm db:push
|
|
|
|
- name: Build and cache BRouter
|
|
id: brouter-cache
|
|
uses: actions/cache@v4
|
|
with:
|
|
path: /tmp/brouter
|
|
key: brouter-1.7.8
|
|
|
|
- name: Download BRouter
|
|
if: steps.brouter-cache.outputs.cache-hit != 'true'
|
|
run: |
|
|
mkdir -p /tmp/brouter
|
|
wget -q "https://github.com/abrensch/brouter/releases/download/v1.7.8/brouter-1.7.8.zip" -O /tmp/brouter/brouter.zip
|
|
cd /tmp/brouter && unzip -o brouter.zip && mv brouter-1.7.8/* . && rmdir brouter-1.7.8 && rm brouter.zip
|
|
|
|
- name: Cache BRouter segment
|
|
id: segment-cache
|
|
uses: actions/cache@v4
|
|
with:
|
|
path: /tmp/brouter-segments
|
|
key: brouter-segment-E10_N50
|
|
|
|
- name: Download Berlin segment
|
|
if: steps.segment-cache.outputs.cache-hit != 'true'
|
|
run: |
|
|
mkdir -p /tmp/brouter-segments
|
|
wget -q "https://brouter.de/brouter/segments4/E10_N50.rd5" -O /tmp/brouter-segments/E10_N50.rd5
|
|
|
|
- name: Start BRouter
|
|
run: |
|
|
cd /tmp/brouter
|
|
java -Xmx256M -Xms64M \
|
|
-DmaxRunningTime=300 \
|
|
-cp brouter-1.7.8-all.jar \
|
|
btools.server.RouteServer \
|
|
/tmp/brouter-segments profiles2 profiles2 \
|
|
17777 2 &
|
|
# Wait for BRouter to start
|
|
for i in $(seq 1 30); do
|
|
curl -sf http://localhost:17777/brouter?lonlats=13.4,52.5\|13.5,52.5\&profile=trekking\&format=geojson > /dev/null 2>&1 && break
|
|
sleep 2
|
|
done
|
|
env:
|
|
BROUTER_URL: http://localhost:17777
|
|
|
|
- name: Cache Playwright browsers
|
|
id: playwright-cache
|
|
uses: actions/cache@v4
|
|
with:
|
|
path: ~/.cache/ms-playwright
|
|
key: playwright-${{ hashFiles('pnpm-lock.yaml') }}
|
|
|
|
- name: Install Playwright
|
|
if: steps.playwright-cache.outputs.cache-hit != 'true'
|
|
run: pnpm exec playwright install --with-deps chromium
|
|
|
|
- name: Install Playwright deps only
|
|
if: steps.playwright-cache.outputs.cache-hit == 'true'
|
|
run: pnpm exec playwright install-deps chromium
|
|
|
|
- name: Build for production
|
|
run: pnpm build
|
|
env:
|
|
VITE_SENTRY_ENVIRONMENT: ci
|
|
|
|
- name: Run E2E tests
|
|
run: pnpm test:e2e
|
|
env:
|
|
BROUTER_URL: http://localhost:17777
|
|
|
|
- name: Playwright job summary
|
|
if: ${{ !cancelled() }}
|
|
run: |
|
|
if [ -f playwright-results.json ]; then
|
|
node -e "
|
|
const r = require('./playwright-results.json');
|
|
const s = r.stats;
|
|
const dur = (s.duration / 1000).toFixed(1);
|
|
let md = '## Playwright E2E Results\n\n';
|
|
md += '| Status | Count |\n|--------|-------|\n';
|
|
md += '| :white_check_mark: Passed | ' + s.expected + ' |\n';
|
|
if (s.unexpected > 0) md += '| :x: Failed | ' + s.unexpected + ' |\n';
|
|
if (s.flaky > 0) md += '| :warning: Flaky | ' + s.flaky + ' |\n';
|
|
if (s.skipped > 0) md += '| :fast_forward: Skipped | ' + s.skipped + ' |\n';
|
|
md += '| :stopwatch: Duration | ' + dur + 's |\n\n';
|
|
for (const file of r.suites) {
|
|
for (const describe of (file.suites || [])) {
|
|
md += '### ' + describe.title + '\n\n';
|
|
for (const spec of (describe.specs || [])) {
|
|
const icon = spec.ok ? ':white_check_mark:' : ':x:';
|
|
const t = spec.tests?.[0]?.results?.[0]?.duration;
|
|
md += '- ' + icon + ' ' + spec.title + (t ? ' (' + t + 'ms)' : '') + '\n';
|
|
}
|
|
md += '\n';
|
|
}
|
|
}
|
|
require('fs').appendFileSync(process.env.GITHUB_STEP_SUMMARY, md);
|
|
"
|
|
fi
|
|
|
|
- uses: actions/upload-artifact@v7
|
|
if: ${{ !cancelled() }}
|
|
with:
|
|
name: playwright-report
|
|
path: playwright-report/
|
|
retention-days: 30
|