name: KiteStacks CI on: push: branches: [main] pull_request: branches: [main] jobs: # ── Lint Docker Compose files ────────────────────────────────────────────── compose-lint: name: Validate compose files runs-on: docker container: image: docker:27-cli steps: - name: Checkout uses: actions/checkout@v4 - name: Install docker compose plugin run: apk add --no-cache docker-cli-compose - name: Validate all compose files run: | find apps -name "docker-compose.yml" | while read f; do echo "Checking $f ..." docker compose -f "$f" config --quiet && echo " OK" done # ── Secret leak detection ────────────────────────────────────────────────── secrets-check: name: Check for accidental secrets runs-on: docker container: image: alpine:3.20 steps: - name: Checkout uses: actions/checkout@v4 - name: Scan for plaintext secrets patterns run: | # Fail if any committed file contains common secret patterns # Add false-positive exclusions via .secretsignore if needed FAIL=0 check() { local pattern="$1" local label="$2" if git grep -qiP "${pattern}" -- ':!*.md' ':!docs/' ':!.forgejo/' 2>/dev/null; then echo "FAIL: possible ${label} found" git grep -ilP "${pattern}" -- ':!*.md' ':!docs/' ':!.forgejo/' FAIL=1 fi } check 'password\s*=\s*["\x27][^"\x27]{8,}' "plaintext password" check 'secret_?key\s*=\s*["\x27][A-Za-z0-9+/]{32,}' "hardcoded secret key" check 'TUNNEL_TOKEN\s*=\s*ey' "Cloudflare tunnel token" check '-----BEGIN.*PRIVATE KEY-----' "private key" exit ${FAIL} # ── Shell script checks ──────────────────────────────────────────────────── shellcheck: name: Shellcheck scripts runs-on: docker container: image: koalaman/shellcheck-alpine:stable steps: - name: Checkout uses: actions/checkout@v4 - name: Run shellcheck run: | find scripts -name "*.sh" -exec shellcheck {} +