'use client' /** * CI/CD Dashboard * * Zentrale Uebersicht fuer: * - Gitea Actions Pipelines * - Runner Status * - Container Deployments * - Pipeline Konfiguration */ import { useState, useEffect, useCallback } from 'react' import { PagePurpose } from '@/components/common/PagePurpose' import { DevOpsPipelineSidebarResponsive } from '@/components/infrastructure/DevOpsPipelineSidebar' // ============================================================================ // Types // ============================================================================ interface PipelineStatus { gitea_connected: boolean gitea_url: string last_sbom_update: string | null total_runs: number successful_runs: number failed_runs: number } interface PipelineRun { id: string workflow: string branch: string commit_sha: string status: 'success' | 'failed' | 'running' | 'pending' started_at: string finished_at: string | null duration_seconds: number | null } interface ContainerInfo { id: string name: string image: string status: string state: string created: string ports: string[] cpu_percent: number memory_usage: string memory_limit: string memory_percent: number network_rx: string network_tx: string } interface SystemStats { hostname: string platform: string arch: string uptime: number cpu: { model: string cores: number usage_percent: number } memory: { total: string used: string free: string usage_percent: number } disk: { total: string used: string free: string usage_percent: number } } interface DockerStats { containers: ContainerInfo[] total_containers: number running_containers: number stopped_containers: number } type TabType = 'overview' | 'woodpecker' | 'pipelines' | 'deployments' | 'setup' | 'scheduler' // Woodpecker Types interface WoodpeckerStep { name: string state: 'pending' | 'running' | 'success' | 'failure' | 'skipped' exit_code: number error?: string } interface WoodpeckerPipeline { id: number number: number status: 'pending' | 'running' | 'success' | 'failure' | 'error' event: string branch: string commit: string message: string author: string created: number started: number finished: number steps: WoodpeckerStep[] errors?: string[] } interface WoodpeckerStatus { status: 'online' | 'offline' pipelines: WoodpeckerPipeline[] lastUpdate: string error?: string } // ============================================================================ // Helper Components // ============================================================================ function ProgressBar({ percent, color = 'blue' }: { percent: number; color?: string }) { const getColor = () => { if (percent > 90) return 'bg-red-500' if (percent > 70) return 'bg-yellow-500' if (color === 'green') return 'bg-green-500' if (color === 'purple') return 'bg-purple-500' return 'bg-blue-500' } return (
Pipeline #{woodpeckerStatus.pipelines[0].number}: {' '} {woodpeckerStatus.pipelines[0].status} {' '}auf {woodpeckerStatus.pipelines[0].branch}
)}Fehlgeschlagene Steps:
{pipelineStatus?.gitea_connected ? 'Verbunden' : 'Nicht verbunden'}
http://macmini:3003
{pipelineStatus?.total_runs || 0}
{pipelineStatus?.successful_runs || 0} erfolgreich
{dockerStats?.running_containers || 0}
von {dockerStats?.total_containers || 0} laufend
{pipelineStatus?.last_sbom_update ? new Date(pipelineStatus.last_sbom_update).toLocaleDateString('de-DE') : 'Nie'}
{pipelineStatus?.last_sbom_update ? new Date(pipelineStatus.last_sbom_update).toLocaleTimeString('de-DE') : '-'}
{run.workflow || 'SBOM Pipeline'}
{run.branch} - {run.commit_sha.substring(0, 8)}
{run.status === 'success' ? 'Erfolgreich' : run.status === 'failed' ? 'Fehlgeschlagen' : run.status === 'running' ? 'Laeuft...' : run.status}
{new Date(run.started_at).toLocaleString('de-DE')}
{woodpeckerStatus?.pipelines?.length || 0}
{woodpeckerStatus?.pipelines?.filter(p => p.status === 'success').length || 0}
{woodpeckerStatus?.pipelines?.filter(p => p.status === 'failure' || p.status === 'error').length || 0}
{woodpeckerStatus?.pipelines?.filter(p => p.status === 'running' || p.status === 'pending').length || 0}
{pipeline.message}
)} {/* Steps Progress */} {pipeline.steps && pipeline.steps.length > 0 && ({new Date(pipeline.created * 1000).toLocaleDateString('de-DE')}
{new Date(pipeline.created * 1000).toLocaleTimeString('de-DE')}
{pipeline.started && pipeline.finished && (Dauer: {Math.round((pipeline.finished - pipeline.started) / 60)}m
)}Keine Pipelines gefunden
Starte eine neue Pipeline oder pruefe die Woodpecker-Konfiguration
{`Woodpecker CI Pipeline (.woodpecker/main.yml)
│
├── 1. go-lint → Go Linting (PR only)
├── 2. python-lint → Python Linting (PR only)
├── 3. secrets-scan → GitLeaks Secrets Scan
│
├── 4. test-go-consent → Go Unit Tests
├── 5. test-go-billing → Billing Service Tests
├── 6. test-go-school → School Service Tests
├── 7. test-python → Python Backend Tests
│
├── 8. build-images → Docker Image Build
├── 9. generate-sbom → SBOM Generation (Syft)
├── 10. vuln-scan → Vulnerability Scan (Grype)
├── 11. container-scan → Container Scan (Trivy)
│
├── 12. sign-images → Cosign Image Signing
├── 13. attest-sbom → SBOM Attestation
├── 14. provenance → SLSA Provenance
│
└── 15. deploy-prod → Production Deployment`}
Um Pipelines ueber das Dashboard zu starten, muss ein WOODPECKER_TOKEN konfiguriert werden:
.env eintragen: WOODPECKER_TOKEN=...docker compose up -d admin-v2Workflows werden bei Push auf main/develop automatisch ausgefuehrt
Generiert Software Bill of Materials
5 Jobs: generate, scan, license, upload, summary
Unit & Integration Tests
Geplant
SAST, SCA, Secrets Scan
Geplant
| Status | Workflow | Branch | Commit | Gestartet | Dauer |
|---|---|---|---|---|---|
| {run.status} | {run.workflow || 'SBOM Pipeline'} | {run.branch} | {run.commit_sha.substring(0, 8)} | {new Date(run.started_at).toLocaleString('de-DE')} | {run.duration_seconds ? `${run.duration_seconds}s` : '-'} |
{`Gitea Actions Pipeline (.gitea/workflows/sbom.yaml)
│
├── 1. generate-sbom → Syft generiert CycloneDX SBOM
│
├── 2. vulnerability-scan → Grype scannt auf CVEs
│
├── 3. license-check → Prueft GPL/AGPL Lizenzen
│
├── 4. upload-dashboard → POST /api/v1/security/sbom/upload
│
└── 5. summary → Job Summary generieren`}
{dockerStats.running_containers} laufend, {dockerStats.stopped_containers} gestoppt, {dockerStats.total_containers} gesamt
)}Anleitung zur Einrichtung der CI/CD Pipeline mit Gitea Actions auf dem Mac Mini Server.
Web-URL
http://macmini:3003
SSH
macmini:2222
Status
{pipelineStatus?.gitea_connected ? 'Verbunden' : 'Konfiguration erforderlich'}
| Komponente | Pfad | Beschreibung |
|---|---|---|
| Gitea Service | docker-compose.yml |
Gitea 1.22 mit Actions enabled |
| Gitea Runner | docker-compose.yml |
act_runner fuer Job-Ausfuehrung |
| SBOM Workflow | .gitea/workflows/sbom.yaml |
5 Jobs: generate, scan, license, upload, summary |
| Backend API | backend/security_api.py |
SBOM Upload, Pipeline Status, History |
| Runner Config | gitea/runner-config.yaml |
Labels: ubuntu-latest, self-hosted |
http://macmini:3003
Username: admin, Email: admin@breakpilot.de
Name: breakpilot-pwa, Visibility: Private
Repository Settings → Actions → Enable Repository Actions
{`export GITEA_RUNNER_TOKEN=
docker compose up -d gitea-runner`}
{`git remote add gitea http://macmini:3003/admin/breakpilot-pwa.git
git push gitea main`}
Taeglich um 07:00 Uhr automatisch
Quick Tests bei voice-service Aenderungen
Desktop-Alerts bei Fehlern aktiviert
Der lokale BQAS Scheduler ersetzt GitHub Actions und bietet DSGVO-konforme, vollstaendig lokale Test-Ausfuehrung.
| Feature | GitHub Actions | Lokaler Scheduler |
|---|---|---|
| Taegliche Tests (07:00) | schedule: cron | macOS launchd |
| Push-basierte Tests | on: push | Git post-commit Hook |
| PR-basierte Tests | on: pull_request | Nicht moeglich |
| DSGVO-Konformitaet | Daten bei GitHub (US) | 100% lokal |
| Offline-Faehig | Nein | Ja |
{`# ~/Library/LaunchAgents/com.breakpilot.bqas.plist
Label: com.breakpilot.bqas
Schedule: 07:00 taeglich
Script: /voice-service/scripts/run_bqas.sh
Logs: /var/log/bqas/`}
Der lokale BQAS Scheduler wurde entwickelt, um die gleiche Funktionalitaet wie GitHub Actions zu bieten, aber mit dem entscheidenden Vorteil, dass alle Daten zu 100% auf dem lokalen Mac Mini verbleiben. Dies ist besonders wichtig fuer DSGVO-Konformitaet, da keine Schuelerdaten oder Testergebnisse an externe Server uebertragen werden.
./voice-service/scripts/install_bqas_scheduler.sh install