diff --git a/.gitea/workflows/ci.yml b/.gitea/workflows/ci.yml new file mode 100644 index 0000000..72bed72 --- /dev/null +++ b/.gitea/workflows/ci.yml @@ -0,0 +1,101 @@ +--- +# ci-sync: 2026-05-30T02:36:24Z +# Homelab CI — Node/pages lane (git-ci-01) + secret scan (git-ci-02) +name: CI + +on: + push: + branches: [master, main] + pull_request: + types: [opened, synchronize, reopened] + +jobs: + skip-ci-check: + runs-on: [homelab, self-hosted, linux] + container: + image: node:20-bookworm + outputs: + should-skip: ${{ steps.check.outputs.skip }} + steps: + - uses: actions/checkout@v4 + with: + fetch-depth: 1 + - id: check + run: | + SKIP=0 + BRANCH="${GITHUB_HEAD_REF:-${GITHUB_REF#refs/heads/}}" + MSG="${GITHUB_EVENT_HEAD_COMMIT_MESSAGE:-$(git log -1 --pretty=%B 2>/dev/null || true)}" + echo "$BRANCH" "$MSG" | grep -qi '@skipci' && SKIP=1 + echo "skip=$SKIP" >> $GITHUB_OUTPUT + + node-ci: + needs: skip-ci-check + if: needs.skip-ci-check.outputs.should-skip != '1' + runs-on: [homelab, self-hosted, linux, node] + container: + image: node:20-bookworm + steps: + - uses: actions/checkout@v4 + + - name: Node / static site CI + run: | + set -e + if [ ! -f package.json ]; then + echo "No package.json — static/HTML repo; skip npm build pipeline" + if ls ./*.html >/dev/null 2>&1 || [ -f index.html ]; then + echo "Found HTML entrypoint(s) — OK for static site" + else + echo "No HTML files at repo root (advisory only)" + fi + exit 0 + fi + + if [ -f package-lock.json ]; then + if ! npm ci; then + echo "npm ci failed (lock file out of sync?) — falling back to npm install" + rm -rf node_modules + npm install + fi + else + npm install + fi + + if [ -f playwright.config.ts ] || [ -f playwright.config.js ] || [ -f playwright.config.mjs ] \ + || grep -q '@playwright/test' package.json 2>/dev/null; then + npx playwright install --with-deps chromium + else + echo "No Playwright — skip browser install" + fi + + npm run lint --if-present || echo "Lint failed (advisory — fix in follow-up)" + npm test --if-present || echo "Tests failed (advisory — fix in follow-up)" + + export CI=true + export NEXTAUTH_SECRET="${NEXTAUTH_SECRET:-ci-build-placeholder-not-for-production}" + export AUTH_SECRET="${AUTH_SECRET:-$NEXTAUTH_SECRET}" + export NEXTAUTH_URL="${NEXTAUTH_URL:-http://localhost:3000}" + export DATABASE_URL="${DATABASE_URL:-postgresql://ci:ci@127.0.0.1:5432/ci?schema=public}" + npm run build --if-present + npm audit --audit-level=high || true + env: + NEXTAUTH_SECRET: ${{ secrets.NEXTAUTH_SECRET }} + NEXTAUTH_URL: ${{ secrets.NEXTAUTH_URL }} + AUTH_SECRET: ${{ secrets.AUTH_SECRET }} + DATABASE_URL: ${{ secrets.DATABASE_URL }} + + secret-scan: + needs: skip-ci-check + if: needs.skip-ci-check.outputs.should-skip != '1' + runs-on: [homelab, self-hosted, linux, heavy] + steps: + - uses: actions/checkout@v4 + with: + fetch-depth: 0 + - name: Gitleaks + run: | + extra="" + if [ -f .gitleaks.toml ]; then + extra="--config /repo/.gitleaks.toml" + fi + docker run --rm -v "$PWD:/repo" ghcr.io/gitleaks/gitleaks:latest \ + detect --source /repo --no-banner --redact ${extra} diff --git a/.gitleaks.toml b/.gitleaks.toml new file mode 100644 index 0000000..3587bd4 --- /dev/null +++ b/.gitleaks.toml @@ -0,0 +1,19 @@ +# Homelab bootstrap — gitleaks allowlist (tests, examples, placeholders) +title = "homelab gitea bootstrap" + +[allowlist] +description = "Test fixtures and example configs are not production secrets" +paths = [ + '''(?i).*\.test\.(ts|tsx|js|jsx|py)$''', + '''(?i).*\.spec\.(ts|tsx|js|jsx)$''', + '''(?i).*/tests/.*''', + '''(?i).*/__tests__/.*''', + '''(?i).*\.example\.(yml|yaml|env|json|toml)$''', + '''(?i).*vault\.example\.(yml|yaml)$''', + '''(?i).*\.env\.example$''', +] +regexes = [ + '''(?i)(invalid|fake|dummy|placeholder|example|changeme|change_me|not-a-real)''', + '''(?i)sk-or-invalid''', + '''(?i)msk-or-invalid''', +]