name: CI - Build & Test run-name: 🔍 CI ${{ gitea.ref_name }} by @${{ gitea.actor }} # ── Concurrency: cancel in-progress CI when new push arrives ── concurrency: group: ci-${{ gitea.ref }} cancel-in-progress: true on: push: branches: [main] pull_request: branches: [main] jobs: # ─── Backend ─────────────────────────────────── backend: name: Backend (.NET) runs-on: linux steps: - name: Checkout uses: actions/checkout@v4 - name: Setup .NET SDK uses: actions/setup-dotnet@v4 with: dotnet-version: '10.0.x' - name: Restore run: dotnet restore backend-tests/Nexus.Api.Tests.csproj - name: Build run: dotnet build backend-tests/Nexus.Api.Tests.csproj --no-restore --configuration Release - name: Test run: dotnet test backend-tests/Nexus.Api.Tests.csproj --no-build --configuration Release --verbosity normal # ─── Frontend ────────────────────────────────── frontend: name: Frontend (Vue/TS) runs-on: linux steps: - name: Checkout uses: actions/checkout@v4 - name: Setup Node.js uses: actions/setup-node@v4 with: node-version: '24' - name: Setup pnpm run: | corepack enable corepack prepare pnpm@latest --activate - name: Install dependencies run: pnpm install --frozen-lockfile working-directory: frontend - name: Type check run: pnpm exec vue-tsc --noEmit working-directory: frontend - name: Test run: pnpm test working-directory: frontend - name: Build run: pnpm build working-directory: frontend # ─── Security ────────────────────────────────── security: name: Security Check runs-on: linux if: github.ref == 'refs/heads/main' steps: - name: Checkout uses: actions/checkout@v4 - name: Check for .env leaks run: | echo "🔍 Scanning for potential secrets in source code..." HITS=$(grep -rPn "(API_KEY|SECRET|PASSWORD|TOKEN)\s*[:=]\s*['\"][^'\"]{8,}" --include="*.cs" --include="*.ts" --include="*.vue" backend/ frontend/src/ 2>/dev/null || true) if [ -n "$HITS" ]; then echo "❌ SECRET LEAK DETECTED — the following lines look like hardcoded credentials:" echo "$HITS" echo "" echo "Remove these values and use environment variables or a secrets manager instead." exit 1 fi # Secondary pass: catch bare assign patterns that are suspicious regardless of length LOOSE=$(grep -rPn "(API_KEY|SECRET|PASSWORD|TOKEN)\s*[:=]\s*['\"]" --include="*.cs" --include="*.ts" --include="*.vue" backend/ frontend/src/ 2>/dev/null || true) if [ -n "$LOOSE" ]; then echo "⚠️ WARNING — potential secrets found (short values may be false positives, review manually):" echo "$LOOSE" else echo "✅ No obvious secrets found" fi