Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
166 changes: 166 additions & 0 deletions .github/workflows/quality-gate.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,166 @@
name: Quality Gate

on:
pull_request:
branches: [main]
push:
branches: [main]
workflow_dispatch:

permissions:
contents: read

concurrency:
group: quality-gate-${{ github.event.pull_request.number || github.ref }}
cancel-in-progress: true

jobs:
changes:
name: Detect changed areas
runs-on: ubuntu-latest
outputs:
frontend: ${{ steps.changed.outputs.frontend }}
backend: ${{ steps.changed.outputs.backend }}
steps:
- name: Checkout
uses: actions/checkout@v4
with:
fetch-depth: 0

- name: Detect frontend and backend changes
id: changed
env:
EVENT_NAME: ${{ github.event_name }}
PR_BASE_SHA: ${{ github.event.pull_request.base.sha }}
PR_HEAD_SHA: ${{ github.event.pull_request.head.sha }}
PUSH_BASE_SHA: ${{ github.event.before }}
PUSH_HEAD_SHA: ${{ github.sha }}
run: |
set -euo pipefail

if [ "$EVENT_NAME" = "pull_request" ]; then
base_sha="$PR_BASE_SHA"
head_sha="$PR_HEAD_SHA"
elif [ "$EVENT_NAME" = "push" ] && [ -n "$PUSH_BASE_SHA" ]; then
base_sha="$PUSH_BASE_SHA"
head_sha="$PUSH_HEAD_SHA"
else
base_sha=""
head_sha=""
fi

if [ -n "$base_sha" ] && [ "$base_sha" != "0000000000000000000000000000000000000000" ]; then
changed_files=$(git diff --name-only "$base_sha" "$head_sha")
else
changed_files=$(git ls-files)
fi

frontend=false
backend=false

if printf '%s\n' "$changed_files" | grep -Eq '^frontend/'; then
frontend=true
fi
if printf '%s\n' "$changed_files" | grep -Eq '^backend/'; then
backend=true
fi

{
echo "frontend=$frontend"
echo "backend=$backend"
} >> "$GITHUB_OUTPUT"

echo "frontend=$frontend backend=$backend"

frontend:
name: Frontend checks
needs: changes
if: needs.changes.outputs.frontend == 'true'
runs-on: ubuntu-latest
timeout-minutes: 15
env:
CI: 'true'
defaults:
run:
working-directory: frontend
steps:
- name: Checkout
uses: actions/checkout@v4

- name: Setup Node.js
uses: actions/setup-node@v4
with:
node-version: 24
cache: npm
cache-dependency-path: frontend/package-lock.json

- name: Install dependencies
run: npm ci

- name: Check formatting
run: npm run format:check

- name: Lint
run: npm run lint

- name: Typecheck
run: npm run typecheck

- name: Test
run: npm run test

- name: Build
run: npm run build

backend:
name: Backend checks
needs: changes
if: needs.changes.outputs.backend == 'true'
runs-on: ubuntu-latest
timeout-minutes: 15
defaults:
run:
working-directory: backend
steps:
- name: Checkout
uses: actions/checkout@v4

- name: Install uv
uses: astral-sh/setup-uv@v8.3.2
with:
enable-cache: true

- name: Setup Python
run: uv python install 3.12

- name: Install dependencies
run: uv sync --frozen

- name: Ruff
run: uv run ruff check .

- name: Import-linter
run: uv run lint-imports

- name: Pytest
run: uv run pytest -q

quality-gate:
name: Quality gate
if: always()
needs: [changes, frontend, backend]
runs-on: ubuntu-latest
steps:
- name: Require all relevant checks
env:
CHANGES_RESULT: ${{ needs.changes.result }}
FRONTEND_RESULT: ${{ needs.frontend.result }}
BACKEND_RESULT: ${{ needs.backend.result }}
run: |
set -euo pipefail
for result in "$CHANGES_RESULT" "$FRONTEND_RESULT" "$BACKEND_RESULT"; do
case "$result" in
success|skipped) ;;
*) echo "::error::A required CI job ended with result: $result"; exit 1 ;;
esac
done
14 changes: 14 additions & 0 deletions vercel.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
{
"$schema": "https://openapi.vercel.sh/vercel.json",
"framework": "vite",
"installCommand": "npm --prefix frontend ci",
"buildCommand": "npm --prefix frontend run build",
"outputDirectory": "frontend/dist",
"ignoreCommand": "test ! -d frontend || git diff --quiet HEAD^ HEAD -- frontend/ vercel.json",
"rewrites": [
{
"source": "/(.*)",
"destination": "/index.html"
}
]
}
Loading