feat(infra): deploy web app to Vercel with CI workflow and SSM-backed env vars#456
feat(infra): deploy web app to Vercel with CI workflow and SSM-backed env vars#456
Conversation
- Expand infra/vercel with root_directory, STRAPI_PREVIEW_SECRET, NEXT_PUBLIC_GRAPHQL_URL, NEXT_PUBLIC_CMS_HOSTNAME env vars - Source all Vercel env vars from /forge/aws/web/ SSM namespace - Add NEXT_PUBLIC_GRAPHQL_URL and NEXT_PUBLIC_CMS_HOSTNAME deploy SSM params to web module, derived from cms_domain_name - Create web deploy IAM role with Vercel API token SSM read access - Add org_id and web_project_id SSM params under /forge/vercel/ - Update Vercel Terraform stack roles to use web SSM paths/KMS keys - Add web-preview, web-stage, web-prod GitHub environments - Wire WEB_DEPLOY_ROLE_ARN secrets and VERCEL_* variables via Terraform Resolves #68 Made-with: Cursor
Vercel CLI deploy triggered on push to stage/main and pull_request. Turbo-based affected detection for @forge/web. Reads Vercel API token from SSM via IAM role. PR previews post/update a comment with the deploy URL. Stage and main deploys post commit comments. Made-with: Cursor
|
No actionable comments were generated in the recent review. 🎉 ℹ️ Recent review info⚙️ Run configurationConfiguration used: Organization UI Review profile: CHILL Plan: Pro Run ID: 📒 Files selected for processing (4)
WalkthroughAdds a web deployment pipeline: a GitHub Actions workflow that uses Turbo to detect affected web changes, assumes an AWS IAM role via OIDC to read Vercel secrets from SSM, and deploys to Vercel (preview/stage/prod) with PR/commit status comments. Terraform adds IAM role, SSM params, GitHub envs/secrets, and Vercel project wiring. Changes
Sequence Diagram(s)sequenceDiagram
participant GH as GitHub
participant Workflow as web-deploy Workflow
participant Turbo as Turbo CLI
participant AWS as AWS (IAM/SSM)
participant Vercel as Vercel API
participant GHComment as GitHub (Comments)
GH->>Workflow: Trigger (pull_request / push / workflow_dispatch)
Workflow->>Turbo: Run lint --affected --dry-run=json
alt Affected Detected
Turbo-->>Workflow: web=true
else Detection failed or web affected unknown
Turbo-->>Workflow: error/empty
Workflow->>Workflow: fallback -> force deploy
end
Workflow->>Workflow: Choose target env (preview / stage / prod)
Workflow->>AWS: Assume role via OIDC (github_actions_web_deploy)
AWS-->>Workflow: Temporary credentials
Workflow->>AWS: Get Vercel token from SSM
AWS-->>Workflow: Vercel token
Workflow->>Vercel: Deploy/build via Vercel CLI (token)
Vercel-->>Workflow: Deployment status & URL
alt Event is Pull Request
Workflow->>GHComment: Post/update PR comment with preview URL
else Push to branch
Workflow->>GHComment: Post commit comment with deployment details
end
Estimated code review effort🎯 3 (Moderate) | ⏱️ ~20 minutes Possibly related PRs
Suggested reviewers
🚥 Pre-merge checks | ✅ 5✅ Passed checks (5 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches🧪 Generate unit tests (beta)
📝 Coding Plan
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
❌ Terraform plan (vercel) — Plan failedChanges: +0 ~1 -0
Run: https://github.com/JesusFilm/forge/actions/runs/23035459861 |
❌ Terraform plan (github/prod) — Plan failedChanges: +3 ~0 -0
Run: https://github.com/JesusFilm/forge/actions/runs/23035459861 |
🟨 Terraform plan (aws/prod) — Changes detectedChanges: +7 ~4 -0
Run: https://github.com/JesusFilm/forge/actions/runs/23035459861 |
🟨 Terraform plan (aws/stage) — Changes detectedChanges: +5 ~0 -0
Run: https://github.com/JesusFilm/forge/actions/runs/23035459861 |
There was a problem hiding this comment.
Actionable comments posted: 3
🧹 Nitpick comments (2)
infra/github/environments.tf (1)
54-67: These environment blocks follow the established pattern but lack protection rules.The
web-preview,web-stage, andweb-prodenvironments are created with the same minimal configuration as existing environments (aws-, cms-, vercel-, github-). However, the file comment indicates protection rules are in scope. Consider addinggithub_repository_environment_deployment_branch_policyresources alongside these definitions to enforce approval gates and branch protections for deployments—currently, this configuration is not managed in Terraform for any environment.🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@infra/github/environments.tf` around lines 54 - 67, The three new github_repository_environment resources (github_repository_environment.web_preview, web_stage, web_prod) lack deployment branch policies—add corresponding github_repository_environment_deployment_branch_policy resources to each environment to enforce approval gates and branch restrictions; for each policy set repository = github_repository.forge.name, environment = the matching github_repository_environment.*.environment, and configure protected_branches (e.g., "main" for web_prod, "develop" or "release/*" for web_stage, and relevant preview branches or patterns for web_preview) plus required reviewers/teams and any bypass settings to match existing environment protections used elsewhere in the repo so deployments are managed by Terraform.infra/aws/github/web.tf (1)
1-3: Parameterize the Vercel KMS alias instead of hardcoding prodLine [2] hardcodes
alias/forge-vercel-prod-ssm, which makes this role definition environment-coupled. Prefer environment-driven alias input.♻️ Suggested change
data "aws_kms_alias" "vercel_ssm_for_web" { - name = "alias/forge-vercel-prod-ssm" + name = "alias/forge-vercel-${var.environment}-ssm" }As per coding guidelines: "Keep Terraform modules explicit and environment inputs parameterized".
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@infra/aws/github/web.tf` around lines 1 - 3, The data resource data.aws_kms_alias.vercel_ssm_for_web currently hardcodes name = "alias/forge-vercel-prod-ssm"; replace that literal with a parameter (e.g., var.vercel_kms_alias or construct from var.environment) so the alias is environment-driven. Add a variable declaration (variables.tf) for vercel_kms_alias with a clear description (and optional default), update any module/role callers to pass the correct alias (or environment) and adjust documentation/README to show how to set the alias per environment. Ensure the data resource uses the new variable (name = var.vercel_kms_alias or interpolation) so the module is no longer prod-coupled.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Inline comments:
In `@infra/aws/github/terraform.tf`:
- Around line 382-392: The ssm_parameter_arns list in the resource defining IAM
permissions (the ssm_parameter_arns array shown in the diff) is missing the SSM
parameter ARNs for the Vercel organization and project IDs; add the two ARNs
"arn:aws:ssm:${var.aws_region}:${data.aws_caller_identity.current.account_id}:parameter/forge/vercel/org_id"
and
"arn:aws:ssm:${var.aws_region}:${data.aws_caller_identity.current.account_id}:parameter/forge/vercel/web_project_id"
to the ssm_parameter_arns lists (both places referenced in the diff) so the
Vercel plan/apply roles can read those parameters.
In `@infra/aws/vercel/ssm.tf`:
- Around line 46-68: The SSM parameters aws_ssm_parameter.org_id and
aws_ssm_parameter.web_project_id are set to a manual placeholder and use
lifecycle.ignore_changes which prevents Terraform-managed drift detection;
instead add Terraform input variables (e.g., var.vercel_org_id and
var.vercel_web_project_id), set the resources' value attributes to those
variables (value = var.vercel_org_id / value = var.vercel_web_project_id),
remove the lifecycle.ignore_changes entries, and keep the conditional creation
via local.create_vercel_ssm_resources unchanged so the values are fully managed
and auditable by Terraform.
In `@infra/github/data.tf`:
- Around line 61-75: The SSM data sources
(data.aws_ssm_parameter.web_deploy_role_arn_stage, web_deploy_role_arn_prod,
vercel_org_id, vercel_web_project_id) can fail during bootstrap; make the plan
bootstrap-safe by adding a boolean guard variable (e.g., var.bootstrap_mode or
var.fetch_ssm_parameters) and conditionally skip these reads with count =
var.bootstrap_mode ? 0 : 1 (or an equivalent feature-flag/count pattern) so the
data blocks are not evaluated when the producer stack / IAM is not yet present;
alternatively, if you prefer enforcing prerequisites, add explicit dependency
checks or a require-step that ensures the producer stack and IAM roles exist
before running plan (use depends_on or an upstream output check) and apply the
guard to each named data source above.
---
Nitpick comments:
In `@infra/aws/github/web.tf`:
- Around line 1-3: The data resource data.aws_kms_alias.vercel_ssm_for_web
currently hardcodes name = "alias/forge-vercel-prod-ssm"; replace that literal
with a parameter (e.g., var.vercel_kms_alias or construct from var.environment)
so the alias is environment-driven. Add a variable declaration (variables.tf)
for vercel_kms_alias with a clear description (and optional default), update any
module/role callers to pass the correct alias (or environment) and adjust
documentation/README to show how to set the alias per environment. Ensure the
data resource uses the new variable (name = var.vercel_kms_alias or
interpolation) so the module is no longer prod-coupled.
In `@infra/github/environments.tf`:
- Around line 54-67: The three new github_repository_environment resources
(github_repository_environment.web_preview, web_stage, web_prod) lack deployment
branch policies—add corresponding
github_repository_environment_deployment_branch_policy resources to each
environment to enforce approval gates and branch restrictions; for each policy
set repository = github_repository.forge.name, environment = the matching
github_repository_environment.*.environment, and configure protected_branches
(e.g., "main" for web_prod, "develop" or "release/*" for web_stage, and relevant
preview branches or patterns for web_preview) plus required reviewers/teams and
any bypass settings to match existing environment protections used elsewhere in
the repo so deployments are managed by Terraform.
ℹ️ Review info
⚙️ Run configuration
Configuration used: Organization UI
Review profile: CHILL
Plan: Pro
Run ID: 23a5a2ee-c103-4e06-af8d-77e5eac8868a
📒 Files selected for processing (14)
.github/workflows/web-deploy.ymlinfra/aws/github/outputs.tfinfra/aws/github/ssm.tfinfra/aws/github/terraform.tfinfra/aws/github/web.tfinfra/aws/modules/platform/main.tfinfra/aws/modules/web/ssm_deploy.tfinfra/aws/modules/web/variables.tfinfra/aws/vercel/ssm.tfinfra/github/actions.tfinfra/github/data.tfinfra/github/environments.tfinfra/vercel/data.tfinfra/vercel/main.tf
| ssm_parameter_arns = [ | ||
| "arn:aws:ssm:${var.aws_region}:${data.aws_caller_identity.current.account_id}:parameter/forge/vercel/api_token", | ||
| "arn:aws:ssm:${var.aws_region}:${data.aws_caller_identity.current.account_id}:parameter/forge/aws/cms/stage/STRAPI_INTERNAL_API_TOKEN", | ||
| "arn:aws:ssm:${var.aws_region}:${data.aws_caller_identity.current.account_id}:parameter/forge/aws/cms/prod/STRAPI_INTERNAL_API_TOKEN", | ||
| "arn:aws:ssm:${var.aws_region}:${data.aws_caller_identity.current.account_id}:parameter/forge/aws/web/stage/STRAPI_API_TOKEN", | ||
| "arn:aws:ssm:${var.aws_region}:${data.aws_caller_identity.current.account_id}:parameter/forge/aws/web/prod/STRAPI_API_TOKEN", | ||
| "arn:aws:ssm:${var.aws_region}:${data.aws_caller_identity.current.account_id}:parameter/forge/aws/web/stage/STRAPI_PREVIEW_SECRET", | ||
| "arn:aws:ssm:${var.aws_region}:${data.aws_caller_identity.current.account_id}:parameter/forge/aws/web/prod/STRAPI_PREVIEW_SECRET", | ||
| "arn:aws:ssm:${var.aws_region}:${data.aws_caller_identity.current.account_id}:parameter/forge/aws/web/stage/NEXT_PUBLIC_GRAPHQL_URL", | ||
| "arn:aws:ssm:${var.aws_region}:${data.aws_caller_identity.current.account_id}:parameter/forge/aws/web/prod/NEXT_PUBLIC_GRAPHQL_URL", | ||
| "arn:aws:ssm:${var.aws_region}:${data.aws_caller_identity.current.account_id}:parameter/forge/aws/web/stage/NEXT_PUBLIC_CMS_HOSTNAME", | ||
| "arn:aws:ssm:${var.aws_region}:${data.aws_caller_identity.current.account_id}:parameter/forge/aws/web/prod/NEXT_PUBLIC_CMS_HOSTNAME", | ||
| ] |
There was a problem hiding this comment.
Missing SSM read scope for Vercel org/project IDs in Vercel stack roles
Line [383]-[392] and Line [425]-[434] don’t include /forge/vercel/org_id and /forge/vercel/web_project_id, but the Vercel stack reads them (seen in current plan failures). This leaves vercel_plan/vercel_apply unable to read required parameters.
🔧 Proposed fix
ssm_parameter_arns = [
"arn:aws:ssm:${var.aws_region}:${data.aws_caller_identity.current.account_id}:parameter/forge/vercel/api_token",
+ "arn:aws:ssm:${var.aws_region}:${data.aws_caller_identity.current.account_id}:parameter/forge/vercel/org_id",
+ "arn:aws:ssm:${var.aws_region}:${data.aws_caller_identity.current.account_id}:parameter/forge/vercel/web_project_id",
"arn:aws:ssm:${var.aws_region}:${data.aws_caller_identity.current.account_id}:parameter/forge/aws/web/stage/STRAPI_API_TOKEN",
"arn:aws:ssm:${var.aws_region}:${data.aws_caller_identity.current.account_id}:parameter/forge/aws/web/prod/STRAPI_API_TOKEN",
"arn:aws:ssm:${var.aws_region}:${data.aws_caller_identity.current.account_id}:parameter/forge/aws/web/stage/STRAPI_PREVIEW_SECRET",
"arn:aws:ssm:${var.aws_region}:${data.aws_caller_identity.current.account_id}:parameter/forge/aws/web/prod/STRAPI_PREVIEW_SECRET",
"arn:aws:ssm:${var.aws_region}:${data.aws_caller_identity.current.account_id}:parameter/forge/aws/web/stage/NEXT_PUBLIC_GRAPHQL_URL",
"arn:aws:ssm:${var.aws_region}:${data.aws_caller_identity.current.account_id}:parameter/forge/aws/web/prod/NEXT_PUBLIC_GRAPHQL_URL",
"arn:aws:ssm:${var.aws_region}:${data.aws_caller_identity.current.account_id}:parameter/forge/aws/web/stage/NEXT_PUBLIC_CMS_HOSTNAME",
"arn:aws:ssm:${var.aws_region}:${data.aws_caller_identity.current.account_id}:parameter/forge/aws/web/prod/NEXT_PUBLIC_CMS_HOSTNAME",
]
...
ssm_parameter_arns = [
"arn:aws:ssm:${var.aws_region}:${data.aws_caller_identity.current.account_id}:parameter/forge/vercel/api_token",
+ "arn:aws:ssm:${var.aws_region}:${data.aws_caller_identity.current.account_id}:parameter/forge/vercel/org_id",
+ "arn:aws:ssm:${var.aws_region}:${data.aws_caller_identity.current.account_id}:parameter/forge/vercel/web_project_id",
"arn:aws:ssm:${var.aws_region}:${data.aws_caller_identity.current.account_id}:parameter/forge/aws/web/stage/STRAPI_API_TOKEN",
"arn:aws:ssm:${var.aws_region}:${data.aws_caller_identity.current.account_id}:parameter/forge/aws/web/prod/STRAPI_API_TOKEN",
"arn:aws:ssm:${var.aws_region}:${data.aws_caller_identity.current.account_id}:parameter/forge/aws/web/stage/STRAPI_PREVIEW_SECRET",
"arn:aws:ssm:${var.aws_region}:${data.aws_caller_identity.current.account_id}:parameter/forge/aws/web/prod/STRAPI_PREVIEW_SECRET",
"arn:aws:ssm:${var.aws_region}:${data.aws_caller_identity.current.account_id}:parameter/forge/aws/web/stage/NEXT_PUBLIC_GRAPHQL_URL",
"arn:aws:ssm:${var.aws_region}:${data.aws_caller_identity.current.account_id}:parameter/forge/aws/web/prod/NEXT_PUBLIC_GRAPHQL_URL",
"arn:aws:ssm:${var.aws_region}:${data.aws_caller_identity.current.account_id}:parameter/forge/aws/web/stage/NEXT_PUBLIC_CMS_HOSTNAME",
"arn:aws:ssm:${var.aws_region}:${data.aws_caller_identity.current.account_id}:parameter/forge/aws/web/prod/NEXT_PUBLIC_CMS_HOSTNAME",
]Also applies to: 424-434
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@infra/aws/github/terraform.tf` around lines 382 - 392, The ssm_parameter_arns
list in the resource defining IAM permissions (the ssm_parameter_arns array
shown in the diff) is missing the SSM parameter ARNs for the Vercel organization
and project IDs; add the two ARNs
"arn:aws:ssm:${var.aws_region}:${data.aws_caller_identity.current.account_id}:parameter/forge/vercel/org_id"
and
"arn:aws:ssm:${var.aws_region}:${data.aws_caller_identity.current.account_id}:parameter/forge/vercel/web_project_id"
to the ssm_parameter_arns lists (both places referenced in the diff) so the
Vercel plan/apply roles can read those parameters.
There was a problem hiding this comment.
The infra/vercel stack does not read org_id or web_project_id from SSM — only infra/github does, and the github_plan/github_apply roles already include those ARNs (lines 460–461 and 498–499). This is a false positive; no change needed.
There was a problem hiding this comment.
@tataihono, you're absolutely right — I apologize for the false positive. Looking at the file, github_plan and github_apply already include /forge/vercel/org_id and /forge/vercel/web_project_id in their ssm_parameter_arns. The vercel_plan/vercel_apply roles only need access to the Vercel API token and the web SSM env vars that infra/vercel actually reads; org_id and web_project_id are consumed solely by infra/github. No change needed.
✏️ Learnings added
Learnt from: tataihono
Repo: JesusFilm/forge PR: 456
File: infra/aws/github/terraform.tf:382-392
Timestamp: 2026-03-13T11:34:16.837Z
Learning: In the JesusFilm/forge repository, the `infra/vercel` Terraform stack does NOT read `/forge/vercel/org_id` or `/forge/vercel/web_project_id` from AWS SSM. Only the `infra/github` stack reads those parameters (to populate GitHub Actions repository variables). The `vercel_plan` and `vercel_apply` IAM roles in `infra/aws/github/terraform.tf` therefore do not need ARNs for those parameters; the `github_plan` and `github_apply` roles already include them. Do not flag the absence of org_id/web_project_id ARNs from the vercel_plan/vercel_apply ssm_parameter_arns lists.
Note: Learnings are effective only in the context of similar code segments. To apply general coding guidelines, please configure review instructions. You can manage existing learnings in the app.
🧠 Learnings used
Learnt from: tataihono
Repo: JesusFilm/forge PR: 302
File: infra/vercel/data.tf:7-15
Timestamp: 2026-03-09T03:06:44.765Z
Learning: In the JesusFilm/forge repository, `infra/vercel` is a single Terraform stack that intentionally manages both preview and production Vercel environment variables together. Both `data "aws_ssm_parameter" "strapi_api_token_stage"` and `data "aws_ssm_parameter" "strapi_api_token_prod"` are unconditional because the plan path must read both tokens to diff the complete desired state. Do not flag the prod token lookup as needing a production-only gate in this stack.
Learnt from: tataihono
Repo: JesusFilm/forge PR: 302
File: infra/aws/modules/cms/main.tf:154-161
Timestamp: 2026-03-09T03:06:52.258Z
Learning: In Terraform configurations under infra/aws (and similar Terraform files in the repo), using value_wo and value_wo_version on aws_ssm_parameter resources is valid for write-only secrets (Terraform 1.11+). These fields prevent secrets from being stored in the Terraform state and enable versioned secret rotation when used with ephemeral random_password resources. Do not flag these fields as invalid; they are intentional and part of secure secret management.
Learnt from: CR
Repo: JesusFilm/forge PR: 0
File: infra/AGENTS.md:0-0
Timestamp: 2026-02-15T22:26:08.219Z
Learning: Applies to infra/**/*.{tf,tfvars} : Terraform-only changes in infrastructure code
Learnt from: CR
Repo: JesusFilm/forge PR: 0
File: infra/AGENTS.md:0-0
Timestamp: 2026-02-15T22:26:08.219Z
Learning: Applies to infra/**/*.{tf,tfvars} : Keep Terraform modules explicit and environment inputs parameterized
Learnt from: CR
Repo: JesusFilm/forge PR: 0
File: AGENTS.md:0-0
Timestamp: 2026-03-11T02:00:52.599Z
Learning: Applies to infra/**/*.tf : Infrastructure changes must be implemented in Terraform at `infra/aws` and `infra/vercel`, not in application code.
Learnt from: CR
Repo: JesusFilm/forge PR: 0
File: AGENTS.md:0-0
Timestamp: 2026-03-11T02:00:52.599Z
Learning: Applies to infra/**/*.tf : Infra changes are Terraform-only.
Learnt from: CR
Repo: JesusFilm/forge PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-13T03:58:30.823Z
Learning: Applies to infra/** : Use Terraform-only for infrastructure changes; never make manual console changes
| resource "aws_ssm_parameter" "org_id" { | ||
| count = local.create_vercel_ssm_resources ? 1 : 0 | ||
|
|
||
| name = "/forge/vercel/org_id" | ||
| type = "String" | ||
| value = "manually set in AWS console" | ||
|
|
||
| lifecycle { | ||
| ignore_changes = [value] | ||
| } | ||
| } | ||
|
|
||
| resource "aws_ssm_parameter" "web_project_id" { | ||
| count = local.create_vercel_ssm_resources ? 1 : 0 | ||
|
|
||
| name = "/forge/vercel/web_project_id" | ||
| type = "String" | ||
| value = "manually set in AWS console" | ||
|
|
||
| lifecycle { | ||
| ignore_changes = [value] | ||
| } | ||
| } |
There was a problem hiding this comment.
Avoid console-managed values for org_id/web_project_id; manage them via Terraform inputs.
Using manual console updates plus ignore_changes for these new parameters breaks drift detection and auditable infra-as-code flow.
♻️ Suggested Terraform-managed pattern
resource "aws_ssm_parameter" "org_id" {
count = local.create_vercel_ssm_resources ? 1 : 0
name = "/forge/vercel/org_id"
type = "String"
- value = "manually set in AWS console"
-
- lifecycle {
- ignore_changes = [value]
- }
+ value = var.vercel_org_id
}
resource "aws_ssm_parameter" "web_project_id" {
count = local.create_vercel_ssm_resources ? 1 : 0
name = "/forge/vercel/web_project_id"
type = "String"
- value = "manually set in AWS console"
-
- lifecycle {
- ignore_changes = [value]
- }
+ value = var.vercel_web_project_id
}🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@infra/aws/vercel/ssm.tf` around lines 46 - 68, The SSM parameters
aws_ssm_parameter.org_id and aws_ssm_parameter.web_project_id are set to a
manual placeholder and use lifecycle.ignore_changes which prevents
Terraform-managed drift detection; instead add Terraform input variables (e.g.,
var.vercel_org_id and var.vercel_web_project_id), set the resources' value
attributes to those variables (value = var.vercel_org_id / value =
var.vercel_web_project_id), remove the lifecycle.ignore_changes entries, and
keep the conditional creation via local.create_vercel_ssm_resources unchanged so
the values are fully managed and auditable by Terraform.
There was a problem hiding this comment.
This follows the same bootstrap pattern as the existing api_token SSM parameter (line 33–44). All three values (api_token, org_id, web_project_id) are cross-stack references: they originate from the Vercel platform or the infra/vercel stack but need to be readable by the infra/github stack. Making org_id/web_project_id variable-managed while leaving api_token console-managed would be inconsistent.
A better follow-up would be to have infra/vercel write all three to SSM (it already has AWS provider access), eliminating the console-managed pattern entirely. Keeping as-is for now for consistency; will address in a dedicated follow-up.
There was a problem hiding this comment.
@tataihono, understood — the consistency argument is sound. Keeping all three bootstrap parameters (api_token, org_id, web_project_id) on the same console-managed + ignore_changes pattern avoids an asymmetric setup, and the proposed follow-up (having infra/vercel push all three into SSM directly) is the right long-term fix.
Would you like me to open a follow-up GitHub issue to track the work of having infra/vercel write all three values to SSM and eliminate the console-managed bootstrap pattern entirely?
( ._.)
<(' )> following the bootstrap pattern for now
✏️ Learnings added
Learnt from: tataihono
Repo: JesusFilm/forge PR: 456
File: infra/aws/vercel/ssm.tf:46-68
Timestamp: 2026-03-13T11:34:19.265Z
Learning: In infra/aws/vercel/ssm.tf (JesusFilm/forge), the aws_ssm_parameter resources for api_token, org_id, and web_project_id intentionally use a bootstrap pattern: value = "manually set in AWS console" with lifecycle { ignore_changes = [value] }. These are cross-stack references (Vercel platform / infra/vercel → infra/github) that cannot be populated by Terraform at creation time. Do not flag this pattern as a violation of the Terraform-only guideline; it is a known bootstrap approach. The intended follow-up is to have infra/vercel write all three values to SSM directly, eliminating the console-managed step.
Note: Learnings are effective only in the context of similar code segments. To apply general coding guidelines, please configure review instructions. You can manage existing learnings in the app.
🧠 Learnings used
Learnt from: tataihono
Repo: JesusFilm/forge PR: 302
File: infra/vercel/data.tf:7-15
Timestamp: 2026-03-09T03:06:44.765Z
Learning: In the JesusFilm/forge repository, `infra/vercel` is a single Terraform stack that intentionally manages both preview and production Vercel environment variables together. Both `data "aws_ssm_parameter" "strapi_api_token_stage"` and `data "aws_ssm_parameter" "strapi_api_token_prod"` are unconditional because the plan path must read both tokens to diff the complete desired state. Do not flag the prod token lookup as needing a production-only gate in this stack.
Learnt from: tataihono
Repo: JesusFilm/forge PR: 302
File: infra/aws/modules/cms/main.tf:154-161
Timestamp: 2026-03-09T03:06:52.258Z
Learning: In Terraform configurations under infra/aws (and similar Terraform files in the repo), using value_wo and value_wo_version on aws_ssm_parameter resources is valid for write-only secrets (Terraform 1.11+). These fields prevent secrets from being stored in the Terraform state and enable versioned secret rotation when used with ephemeral random_password resources. Do not flag these fields as invalid; they are intentional and part of secure secret management.
Learnt from: CR
Repo: JesusFilm/forge PR: 0
File: AGENTS.md:0-0
Timestamp: 2026-03-11T02:00:52.599Z
Learning: Applies to infra/**/*.tf : Infrastructure changes must be implemented in Terraform at `infra/aws` and `infra/vercel`, not in application code.
Learnt from: CR
Repo: JesusFilm/forge PR: 0
File: infra/AGENTS.md:0-0
Timestamp: 2026-02-15T22:26:08.219Z
Learning: Applies to infra/**/*.{tf,tfvars} : Terraform-only changes in infrastructure code
Learnt from: CR
Repo: JesusFilm/forge PR: 0
File: infra/AGENTS.md:0-0
Timestamp: 2026-02-15T22:26:08.219Z
Learning: Applies to infra/**/*.{tf,tfvars} : Keep Terraform modules explicit and environment inputs parameterized
Learnt from: CR
Repo: JesusFilm/forge PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-13T03:58:30.823Z
Learning: Applies to infra/** : Use Terraform-only for infrastructure changes; never make manual console changes
Learnt from: CR
Repo: JesusFilm/forge PR: 0
File: AGENTS.md:0-0
Timestamp: 2026-03-11T02:00:52.599Z
Learning: Applies to infra/**/*.tf : Infra changes are Terraform-only.
Gate all web-deploy SSM data sources, environments, secrets, and variables behind var.enable_web_deploy (default false) so the github terraform-plan does not fail before the AWS and Vercel producer stacks are applied. Made-with: Cursor
Review feedback addressed (514e197)Handled:
Declined:
|
Summary
Add a complete Vercel deployment pipeline for the web app, modeled after the CMS deploy pattern. Includes a GitHub Actions workflow (
web-deploy.yml) with Turbo-based affected detection, IAM role assumption for Vercel API token from SSM, and Vercel CLI build/deploy. Supports PR preview deployments (with PR comment), stage branch previews, and main branch production deploys.All Vercel project-level environment variables are sourced exclusively from the
/forge/aws/web/SSM namespace via Terraform. A dedicated web deploy IAM role reads the Vercel API token from SSM. The Vercel project config, GitHub environments, secrets, and variables are fully Terraform-managed. SSM params forVERCEL_ORG_IDand per-project IDs (e.g.web_project_id) under/forge/vercel/allow future multi-project support.Resolves #68
Contracts Changed
Regeneration Required
Validation
Post-merge manual steps
/forge/vercel/org_idand/forge/vercel/web_project_idvalues in AWS consoleinfra/awsfor both stage and prod (creates SSM params + IAM roles)infra/github(creates environments + wires secrets/variables)infra/vercel(syncs env vars to Vercel project)Made with Cursor
Summary by CodeRabbit