Skip to content

feat: promote development to main #223

feat: promote development to main

feat: promote development to main #223

Workflow file for this run

name: PR Gates
on:
pull_request:
push:
branches: [development]
concurrency:
group: pr-gates-${{ github.ref }}
cancel-in-progress: true
jobs:
changes:
name: Detect backend-impacting changes
runs-on: ubuntu-latest
outputs:
backend: ${{ steps.filter.outputs.backend }}
steps:
- name: Checkout
uses: actions/checkout@v4
- name: Filter changed paths
id: filter
uses: dorny/paths-filter@v3
with:
filters: |
backend:
- "backend/**"
- "Dockerfile"
- "docker-compose.yaml"
- "docker-compose.prod.yaml"
- "docker-compose.prod.split.yaml"
backend-lint-types:
name: Backend Lint + API Types
needs: scope
if: ${{ github.event_name != 'pull_request' || needs.scope.outputs.docs_only != 'true' }}
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v4
- name: Set up Python
uses: actions/setup-python@v5
with:
python-version: "3.14"
- name: Install uv
run: python -m pip install --upgrade pip uv
- name: Sync backend environment
working-directory: backend
run: uv sync --frozen
- name: Set up Node
uses: actions/setup-node@v4
with:
node-version: "22"
cache: npm
cache-dependency-path: frontend/package-lock.json
- name: Install frontend dependencies
run: npm --prefix frontend ci
- name: Run backend lint and API type generation
working-directory: backend
run: |
uv run ade api lint
uv run ade api types
- name: Assert generated API artifacts are committed
run: |
git diff --exit-code -- backend/src/ade_api/openapi.json frontend/src/types/generated/openapi.d.ts
backend-unit:
name: Backend Unit Tests
needs: changes
if: ${{ needs.changes.outputs.backend == 'true' }}
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v4
- name: Set up Python
uses: actions/setup-python@v5
with:
python-version: "3.14"
- name: Install uv
run: python -m pip install --upgrade pip uv
- name: Sync backend environment
working-directory: backend
run: uv sync --frozen
- name: Run backend unit suites
working-directory: backend
run: |
uv run ade api test
uv run ade worker test
backend-api-integration:
name: Backend API Integration
needs: changes
if: ${{ needs.changes.outputs.backend == 'true' }}
runs-on: ubuntu-latest
services:
postgres:
image: postgres:16
env:
POSTGRES_USER: postgres
POSTGRES_PASSWORD: postgres
POSTGRES_DB: ade_test
ports:
- 5432:5432
options: >-
--health-cmd "pg_isready -U postgres"
--health-interval 5s
--health-timeout 5s
--health-retries 20
env:
CI: "true"
ADE_TEST_DATABASE_URL: postgresql+psycopg://postgres:postgres@127.0.0.1:5432/ade_test?sslmode=disable
ADE_TEST_BLOB_CONNECTION_STRING: DefaultEndpointsProtocol=http;AccountName=devstoreaccount1;AccountKey=Eby8vdM02xNOcqFlqUwJPLlmEtlCDXJ1OUzFT50uSRZ6IFsuFq2UVErCz4I6tq/K1SZFPTOtr/KBHBeksoGMGw==;BlobEndpoint=http://127.0.0.1:10000/devstoreaccount1;
ADE_TEST_DATABASE_NAME_PREFIX: ade_api_test
steps:
- name: Checkout
uses: actions/checkout@v4
- name: Set up Python
uses: actions/setup-python@v5
with:
python-version: "3.14"
- name: Install uv
run: python -m pip install --upgrade pip uv
- name: Sync backend environment
working-directory: backend
run: uv sync --frozen
- name: Start Azurite blob emulator
run: |
docker run --detach \
--name azurite \
--publish 10000:10000 \
mcr.microsoft.com/azure-storage/azurite:3.35.0 \
azurite-blob --silent --location /data --blobHost 0.0.0.0 --blobPort 10000 --disableTelemetry --skipApiVersionCheck
if ! python - <<'PY'
import socket
import subprocess
import sys
import time
deadline = time.time() + 30
last_error = "unknown"
while time.time() < deadline:
inspect = subprocess.run(
["docker", "inspect", "-f", "{{.State.Running}}", "azurite"],
check=False,
capture_output=True,
text=True,
)
if inspect.returncode != 0:
last_error = inspect.stderr.strip() or "docker inspect failed"
time.sleep(1)
continue
if inspect.stdout.strip() != "true":
last_error = "container stopped"
time.sleep(1)
continue
with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as sock:
sock.settimeout(1)
try:
sock.connect(("127.0.0.1", 10000))
except OSError as exc:
last_error = str(exc)
time.sleep(1)
continue
sys.exit(0)
print(f"Azurite readiness probe failed: {last_error}", file=sys.stderr)
sys.exit(1)
PY
then
docker logs azurite --tail 200 || true
docker ps -a || true
exit 1
fi
- name: Run API integration tests
working-directory: backend
run: uv run ade api test integration
backend-worker-integration:
name: Backend Worker Integration
needs: changes
if: ${{ needs.changes.outputs.backend == 'true' }}
runs-on: ubuntu-latest
services:
postgres:
image: postgres:16
env:
POSTGRES_USER: postgres
POSTGRES_PASSWORD: postgres
POSTGRES_DB: ade_test
ports:
- 5432:5432
options: >-
--health-cmd "pg_isready -U postgres"
--health-interval 5s
--health-timeout 5s
--health-retries 20
env:
CI: "true"
ADE_TEST_DATABASE_URL: postgresql+psycopg://postgres:postgres@127.0.0.1:5432/ade_test?sslmode=disable
ADE_TEST_BLOB_CONNECTION_STRING: DefaultEndpointsProtocol=http;AccountName=devstoreaccount1;AccountKey=Eby8vdM02xNOcqFlqUwJPLlmEtlCDXJ1OUzFT50uSRZ6IFsuFq2UVErCz4I6tq/K1SZFPTOtr/KBHBeksoGMGw==;BlobEndpoint=http://127.0.0.1:10000/devstoreaccount1;
ADE_TEST_DATABASE_NAME_PREFIX: ade_worker_test
steps:
- name: Checkout
uses: actions/checkout@v4
- name: Set up Python
uses: actions/setup-python@v5
with:
python-version: "3.14"
- name: Install uv
run: python -m pip install --upgrade pip uv
- name: Sync backend environment
working-directory: backend
run: uv sync --frozen
- name: Start Azurite blob emulator
run: |
docker run --detach \
--name azurite \
--publish 10000:10000 \
mcr.microsoft.com/azure-storage/azurite:3.35.0 \
azurite-blob --silent --location /data --blobHost 0.0.0.0 --blobPort 10000 --disableTelemetry --skipApiVersionCheck
if ! python - <<'PY'
import socket
import subprocess
import sys
import time
deadline = time.time() + 30
last_error = "unknown"
while time.time() < deadline:
inspect = subprocess.run(
["docker", "inspect", "-f", "{{.State.Running}}", "azurite"],
check=False,
capture_output=True,
text=True,
)
if inspect.returncode != 0:
last_error = inspect.stderr.strip() or "docker inspect failed"
time.sleep(1)
continue
if inspect.stdout.strip() != "true":
last_error = "container stopped"
time.sleep(1)
continue
with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as sock:
sock.settimeout(1)
try:
sock.connect(("127.0.0.1", 10000))
except OSError as exc:
last_error = str(exc)
time.sleep(1)
continue
sys.exit(0)
print(f"Azurite readiness probe failed: {last_error}", file=sys.stderr)
sys.exit(1)
PY
then
docker logs azurite --tail 200 || true
docker ps -a || true
exit 1
fi
- name: Run worker integration tests
working-directory: backend
run: uv run ade worker test integration
frontend-checks:
name: Frontend Checks
needs: scope
if: ${{ github.event_name != 'pull_request' || needs.scope.outputs.docs_only != 'true' }}
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v4
- name: Set up Python
uses: actions/setup-python@v5
with:
python-version: "3.14"
- name: Install uv
run: python -m pip install --upgrade pip uv
- name: Sync backend environment
working-directory: backend
run: uv sync --frozen
- name: Set up Node
uses: actions/setup-node@v4
with:
node-version: "22"
cache: npm
cache-dependency-path: frontend/package-lock.json
- name: Install frontend dependencies
run: npm --prefix frontend ci
- name: Run frontend checks
working-directory: backend
run: |
uv run ade web lint
uv run ade web typecheck
uv run ade web test
infra-azure-validation:
name: Infra Azure Validation
needs: scope
if: ${{ (github.event_name != 'pull_request' || needs.scope.outputs.docs_only != 'true') && needs.scope.outputs.infra_azure_changed == 'true' }}
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v4
- name: Ensure Azure CLI and Bicep are available
run: |
az version
az bicep upgrade
- name: Validate Azure infra templates and scripts
run: bash infra/azure/validate.sh
scope:
name: Detect docs and infra changes
runs-on: ubuntu-latest
outputs:
docs_only: ${{ steps.scope.outputs.docs_only }}
infra_azure_changed: ${{ steps.scope.outputs.infra_azure_changed }}
steps:
- name: Checkout
uses: actions/checkout@v4
with:
fetch-depth: 0
- name: Classify change scope
id: scope
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_BEFORE_SHA: ${{ github.event.before }}
PUSH_AFTER_SHA: ${{ github.sha }}
run: |
set -euo pipefail
if [[ "$EVENT_NAME" == "pull_request" ]]; then
from_sha="$PR_BASE_SHA"
to_sha="$PR_HEAD_SHA"
else
from_sha="$PUSH_BEFORE_SHA"
to_sha="$PUSH_AFTER_SHA"
fi
if [[ -z "${from_sha:-}" || "$from_sha" == "0000000000000000000000000000000000000000" ]]; then
echo "docs_only=false" >> "$GITHUB_OUTPUT"
echo "infra_azure_changed=true" >> "$GITHUB_OUTPUT"
exit 0
fi
changed_files="$(git diff --name-only "$from_sha" "$to_sha")"
if [[ -z "$changed_files" ]]; then
echo "docs_only=false" >> "$GITHUB_OUTPUT"
echo "infra_azure_changed=false" >> "$GITHUB_OUTPUT"
exit 0
fi
docs_only=true
infra_azure_changed=false
while IFS= read -r file; do
if [[ "$file" =~ ^docs/ ]] || \
[[ "$file" =~ ^README\.md$ ]] || \
[[ "$file" =~ ^CONTRIBUTING\.md$ ]] || \
[[ "$file" =~ ^CHANGELOG\.md$ ]] || \
[[ "$file" =~ ^\.github/pull_request_template\.md$ ]] || \
[[ "$file" =~ ^\.github/ISSUE_TEMPLATE/ ]]; then
:
else
docs_only=false
fi
if [[ "$file" =~ ^infra/azure/ ]]; then
infra_azure_changed=true
fi
done <<< "$changed_files"
echo "docs_only=$docs_only" >> "$GITHUB_OUTPUT"
echo "infra_azure_changed=$infra_azure_changed" >> "$GITHUB_OUTPUT"
echo "docs_only=$docs_only"
echo "infra_azure_changed=$infra_azure_changed"
echo "changed files:"
echo "$changed_files"