managarten/.github/workflows/ci.yml
Till JS 774852ba2d feat(cutover): platform services build from ../mana, not from this repo
Part of the 8-Doppel-Cutover (2026-05-08, plan
~/.claude/plans/floating-swinging-flurry.md):

- docker-compose.{macmini,dev,test}.yml: build context for
  mana-{auth,credits,media,llm,notify} switched to ../mana/services/...
  so the Mac Mini stack pulls platform services from the platform repo
  (sibling clone), not from services/ in this monorepo.
- .npmrc + apps/api/{Dockerfile,package.json}: @mana/media-client now
  resolved from Verdaccio (npm.mana.how, ^0.1.0) instead of as a
  workspace COPY from services/mana-media/packages/client. Build-arg
  NPM_TOKEN flows through .npmrc for pnpm install auth. Required
  before services/mana-media/ can be deleted.
- .github/workflows/{ci,cd-macmini,daily-tests}.yml: removed the
  detect-/build-/test-jobs that targeted services/mana-{auth,credits,
  notify,media}/. Those services build out of the platform repo now —
  CI for them belongs in mana/-repo (open). cd-macmini's
  workflow_dispatch can still rebuild any of them on demand;
  auto-detect on path-change is gone for these five.
- scripts/{mac-mini/push-schemas.sh,run-integration-tests.sh}:
  rewritten to look in ../mana/ for the platform services.
- package.json dev:{auth,credits,notify,media}: paths point at
  ../mana/services/... so local dev still works post-cutover.

What this commit does NOT do: delete services/mana-{auth,credits,...}
from this repo. That waits for Phase 7 once the Mac Mini stack has
booted cleanly from the new build paths.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-08 18:40:08 +02:00

440 lines
17 KiB
YAML

# CI Pipeline: validates PRs and sanity-builds standalone service images.
#
# Flow:
# PR → main/dev : validate + auth-integration (required status check).
# Push → main/dev : sanity-build the changed standalone services
# (mana-auth/-search/-sync/-notify/-api-gateway/-crawler/
# -media/-credits) on GitHub-hosted runners.
#
# What's NOT in CI:
# - mana-web: built by the Mac Mini CD pipeline (cd-macmini.yml). Its
# Dockerfile starts FROM `sveltekit-base:local`, an image only the
# self-hosted runner has. Putting it in CI would require a sveltekit-
# base prebuild step here too — out of scope until we add a real
# consumer for the ghcr.io image.
# - apps/api, mana-events, mana-analytics, mana-geocoding, mana-mail,
# mana-llm, mana-research, mana-mcp, mana-ai, mana-persona-runner,
# mana-user, mana-subscriptions, mana-image-gen, mana-video-gen,
# mana-stt, mana-tts, mana-voice-bot, mana-landing-builder: also
# built by the Mac Mini CD pipeline. Add a sanity-build job here
# only when there's a non-CD consumer for the image.
#
# Production deployment is the cd-macmini.yml workflow, which auto-fires
# on push to main and rebuilds only the affected services on the
# self-hosted runner.
name: CI
on:
push:
branches:
- main
- dev
pull_request:
branches:
- main
- dev
workflow_dispatch:
inputs:
force_build_all:
description: 'Force rebuild all services'
required: false
default: 'false'
type: boolean
concurrency:
group: ${{ github.workflow }}-${{ github.ref }}
cancel-in-progress: true
env:
NODE_VERSION: '20'
PNPM_VERSION: '9.15.0'
jobs:
# ===========================================
# Detect Changes - determines which services to build
# ===========================================
detect-changes:
name: Detect Changes
runs-on: ubuntu-latest
if: github.event_name == 'push' || github.event_name == 'workflow_dispatch'
outputs:
mana-auth: ${{ steps.changes.outputs.mana-auth }}
mana-sync: ${{ steps.changes.outputs.mana-sync }}
mana-media: ${{ steps.changes.outputs.mana-media }}
mana-notify: ${{ steps.changes.outputs.mana-notify }}
mana-api-gateway: ${{ steps.changes.outputs.mana-api-gateway }}
mana-crawler: ${{ steps.changes.outputs.mana-crawler }}
mana-credits: ${{ steps.changes.outputs.mana-credits }}
mana-search: ${{ steps.changes.outputs.mana-search }}
any-changes: ${{ steps.changes.outputs.any-changes }}
steps:
- name: Checkout code
uses: actions/checkout@v4
with:
fetch-depth: 0
- name: Check for changes
id: changes
run: |
# Force build all if workflow_dispatch with force_build_all
if [ "${{ github.event_name }}" == "workflow_dispatch" ] && [ "${{ inputs.force_build_all }}" == "true" ]; then
echo "Force rebuild all services requested"
echo "mana-auth=true" >> $GITHUB_OUTPUT
echo "mana-sync=true" >> $GITHUB_OUTPUT
echo "mana-media=true" >> $GITHUB_OUTPUT
echo "mana-notify=true" >> $GITHUB_OUTPUT
echo "mana-api-gateway=true" >> $GITHUB_OUTPUT
echo "mana-crawler=true" >> $GITHUB_OUTPUT
echo "mana-credits=true" >> $GITHUB_OUTPUT
echo "mana-search=true" >> $GITHUB_OUTPUT
echo "any-changes=true" >> $GITHUB_OUTPUT
exit 0
fi
# Get changed files between current commit and previous
if [ "${{ github.event_name }}" == "push" ]; then
BASE_SHA="${{ github.event.before }}"
HEAD_SHA="${{ github.sha }}"
# Handle initial commit case
if [ "$BASE_SHA" == "0000000000000000000000000000000000000000" ]; then
CHANGED_FILES=$(git ls-files)
else
CHANGED_FILES=$(git diff --name-only $BASE_SHA $HEAD_SHA 2>/dev/null || git ls-files)
fi
else
# workflow_dispatch without force - build all
echo "Workflow dispatch without force_build_all - building all"
echo "mana-auth=true" >> $GITHUB_OUTPUT
echo "mana-sync=true" >> $GITHUB_OUTPUT
echo "mana-media=true" >> $GITHUB_OUTPUT
echo "mana-notify=true" >> $GITHUB_OUTPUT
echo "mana-api-gateway=true" >> $GITHUB_OUTPUT
echo "mana-crawler=true" >> $GITHUB_OUTPUT
echo "mana-credits=true" >> $GITHUB_OUTPUT
echo "mana-search=true" >> $GITHUB_OUTPUT
echo "any-changes=true" >> $GITHUB_OUTPUT
exit 0
fi
echo "Changed files:"
echo "$CHANGED_FILES"
# Common files that trigger all builds
COMMON_PATTERNS="pnpm-lock.yaml|pnpm-workspace.yaml|package.json|.github/workflows/ci.yml"
# Shared packages that affect multiple services
SHARED_AUTH_PATTERN="packages/shared-auth/|packages/shared-types/"
SHARED_UI_PATTERN="packages/shared-ui/|packages/shared-theme/|packages/shared-icons/|packages/shared-tailwind/|packages/shared-branding/"
SHARED_WEB_PATTERN="packages/shared-auth-ui/|packages/shared-theme-ui/|packages/shared-feedback-ui/|packages/shared-subscription-ui/|packages/shared-splitscreen/"
# Function to check if any pattern matches
check_pattern() {
echo "$CHANGED_FILES" | grep -qE "$1" && echo "true" || echo "false"
}
# Check common changes
COMMON_CHANGED=$(check_pattern "$COMMON_PATTERNS")
SHARED_AUTH_CHANGED=$(check_pattern "$SHARED_AUTH_PATTERN")
SHARED_UI_CHANGED=$(check_pattern "$SHARED_UI_PATTERN")
SHARED_WEB_CHANGED=$(check_pattern "$SHARED_WEB_PATTERN")
echo "Common changed: $COMMON_CHANGED"
echo "Shared auth changed: $SHARED_AUTH_CHANGED"
echo "Shared UI changed: $SHARED_UI_CHANGED"
echo "Shared web changed: $SHARED_WEB_CHANGED"
# mana-auth: services/mana-auth
AUTH_CHANGED=$(check_pattern "services/mana-auth/")
if [ "$COMMON_CHANGED" == "true" ] || [ "$AUTH_CHANGED" == "true" ]; then
echo "mana-auth=true" >> $GITHUB_OUTPUT
else
echo "mana-auth=false" >> $GITHUB_OUTPUT
fi
# mana-search: services/mana-search
SEARCH_CHANGED=$(check_pattern "services/mana-search/")
if [ "$COMMON_CHANGED" == "true" ] || [ "$SEARCH_CHANGED" == "true" ]; then
echo "mana-search=true" >> $GITHUB_OUTPUT
else
echo "mana-search=false" >> $GITHUB_OUTPUT
fi
# Go services (standalone — no COMMON trigger, only own path + shared-go)
SHARED_GO_PATTERN="packages/shared-go/"
SHARED_GO_CHANGED=$(check_pattern "$SHARED_GO_PATTERN")
for GO_SVC in mana-sync mana-notify mana-api-gateway mana-crawler; do
SVC_CHANGED=$(check_pattern "services/${GO_SVC}/")
if [ "$SVC_CHANGED" == "true" ] || [ "$SHARED_GO_CHANGED" == "true" ]; then
echo "${GO_SVC}=true" >> $GITHUB_OUTPUT
else
echo "${GO_SVC}=false" >> $GITHUB_OUTPUT
fi
done
# Hono/Bun services (standalone — only own path)
for HONO_SVC in mana-media mana-credits; do
SVC_CHANGED=$(check_pattern "services/${HONO_SVC}/")
if [ "$SVC_CHANGED" == "true" ]; then
echo "${HONO_SVC}=true" >> $GITHUB_OUTPUT
else
echo "${HONO_SVC}=false" >> $GITHUB_OUTPUT
fi
done
# mana-web build job is handled by the Mac Mini CD pipeline
# (cd-macmini.yml) — that runner has the `sveltekit-base:local`
# image required by apps/mana/apps/web/Dockerfile. CI here only
# validates the standalone services that build cleanly on
# GitHub-hosted runners.
# Per-product apps (chat / todo / calendar / clock / contacts /
# presi / storage / food / skilltree) and `telegram-stats-bot`
# were removed when the unified mana app + apps/api consolidated
# everything in 2026-04. Their CI build jobs have been deleted
# along with the source dirs.
# Check if any service needs building
if grep -q "=true" $GITHUB_OUTPUT; then
echo "any-changes=true" >> $GITHUB_OUTPUT
else
echo "any-changes=false" >> $GITHUB_OUTPUT
fi
- name: Summary
run: |
echo "## Build Summary" >> $GITHUB_STEP_SUMMARY
echo "" >> $GITHUB_STEP_SUMMARY
echo "| Service | Will Build |" >> $GITHUB_STEP_SUMMARY
echo "|---------|------------|" >> $GITHUB_STEP_SUMMARY
echo "| mana-auth | ${{ steps.changes.outputs.mana-auth }} |" >> $GITHUB_STEP_SUMMARY
echo "| mana-sync | ${{ steps.changes.outputs.mana-sync }} |" >> $GITHUB_STEP_SUMMARY
echo "| mana-media | ${{ steps.changes.outputs.mana-media }} |" >> $GITHUB_STEP_SUMMARY
echo "| mana-notify | ${{ steps.changes.outputs.mana-notify }} |" >> $GITHUB_STEP_SUMMARY
echo "| mana-api-gateway | ${{ steps.changes.outputs.mana-api-gateway }} |" >> $GITHUB_STEP_SUMMARY
echo "| mana-crawler | ${{ steps.changes.outputs.mana-crawler }} |" >> $GITHUB_STEP_SUMMARY
echo "| mana-credits | ${{ steps.changes.outputs.mana-credits }} |" >> $GITHUB_STEP_SUMMARY
echo "| mana-search | ${{ steps.changes.outputs.mana-search }} |" >> $GITHUB_STEP_SUMMARY
# ===========================================
# Validation job - runs on PRs
# ===========================================
validate:
name: Validate
runs-on: ubuntu-latest
if: github.event_name == 'pull_request'
steps:
- name: Checkout code
uses: actions/checkout@v4
- name: Setup pnpm
uses: pnpm/action-setup@v2
with:
version: ${{ env.PNPM_VERSION }}
- name: Setup Node.js
uses: actions/setup-node@v4
with:
node-version: ${{ env.NODE_VERSION }}
cache: 'pnpm'
- name: Install dependencies
run: pnpm install --frozen-lockfile
- name: Validate monorepo invariants (turbo + pgSchema + crypto)
run: pnpm run validate:all
- name: Type check
run: pnpm run type-check
- name: Format check
run: pnpm run format:check
- name: Lint
run: pnpm run lint
# Coverage is produced but not yet gating — we're establishing a baseline
# before flipping to hard-fail. `continue-on-error: true` surfaces a
# proper failure signal in the PR (unlike `|| true`, which silently
# swallowed everything) but still lets the overall job pass.
# Flip to blocking by removing `continue-on-error` once the suite is
# green on main for a full week. Tracked in docs/plans/concern-4-coverage.md.
- name: Test (with coverage)
id: tests
continue-on-error: true
run: pnpm run test:coverage
- name: Annotate test failure (non-blocking)
if: steps.tests.outcome == 'failure'
run: |
echo "::warning title=Test suite failed::pnpm run test:coverage returned a non-zero exit code. Coverage artifacts were still uploaded. See the 'Test (with coverage)' step above for details."
- name: Upload coverage reports
if: always()
uses: actions/upload-artifact@v4
with:
name: coverage-${{ github.run_id }}
path: |
**/coverage/lcov.info
**/coverage/coverage-summary.json
if-no-files-found: warn
retention-days: 14
- name: Security Audit
run: |
# Run pnpm audit and capture exit code
# Exit 0 if only moderate/low vulnerabilities
pnpm audit --audit-level=high || {
echo "::warning::Security vulnerabilities found. Run 'pnpm audit' locally for details."
exit 0 # Don't fail build on audit issues (just warn)
}
- name: Check for known vulnerable packages
run: |
# Check for packages with known critical vulnerabilities
# This is a basic check - for production, consider Snyk or similar
if grep -r "lodash@[0-3]\." pnpm-lock.yaml 2>/dev/null; then
echo "::warning::Potentially vulnerable lodash version detected"
fi
if grep -r "axios@0\.[0-9]\." pnpm-lock.yaml 2>/dev/null; then
echo "::warning::Potentially vulnerable axios version detected"
fi
# ===========================================
# NOTE 2026-05-08 — Plattform-Service-Tests/Builds (mana-auth,
# mana-credits, mana-mail, mana-media, mana-llm, mana-notify, mana-stt,
# mana-tts) leben jetzt im Schwester-Repo `mana/` und sind dort zu
# testen + zu bauen. Der frühere `auth-integration`-Job und
# `build-mana-{auth,notify,media,credits}` wurden entfernt, weil sie
# `services/mana-<x>/` aus diesem Repo erwarteten — diese Verzeichnisse
# gibt es ab Phase 7 nicht mehr. Offener Punkt: eigene CI im
# `mana/`-Repo aufsetzen (mit auth-integration + Build-Push).
# ===========================================
# ===========================================
# Build Docker images - only changed services
# ===========================================
build-mana-search:
name: Build mana-search
runs-on: ubuntu-latest
needs: detect-changes
if: needs.detect-changes.outputs.mana-search == 'true'
steps:
- uses: actions/checkout@v4
- uses: docker/setup-qemu-action@v3
- uses: docker/setup-buildx-action@v3
- uses: docker/login-action@v3
with:
registry: ghcr.io
username: ${{ github.actor }}
password: ${{ secrets.GITHUB_TOKEN }}
- uses: docker/metadata-action@v5
id: meta
with:
images: ghcr.io/${{ github.repository_owner }}/mana-search
tags: type=raw,value=latest
- uses: docker/build-push-action@v5
with:
context: .
file: services/mana-search/Dockerfile
platforms: linux/amd64,linux/arm64
push: true
tags: ${{ steps.meta.outputs.tags }}
cache-from: type=gha
cache-to: type=gha,mode=max
# Go services
build-mana-sync:
name: Build mana-sync
runs-on: ubuntu-latest
needs: detect-changes
if: needs.detect-changes.outputs.mana-sync == 'true'
steps:
- uses: actions/checkout@v4
- uses: docker/setup-qemu-action@v3
- uses: docker/setup-buildx-action@v3
- uses: docker/login-action@v3
with:
registry: ghcr.io
username: ${{ github.actor }}
password: ${{ secrets.GITHUB_TOKEN }}
- uses: docker/metadata-action@v5
id: meta
with:
images: ghcr.io/${{ github.repository_owner }}/mana-sync
tags: type=raw,value=latest
- uses: docker/build-push-action@v5
with:
context: .
file: services/mana-sync/Dockerfile
platforms: linux/amd64,linux/arm64
push: true
tags: ${{ steps.meta.outputs.tags }}
cache-from: type=gha
cache-to: type=gha,mode=max
build-mana-api-gateway:
name: Build mana-api-gateway
runs-on: ubuntu-latest
needs: detect-changes
if: needs.detect-changes.outputs.mana-api-gateway == 'true'
steps:
- uses: actions/checkout@v4
- uses: docker/setup-qemu-action@v3
- uses: docker/setup-buildx-action@v3
- uses: docker/login-action@v3
with:
registry: ghcr.io
username: ${{ github.actor }}
password: ${{ secrets.GITHUB_TOKEN }}
- uses: docker/metadata-action@v5
id: meta
with:
images: ghcr.io/${{ github.repository_owner }}/mana-api-gateway
tags: type=raw,value=latest
- uses: docker/build-push-action@v5
with:
context: .
file: services/mana-api-gateway/Dockerfile
platforms: linux/amd64,linux/arm64
push: true
tags: ${{ steps.meta.outputs.tags }}
cache-from: type=gha
cache-to: type=gha,mode=max
build-mana-crawler:
name: Build mana-crawler
runs-on: ubuntu-latest
needs: detect-changes
if: needs.detect-changes.outputs.mana-crawler == 'true'
steps:
- uses: actions/checkout@v4
- uses: docker/setup-qemu-action@v3
- uses: docker/setup-buildx-action@v3
- uses: docker/login-action@v3
with:
registry: ghcr.io
username: ${{ github.actor }}
password: ${{ secrets.GITHUB_TOKEN }}
- uses: docker/metadata-action@v5
id: meta
with:
images: ghcr.io/${{ github.repository_owner }}/mana-crawler
tags: type=raw,value=latest
- uses: docker/build-push-action@v5
with:
context: .
file: services/mana-crawler/Dockerfile
platforms: linux/amd64,linux/arm64
push: true
tags: ${{ steps.meta.outputs.tags }}
cache-from: type=gha
cache-to: type=gha,mode=max
# Hono/Bun services
# NOTE 2026-05-08 — `build-mana-media` und `build-mana-credits` sind
# entfallen: Plattform-Services leben jetzt im Schwester-Repo `mana/`.
# Build-Push gehört in eine separate CI dort. Siehe Header oben.