diff --git a/.github/workflows/cd.yml b/.github/workflows/cd.yml new file mode 100644 index 0000000..ebaf321 --- /dev/null +++ b/.github/workflows/cd.yml @@ -0,0 +1,93 @@ +name: CD + +on: + workflow_run: + workflows: + - CI + types: + - completed + branches: + - main + workflow_dispatch: + +permissions: + contents: read + id-token: write + +concurrency: + group: cd-main + cancel-in-progress: false + +jobs: + check-secrets: + name: Check deploy prerequisites + runs-on: ubuntu-latest + outputs: + eligible_event: ${{ steps.check.outputs.eligible_event }} + secrets_ok: ${{ steps.check.outputs.secrets_ok }} + + steps: + - name: Evaluate trigger and required secrets + id: check + env: + GCP_PROJECT_ID: ${{ secrets.GCP_PROJECT_ID }} + GCP_WORKLOAD_IDENTITY_PROVIDER: ${{ secrets.GCP_WORKLOAD_IDENTITY_PROVIDER }} + GCP_SERVICE_ACCOUNT: ${{ secrets.GCP_SERVICE_ACCOUNT }} + run: | + eligible_event=false + if [[ "${{ github.event_name }}" == "workflow_dispatch" ]]; then + eligible_event=true + elif [[ "${{ github.event_name }}" == "workflow_run" && "${{ github.event.workflow_run.conclusion }}" == "success" ]]; then + eligible_event=true + fi + + secrets_ok=false + if [[ -n "$GCP_PROJECT_ID" && -n "$GCP_WORKLOAD_IDENTITY_PROVIDER" && -n "$GCP_SERVICE_ACCOUNT" ]]; then + secrets_ok=true + fi + + echo "eligible_event=$eligible_event" >> "$GITHUB_OUTPUT" + echo "secrets_ok=$secrets_ok" >> "$GITHUB_OUTPUT" + + deploy: + name: Deploy Cloud Run + runs-on: ubuntu-latest + needs: check-secrets + timeout-minutes: 30 + if: ${{ needs.check-secrets.outputs.eligible_event == 'true' && needs.check-secrets.outputs.secrets_ok == 'true' }} + + steps: + - name: Checkout deployment ref + uses: actions/checkout@v4 + with: + ref: ${{ github.event_name == 'workflow_run' && github.event.workflow_run.head_sha || github.sha }} + + - name: Authenticate to Google Cloud + uses: google-github-actions/auth@v2 + with: + workload_identity_provider: ${{ secrets.GCP_WORKLOAD_IDENTITY_PROVIDER }} + service_account: ${{ secrets.GCP_SERVICE_ACCOUNT }} + + - name: Setup gcloud + uses: google-github-actions/setup-gcloud@v2 + + - name: Deploy bot service + run: | + gcloud run deploy "${{ vars.CLOUD_RUN_SERVICE_BOT || 'household-bot' }}" \ + --source . \ + --region "${{ vars.GCP_REGION || 'europe-west1' }}" \ + --project "${{ secrets.GCP_PROJECT_ID }}" \ + --allow-unauthenticated \ + --quiet + + deploy-skipped: + name: Deploy skipped (missing config) + runs-on: ubuntu-latest + needs: check-secrets + if: ${{ needs.check-secrets.outputs.eligible_event == 'true' && needs.check-secrets.outputs.secrets_ok == 'false' }} + + steps: + - name: Print configuration hint + run: | + echo "CD skipped: configure required GitHub secrets." + echo "Required: GCP_PROJECT_ID, GCP_WORKLOAD_IDENTITY_PROVIDER, GCP_SERVICE_ACCOUNT" diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index f5fe25a..1eb65d7 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -8,11 +8,27 @@ on: branches: - main +permissions: + contents: read + +concurrency: + group: ci-${{ github.ref }} + cancel-in-progress: true + jobs: quality: - name: Quality Gates + name: Quality / ${{ matrix.task }} runs-on: ubuntu-latest timeout-minutes: 20 + strategy: + fail-fast: false + matrix: + task: + - format + - lint + - typecheck + - test + - build steps: - name: Checkout @@ -23,20 +39,39 @@ jobs: with: bun-version: 1.3.10 + - name: Restore Bun cache + uses: actions/cache@v4 + with: + path: | + ~/.bun/install/cache + node_modules + key: ${{ runner.os }}-bun-${{ hashFiles('bun.lock') }} + restore-keys: | + ${{ runner.os }}-bun- + - name: Install dependencies run: bun install --frozen-lockfile - - name: Check formatting - run: bun run format:check - - - name: Lint - run: bun run lint - - - name: Typecheck - run: bun run typecheck - - - name: Test - run: bun run test - - - name: Build - run: bun run build + - name: Run quality gate + run: | + case "${{ matrix.task }}" in + format) + bun run format:check + ;; + lint) + bun run lint + ;; + typecheck) + bun run typecheck + ;; + test) + bun run test + ;; + build) + bun run build + ;; + *) + echo "Unknown task: ${{ matrix.task }}" + exit 1 + ;; + esac diff --git a/docs/runbooks/dev-setup.md b/docs/runbooks/dev-setup.md index 1323c16..38dde37 100644 --- a/docs/runbooks/dev-setup.md +++ b/docs/runbooks/dev-setup.md @@ -42,4 +42,13 @@ bun run review:coderabbit - Linting uses `oxlint`. - Formatting uses `oxfmt` with no-semicolon style. - AI review uses CodeRabbit CLI in `--prompt-only` mode against `main`. -- `WHE-19` will add CI checks for the same root commands. + +## CI/CD + +- CI runs in parallel matrix jobs on push/PR to `main`: + - `format:check`, `lint`, `typecheck`, `test`, `build` +- CD deploys on successful `main` CI completion (or manual dispatch). +- CD is enabled when GitHub secrets are configured: + - `GCP_PROJECT_ID` + - `GCP_WORKLOAD_IDENTITY_PROVIDER` + - `GCP_SERVICE_ACCOUNT`