@ripla/godd-mcp 0.1.2-beta-20260312091403

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (180) hide show
  1. package/README.md +412 -0
  2. package/dist/godd.cjs +465 -0
  3. package/dist/godd.js +34845 -0
  4. package/dist/index.js +224 -0
  5. package/notes-api/.env.example +29 -0
  6. package/notes-api/README.md +65 -0
  7. package/notes-api/alembic/README +1 -0
  8. package/notes-api/alembic/env.py +62 -0
  9. package/notes-api/alembic/script.py.mako +28 -0
  10. package/notes-api/alembic/versions/001_initial_notes_tables.py +71 -0
  11. package/notes-api/alembic.ini +149 -0
  12. package/notes-api/app/__init__.py +1 -0
  13. package/notes-api/app/config.py +90 -0
  14. package/notes-api/app/database.py +31 -0
  15. package/notes-api/app/main.py +55 -0
  16. package/notes-api/app/models/__init__.py +17 -0
  17. package/notes-api/app/models/audit.py +38 -0
  18. package/notes-api/app/models/base.py +38 -0
  19. package/notes-api/app/models/session.py +32 -0
  20. package/notes-api/app/models/setting.py +16 -0
  21. package/notes-api/app/models/user.py +38 -0
  22. package/notes-api/app/routers/__init__.py +1 -0
  23. package/notes-api/app/routers/auth.py +205 -0
  24. package/notes-api/app/routers/files.py +76 -0
  25. package/notes-api/app/routers/pr.py +92 -0
  26. package/notes-api/app/routers/settings.py +44 -0
  27. package/notes-api/app/routers/tree.py +22 -0
  28. package/notes-api/app/routers/users.py +145 -0
  29. package/notes-api/app/schemas/__init__.py +1 -0
  30. package/notes-api/app/schemas/auth.py +45 -0
  31. package/notes-api/app/schemas/user.py +41 -0
  32. package/notes-api/app/security.py +94 -0
  33. package/notes-api/app/services/__init__.py +1 -0
  34. package/notes-api/app/services/audit.py +25 -0
  35. package/notes-api/app/services/business_date.py +38 -0
  36. package/notes-api/app/services/github.py +140 -0
  37. package/notes-api/app/services/github_pr.py +120 -0
  38. package/notes-api/app/services/pr_chain.py +225 -0
  39. package/notes-api/app/startup.py +66 -0
  40. package/notes-api/docker/Dockerfile +14 -0
  41. package/notes-api/docker/Dockerfile.test +14 -0
  42. package/notes-api/pyproject.toml +53 -0
  43. package/notes-api/tests/conftest.py +21 -0
  44. package/notes-api/tests/test_business_date.py +32 -0
  45. package/notes-api/tests/test_health.py +11 -0
  46. package/notes-api/uv.lock +1012 -0
  47. package/notes-app/README.md +30 -0
  48. package/notes-app/docker/Dockerfile.prod +12 -0
  49. package/notes-app/docker/Dockerfile.test +12 -0
  50. package/notes-app/eslint.config.js +23 -0
  51. package/notes-app/index.html +12 -0
  52. package/notes-app/nginx.conf +23 -0
  53. package/notes-app/package.json +37 -0
  54. package/notes-app/pnpm-lock.yaml +3124 -0
  55. package/notes-app/src/App.tsx +13 -0
  56. package/notes-app/src/main.tsx +13 -0
  57. package/notes-app/src/pages/LoginPage.test.tsx +15 -0
  58. package/notes-app/src/pages/LoginPage.tsx +10 -0
  59. package/notes-app/src/pages/MainPage.tsx +14 -0
  60. package/notes-app/src/styles/globals.css +1 -0
  61. package/notes-app/src/vite-env.d.ts +1 -0
  62. package/notes-app/tsconfig.json +24 -0
  63. package/notes-app/tsconfig.node.json +10 -0
  64. package/notes-app/vite.config.js +21 -0
  65. package/notes-app/vite.config.ts +22 -0
  66. package/notes-app/vitest.config.ts +16 -0
  67. package/package.json +58 -0
  68. package/stacks/django-vue.yaml +33 -0
  69. package/stacks/fastapi-react.yaml +160 -0
  70. package/stacks/flutter-firebase.yaml +58 -0
  71. package/stacks/nextjs-prisma.yaml +33 -0
  72. package/stacks/schema.json +144 -0
  73. package/templates/agents/architect.hbs +183 -0
  74. package/templates/agents/auto-documenter.hbs +36 -0
  75. package/templates/agents/backend-developer.hbs +175 -0
  76. package/templates/agents/code-reviewer.hbs +29 -0
  77. package/templates/agents/database-developer.hbs +52 -0
  78. package/templates/agents/debug-specialist.hbs +39 -0
  79. package/templates/agents/environment-setup.hbs +41 -0
  80. package/templates/agents/frontend-developer.hbs +45 -0
  81. package/templates/agents/integration-qa.hbs +26 -0
  82. package/templates/agents/performance-qa.hbs +24 -0
  83. package/templates/agents/pr-writer.hbs +53 -0
  84. package/templates/agents/quality-lead.hbs +26 -0
  85. package/templates/agents/requirements-analyst.hbs +26 -0
  86. package/templates/agents/security-analyst.hbs +21 -0
  87. package/templates/agents/security-reviewer.hbs +20 -0
  88. package/templates/agents/ssot-updater.hbs +45 -0
  89. package/templates/agents/technical-writer.hbs +28 -0
  90. package/templates/agents/unit-test-engineer.hbs +19 -0
  91. package/templates/agents/user-clone.hbs +43 -0
  92. package/templates/github-actions/aws/deploy-notes.yml.hbs +118 -0
  93. package/templates/github-actions/azure/deploy-notes.yml.hbs +78 -0
  94. package/templates/github-actions/gcp/deploy-notes.yml.hbs +76 -0
  95. package/templates/github-actions/vercel-railway/deploy-notes.yml.hbs +49 -0
  96. package/templates/mindsets/agent-stdio.hbs +51 -0
  97. package/templates/mindsets/dart.hbs +39 -0
  98. package/templates/mindsets/ddd-clean-architecture.hbs +52 -0
  99. package/templates/mindsets/electron.hbs +41 -0
  100. package/templates/mindsets/fastapi.hbs +40 -0
  101. package/templates/mindsets/flutter.hbs +56 -0
  102. package/templates/mindsets/kotlin.hbs +46 -0
  103. package/templates/mindsets/postgresql.hbs +39 -0
  104. package/templates/mindsets/python.hbs +39 -0
  105. package/templates/mindsets/react.hbs +51 -0
  106. package/templates/mindsets/swift.hbs +46 -0
  107. package/templates/mindsets/terraform.hbs +53 -0
  108. package/templates/mindsets/typescript.hbs +34 -0
  109. package/templates/mindsets/vite.hbs +37 -0
  110. package/templates/notes-compose.yml +79 -0
  111. package/templates/partials/cogito-geo.hbs +84 -0
  112. package/templates/partials/cogito-godd.hbs +22 -0
  113. package/templates/partials/cogito-grammar.hbs +105 -0
  114. package/templates/partials/cogito-particle.hbs +76 -0
  115. package/templates/partials/cogito-root-ai.hbs +79 -0
  116. package/templates/partials/cogito-root-arch.hbs +75 -0
  117. package/templates/partials/cogito-root-core.hbs +75 -0
  118. package/templates/partials/cogito-root-daily.hbs +58 -0
  119. package/templates/partials/cogito-root-dev.hbs +71 -0
  120. package/templates/partials/cogito-root-devops.hbs +71 -0
  121. package/templates/partials/cogito-root-framework.hbs +80 -0
  122. package/templates/partials/cogito-root-lang.hbs +74 -0
  123. package/templates/partials/godd-core.hbs +50 -0
  124. package/templates/partials/project-context.hbs +52 -0
  125. package/templates/partials/ssot-header.hbs +40 -0
  126. package/templates/partials/ssot-rules.hbs +30 -0
  127. package/templates/prompts/adr.hbs +39 -0
  128. package/templates/prompts/agent-dev-workflow.hbs +34 -0
  129. package/templates/prompts/analyze-report.hbs +36 -0
  130. package/templates/prompts/auto-documentation.hbs +37 -0
  131. package/templates/prompts/check.hbs +52 -0
  132. package/templates/prompts/commit.hbs +96 -0
  133. package/templates/prompts/dev.hbs +127 -0
  134. package/templates/prompts/docs-init.hbs +95 -0
  135. package/templates/prompts/docs-update.hbs +51 -0
  136. package/templates/prompts/git-workflow.hbs +125 -0
  137. package/templates/prompts/impact-analysis.hbs +43 -0
  138. package/templates/prompts/install.hbs +40 -0
  139. package/templates/prompts/new-branch.hbs +61 -0
  140. package/templates/prompts/notes-deploy.hbs +72 -0
  141. package/templates/prompts/pr-analyze.hbs +44 -0
  142. package/templates/prompts/pr-create.hbs +125 -0
  143. package/templates/prompts/push-execute.hbs +83 -0
  144. package/templates/prompts/push.hbs +24 -0
  145. package/templates/prompts/release-notes.hbs +38 -0
  146. package/templates/prompts/requirements-to-business-flow.hbs +29 -0
  147. package/templates/prompts/requirements-to-tickets.hbs +26 -0
  148. package/templates/prompts/review.hbs +126 -0
  149. package/templates/prompts/setup.hbs +247 -0
  150. package/templates/prompts/spec.hbs +35 -0
  151. package/templates/prompts/specification-to-tickets.hbs +24 -0
  152. package/templates/prompts/submit.hbs +143 -0
  153. package/templates/prompts/sync.hbs +63 -0
  154. package/templates/prompts/test-plan.hbs +56 -0
  155. package/templates/terraform/aws/alb.tf.hbs +58 -0
  156. package/templates/terraform/aws/backend.tf.hbs +10 -0
  157. package/templates/terraform/aws/cloudfront.tf.hbs +96 -0
  158. package/templates/terraform/aws/ecr.tf.hbs +30 -0
  159. package/templates/terraform/aws/ecs.tf.hbs +136 -0
  160. package/templates/terraform/aws/iam.tf.hbs +88 -0
  161. package/templates/terraform/aws/local.tf.hbs +34 -0
  162. package/templates/terraform/aws/output.tf.hbs +49 -0
  163. package/templates/terraform/aws/provider.tf.hbs +26 -0
  164. package/templates/terraform/aws/rds.tf.hbs +38 -0
  165. package/templates/terraform/aws/s3.tf.hbs +34 -0
  166. package/templates/terraform/aws/secrets.tf.hbs +38 -0
  167. package/templates/terraform/aws/security_groups.tf.hbs +72 -0
  168. package/templates/terraform/aws/vpc.tf.hbs +63 -0
  169. package/templates/terraform/azure/backend.tf.hbs +8 -0
  170. package/templates/terraform/azure/iam.tf.hbs +36 -0
  171. package/templates/terraform/azure/main.tf.hbs +199 -0
  172. package/templates/terraform/azure/output.tf.hbs +19 -0
  173. package/templates/terraform/azure/provider.tf.hbs +19 -0
  174. package/templates/terraform/gcp/backend.tf.hbs +6 -0
  175. package/templates/terraform/gcp/iam.tf.hbs +47 -0
  176. package/templates/terraform/gcp/main.tf.hbs +171 -0
  177. package/templates/terraform/gcp/output.tf.hbs +15 -0
  178. package/templates/terraform/gcp/provider.tf.hbs +19 -0
  179. package/templates/terraform/vercel-railway/main.tf.hbs +64 -0
  180. package/templates/terraform/vercel-railway/output.tf.hbs +11 -0
@@ -0,0 +1,63 @@
1
+ resource "aws_vpc" "main" {
2
+ cidr_block = local.vpc_cidr
3
+ enable_dns_support = true
4
+ enable_dns_hostnames = true
5
+
6
+ tags = merge(local.tags, { Name = "${local.project}-vpc" })
7
+ }
8
+
9
+ resource "aws_internet_gateway" "main" {
10
+ vpc_id = aws_vpc.main.id
11
+ tags = merge(local.tags, { Name = "${local.project}-igw" })
12
+ }
13
+
14
+ resource "aws_subnet" "public_1a" {
15
+ vpc_id = aws_vpc.main.id
16
+ cidr_block = local.public_subnet_1a
17
+ availability_zone = "{{region}}a"
18
+ map_public_ip_on_launch = true
19
+ tags = merge(local.tags, { Name = "${local.project}-public-1a" })
20
+ }
21
+
22
+ resource "aws_subnet" "public_1c" {
23
+ vpc_id = aws_vpc.main.id
24
+ cidr_block = local.public_subnet_1c
25
+ availability_zone = "{{region}}c"
26
+ map_public_ip_on_launch = true
27
+ tags = merge(local.tags, { Name = "${local.project}-public-1c" })
28
+ }
29
+
30
+ resource "aws_subnet" "private_1a" {
31
+ vpc_id = aws_vpc.main.id
32
+ cidr_block = local.private_subnet_1a
33
+ availability_zone = "{{region}}a"
34
+ tags = merge(local.tags, { Name = "${local.project}-private-1a" })
35
+ }
36
+
37
+ resource "aws_subnet" "private_1c" {
38
+ vpc_id = aws_vpc.main.id
39
+ cidr_block = local.private_subnet_1c
40
+ availability_zone = "{{region}}c"
41
+ tags = merge(local.tags, { Name = "${local.project}-private-1c" })
42
+ }
43
+
44
+ resource "aws_route_table" "public" {
45
+ vpc_id = aws_vpc.main.id
46
+ tags = merge(local.tags, { Name = "${local.project}-public-rt" })
47
+ }
48
+
49
+ resource "aws_route" "public_internet" {
50
+ route_table_id = aws_route_table.public.id
51
+ destination_cidr_block = "0.0.0.0/0"
52
+ gateway_id = aws_internet_gateway.main.id
53
+ }
54
+
55
+ resource "aws_route_table_association" "public_1a" {
56
+ subnet_id = aws_subnet.public_1a.id
57
+ route_table_id = aws_route_table.public.id
58
+ }
59
+
60
+ resource "aws_route_table_association" "public_1c" {
61
+ subnet_id = aws_subnet.public_1c.id
62
+ route_table_id = aws_route_table.public.id
63
+ }
@@ -0,0 +1,8 @@
1
+ terraform {
2
+ backend "azurerm" {
3
+ resource_group_name = "godd-notes-tfstate"
4
+ storage_account_name = "goddnotestfstate"
5
+ container_name = "tfstate"
6
+ key = "{{env}}/terraform.tfstate"
7
+ }
8
+ }
@@ -0,0 +1,36 @@
1
+ # --- Federated Credentials (GitHub Actions OIDC) ---
2
+ data "azurerm_client_config" "current" {}
3
+
4
+ resource "azurerm_user_assigned_identity" "deploy" {
5
+ name = "godd-deploy"
6
+ resource_group_name = azurerm_resource_group.main.name
7
+ location = local.location
8
+ tags = local.tags
9
+ }
10
+
11
+ resource "azurerm_federated_identity_credential" "github" {
12
+ name = "github-actions"
13
+ resource_group_name = azurerm_resource_group.main.name
14
+ parent_id = azurerm_user_assigned_identity.deploy.id
15
+ audience = ["api://AzureADTokenExchange"]
16
+ issuer = "https://token.actions.githubusercontent.com"
17
+ subject = "repo:{{githubOwner}}/{{githubRepo}}:ref:refs/heads/main"
18
+ }
19
+
20
+ resource "azurerm_role_assignment" "acr_push" {
21
+ scope = azurerm_container_registry.main.id
22
+ role_definition_name = "AcrPush"
23
+ principal_id = azurerm_user_assigned_identity.deploy.principal_id
24
+ }
25
+
26
+ resource "azurerm_role_assignment" "container_app_contributor" {
27
+ scope = azurerm_resource_group.main.id
28
+ role_definition_name = "Contributor"
29
+ principal_id = azurerm_user_assigned_identity.deploy.principal_id
30
+ }
31
+
32
+ resource "azurerm_role_assignment" "storage_contributor" {
33
+ scope = azurerm_storage_account.notes_app.id
34
+ role_definition_name = "Storage Blob Data Contributor"
35
+ principal_id = azurerm_user_assigned_identity.deploy.principal_id
36
+ }
@@ -0,0 +1,199 @@
1
+ locals {
2
+ project = "godd-ripla-notes"
3
+ env = "{{env}}"
4
+ location = "{{region}}"
5
+
6
+ github_owner = "{{githubOwner}}"
7
+ github_repo = "{{githubRepo}}"
8
+
9
+ tags = {
10
+ project = local.project
11
+ environment = local.env
12
+ managed-by = "terraform"
13
+ }
14
+ }
15
+
16
+ # --- Resource Group ---
17
+ resource "azurerm_resource_group" "main" {
18
+ name = "${local.project}-${local.env}"
19
+ location = local.location
20
+ tags = local.tags
21
+ }
22
+
23
+ # --- VNet ---
24
+ resource "azurerm_virtual_network" "main" {
25
+ name = "${local.project}-vnet"
26
+ resource_group_name = azurerm_resource_group.main.name
27
+ location = local.location
28
+ address_space = ["{{vpcCidr}}"]
29
+ tags = local.tags
30
+ }
31
+
32
+ resource "azurerm_subnet" "app" {
33
+ name = "app-subnet"
34
+ resource_group_name = azurerm_resource_group.main.name
35
+ virtual_network_name = azurerm_virtual_network.main.name
36
+ address_prefixes = [cidrsubnet("{{vpcCidr}}", 8, 1)]
37
+
38
+ delegation {
39
+ name = "container-apps"
40
+ service_delegation {
41
+ name = "Microsoft.App/environments"
42
+ actions = [
43
+ "Microsoft.Network/virtualNetworks/subnets/join/action"
44
+ ]
45
+ }
46
+ }
47
+ }
48
+
49
+ resource "azurerm_subnet" "db" {
50
+ name = "db-subnet"
51
+ resource_group_name = azurerm_resource_group.main.name
52
+ virtual_network_name = azurerm_virtual_network.main.name
53
+ address_prefixes = [cidrsubnet("{{vpcCidr}}", 8, 2)]
54
+
55
+ delegation {
56
+ name = "postgresql"
57
+ service_delegation {
58
+ name = "Microsoft.DBforPostgreSQL/flexibleServers"
59
+ actions = [
60
+ "Microsoft.Network/virtualNetworks/subnets/join/action"
61
+ ]
62
+ }
63
+ }
64
+ }
65
+
66
+ # --- PostgreSQL Flexible Server ---
67
+ resource "random_password" "db_password" {
68
+ length = 32
69
+ special = false
70
+ }
71
+
72
+ resource "azurerm_postgresql_flexible_server" "main" {
73
+ name = "${local.project}-db-${local.env}"
74
+ resource_group_name = azurerm_resource_group.main.name
75
+ location = local.location
76
+ version = "16"
77
+ administrator_login = "app_user"
78
+ administrator_password = random_password.db_password.result
79
+ sku_name = "{{dbSku}}"
80
+ storage_mb = 32768
81
+ delegated_subnet_id = azurerm_subnet.db.id
82
+ zone = "1"
83
+ tags = local.tags
84
+ }
85
+
86
+ resource "azurerm_postgresql_flexible_server_database" "notes" {
87
+ name = "notes"
88
+ server_id = azurerm_postgresql_flexible_server.main.id
89
+ }
90
+
91
+ # --- Container Registry ---
92
+ resource "azurerm_container_registry" "main" {
93
+ name = replace("${local.project}${local.env}acr", "-", "")
94
+ resource_group_name = azurerm_resource_group.main.name
95
+ location = local.location
96
+ sku = "Basic"
97
+ admin_enabled = false
98
+ tags = local.tags
99
+ }
100
+
101
+ # --- Log Analytics ---
102
+ resource "azurerm_log_analytics_workspace" "main" {
103
+ name = "${local.project}-logs"
104
+ resource_group_name = azurerm_resource_group.main.name
105
+ location = local.location
106
+ sku = "PerGB2018"
107
+ retention_in_days = 30
108
+ tags = local.tags
109
+ }
110
+
111
+ # --- Container Apps Environment ---
112
+ resource "azurerm_container_app_environment" "main" {
113
+ name = "${local.project}-env"
114
+ resource_group_name = azurerm_resource_group.main.name
115
+ location = local.location
116
+ log_analytics_workspace_id = azurerm_log_analytics_workspace.main.id
117
+ tags = local.tags
118
+ }
119
+
120
+ # --- Container App (notes-api) ---
121
+ resource "azurerm_container_app" "api" {
122
+ name = "${local.project}-api"
123
+ container_app_environment_id = azurerm_container_app_environment.main.id
124
+ resource_group_name = azurerm_resource_group.main.name
125
+ revision_mode = "Single"
126
+ tags = local.tags
127
+
128
+ template {
129
+ container {
130
+ name = "notes-api"
131
+ image = "${azurerm_container_registry.main.login_server}/notes-api:latest"
132
+ cpu = {{cpuLimit}}
133
+ memory = "{{memoryLimit}}"
134
+
135
+ env {
136
+ name = "DB_NAME"
137
+ value = "notes"
138
+ }
139
+ env {
140
+ name = "API_HOST"
141
+ value = "0.0.0.0"
142
+ }
143
+ env {
144
+ name = "API_PORT"
145
+ value = "3100"
146
+ }
147
+ }
148
+
149
+ min_replicas = 0
150
+ max_replicas = 2
151
+ }
152
+
153
+ ingress {
154
+ external_enabled = true
155
+ target_port = 3100
156
+ transport = "auto"
157
+
158
+ traffic_weight {
159
+ percentage = 100
160
+ latest_revision = true
161
+ }
162
+ }
163
+ }
164
+
165
+ # --- Storage Account (notes-app SPA) ---
166
+ resource "azurerm_storage_account" "notes_app" {
167
+ name = replace("${local.project}${local.env}app", "-", "")
168
+ resource_group_name = azurerm_resource_group.main.name
169
+ location = local.location
170
+ account_tier = "Standard"
171
+ account_replication_type = "LRS"
172
+ tags = local.tags
173
+
174
+ static_website {
175
+ index_document = "index.html"
176
+ error_404_document = "index.html"
177
+ }
178
+ }
179
+
180
+ # --- CDN (Front Door alternative — CDN Profile + Endpoint) ---
181
+ resource "azurerm_cdn_profile" "main" {
182
+ name = "${local.project}-cdn"
183
+ resource_group_name = azurerm_resource_group.main.name
184
+ location = "global"
185
+ sku = "Standard_Microsoft"
186
+ tags = local.tags
187
+ }
188
+
189
+ resource "azurerm_cdn_endpoint" "app" {
190
+ name = "${local.project}-app"
191
+ profile_name = azurerm_cdn_profile.main.name
192
+ resource_group_name = azurerm_resource_group.main.name
193
+ location = "global"
194
+
195
+ origin {
196
+ name = "spa"
197
+ host_name = azurerm_storage_account.notes_app.primary_web_host
198
+ }
199
+ }
@@ -0,0 +1,19 @@
1
+ output "container_app_url" {
2
+ value = "https://${azurerm_container_app.api.latest_revision_fqdn}"
3
+ }
4
+
5
+ output "notes_app_url" {
6
+ value = azurerm_storage_account.notes_app.primary_web_endpoint
7
+ }
8
+
9
+ output "cdn_endpoint" {
10
+ value = azurerm_cdn_endpoint.app.fqdn
11
+ }
12
+
13
+ output "acr_login_server" {
14
+ value = azurerm_container_registry.main.login_server
15
+ }
16
+
17
+ output "db_host" {
18
+ value = azurerm_postgresql_flexible_server.main.fqdn
19
+ }
@@ -0,0 +1,19 @@
1
+ terraform {
2
+ required_version = ">= 1.5"
3
+
4
+ required_providers {
5
+ azurerm = {
6
+ source = "hashicorp/azurerm"
7
+ version = "~> 3.80"
8
+ }
9
+ random = {
10
+ source = "hashicorp/random"
11
+ version = "~> 3.0"
12
+ }
13
+ }
14
+ }
15
+
16
+ provider "azurerm" {
17
+ features {}
18
+ subscription_id = "{{subscriptionId}}"
19
+ }
@@ -0,0 +1,6 @@
1
+ terraform {
2
+ backend "gcs" {
3
+ bucket = "godd-ripla-notes-tfstate-{{projectId}}"
4
+ prefix = "{{env}}/terraform.tfstate"
5
+ }
6
+ }
@@ -0,0 +1,47 @@
1
+ # --- Workload Identity Federation (GitHub Actions OIDC) ---
2
+ resource "google_iam_workload_identity_pool" "github" {
3
+ workload_identity_pool_id = "github-pool"
4
+ display_name = "GitHub Pool"
5
+ }
6
+
7
+ resource "google_iam_workload_identity_pool_provider" "github" {
8
+ workload_identity_pool_id = google_iam_workload_identity_pool.github.workload_identity_pool_id
9
+ workload_identity_pool_provider_id = "github-provider"
10
+ display_name = "GitHub Provider"
11
+
12
+ attribute_mapping = {
13
+ "google.subject" = "assertion.sub"
14
+ "attribute.repository" = "assertion.repository"
15
+ "attribute.actor" = "assertion.actor"
16
+ }
17
+
18
+ oidc {
19
+ issuer_uri = "https://token.actions.githubusercontent.com"
20
+ }
21
+
22
+ attribute_condition = "assertion.repository == '{{githubOwner}}/{{githubRepo}}'"
23
+ }
24
+
25
+ resource "google_service_account" "deploy" {
26
+ account_id = "godd-deploy"
27
+ display_name = "GoDD Deploy Service Account"
28
+ }
29
+
30
+ resource "google_service_account_iam_member" "workload_identity" {
31
+ service_account_id = google_service_account.deploy.name
32
+ role = "roles/iam.workloadIdentityUser"
33
+ member = "principalSet://iam.googleapis.com/${google_iam_workload_identity_pool.github.name}/attribute.repository/{{githubOwner}}/{{githubRepo}}"
34
+ }
35
+
36
+ resource "google_project_iam_member" "deploy_roles" {
37
+ for_each = toset([
38
+ "roles/run.admin",
39
+ "roles/artifactregistry.writer",
40
+ "roles/storage.admin",
41
+ "roles/iam.serviceAccountUser",
42
+ ])
43
+
44
+ project = "{{projectId}}"
45
+ role = each.key
46
+ member = "serviceAccount:${google_service_account.deploy.email}"
47
+ }
@@ -0,0 +1,171 @@
1
+ locals {
2
+ project = "godd-ripla-notes"
3
+ env = "{{env}}"
4
+ project_id = "{{projectId}}"
5
+ region = "{{region}}"
6
+
7
+ github_owner = "{{githubOwner}}"
8
+ github_repo = "{{githubRepo}}"
9
+
10
+ labels = {
11
+ project = local.project
12
+ environment = local.env
13
+ managed-by = "terraform"
14
+ }
15
+ }
16
+
17
+ # --- VPC ---
18
+ resource "google_compute_network" "main" {
19
+ name = "${local.project}-vpc"
20
+ auto_create_subnetworks = false
21
+ }
22
+
23
+ resource "google_compute_subnetwork" "main" {
24
+ name = "${local.project}-subnet"
25
+ ip_cidr_range = "{{vpcCidr}}"
26
+ region = local.region
27
+ network = google_compute_network.main.id
28
+ }
29
+
30
+ # --- Cloud SQL (PostgreSQL) ---
31
+ resource "google_sql_database_instance" "main" {
32
+ name = "${local.project}-db-${local.env}"
33
+ database_version = "POSTGRES_16"
34
+ region = local.region
35
+
36
+ settings {
37
+ tier = "{{dbTier}}"
38
+ availability_type = "ZONAL"
39
+ disk_size = 20
40
+ disk_type = "PD_SSD"
41
+
42
+ ip_configuration {
43
+ ipv4_enabled = true
44
+ }
45
+
46
+ backup_configuration {
47
+ enabled = true
48
+ }
49
+ }
50
+
51
+ deletion_protection = local.env != "dev"
52
+ }
53
+
54
+ resource "google_sql_database" "notes" {
55
+ name = "notes"
56
+ instance = google_sql_database_instance.main.name
57
+ }
58
+
59
+ resource "random_password" "db_password" {
60
+ length = 32
61
+ special = false
62
+ }
63
+
64
+ resource "google_sql_user" "app" {
65
+ name = "app_user"
66
+ instance = google_sql_database_instance.main.name
67
+ password = random_password.db_password.result
68
+ }
69
+
70
+ # --- Artifact Registry ---
71
+ resource "google_artifact_registry_repository" "notes_api" {
72
+ location = local.region
73
+ repository_id = "godd-notes-api"
74
+ format = "DOCKER"
75
+ labels = local.labels
76
+ }
77
+
78
+ # --- Cloud Run (notes-api) ---
79
+ resource "google_cloud_run_v2_service" "api" {
80
+ name = "${local.project}-api"
81
+ location = local.region
82
+ labels = local.labels
83
+
84
+ template {
85
+ containers {
86
+ image = "${local.region}-docker.pkg.dev/${local.project_id}/${google_artifact_registry_repository.notes_api.repository_id}/notes-api:latest"
87
+
88
+ ports {
89
+ container_port = 3100
90
+ }
91
+
92
+ resources {
93
+ limits = {
94
+ cpu = "{{cpuLimit}}"
95
+ memory = "{{memoryLimit}}"
96
+ }
97
+ }
98
+
99
+ env {
100
+ name = "DB_NAME"
101
+ value = "notes"
102
+ }
103
+ env {
104
+ name = "API_HOST"
105
+ value = "0.0.0.0"
106
+ }
107
+ env {
108
+ name = "API_PORT"
109
+ value = "3100"
110
+ }
111
+ }
112
+
113
+ scaling {
114
+ min_instance_count = 0
115
+ max_instance_count = 2
116
+ }
117
+ }
118
+ }
119
+
120
+ resource "google_cloud_run_service_iam_member" "public" {
121
+ location = google_cloud_run_v2_service.api.location
122
+ service = google_cloud_run_v2_service.api.name
123
+ role = "roles/run.invoker"
124
+ member = "allUsers"
125
+ }
126
+
127
+ # --- Cloud Storage (notes-app SPA) ---
128
+ resource "google_storage_bucket" "notes_app" {
129
+ name = "${local.project}-${local.project_id}"
130
+ location = local.region
131
+ force_destroy = local.env == "dev"
132
+ labels = local.labels
133
+
134
+ website {
135
+ main_page_suffix = "index.html"
136
+ not_found_page = "index.html"
137
+ }
138
+
139
+ uniform_bucket_level_access = true
140
+ }
141
+
142
+ resource "google_storage_bucket_iam_member" "public_read" {
143
+ bucket = google_storage_bucket.notes_app.name
144
+ role = "roles/storage.objectViewer"
145
+ member = "allUsers"
146
+ }
147
+
148
+ # --- Load Balancer (HTTPS → Cloud Run + GCS) ---
149
+ resource "google_compute_backend_bucket" "notes_app" {
150
+ name = "${local.project}-app-backend"
151
+ bucket_name = google_storage_bucket.notes_app.name
152
+ enable_cdn = true
153
+ }
154
+
155
+ resource "google_compute_region_network_endpoint_group" "api" {
156
+ name = "${local.project}-api-neg"
157
+ region = local.region
158
+ network_endpoint_type = "SERVERLESS"
159
+
160
+ cloud_run {
161
+ service = google_cloud_run_v2_service.api.name
162
+ }
163
+ }
164
+
165
+ resource "google_compute_backend_service" "api" {
166
+ name = "${local.project}-api-backend"
167
+
168
+ backend {
169
+ group = google_compute_region_network_endpoint_group.api.id
170
+ }
171
+ }
@@ -0,0 +1,15 @@
1
+ output "cloud_run_url" {
2
+ value = google_cloud_run_v2_service.api.uri
3
+ }
4
+
5
+ output "notes_app_url" {
6
+ value = "https://storage.googleapis.com/${google_storage_bucket.notes_app.name}/index.html"
7
+ }
8
+
9
+ output "artifact_registry" {
10
+ value = "${google_artifact_registry_repository.notes_api.location}-docker.pkg.dev/{{projectId}}/${google_artifact_registry_repository.notes_api.repository_id}"
11
+ }
12
+
13
+ output "cloud_sql_instance" {
14
+ value = google_sql_database_instance.main.connection_name
15
+ }
@@ -0,0 +1,19 @@
1
+ terraform {
2
+ required_version = ">= 1.5"
3
+
4
+ required_providers {
5
+ google = {
6
+ source = "hashicorp/google"
7
+ version = "~> 5.0"
8
+ }
9
+ random = {
10
+ source = "hashicorp/random"
11
+ version = "~> 3.0"
12
+ }
13
+ }
14
+ }
15
+
16
+ provider "google" {
17
+ project = "{{projectId}}"
18
+ region = "{{region}}"
19
+ }
@@ -0,0 +1,64 @@
1
+ terraform {
2
+ required_version = ">= 1.5"
3
+
4
+ required_providers {
5
+ vercel = {
6
+ source = "vercel/vercel"
7
+ version = "~> 1.0"
8
+ }
9
+ }
10
+ }
11
+
12
+ provider "vercel" {
13
+ api_token = var.vercel_api_token
14
+ team = var.vercel_team_id != "" ? var.vercel_team_id : null
15
+ }
16
+
17
+ variable "vercel_api_token" {
18
+ type = string
19
+ sensitive = true
20
+ }
21
+
22
+ variable "vercel_team_id" {
23
+ type = string
24
+ default = ""
25
+ }
26
+
27
+ variable "railway_token" {
28
+ type = string
29
+ sensitive = true
30
+ }
31
+
32
+ locals {
33
+ project = "godd-ripla-notes"
34
+ env = "{{env}}"
35
+ github_owner = "{{githubOwner}}"
36
+ github_repo = "{{githubRepo}}"
37
+ }
38
+
39
+ # --- Vercel Project (notes-app SPA) ---
40
+ resource "vercel_project" "notes_app" {
41
+ name = "${local.project}-app"
42
+ framework = "vite"
43
+
44
+ git_repository {
45
+ type = "github"
46
+ repo = "${local.github_owner}/${local.github_repo}"
47
+ }
48
+
49
+ root_directory = "notes-app"
50
+ build_command = "npm run build"
51
+ output_directory = "dist"
52
+
53
+ environment {
54
+ key = "VITE_API_URL"
55
+ value = "{{railwayApiUrl}}"
56
+ target = ["production", "preview"]
57
+ }
58
+ }
59
+
60
+ resource "vercel_deployment" "notes_app" {
61
+ project_id = vercel_project.notes_app.id
62
+ ref = "main"
63
+ production = local.env == "prod"
64
+ }
@@ -0,0 +1,11 @@
1
+ output "vercel_url" {
2
+ value = vercel_deployment.notes_app.url
3
+ }
4
+
5
+ output "vercel_project_id" {
6
+ value = vercel_project.notes_app.id
7
+ }
8
+
9
+ output "railway_api_url" {
10
+ value = "{{railwayApiUrl}}"
11
+ }