penpot/.github/workflows/perf-regression.yml
Andrey Antukh 05fa07f911 Add k6 performance test suite for backend
- run.sh CLI orchestrator with per-script defaults and env-var override
- Shared penpot-client.js library (JSON RPC, cookie auth, tagged metrics)
- Scripts: lifecycle, workspace-open, workspace-edit, concurrent-edit,
  media-upload, font-upload, file-size-matrix, compare-results
- Concurrent-edit supports same-file and multi-file modes via shared teams
- CI workflow (perf-regression) comparing baseline vs PR branch
- k6 binary installed in devenv Dockerfile
2026-06-25 11:29:37 +02:00

202 lines
6.1 KiB
YAML

name: "CI: Performance Regression"
defaults:
run:
shell: bash
on:
pull_request:
paths:
- 'backend/src/**'
- 'common/src/**'
types:
- opened
- synchronize
- ready_for_review
concurrency:
group: ${{ github.workflow }}-${{ github.event.pull_request.number }}
cancel-in-progress: true
jobs:
perf-regression:
if: ${{ !github.event.pull_request.draft }}
name: "Performance Regression Check"
runs-on: penpot-runner-02
container:
image: penpotapp/devenv:latest
volumes:
- /var/cache/github-runner/m2:/root/.m2
- /var/cache/github-runner/gitlib:/root/.gitlibs
services:
postgres:
image: postgres:17
env:
POSTGRES_USER: penpot
POSTGRES_PASSWORD: penpot
POSTGRES_DB: penpot
options: >-
--health-cmd pg_isready
--health-interval 10s
--health-timeout 5s
--health-retries 5
redis:
image: valkey/valkey:9
env:
PENPOT_DATABASE_URI: "postgresql://postgres/penpot"
PENPOT_DATABASE_USERNAME: penpot
PENPOT_DATABASE_PASSWORD: penpot
PENPOT_REDIS_URI: "redis://redis/1"
PENPOT_FLAGS: "enable-demo-users enable-backend-api-doc"
steps:
- name: Checkout repository
uses: actions/checkout@v6
with:
fetch-depth: 0
- name: Install k6
run: |
curl -sSL https://dl.k6.io/key.gpg | gpg --dearmor -o /usr/share/keyrings/k6-archive-keyring.gpg
echo "deb [signed-by=/usr/share/keyrings/k6-archive-keyring.gpg] https://dl.k6.io/deb stable main" | tee /etc/apt/sources.list.d/k6.list
apt-get update
apt-get install -y k6
- name: Save performance suite
run: cp -r backend/performance /tmp/performance
- name: Cache Maven dependencies
uses: actions/cache@v4
with:
path: |
~/.m2
~/.gitlibs
key: ${{ runner.os }}-m2-${{ hashFiles('backend/deps.edn', 'common/deps.edn') }}
restore-keys: |
${{ runner.os }}-m2-
# -------------------------------------------------------------------------
# Run performance tests on BASE branch (before change)
# -------------------------------------------------------------------------
- name: Checkout base branch
run: |
git fetch origin ${{ github.event.pull_request.base.ref }}
git checkout origin/${{ github.event.pull_request.base.ref }}
- name: Restore performance suite (base)
run: cp -r /tmp/performance backend/performance
- name: Start backend (base branch)
working-directory: backend
run: |
clojure -M:dev -m app.main &
# Wait for backend to be ready
for i in $(seq 1 30); do
if curl -s http://localhost:6060/api/rpc/command/get-profile > /dev/null 2>&1; then
echo "Backend ready"
break
fi
echo "Waiting for backend... ($i/30)"
sleep 2
done
- name: Run performance tests (baseline)
working-directory: backend/performance
run: |
mkdir -p results/baseline
./run.sh smoke
./run.sh lifecycle -v 5 -n 10
cp -r results/latest/* results/baseline/ 2>/dev/null || true
- name: Save baseline results
run: cp -r backend/performance/results /tmp/results-baseline
- name: Stop backend
run: |
pkill -f "app.main" || true
sleep 2
- name: Clean untracked files
run: git clean -fd
# -------------------------------------------------------------------------
# Run performance tests on PR branch (after change)
# -------------------------------------------------------------------------
- name: Checkout PR branch
run: |
git checkout ${{ github.event.pull_request.head.sha }}
- name: Restore baseline results
run: cp -r /tmp/results-baseline backend/performance/results
- name: Start backend (PR branch)
working-directory: backend
run: |
clojure -M:dev -m app.main &
# Wait for backend to be ready
for i in $(seq 1 30); do
if curl -s http://localhost:6060/api/rpc/command/get-profile > /dev/null 2>&1; then
echo "Backend ready"
break
fi
echo "Waiting for backend... ($i/30)"
sleep 2
done
- name: Run performance tests (current)
working-directory: backend/performance
run: |
mkdir -p results/current
./run.sh smoke
./run.sh lifecycle -v 5 -n 10
# Copy results
cp -r results/latest/* results/current/ 2>/dev/null || true
- name: Stop backend
run: |
pkill -f "app.main" || true
sleep 2
# -------------------------------------------------------------------------
# Compare results
# -------------------------------------------------------------------------
- name: Compare results
working-directory: backend/performance
run: |
BASELINE=$(find results/baseline -name "k6-summary.json" | head -1)
CURRENT=$(find results/current -name "k6-summary.json" | head -1)
if [ -z "$BASELINE" ] || [ -z "$CURRENT" ]; then
echo "Warning: Could not find k6 summary files"
echo "Baseline: $BASELINE"
echo "Current: $CURRENT"
exit 0
fi
echo "Comparing:"
echo " Baseline: $BASELINE"
echo " Current: $CURRENT"
echo ""
node scripts/compare-results.cjs "$BASELINE" "$CURRENT" --threshold 20
# -------------------------------------------------------------------------
# Upload artifacts
# -------------------------------------------------------------------------
- name: Upload results
if: always()
uses: actions/upload-artifact@v4
with:
name: performance-results
path: backend/performance/results/
retention-days: 30