# Continuous Integration für Syncova. # # Die Pipeline muss jede Änderung prüfen, bevor sie in den Hauptzweig gelangt # (SYNCOVA_IMPLEMENTATION_PLAN.md Phase 0). name: CI on: push: branches: [main] pull_request: # Ein laufender Durchlauf wird abgebrochen, sobald ein neuerer Commit erscheint. concurrency: group: ${{ github.workflow }}-${{ github.ref }} cancel-in-progress: true # Standardmäßig nur Lesezugriff: die Pipeline braucht keine Schreibrechte. permissions: contents: read env: GO_VERSION: '1.26' NODE_VERSION: '22' jobs: backend: name: Backend (Go) runs-on: ubuntu-latest services: # Die Tests laufen gegen eine echte PostgreSQL-Instanz, nicht gegen eine Attrappe. postgres: image: postgres:17-alpine env: POSTGRES_DB: syncova_test POSTGRES_USER: syncova_test # Nur für diesen kurzlebigen CI-Container; die Datenbank ist von außen nicht erreichbar. POSTGRES_PASSWORD: ci-only-ephemeral-password ports: - 5432:5432 options: >- --health-cmd "pg_isready -U syncova_test -d syncova_test" --health-interval 5s --health-timeout 5s --health-retries 10 steps: - uses: actions/checkout@v4 - uses: actions/setup-go@v5 with: go-version: ${{ env.GO_VERSION }} cache: true - name: Formatierung prüfen run: | UNFORMATTED_FILES="$(gofmt -l apps/api packages migrations)" if [ -n "$UNFORMATTED_FILES" ]; then echo "Nicht formatierte Dateien gefunden:" echo "$UNFORMATTED_FILES" exit 1 fi - name: Statische Analyse run: | # node_modules ausschliessen: NPM-Pakete koennen eigenen Go-Code enthalten. go vet $(go list ./... | grep -v '/node_modules/') - name: Modulabhängigkeiten prüfen run: | go mod tidy # Eine Änderung an go.mod/go.sum bedeutet: die Abhängigkeiten sind nicht eingecheckt. git diff --exit-code go.mod go.sum - name: Schema vor den Tests anlegen env: SYNCOVA_ENV: test SYNCOVA_DB_HOST: 127.0.0.1 SYNCOVA_DB_PORT: '5432' SYNCOVA_DB_NAME: syncova_test SYNCOVA_DB_USER: syncova_test SYNCOVA_DB_PASSWORD: ci-only-ephemeral-password SYNCOVA_DB_SSLMODE: disable # Nur für diesen CI-Lauf; schützt keine echten Daten. SYNCOVA_ENCRYPTION_KEYS: 'v1:AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA=' # Muss vor den Tests laufen. Die Datenbanktests erwarten ein Schema; auf # einer leeren Datenbank scheitern sie. Solange SYNCOVA_TEST_DATABASE_URL # fehlte, uebersprangen sie und der Fehler blieb verborgen. run: go run ./apps/api/cmd/syncova-migrate up - name: Tests mit Race-Detector env: SYNCOVA_ENV: test SYNCOVA_DB_HOST: 127.0.0.1 SYNCOVA_DB_PORT: '5432' SYNCOVA_DB_NAME: syncova_test SYNCOVA_DB_USER: syncova_test SYNCOVA_DB_PASSWORD: ci-only-ephemeral-password SYNCOVA_DB_SSLMODE: disable # Nur für diesen CI-Lauf; schützt keine echten Daten. SYNCOVA_ENCRYPTION_KEYS: 'v1:AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA=' # Ohne diese Variable überspringen neun Testdateien ihre Datenbanktests # still — darunter der Upgrade- und der Rollback-Test. Ein übersprungener # Test sieht in der Zusammenfassung aus wie ein bestandener. SYNCOVA_TEST_DATABASE_URL: 'postgres://syncova_test:ci-only-ephemeral-password@127.0.0.1:5432/syncova_test?sslmode=disable' # secretscan:erlaubt: kurzlebiger CI-Container, von aussen nicht erreichbar run: go test -race -coverprofile=coverage.out $(go list ./... | grep -v '/node_modules/') - name: Migrationen gegen echte Datenbank prüfen env: SYNCOVA_ENV: test SYNCOVA_DB_HOST: 127.0.0.1 SYNCOVA_DB_PORT: '5432' SYNCOVA_DB_NAME: syncova_test SYNCOVA_DB_USER: syncova_test SYNCOVA_DB_PASSWORD: ci-only-ephemeral-password SYNCOVA_DB_SSLMODE: disable # Nur für diesen CI-Lauf; schützt keine echten Daten. SYNCOVA_ENCRYPTION_KEYS: 'v1:AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA=' run: | # Der vollständige Zyklus belegt, dass Migrationen umkehrbar sind. # Er läuft nach den Tests: Zwischen 'down' und 'up' fehlt eine # Migration, und ein Test in diesem Moment scheiterte an einem # Schemastand, den es im Betrieb nie gibt. go run ./apps/api/cmd/syncova-migrate status go run ./apps/api/cmd/syncova-migrate down go run ./apps/api/cmd/syncova-migrate up - name: Binaries bauen run: go build $(go list ./... | grep -v '/node_modules/') - name: Übersetzbarkeit aller Zielplattformen # Der Agent liess sich in Phase 5 monatelang gar nicht für Windows # übersetzen, ohne dass es jemandem auffiel: unix.Statfs gibt es dort # nicht. Der Bau auf der eigenen Plattform bemerkt das nie. run: make cross-build frontend: name: Frontend (React/TypeScript) runs-on: ubuntu-latest defaults: run: working-directory: apps/web steps: - uses: actions/checkout@v4 - uses: actions/setup-node@v4 with: node-version: ${{ env.NODE_VERSION }} cache: npm cache-dependency-path: apps/web/package-lock.json - name: Abhängigkeiten installieren run: npm ci - name: Statische Analyse run: npm run lint - name: Tests run: npm run test - name: Auslieferungs-Build run: npm run build security: name: Sicherheitsprüfungen runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 - uses: actions/setup-go@v5 with: go-version: ${{ env.GO_VERSION }} cache: true - name: Bekannte Schwachstellen in Go-Abhängigkeiten run: | go install golang.org/x/vuln/cmd/govulncheck@latest govulncheck $(go list ./... | grep -v '/node_modules/') - uses: actions/setup-node@v4 with: node-version: ${{ env.NODE_VERSION }} cache: npm cache-dependency-path: apps/web/package-lock.json - name: Bekannte Schwachstellen in NPM-Abhängigkeiten working-directory: apps/web run: | npm ci # Nur hohe und kritische Befunde brechen den Lauf ab. npm audit --audit-level=high