feat(WHE-28): add terraform baseline for cloud run and scheduler

This commit is contained in:
2026-03-05 03:36:54 +04:00
parent 18168a8dab
commit d393c08263
19 changed files with 914 additions and 0 deletions

View File

@@ -75,3 +75,25 @@ jobs:
exit 1
;;
esac
terraform:
name: Terraform / validate
runs-on: ubuntu-latest
timeout-minutes: 10
steps:
- name: Checkout
uses: actions/checkout@v4
- name: Setup Terraform
uses: hashicorp/setup-terraform@v3
with:
terraform_version: 1.8.5
- name: Terraform format check
run: terraform -chdir=infra/terraform fmt -check -recursive
- name: Terraform validate
run: |
terraform -chdir=infra/terraform init -backend=false
terraform -chdir=infra/terraform validate

View File

@@ -1,9 +1,11 @@
# AGENTS.md
## Project
Household Telegram bot + mini app monorepo for shared rent/utilities/purchase accounting.
## Core stack
- Runtime/tooling: Bun
- Language: TypeScript (strict)
- Typecheck: tsgo (`@typescript/native-preview`)
@@ -15,24 +17,29 @@ Household Telegram bot + mini app monorepo for shared rent/utilities/purchase ac
- Deploy: Cloud Run + Cloud Scheduler (planned)
## Architecture
Hexagonal architecture:
- `packages/domain`: pure business rules/value objects only
- `packages/application`: use-cases only
- `packages/ports`: interfaces for external boundaries
- `apps/*`: composition/wiring and delivery endpoints
Boundary rules:
- No framework/DB/HTTP imports in `packages/domain`
- `packages/application` must not import adapter implementations
- External SDK usage belongs outside domain/application core
## Money and accounting rules
- Never use floating-point money math
- Store amounts in minor units
- Deterministic split behavior only
- Persist raw parsed purchase text + confidence + parser mode
## Workflow
- Work from Linear tickets and linked specs in `docs/specs/`
- One ticket at a time, small commits
- Before implementation: re-check ticket/spec and assumptions
@@ -40,7 +47,9 @@ Boundary rules:
- Run CodeRabbit review before merge (`bun run review:coderabbit`)
## Quality gates
Required before PR/merge:
- `bun run format:check`
- `bun run lint`
- `bun run typecheck`
@@ -48,6 +57,7 @@ Required before PR/merge:
- `bun run build`
## CI/CD
- CI workflow runs parallel quality jobs on push/PR to `main`
- CD workflow deploys on successful `main` CI or manual trigger
- Required CD secrets:
@@ -56,6 +66,7 @@ Required before PR/merge:
- `GCP_SERVICE_ACCOUNT`
## Docs as source of truth
- Roadmap: `docs/roadmap.md`
- Specs template: `docs/specs/README.md`
- ADRs: `docs/decisions/*`

View File

@@ -4,6 +4,7 @@
- Bun 1.3+
- Node.js 22+
- Terraform 1.8+ (for IaC checks/plans)
## First-time setup
@@ -21,6 +22,8 @@ bun run format:check
bun run typecheck
bun run test
bun run build
bun run infra:fmt:check
bun run infra:validate
```
## App commands
@@ -47,8 +50,13 @@ bun run review:coderabbit
- CI runs in parallel matrix jobs on push/PR to `main`:
- `format:check`, `lint`, `typecheck`, `test`, `build`
- `terraform fmt -check`, `terraform validate`
- 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`
## IaC Runbook
- See `docs/runbooks/iac-terraform.md` for provisioning flow.

View File

@@ -0,0 +1,55 @@
# Terraform IaC Runbook
## Purpose
Provision and maintain GCP infrastructure for bot API, mini app, scheduler, and runtime secrets.
## Prerequisites
- Terraform `>= 1.8`
- GCP project with billing enabled
- Local auth:
```bash
gcloud auth application-default login
```
## Bootstrap
```bash
cp infra/terraform/terraform.tfvars.example infra/terraform/terraform.tfvars
terraform -chdir=infra/terraform init
terraform -chdir=infra/terraform plan
terraform -chdir=infra/terraform apply
```
## Quality checks
```bash
bun run infra:fmt:check
bun run infra:validate
```
## Add secret values
After first apply, add secret versions:
```bash
echo -n "<telegram-webhook-secret>" | gcloud secrets versions add telegram-webhook-secret --data-file=- --project <project_id>
echo -n "<scheduler-shared-secret>" | gcloud secrets versions add scheduler-shared-secret --data-file=- --project <project_id>
```
## Environment strategy
- Keep separate states for `dev` and `prod`.
- Prefer separate GCP projects for stronger isolation.
- Keep environment-specific variables in dedicated `*.tfvars` files.
## Destructive operations
Review plan output before apply/destroy:
```bash
terraform -chdir=infra/terraform plan -destroy
terraform -chdir=infra/terraform destroy
```

View File

@@ -0,0 +1,77 @@
# HOUSEBOT-007 Terraform IaC Baseline
## Summary
Define a reproducible GCP infrastructure baseline for deployment of the bot API and mini app, including scheduling and secrets.
## Goals
- Provision Cloud Run services for bot API and mini app.
- Provision Cloud Scheduler reminder trigger.
- Provision Secret Manager placeholders and runtime access bindings.
- Provision Artifact Registry repository for container images.
- Provide optional GitHub OIDC Workload Identity resources.
## Non-goals
- Business feature implementation.
- Full observability stack (Grafana/Prometheus) in this ticket.
- Multi-region failover.
## Scope
- In: Terraform scaffold, docs, CI validation.
- Out: runtime deploy script rewrites, production dashboard configuration.
## Interfaces and Contracts
- Scheduler sends HTTP request to `POST /internal/scheduler/reminders`.
- Bot runtime reads secret-backed env vars:
- `TELEGRAM_WEBHOOK_SECRET`
- `SCHEDULER_SHARED_SECRET`
- `SUPABASE_URL` (optional)
- `SUPABASE_PUBLISHABLE_KEY` (optional)
## Domain Rules
- N/A (infrastructure-only change).
## Data Model Changes
- None.
## Security and Privacy
- Runtime access to secrets is explicit via `roles/secretmanager.secretAccessor`.
- Scheduler uses OIDC token with dedicated service account.
- GitHub OIDC setup is optional and repository-scoped.
## Observability
- Out of scope for this ticket.
## Edge Cases and Failure Modes
- Missing secret versions causes runtime startup/read failures.
- Scheduler remains paused by default to avoid accidental reminders.
- Incorrect `bot_api_image` or `mini_app_image` tags causes deployment failures.
## Test Plan
- Unit: N/A
- Integration: `terraform validate`
- E2E: Apply in dev project and verify service URLs + scheduler job presence.
## Acceptance Criteria
- [ ] `terraform plan` succeeds with provided vars.
- [ ] Two Cloud Run services and one Scheduler job are provisioned.
- [ ] Runtime secret access is bound explicitly.
- [ ] CI validates Terraform formatting and configuration.
- [ ] Runbook documents local and CI workflow.
## Rollout Plan
- Apply to dev first with scheduler paused.
- Add secret versions.
- Unpause scheduler after reminder endpoint is implemented and verified.

4
infra/terraform/.gitignore vendored Normal file
View File

@@ -0,0 +1,4 @@
.terraform
*.tfstate
*.tfstate.*
terraform.tfvars

22
infra/terraform/.terraform.lock.hcl generated Normal file
View File

@@ -0,0 +1,22 @@
# This file is maintained automatically by "terraform init".
# Manual edits may be lost in future updates.
provider "registry.terraform.io/hashicorp/google" {
version = "6.50.0"
constraints = "~> 6.0"
hashes = [
"h1:79CwMTsp3Ud1nOl5hFS5mxQHyT0fGVye7pqpU0PPlHI=",
"zh:1f3513fcfcbf7ca53d667a168c5067a4dd91a4d4cccd19743e248ff31065503c",
"zh:3da7db8fc2c51a77dd958ea8baaa05c29cd7f829bd8941c26e2ea9cb3aadc1e5",
"zh:3e09ac3f6ca8111cbb659d38c251771829f4347ab159a12db195e211c76068bb",
"zh:7bb9e41c568df15ccf1a8946037355eefb4dfb4e35e3b190808bb7c4abae547d",
"zh:81e5d78bdec7778e6d67b5c3544777505db40a826b6eb5abe9b86d4ba396866b",
"zh:8d309d020fb321525883f5c4ea864df3d5942b6087f6656d6d8b3a1377f340fc",
"zh:93e112559655ab95a523193158f4a4ac0f2bfed7eeaa712010b85ebb551d5071",
"zh:d3efe589ffd625b300cef5917c4629513f77e3a7b111c9df65075f76a46a63c7",
"zh:d4a4d672bbef756a870d8f32b35925f8ce2ef4f6bbd5b71a3cb764f1b6c85421",
"zh:e13a86bca299ba8a118e80d5f84fbdd708fe600ecdceea1a13d4919c068379fe",
"zh:f569b65999264a9416862bca5cd2a6177d94ccb0424f3a4ef424428912b9cb3c",
"zh:fec30c095647b583a246c39d557704947195a1b7d41f81e369ba377d997faef6",
]
}

79
infra/terraform/README.md Normal file
View File

@@ -0,0 +1,79 @@
# Terraform Infrastructure (WHE-28)
This directory contains baseline IaC for deploying the household bot platform on GCP.
## Provisioned resources
- Artifact Registry Docker repository
- Cloud Run service: bot API (public webhook endpoint)
- Cloud Run service: mini app (public web UI)
- Cloud Scheduler job for reminder triggers
- Runtime and scheduler service accounts with least-privilege bindings
- Secret Manager secrets (IDs only, secret values are added separately)
- Optional GitHub OIDC Workload Identity setup for deploy automation
## Architecture (v1)
- `bot-api`: Telegram webhook + app API endpoints
- `mini-app`: front-end delivery
- `scheduler`: triggers `bot-api` internal reminder endpoint using OIDC token
## Prerequisites
- Terraform `>= 1.8`
- Authenticated GCP CLI context (`gcloud auth application-default login` for local)
- Enabled billing on the target GCP project
## Usage
1. Initialize:
```bash
terraform -chdir=infra/terraform init
```
2. Prepare variables:
```bash
cp infra/terraform/terraform.tfvars.example infra/terraform/terraform.tfvars
```
3. Plan:
```bash
terraform -chdir=infra/terraform plan
```
4. Apply:
```bash
terraform -chdir=infra/terraform apply
```
5. Add secret values (after apply):
```bash
echo -n "<value>" | gcloud secrets versions add telegram-webhook-secret --data-file=- --project <project_id>
echo -n "<value>" | gcloud secrets versions add scheduler-shared-secret --data-file=- --project <project_id>
```
## Environments
Recommended approach:
- Keep one state per environment (dev/prod) using separate backend configs or workspaces
- Use `terraform.tfvars` per environment (`dev.tfvars`, `prod.tfvars`)
- Keep `project_id` separate for dev/prod when possible
## CI validation
CI runs:
- `terraform -chdir=infra/terraform fmt -check -recursive`
- `terraform -chdir=infra/terraform init -backend=false`
- `terraform -chdir=infra/terraform validate`
## Notes
- Scheduler job defaults to `paused = true` to prevent accidental sends before app logic is ready.
- Bot API is public to accept Telegram webhooks; scheduler endpoint should still verify app-level auth.

37
infra/terraform/locals.tf Normal file
View File

@@ -0,0 +1,37 @@
locals {
name_prefix = "${var.service_prefix}-${var.environment}"
common_labels = merge(
{
environment = var.environment
managed_by = "terraform"
project = "household-bot"
},
var.labels
)
artifact_location = coalesce(var.artifact_repository_location, var.region)
runtime_secret_ids = toset(compact([
var.telegram_webhook_secret_id,
var.scheduler_shared_secret_id,
var.supabase_url_secret_id,
var.supabase_publishable_key_secret_id
]))
api_services = toset([
"artifactregistry.googleapis.com",
"cloudscheduler.googleapis.com",
"iam.googleapis.com",
"iamcredentials.googleapis.com",
"run.googleapis.com",
"secretmanager.googleapis.com",
"sts.googleapis.com"
])
github_deploy_roles = toset([
"roles/artifactregistry.writer",
"roles/iam.serviceAccountUser",
"roles/run.admin"
])
}

218
infra/terraform/main.tf Normal file
View File

@@ -0,0 +1,218 @@
data "google_project" "current" {
project_id = var.project_id
}
resource "google_project_service" "enabled" {
for_each = local.api_services
project = var.project_id
service = each.value
disable_on_destroy = false
disable_dependent_services = false
}
resource "google_artifact_registry_repository" "containers" {
location = local.artifact_location
project = var.project_id
repository_id = var.artifact_repository_id
description = "Container images for household bot"
format = "DOCKER"
labels = local.common_labels
depends_on = [google_project_service.enabled]
}
resource "google_service_account" "bot_runtime" {
project = var.project_id
account_id = "${var.environment}-bot-runtime"
display_name = "${local.name_prefix} bot runtime"
}
resource "google_service_account" "mini_runtime" {
project = var.project_id
account_id = "${var.environment}-mini-runtime"
display_name = "${local.name_prefix} mini runtime"
}
resource "google_service_account" "scheduler_invoker" {
project = var.project_id
account_id = "${var.environment}-scheduler"
display_name = "${local.name_prefix} scheduler invoker"
}
resource "google_secret_manager_secret" "runtime" {
for_each = local.runtime_secret_ids
project = var.project_id
secret_id = each.value
replication {
auto {}
}
labels = local.common_labels
depends_on = [google_project_service.enabled]
}
resource "google_secret_manager_secret_iam_member" "bot_runtime_access" {
for_each = google_secret_manager_secret.runtime
project = var.project_id
secret_id = each.value.secret_id
role = "roles/secretmanager.secretAccessor"
member = "serviceAccount:${google_service_account.bot_runtime.email}"
}
module "bot_api_service" {
source = "./modules/cloud_run_service"
project_id = var.project_id
region = var.region
name = "${local.name_prefix}-bot-api"
service_account_email = google_service_account.bot_runtime.email
image = var.bot_api_image
allow_unauthenticated = true
min_instance_count = var.bot_min_instances
max_instance_count = var.bot_max_instances
labels = local.common_labels
env = {
NODE_ENV = var.environment
}
secret_env = merge(
{
TELEGRAM_WEBHOOK_SECRET = var.telegram_webhook_secret_id
SCHEDULER_SHARED_SECRET = var.scheduler_shared_secret_id
},
var.supabase_url_secret_id == null ? {} : {
SUPABASE_URL = var.supabase_url_secret_id
},
var.supabase_publishable_key_secret_id == null ? {} : {
SUPABASE_PUBLISHABLE_KEY = var.supabase_publishable_key_secret_id
}
)
depends_on = [
google_project_service.enabled,
google_secret_manager_secret.runtime
]
}
module "mini_app_service" {
source = "./modules/cloud_run_service"
project_id = var.project_id
region = var.region
name = "${local.name_prefix}-mini-app"
service_account_email = google_service_account.mini_runtime.email
image = var.mini_app_image
allow_unauthenticated = true
min_instance_count = var.mini_min_instances
max_instance_count = var.mini_max_instances
labels = local.common_labels
env = {
NODE_ENV = var.environment
}
depends_on = [google_project_service.enabled]
}
resource "google_cloud_run_v2_service_iam_member" "scheduler_invoker" {
project = var.project_id
location = var.region
name = module.bot_api_service.name
role = "roles/run.invoker"
member = "serviceAccount:${google_service_account.scheduler_invoker.email}"
}
resource "google_service_account_iam_member" "scheduler_token_creator" {
service_account_id = google_service_account.scheduler_invoker.name
role = "roles/iam.serviceAccountTokenCreator"
member = "serviceAccount:service-${data.google_project.current.number}@gcp-sa-cloudscheduler.iam.gserviceaccount.com"
}
resource "google_cloud_scheduler_job" "reminders" {
project = var.project_id
region = var.region
name = "${local.name_prefix}-reminders"
schedule = var.scheduler_cron
time_zone = var.scheduler_timezone
paused = var.scheduler_paused
http_target {
uri = "${module.bot_api_service.uri}${var.scheduler_path}"
http_method = var.scheduler_http_method
headers = {
"Content-Type" = "application/json"
}
body = base64encode(var.scheduler_body_json)
oidc_token {
service_account_email = google_service_account.scheduler_invoker.email
audience = module.bot_api_service.uri
}
}
depends_on = [
module.bot_api_service,
google_service_account_iam_member.scheduler_token_creator
]
}
resource "google_service_account" "github_deployer" {
count = var.create_workload_identity ? 1 : 0
project = var.project_id
account_id = var.github_deploy_service_account_id
display_name = "${local.name_prefix} GitHub deployer"
}
resource "google_iam_workload_identity_pool" "github" {
count = var.create_workload_identity ? 1 : 0
project = var.project_id
workload_identity_pool_id = var.workload_identity_pool_id
display_name = "GitHub Actions Pool"
}
resource "google_iam_workload_identity_pool_provider" "github" {
count = var.create_workload_identity ? 1 : 0
project = var.project_id
workload_identity_pool_id = google_iam_workload_identity_pool.github[0].workload_identity_pool_id
workload_identity_pool_provider_id = var.workload_identity_provider_id
display_name = "GitHub Actions Provider"
attribute_mapping = {
"google.subject" = "assertion.sub"
"attribute.actor" = "assertion.actor"
"attribute.repository" = "assertion.repository"
}
attribute_condition = "assertion.repository == \"${var.github_repository}\""
oidc {
issuer_uri = "https://token.actions.githubusercontent.com"
}
}
resource "google_service_account_iam_member" "github_oidc" {
count = var.create_workload_identity ? 1 : 0
service_account_id = google_service_account.github_deployer[0].name
role = "roles/iam.workloadIdentityUser"
member = "principalSet://iam.googleapis.com/${google_iam_workload_identity_pool.github[0].name}/attribute.repository/${var.github_repository}"
}
resource "google_project_iam_member" "github_deployer_roles" {
for_each = var.create_workload_identity ? local.github_deploy_roles : toset([])
project = var.project_id
role = each.value
member = "serviceAccount:${google_service_account.github_deployer[0].email}"
}

View File

@@ -0,0 +1,67 @@
resource "google_cloud_run_v2_service" "this" {
project = var.project_id
location = var.region
name = var.name
ingress = "INGRESS_TRAFFIC_ALL"
deletion_protection = false
labels = var.labels
template {
service_account = var.service_account_email
scaling {
min_instance_count = var.min_instance_count
max_instance_count = var.max_instance_count
}
containers {
image = var.image
ports {
container_port = var.container_port
}
resources {
limits = var.limits
}
dynamic "env" {
for_each = var.env
content {
name = env.key
value = env.value
}
}
dynamic "env" {
for_each = var.secret_env
content {
name = env.key
value_source {
secret_key_ref {
secret = env.value
version = "latest"
}
}
}
}
}
}
traffic {
percent = 100
type = "TRAFFIC_TARGET_ALLOCATION_TYPE_LATEST"
}
}
resource "google_cloud_run_v2_service_iam_member" "public_invoker" {
count = var.allow_unauthenticated ? 1 : 0
project = var.project_id
location = var.region
name = google_cloud_run_v2_service.this.name
role = "roles/run.invoker"
member = "allUsers"
}

View File

@@ -0,0 +1,7 @@
output "name" {
value = google_cloud_run_v2_service.this.name
}
output "uri" {
value = google_cloud_run_v2_service.this.uri
}

View File

@@ -0,0 +1,63 @@
variable "project_id" {
type = string
}
variable "region" {
type = string
}
variable "name" {
type = string
}
variable "image" {
type = string
}
variable "service_account_email" {
type = string
default = null
}
variable "allow_unauthenticated" {
type = bool
default = false
}
variable "env" {
type = map(string)
default = {}
}
variable "secret_env" {
type = map(string)
default = {}
}
variable "labels" {
type = map(string)
default = {}
}
variable "container_port" {
type = number
default = 8080
}
variable "min_instance_count" {
type = number
default = 0
}
variable "max_instance_count" {
type = number
default = 3
}
variable "limits" {
type = map(string)
default = {
cpu = "1"
memory = "512Mi"
}
}

View File

@@ -0,0 +1,44 @@
output "artifact_repository" {
description = "Artifact Registry repository"
value = google_artifact_registry_repository.containers.id
}
output "bot_api_service_name" {
description = "Cloud Run bot API service name"
value = module.bot_api_service.name
}
output "bot_api_service_url" {
description = "Cloud Run bot API URL"
value = module.bot_api_service.uri
}
output "mini_app_service_name" {
description = "Cloud Run mini app service name"
value = module.mini_app_service.name
}
output "mini_app_service_url" {
description = "Cloud Run mini app URL"
value = module.mini_app_service.uri
}
output "scheduler_job_name" {
description = "Cloud Scheduler job for reminders"
value = google_cloud_scheduler_job.reminders.name
}
output "runtime_secret_ids" {
description = "Secret Manager IDs expected by runtime"
value = sort([for secret in google_secret_manager_secret.runtime : secret.secret_id])
}
output "github_deployer_service_account" {
description = "GitHub OIDC deployer service account email"
value = var.create_workload_identity ? google_service_account.github_deployer[0].email : null
}
output "github_workload_identity_provider" {
description = "Full Workload Identity Provider resource name"
value = var.create_workload_identity ? google_iam_workload_identity_pool_provider.github[0].name : null
}

View File

@@ -0,0 +1,4 @@
provider "google" {
project = var.project_id
region = var.region
}

View File

@@ -0,0 +1,16 @@
project_id = "my-gcp-project"
region = "europe-west1"
environment = "dev"
service_prefix = "household"
artifact_repository_id = "household-bot"
bot_api_image = "europe-west1-docker.pkg.dev/my-gcp-project/household-bot/bot-api:latest"
mini_app_image = "europe-west1-docker.pkg.dev/my-gcp-project/household-bot/mini-app:latest"
scheduler_cron = "0 9 * * *"
scheduler_timezone = "Asia/Tbilisi"
scheduler_paused = true
create_workload_identity = true
github_repository = "whekin/household-bot"

View File

@@ -0,0 +1,167 @@
variable "project_id" {
description = "GCP project ID"
type = string
}
variable "region" {
description = "Primary GCP region for Cloud Run services"
type = string
default = "europe-west1"
}
variable "environment" {
description = "Environment name (e.g. dev, prod)"
type = string
default = "dev"
}
variable "service_prefix" {
description = "Prefix for service names"
type = string
default = "household"
}
variable "artifact_repository_id" {
description = "Artifact Registry repository ID"
type = string
default = "household-bot"
}
variable "artifact_repository_location" {
description = "Artifact Registry location (defaults to region)"
type = string
default = null
nullable = true
}
variable "bot_api_image" {
description = "Container image for bot API service"
type = string
}
variable "mini_app_image" {
description = "Container image for mini app service"
type = string
}
variable "telegram_webhook_secret_id" {
description = "Secret Manager ID for Telegram webhook secret token"
type = string
default = "telegram-webhook-secret"
}
variable "scheduler_shared_secret_id" {
description = "Secret Manager ID for app-level scheduler secret"
type = string
default = "scheduler-shared-secret"
}
variable "supabase_url_secret_id" {
description = "Optional Secret Manager ID for SUPABASE_URL"
type = string
default = null
nullable = true
}
variable "supabase_publishable_key_secret_id" {
description = "Optional Secret Manager ID for SUPABASE_PUBLISHABLE_KEY"
type = string
default = null
nullable = true
}
variable "scheduler_path" {
description = "Reminder endpoint path on bot API"
type = string
default = "/internal/scheduler/reminders"
}
variable "scheduler_http_method" {
description = "Scheduler HTTP method"
type = string
default = "POST"
}
variable "scheduler_cron" {
description = "Cron expression for reminder scheduler"
type = string
default = "0 9 * * *"
}
variable "scheduler_timezone" {
description = "Scheduler timezone"
type = string
default = "Asia/Tbilisi"
}
variable "scheduler_body_json" {
description = "JSON payload for scheduler requests"
type = string
default = "{\"kind\":\"monthly-reminder\"}"
}
variable "scheduler_paused" {
description = "Whether scheduler should be paused initially"
type = bool
default = true
}
variable "bot_min_instances" {
description = "Minimum bot API instances"
type = number
default = 0
}
variable "bot_max_instances" {
description = "Maximum bot API instances"
type = number
default = 3
}
variable "mini_min_instances" {
description = "Minimum mini app instances"
type = number
default = 0
}
variable "mini_max_instances" {
description = "Maximum mini app instances"
type = number
default = 2
}
variable "labels" {
description = "Additional labels"
type = map(string)
default = {}
}
variable "create_workload_identity" {
description = "Create GitHub OIDC Workload Identity resources"
type = bool
default = false
}
variable "github_repository" {
description = "GitHub repository in owner/repo format"
type = string
default = "whekin/household-bot"
}
variable "workload_identity_pool_id" {
description = "Workload Identity Pool ID"
type = string
default = "github-pool"
}
variable "workload_identity_provider_id" {
description = "Workload Identity Provider ID"
type = string
default = "github-provider"
}
variable "github_deploy_service_account_id" {
description = "Service account ID used by GitHub Actions via OIDC"
type = string
default = "github-deployer"
}

View File

@@ -0,0 +1,10 @@
terraform {
required_version = ">= 1.8.0"
required_providers {
google = {
source = "hashicorp/google"
version = "~> 6.0"
}
}
}

View File

@@ -15,6 +15,9 @@
"format": "bunx oxfmt .",
"format:check": "bunx oxfmt --check .",
"review:coderabbit": "coderabbit --prompt-only --base main || ~/.local/bin/coderabbit --prompt-only --base main",
"infra:fmt": "terraform -chdir=infra/terraform fmt -recursive",
"infra:fmt:check": "terraform -chdir=infra/terraform fmt -check -recursive",
"infra:validate": "terraform -chdir=infra/terraform init -backend=false && terraform -chdir=infra/terraform validate",
"dev:bot": "bun run --filter @household/bot dev",
"dev:miniapp": "bun run --filter @household/miniapp dev"
},