ci: consolidate workflows into single CI pipeline with job deps
CI / Assistant Split Regression (push) Failing after 5m21s
CI / Architecture Guardrails (push) Failing after 5m28s
CI / Unit Tests (push) Failing after 27s
CI / Typecheck (push) Failing after 8m39s
CI / Build (push) Has been skipped
CI / E2E Tests (push) Has been skipped
CI / Lint (push) Successful in 9m32s
CI / Release Images (push) Has been skipped
CI / Fresh-Linux Docker Deploy (push) Has been skipped

Collapses ci.yml, release-image.yml, and deploy-test.yml from three
parallel push-triggered workflows into one orchestrated pipeline:

- release-image.yml: converted to reusable workflow (workflow_call +
  workflow_dispatch). No longer triggers on push directly.
- deploy-test.yml: deleted, content inlined into ci.yml as the
  docker-deploy-test job with needs: [build].
- ci.yml: adds docker-deploy-test job and release-images job. The
  release-images job calls release-image.yml via uses: and is gated
  to push events on main, so PRs do not publish images.
- check-architecture-guardrails.mjs: updated to enforce the new
  reusable-workflow shape (workflow_call trigger, ci.yml chains
  release-image.yml, main-push gating).

One run per commit, clear Success/Failure status, no wasted image
builds when CI fails.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-04-12 14:54:05 +02:00
parent 002f44ea3d
commit 3391ae5ce6
4 changed files with 118 additions and 109 deletions
+95
View File
@@ -360,3 +360,98 @@ jobs:
name: playwright-report
path: apps/web/playwright-report/
retention-days: 14
# ──────────────────────────────────────────────
# Fresh Docker Compose deploy test — validates
# that the prod compose bundle comes up clean
# from scratch and the smoke tests pass.
# ──────────────────────────────────────────────
docker-deploy-test:
name: Fresh-Linux Docker Deploy
needs: [build]
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Create minimal .env
run: |
cat <<'EOF' > .env
NEXTAUTH_URL=http://localhost:3100
NEXTAUTH_SECRET=ci-test-secret-minimum-32-chars-xx
PGADMIN_PASSWORD=ci-pgadmin
EOF
- name: Start infrastructure (postgres + redis)
run: docker compose up -d postgres redis
- name: Wait for postgres
run: |
for i in $(seq 1 20); do
docker compose exec -T postgres pg_isready -U capakraken -d capakraken && break
sleep 3
done
- name: Build and start app (full profile)
run: docker compose --profile full up -d --build app
- name: Wait for /api/health (up to 3 minutes)
run: |
for i in $(seq 1 36); do
STATUS=$(curl -s -o /dev/null -w "%{http_code}" http://localhost:3100/api/health || echo "000")
echo "Attempt $i: HTTP $STATUS"
if [ "$STATUS" = "200" ]; then exit 0; fi
sleep 5
done
echo "Health check timed out"
docker compose logs app --tail=50
exit 1
- name: Verify health response contains status ok
run: |
BODY=$(curl -sf http://localhost:3100/api/health)
echo "$BODY"
echo "$BODY" | grep '"status":"ok"'
- name: Seed admin user
run: |
docker compose exec -T app node /app/scripts/setup-admin.mjs \
--email admin@capakraken.dev \
--name "Admin" \
--password admin123
- name: Set up Node.js 20
uses: actions/setup-node@v4
with:
node-version: "20"
- name: Install Playwright and Chromium
run: |
npm install -g @playwright/test@1.49
playwright install chromium --with-deps
- name: Run smoke tests
run: npx playwright test --config apps/web/playwright.ci.config.ts
- name: Upload Playwright report
if: failure()
uses: actions/upload-artifact@v4
with:
name: playwright-smoke-report
path: apps/web/playwright-report/
retention-days: 7
- name: Show logs on failure
if: failure()
run: docker compose logs --tail=100
# ──────────────────────────────────────────────
# Release images — only on push to main, after
# every check has passed. Calls the reusable
# release-image.yml workflow.
# ──────────────────────────────────────────────
release-images:
name: Release Images
if: github.event_name == 'push' && github.ref == 'refs/heads/main'
needs: [lint, test, e2e, assistant-split, docker-deploy-test]
uses: ./.github/workflows/release-image.yml
secrets: inherit
-100
View File
@@ -1,100 +0,0 @@
name: Docker Deploy Test
on:
push:
branches: [main]
paths-ignore:
- "docs/**"
- ".gitea/**"
- "**/*.md"
- "LICENSE"
pull_request:
branches: [main]
paths-ignore:
- "docs/**"
- ".gitea/**"
- "**/*.md"
- "LICENSE"
concurrency:
group: deploy-test-${{ github.ref }}
cancel-in-progress: true
jobs:
docker-deploy-test:
name: Fresh-Linux Docker Deploy
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Create minimal .env
run: |
cat <<'EOF' > .env
NEXTAUTH_URL=http://localhost:3100
NEXTAUTH_SECRET=ci-test-secret-minimum-32-chars-xx
PGADMIN_PASSWORD=ci-pgadmin
EOF
- name: Start infrastructure (postgres + redis)
run: docker compose up -d postgres redis
- name: Wait for postgres
run: |
for i in $(seq 1 20); do
docker compose exec -T postgres pg_isready -U capakraken -d capakraken && break
sleep 3
done
- name: Build and start app (full profile)
run: docker compose --profile full up -d --build app
- name: Wait for /api/health (up to 3 minutes)
run: |
for i in $(seq 1 36); do
STATUS=$(curl -s -o /dev/null -w "%{http_code}" http://localhost:3100/api/health || echo "000")
echo "Attempt $i: HTTP $STATUS"
if [ "$STATUS" = "200" ]; then exit 0; fi
sleep 5
done
echo "Health check timed out"
docker compose logs app --tail=50
exit 1
- name: Verify health response contains status ok
run: |
BODY=$(curl -sf http://localhost:3100/api/health)
echo "$BODY"
echo "$BODY" | grep '"status":"ok"'
- name: Seed admin user
run: |
docker compose exec -T app node /app/scripts/setup-admin.mjs \
--email admin@capakraken.dev \
--name "Admin" \
--password admin123
- name: Set up Node.js 20
uses: actions/setup-node@v4
with:
node-version: "20"
- name: Install Playwright and Chromium
run: |
npm install -g @playwright/test@1.49
playwright install chromium --with-deps
- name: Run smoke tests
run: npx playwright test --config apps/web/playwright.ci.config.ts
- name: Upload Playwright report
if: failure()
uses: actions/upload-artifact@v4
with:
name: playwright-smoke-report
path: apps/web/playwright-report/
retention-days: 7
- name: Show logs on failure
if: failure()
run: docker compose logs --tail=100
+13 -7
View File
@@ -1,13 +1,19 @@
name: Release Image
# Reusable workflow: called from ci.yml after all checks pass.
# Can also be dispatched manually for rebuilds or tag overrides.
on:
push:
branches: [main]
paths-ignore:
- "docs/**"
- ".gitea/**"
- "**/*.md"
- "LICENSE"
workflow_call:
inputs:
image_tag:
description: Optional tag override, defaults to sha-<commit>
required: false
type: string
secrets:
GHCR_USERNAME:
required: true
GHCR_TOKEN:
required: true
workflow_dispatch:
inputs:
image_tag:
+10 -2
View File
@@ -663,8 +663,8 @@ export const rules = [
file: ".github/workflows/release-image.yml",
required: [
{
pattern: /push:\s*\n\s*branches:\s*\[main\]/,
message: "image releases must build automatically on pushes to main",
pattern: /workflow_call:/,
message: "release workflow must remain callable as a reusable workflow from ci.yml",
},
{
pattern: /workflow_dispatch:/,
@@ -708,6 +708,14 @@ export const rules = [
pattern: /run:\s+pnpm db:generate/,
message: "CI must route Prisma client generation through the workspace env/schema wrapper",
},
{
pattern: /uses:\s+\.\/\.github\/workflows\/release-image\.yml/,
message: "ci.yml must chain release-image.yml so image builds run after checks pass",
},
{
pattern: /github\.event_name == 'push' && github\.ref == 'refs\/heads\/main'/,
message: "release-images job must be gated to main-branch pushes to avoid PR image pushes",
},
],
forbidden: [
{