Skip to content

ci(terraform): use upstream's fixed state bucket name - #41

Merged
rhlsthrm merged 1 commit into
mainfrom
fork/converge-state-bucket
Sep 26, 2026
Merged

rhlsthrm merged 1 commit into
mainfrom
fork/converge-state-bucket

Conversation

@rhlsthrm

@rhlsthrm rhlsthrm commented Sep 26, 2026 •

Copy link
Copy Markdown
Collaborator

Drops the fork-only R2_BUCKET backend wiring, so .github/workflows/terraform.yml equals upstream plus ColeMurray#1862's reviewer-App variables. Upstream hardcodes the state bucket open-inspect-terraform-state in backend.tf. Upstream ColeMurray#1972, which would have made the bucket configurable, was closed as moot.

Migration (done by hand around the merge)

  1. Create R2 bucket open-inspect-terraform-state in this account.
  2. With no Terraform run in flight, copy production/terraform.tfstate from open-inspect-codos-tf-state and check the sha256, serial and lineage match.
  3. terraform init -reconfigure against the new bucket plus terraform state list returns the same resource count as the old state.
  4. Merge. The apply on main reads the copied state, and the post-deploy checks (bundle hashes, health) confirm it.
  5. Remove the R2_BUCKET Actions variable. Keep the old bucket as a backup and retire it later.

Between step 2 and the merge, nothing else may merge to main: an apply in that window would write to the old bucket.

Summary by CodeRabbit

  • Chores
    • Updated infrastructure deployment checks to validate required Cloudflare credentials and account information.
    • Terraform planning and deployment no longer require a storage bucket to be supplied separately during initialization.
    • These updates affect automated deployment workflows; no user-facing application changes are included.

@coderabbitai

coderabbitai Bot commented Sep 26, 2026 •

Copy link
Copy Markdown

Review in Change Stack →

Navigate logical layers of code changes, visualize relationships, and explore their blast radius.

📝 Walkthrough

Walkthrough

The Terraform workflow no longer requires R2_BUCKET in its secrets gate or passes the bucket as an explicit backend configuration value during plan and apply initialization.

Changes

Terraform backend configuration

Layer / File(s) Summary
Secrets gate and Terraform initialization
.github/workflows/terraform.yml
The secrets gate checks the Cloudflare API token, R2 access and secret keys, and account ID without requiring R2_BUCKET. Plan and apply initialization retain the R2 keys and omit the bucket configuration.

Priority: ➖ Normal

Merge Risk: 🟡 Moderate · up to f9c78

Without the production state copied to the new bucket, Terraform may treat managed resources as absent and attempt to recreate them. Confirm the state copy and resource count before merging.

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly and concisely describes the main change: using the upstream fixed Terraform state bucket name.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check. Docstring coverage is scoped to functions touched by this diff. Analyzed 0 functions across 0…
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

Comment @coderabbitai help to get the list of available commands.

@github-actions

Copy link
Copy Markdown

Terraform Validation Results

Step Status
Format ✅
Init ✅
Validate ✅
Tests ✅

Pushed by: @rhlsthrm, Action: pull_request

@github-actions

Copy link
Copy Markdown

Terraform Plan Results

Status: ✅ Success

Show Plan
data.external.modal_source_hash[0]: Reading...
data.external.modal_source_hash[0]: Read complete after 1s [id=-]

Terraform used the selected providers to generate the following execution
plan. Resource actions are indicated with the following symbols:
  + create

Terraform will perform the following actions:

  # cloudflare_d1_database.main will be created
  + resource "cloudflare_d1_database" "main" {
      + account_id       = "96cfb35986e899baff4a02176a12f666"
      + created_at       = (known after apply)
      + file_size        = (known after apply)
      + id               = (known after apply)
      + name             = "open-inspect-codos"
      + num_tables       = (known after apply)
      + read_replication = {
          + mode = "disabled"
        }
      + uuid             = (known after apply)
      + version          = (known after apply)
    }

  # cloudflare_queue.github_autofix[0] will be created
  + resource "cloudflare_queue" "github_autofix" {
      + account_id            = "96cfb35986e899baff4a02176a12f666"
      + consumers             = (known after apply)
      + consumers_total_count = (known after apply)
      + created_on            = (known after apply)
      + id                    = (known after apply)
      + modified_on           = (known after apply)
      + producers             = (known after apply)
      + producers_total_count = (known after apply)
      + queue_id              = (known after apply)
      + queue_name            = "open-inspect-github-autofix-codos"
      + settings              = (known after apply)
    }

  # cloudflare_queue.github_autofix_dlq[0] will be created
  + resource "cloudflare_queue" "github_autofix_dlq" {
      + account_id            = "96cfb35986e899baff4a02176a12f666"
      + consumers             = (known after apply)
      + consumers_total_count = (known after apply)
      + created_on            = (known after apply)
      + id                    = (known after apply)
      + modified_on           = (known after apply)
      + producers             = (known after apply)
      + producers_total_count = (known after apply)
      + queue_id              = (known after apply)
      + queue_name            = "open-inspect-github-autofix-dlq-codos"
      + settings              = (known after apply)
    }

  # cloudflare_queue.image_build_finalization will be created
  + resource "cloudflare_queue" "image_build_finalization" {
      + account_id            = "96cfb35986e899baff4a02176a12f666"
      + consumers             = (known after apply)
      + consumers_total_count = (known after apply)
      + created_on            = (known after apply)
      + id                    = (known after apply)
      + modified_on           = (known after apply)
      + producers             = (known after apply)
      + producers_total_count = (known after apply)
      + queue_id              = (known after apply)
      + queue_name            = "open-inspect-image-build-finalization-codos"
      + settings              = (known after apply)
    }

  # cloudflare_queue.image_build_finalization_dlq will be created
  + resource "cloudflare_queue" "image_build_finalization_dlq" {
      + account_id            = "96cfb35986e899baff4a02176a12f666"
      + consumers             = (known after apply)
      + consumers_total_count = (known after apply)
      + created_on            = (known after apply)
      + id                    = (known after apply)
      + modified_on           = (known after apply)
      + producers             = (known after apply)
      + producers_total_count = (known after apply)
      + queue_id              = (known after apply)
      + queue_name            = "open-inspect-image-build-finalization-dlq-codos"
      + settings              = (known after apply)
    }

  # cloudflare_queue.slack_completion_delivery[0] will be created
  + resource "cloudflare_queue" "slack_completion_delivery" {
      + account_id            = "96cfb35986e899baff4a02176a12f666"
      + consumers             = (known after apply)
      + consumers_total_count = (known after apply)
      + created_on            = (known after apply)
      + id                    = (known after apply)
      + modified_on           = (known after apply)
      + producers             = (known after apply)
      + producers_total_count = (known after apply)
      + queue_id              = (known after apply)
      + queue_name            = "open-inspect-slack-completion-codos"
      + settings              = (known after apply)
    }

  # cloudflare_queue.slack_completion_delivery_dlq[0] will be created
  + resource "cloudflare_queue" "slack_completion_delivery_dlq" {
      + account_id            = "96cfb35986e899baff4a02176a12f666"
      + consumers             = (known after apply)
      + consumers_total_count = (known after apply)
      + created_on            = (known after apply)
      + id                    = (known after apply)
      + modified_on           = (known after apply)
      + producers             = (known after apply)
      + producers_total_count = (known after apply)
      + queue_id              = (known after apply)
      + queue_name            = "open-inspect-slack-completion-dlq-codos"
      + settings              = (known after apply)
    }

  # cloudflare_queue_consumer.github_autofix[0] will be created
  + resource "cloudflare_queue_consumer" "github_autofix" {
      + account_id        = "96cfb35986e899baff4a02176a12f666"
      + consumer_id       = (known after apply)
      + created_on        = (known after apply)
      + dead_letter_queue = "open-inspect-github-autofix-dlq-codos"
      + queue_id          = (known after apply)
      + queue_name        = (known after apply)
      + script_name       = "open-inspect-control-plane-codos"
      + settings          = {
          + batch_size            = 1
          + max_concurrency       = 5
          + max_retries           = 4
          + max_wait_time_ms      = 1000
          + retry_delay           = 30
          + visibility_timeout_ms = (known after apply)
        }
      + type              = "worker"
    }

  # cloudflare_queue_consumer.image_build_finalization will be created
  + resource "cloudflare_queue_consumer" "image_build_finalization" {
      + account_id        = "96cfb35986e899baff4a02176a12f666"
      + consumer_id       = (known after apply)
      + created_on        = (known after apply)
      + dead_letter_queue = "open-inspect-image-build-finalization-dlq-codos"
      + queue_id          = (known after apply)
      + queue_name        = (known after apply)
      + script_name       = "open-inspect-control-plane-codos"
      + settings          = {
          + batch_size            = 1
          + max_concurrency       = 5
          + max_retries           = 12
          + max_wait_time_ms      = 1000
          + retry_delay           = 15
          + visibility_timeout_ms = (known after apply)
        }
      + type              = "worker"
    }

  # cloudflare_queue_consumer.slack_completion_delivery[0] will be created
  + resource "cloudflare_queue_consumer" "slack_completion_delivery" {
      + account_id        = "96cfb35986e899baff4a02176a12f666"
      + consumer_id       = (known after apply)
      + created_on        = (known after apply)
      + dead_letter_queue = "open-inspect-slack-completion-dlq-codos"
      + queue_id          = (known after apply)
      + queue_name        = (known after apply)
      + script_name       = "open-inspect-slack-bot-codos"
      + settings          = {
          + batch_size            = 1
          + max_concurrency       = 5
          + max_retries           = 1
          + max_wait_time_ms      = 1000
          + retry_delay           = 15
          + visibility_timeout_ms = (known after apply)
        }
      + type              = "worker"
    }

  # cloudflare_r2_bucket.media will be created
  + resource "cloudflare_r2_bucket" "media" {
      + account_id    = "96cfb35986e899baff4a02176a12f666"
      + creation_date = (known after apply)
      + id            = (known after apply)
      + jurisdiction  = "default"
      + location      = "ENAM"
      + name          = "open-inspect-media-codos"
      + storage_class = "Standard"
    }

  # local_file.web_app_wrangler_production[0] will be created
  + resource "local_file" "web_app_wrangler_production" {
      + content              = <<-EOT
            name = "open-inspect-web-codos"
            main = ".open-next/worker.js"
            compatibility_date = "2025-08-15"
            compatibility_flags = ["nodejs_compat", "global_fetch_strictly_public"]
            
            # A custom-domain deployment has one canonical browser origin.
            workers_dev = true
            
            [vars]
            CONTROL_PLANE_URL = "https://open-inspect-control-plane-codos.opencodos.workers.dev"
            NEXT_PUBLIC_WS_URL = "wss://open-inspect-control-plane-codos.opencodos.workers.dev"
            NEXT_PUBLIC_SANDBOX_PROVIDER = "modal"
            NEXT_PUBLIC_APP_NAME = "Open-Inspect"
            NEXT_PUBLIC_APP_ICON_URL = ""
            
            [assets]
            directory = ".open-next/assets"
            binding = "ASSETS"
            
            [[services]]
            binding = "CONTROL_PLANE_WORKER"
            service = "open-inspect-control-plane-codos"
        EOT
      + content_base64sha256 = (known after apply)
      + content_base64sha512 = (known after apply)
      + content_md5          = (known after apply)
      + content_sha1         = (known after apply)
      + content_sha256       = (known after apply)
      + content_sha512       = (known after apply)
      + directory_permission = "0777"
      + file_permission      = "0777"
      + filename             = "../../..//packages/web/wrangler.production.toml"
      + id                   = (known after apply)
    }

  # null_resource.control_plane_build will be created
  + resource "null_resource" "control_plane_build" {
      + id       = (known after apply)
      + triggers = {
          + "always_run" = (known after apply)
        }
    }

  # null_resource.d1_migrations will be created
  + resource "null_resource" "d1_migrations" {
      + id       = (known after apply)
      + triggers = {
          + "database_id"    = (known after apply)
          + "migrations_sha" = "cf4b1aaf7de0d34609ec5854f6ce6958a0d6280639d0af16da6edea04d1387f4"
        }
    }

  # null_resource.github_bot_build[0] will be created
  + resource "null_resource" "github_bot_build" {
      + id       = (known after apply)
      + triggers = {
          + "always_run" = (known after apply)
        }
    }

  # null_resource.linear_bot_build[0] will be created
  + resource "null_resource" "linear_bot_build" {
      + id       = (known after apply)
      + triggers = {
          + "always_run" = (known after apply)
        }
    }

  # null_resource.slack_bot_build[0] will be created
  + resource "null_resource" "slack_bot_build" {
      + id       = (known after apply)
      + triggers = {
          + "always_run" = (known after apply)
        }
    }

  # null_resource.web_app_cloudflare_build[0] will be created
  + resource "null_resource" "web_app_cloudflare_build" {
      + id       = (known after apply)
      + triggers = {
          + "always_run" = (known after apply)
        }
    }

  # null_resource.web_app_cloudflare_deploy[0] will be created
  + resource "null_resource" "web_app_cloudflare_deploy" {
      + id       = (known after apply)
      + triggers = {
          + "always_run" = (known after apply)
        }
    }

  # null_resource.web_app_cloudflare_secrets[0] will be created
  + resource "null_resource" "web_app_cloudflare_secrets" {
      + id       = (known after apply)
      + triggers = {
          + "secrets_hash" = (sensitive value)
        }
    }

  # random_bytes.provider_accounts_encryption_key will be created
  + resource "random_bytes" "provider_accounts_encryption_key" {
      + base64 = (sensitive value)
      + hex    = (sensitive value)
      + length = 32
    }

  # random_password.image_callback_token_pepper will be created
  + resource "random_password" "image_callback_token_pepper" {
      + bcrypt_hash = (sensitive value)
      + id          = (known after apply)
      + length      = 64
      + lower       = true
      + min_lower   = 0
      + min_numeric = 0
      + min_special = 0
      + min_upper   = 0
      + number      = true
      + numeric     = true
      + result      = (sensitive value)
      + special     = false
      + upper       = true
    }

  # random_password.service_auth_secret_github_bot will be created
  + resource "random_password" "service_auth_secret_github_bot" {
      + bcrypt_hash = (sensitive value)
      + id          = (known after apply)
      + length      = 64
      + lower       = true
      + min_lower   = 0
      + min_numeric = 0
      + min_special = 0
      + min_upper   = 0
      + number      = true
      + numeric     = true
      + result      = (sensitive value)
      + special     = false
      + upper       = true
    }

  # random_password.service_auth_secret_linear_bot will be created
  + resource "random_password" "service_auth_secret_linear_bot" {
      + bcrypt_hash = (sensitive value)
      + id          = (known after apply)
      + length      = 64
      + lower       = true
      + min_lower   = 0
      + min_numeric = 0
      + min_special = 0
      + min_upper   = 0
      + number      = true
      + numeric     = true
      + result      = (sensitive value)
      + special     = false
      + upper       = true
    }

  # random_password.service_auth_secret_slack_bot will be created
  + resource "random_password" "service_auth_secret_slack_bot" {
      + bcrypt_hash = (sensitive value)
      + id          = (known after apply)
      + length      = 64
      + lower       = true
      + min_lower   = 0
      + min_numeric = 0
      + min_special = 0
      + min_upper   = 0
      + number      = true
      + numeric     = true
      + result      = (sensitive value)
      + special     = false
      + upper       = true
    }

  # random_password.service_auth_secret_web will be created
  + resource "random_password" "service_auth_secret_web" {
      + bcrypt_hash = (sensitive value)
      + id          = (known after apply)
      + length      = 64
      + lower       = true
      + min_lower   = 0
      + min_numeric = 0
      + min_special = 0
      + min_upper   = 0
      + number      = true
      + numeric     = true
      + result      = (sensitive value)
      + special     = false
      + upper       = true
    }

  # terraform_data.access_control_gate will be created
  + resource "terraform_data" "access_control_gate" {
      + id = (known after apply)
    }

  # terraform_data.cloudflare_custom_domain_gate will be created
  + resource "terraform_data" "cloudflare_custom_domain_gate" {
      + id = (known after apply)
    }

  # terraform_data.sign_in_provider_gate will be created
  + resource "terraform_data" "sign_in_provider_gate" {
      + id = (known after apply)
    }

  # module.control_plane_worker.cloudflare_worker.this will be created
  + resource "cloudflare_worker" "this" {
      + account_id     = "96cfb35986e899baff4a02176a12f666"
      + created_on     = (known after apply)
      + deployed_on    = (known after apply)
      + id             = (known after apply)
      + logpush        = false
      + name           = "open-inspect-control-plane-codos"
      + observability  = {
          + enabled            = true
          + head_sampling_rate = 1
          + logs               = {
              + destinations       = (known after apply)
              + enabled            = true
              + head_sampling_rate = 1
              + invocation_logs    = true
              + persist            = true
            }
          + traces             = {
              + destinations       = (known after apply)
              + enabled            = false
              + head_sampling_rate = 1
              + persist            = true
            }
        }
      + references     = (known after apply)
      + subdomain      = {
          + enabled          = true
          + previews_enabled = true
        }
      + tags           = []
      + tail_consumers = []
      + updated_on     = (known after apply)
    }

  # module.control_plane_worker.cloudflare_worker_version.this will be created
  + resource "cloudflare_worker_version" "this" {
      + account_id          = "96cfb35986e899baff4a02176a12f666"
      + annotations         = (known after apply)
      + bindings            = (sensitive value)
      + compatibility_date  = "2024-09-23"
      + compatibility_flags = [
          + "nodejs_compat",
        ]
      + created_on          = (known after apply)
      + id                  = (known after apply)
      + limits              = (known after apply)
      + main_module         = "index.js"
      + main_script_base64  = (known after apply)
      + migration_tag       = (known after apply)
      + modules             = [
          + {
              + content_file   = "../../..//packages/control-plane/dist/index.js"
              + content_sha256 = "ad17288672984950bf9525b434d3b5a7d557c3cfc8cbcd08d48dcefa0c7e978f"
              + content_type   = "application/javascript+module"
              + name           = "index.js"
            },
        ]
      + number              = (known after apply)
      + source              = (known after apply)
      + startup_time_ms     = (known after apply)
      + urls                = (known after apply)
      + usage_model         = "standard"
      + worker_id           = (known after apply)
    }

  # module.control_plane_worker.cloudflare_workers_cron_trigger.this[0] will be created
  + resource "cloudflare_workers_cron_trigger" "this" {
      + account_id  = "96cfb35986e899baff4a02176a12f666"
      + id          = (known after apply)
      + schedules   = [
          + {
              + created_on  = (known after apply)
              + cron        = "* * * * *"
              + modified_on = (known after apply)
            },
          + {
              + created_on  = (known after apply)
              + cron        = "7,37 * * * *"
              + modified_on = (known after apply)
            },
          + {
              + created_on  = (known after apply)
              + cron        = "23 * * * *"
              + modified_on = (known after apply)
            },
        ]
      + script_name = "open-inspect-control-plane-codos"
    }

  # module.control_plane_worker.cloudflare_workers_deployment.this will be created
  + resource "cloudflare_workers_deployment" "this" {
      + account_id   = "96cfb35986e899baff4a02176a12f666"
      + annotations  = (known after apply)
      + author_email = (known after apply)
      + created_on   = (known after apply)
      + id           = (known after apply)
      + script_name  = "open-inspect-control-plane-codos"
      + source       = (known after apply)
      + strategy     = "percentage"
      + versions     = [
          + {
              + percentage = 100
              + version_id = (known after apply)
            },
        ]
    }

  # module.github_bot_worker[0].cloudflare_worker.this will be created
  + resource "cloudflare_worker" "this" {
      + account_id     = "96cfb35986e899baff4a02176a12f666"
      + created_on     = (known after apply)
      + deployed_on    = (known after apply)
      + id             = (known after apply)
      + logpush        = false
      + name           = "open-inspect-github-bot-codos"
      + observability  = {
          + enabled            = true
          + head_sampling_rate = 1
          + logs               = {
              + destinations       = (known after apply)
              + enabled            = true
              + head_sampling_rate = 1
              + invocation_logs    = true
              + persist            = true
            }
          + traces             = {
              + destinations       = (known after apply)
              + enabled            = false
              + head_sampling_rate = 1
              + persist            = true
            }
        }
      + references     = (known after apply)
      + subdomain      = {
          + enabled          = true
          + previews_enabled = true
        }
      + tags           = []
      + tail_consumers = []
      + updated_on     = (known after apply)
    }

  # module.github_bot_worker[0].cloudflare_worker_version.this will be created
  + resource "cloudflare_worker_version" "this" {
      + account_id          = "96cfb35986e899baff4a02176a12f666"
      + annotations         = (known after apply)
      + bindings            = (sensitive value)
      + compatibility_date  = "2024-09-23"
      + compatibility_flags = [
          + "nodejs_compat",
        ]
      + created_on          = (known after apply)
      + id                  = (known after apply)
      + limits              = (known after apply)
      + main_module         = "index.js"
      + main_script_base64  = (known after apply)
      + migration_tag       = (known after apply)
      + modules             = [
          + {
              + content_file   = "../../..//packages/github-bot/dist/index.js"
              + content_sha256 = "a085b7de7608de48ea372b1a49828b6e688b2fbf8dddf54a35e3ec366f357de5"
              + content_type   = "application/javascript+module"
              + name           = "index.js"
            },
        ]
      + number              = (known after apply)
      + source              = (known after apply)
      + startup_time_ms     = (known after apply)
      + urls                = (known after apply)
      + usage_model         = "standard"
      + worker_id           = (known after apply)
    }

  # module.github_bot_worker[0].cloudflare_workers_deployment.this will be created
  + resource "cloudflare_workers_deployment" "this" {
      + account_id   = "96cfb35986e899baff4a02176a12f666"
      + annotations  = (known after apply)
      + author_email = (known after apply)
      + created_on   = (known after apply)
      + id           = (known after apply)
      + script_name  = "open-inspect-github-bot-codos"
      + source       = (known after apply)
      + strategy     = "percentage"
      + versions     = [
          + {
              + percentage = 100
              + version_id = (known after apply)
            },
        ]
    }

  # module.github_kv[0].cloudflare_workers_kv_namespace.this will be created
  + resource "cloudflare_workers_kv_namespace" "this" {
      + account_id            = "96cfb35986e899baff4a02176a12f666"
      + id                    = (known after apply)
      + supports_url_encoding = (known after apply)
      + title                 = "open-inspect-github-kv-codos"
    }

  # module.linear_bot_worker[0].cloudflare_worker.this will be created
  + resource "cloudflare_worker" "this" {
      + account_id     = "96cfb35986e899baff4a02176a12f666"
      + created_on     = (known after apply)
      + deployed_on    = (known after apply)
      + id             = (known after apply)
      + logpush        = false
      + name           = "open-inspect-linear-bot-codos"
      + observability  = {
          + enabled            = true
          + head_sampling_rate = 1
          + logs               = {
              + destinations       = (known after apply)
              + enabled            = true
              + head_sampling_rate = 1
              + invocation_logs    = true
              + persist            = true
            }
          + traces             = {
              + destinations       = (known after apply)
              + enabled            = false
              + head_sampling_rate = 1
              + persist            = true
            }
        }
      + references     = (known after apply)
      + subdomain      = {
          + enabled          = true
          + previews_enabled = true
        }
      + tags           = []
      + tail_consumers = []
      + updated_on     = (known after apply)
    }

  # module.linear_bot_worker[0].cloudflare_worker_version.this will be created
  + resource "cloudflare_worker_version" "this" {
      + account_id          = "96cfb35986e899baff4a02176a12f666"
      + annotations         = (known after apply)
      + bindings            = (sensitive value)
      + compatibility_date  = "2024-09-23"
      + compatibility_flags = [
          + "nodejs_compat",
        ]
      + created_on          = (known after apply)
      + id                  = (known after apply)
      + limits              = (known after apply)
      + main_module         = "index.js"
      + main_script_base64  = (known after apply)
      + migration_tag       = (known after apply)
      + modules             = [
          + {
              + content_file   = "../../..//packages/linear-bot/dist/index.js"
              + content_sha256 = "8847840c62bce5236a0bf26241b8b3258e3cd3ccd4e9150e2f72c1980041d248"
              + content_type   = "application/javascript+module"
              + name           = "index.js"
            },
        ]
      + number              = (known after apply)
      + source              = (known after apply)
      + startup_time_ms     = (known after apply)
      + urls                = (known after apply)
      + usage_model         = "standard"
      + worker_id           = (known after apply)
    }

  # module.linear_bot_worker[0].cloudflare_workers_deployment.this will be created
  + resource "cloudflare_workers_deployment" "this" {
      + account_id   = "96cfb35986e899baff4a02176a12f666"
      + annotations  = (known after apply)
      + author_email = (known after apply)
      + created_on   = (known after apply)
      + id           = (known after apply)
      + script_name  = "open-inspect-linear-bot-codos"
      + source       = (known after apply)
      + strategy     = "percentage"
      + versions     = [
          + {
              + percentage = 100
              + version_id = (known after apply)
            },
        ]
    }

  # module.linear_kv[0].cloudflare_workers_kv_namespace.this will be created
  + resource "cloudflare_workers_kv_namespace" "this" {
      + account_id            = "96cfb35986e899baff4a02176a12f666"
      + id                    = (known after apply)
      + supports_url_encoding = (known after apply)
      + title                 = "open-inspect-linear-kv-codos"
    }

  # module.modal_app[0].null_resource.modal_deploy will be created
  + resource "null_resource" "modal_deploy" {
      + id       = (known after apply)
      + triggers = {
          + "app_name"          = "open-inspect"
          + "modal_environment" = "main"
          + "secrets_created"   = (sensitive value)
          + "source_hash"       = "8d3b7275f37cd7d6d0acd98b19de4607571ec5d34cd86bb75a47f24d61fcc89f"
        }
    }

  # module.modal_app[0].null_resource.modal_secrets[0] will be created
  + resource "null_resource" "modal_secrets" {
      + id       = (known after apply)
      + triggers = {
          + "modal_environment" = "main"
          + "secrets_hash"      = (sensitive value)
        }
    }

  # module.session_index_kv.cloudflare_workers_kv_namespace.this will be created
  + resource "cloudflare_workers_kv_namespace" "this" {
      + account_id            = "96cfb35986e899baff4a02176a12f666"
      + id                    = (known after apply)
      + supports_url_encoding = (known after apply)
      + title                 = "open-inspect-session-index-codos"
    }

  # module.slack_bot_worker[0].cloudflare_worker.this will be created
  + resource "cloudflare_worker" "this" {
      + account_id     = "96cfb35986e899baff4a02176a12f666"
      + created_on     = (known after apply)
      + deployed_on    = (known after apply)
      + id             = (known after apply)
      + logpush        = false
      + name           = "open-inspect-slack-bot-codos"
      + observability  = {
          + enabled            = true
          + head_sampling_rate = 1
          + logs               = {
              + destinations       = (known after apply)
              + enabled            = true
              + head_sampling_rate = 1
              + invocation_logs    = true
              + persist            = true
            }
          + traces             = {
              + destinations       = (known after apply)
              + enabled            = false
              + head_sampling_rate = 1
              + persist            = true
            }
        }
      + references     = (known after apply)
      + subdomain      = {
          + enabled          = true
          + previews_enabled = true
        }
      + tags           = []
      + tail_consumers = []
      + updated_on     = (known after apply)
    }

  # module.slack_bot_worker[0].cloudflare_worker_version.this will be created
  + resource "cloudflare_worker_version" "this" {
      + account_id          = "96cfb35986e899baff4a02176a12f666"
      + annotations         = (known after apply)
      + bindings            = (sensitive value)
      + compatibility_date  = "2024-09-23"
      + compatibility_flags = [
          + "nodejs_compat",
        ]
      + created_on          = (known after apply)
      + id                  = (known after apply)
      + limits              = (known after apply)
      + main_module         = "index.js"
      + main_script_base64  = (known after apply)
      + migration_tag       = (known after apply)
      + modules             = [
          + {
              + content_file   = "../../..//packages/slack-bot/dist/index.js"
              + content_sha256 = "0785ae43732e0dc434673babfff360be6b32a8641917c05a2fa039f13ffe5342"
              + content_type   = "application/javascript+module"
              + name           = "index.js"
            },
        ]
      + number              = (known after apply)
      + source              = (known after apply)
      + startup_time_ms     = (known after apply)
      + urls                = (known after apply)
      + usage_model         = "standard"
      + worker_id           = (known after apply)
    }

  # module.slack_bot_worker[0].cloudflare_workers_deployment.this will be created
  + resource "cloudflare_workers_deployment" "this" {
      + account_id   = "96cfb35986e899baff4a02176a12f666"
      + annotations  = (known after apply)
      + author_email = (known after apply)
      + created_on   = (known after apply)
      + id           = (known after apply)
      + script_name  = "open-inspect-slack-bot-codos"
      + source       = (known after apply)
      + strategy     = "percentage"
      + versions     = [
          + {
              + percentage = 100
              + version_id = (known after apply)
            },
        ]
    }

  # module.slack_kv[0].cloudflare_workers_kv_namespace.this will be created
  + resource "cloudflare_workers_kv_namespace" "this" {
      + account_id            = "96cfb35986e899baff4a02176a12f666"
      + id                    = (known after apply)
      + supports_url_encoding = (known after apply)
      + title                 = "open-inspect-slack-kv-codos"
    }

Plan: 48 to add, 0 to change, 0 to destroy.

Changes to Outputs:
  + control_plane_url              = "https://open-inspect-control-plane-codos.opencodos.workers.dev"
  + control_plane_worker_name      = "open-inspect-control-plane-codos"
  + d1_database_id                 = (known after apply)
  + d1_database_name               = "open-inspect-codos"
  + github_bot_worker_name         = "open-inspect-github-bot-codos"
  + github_kv_id                   = (known after apply)
  + linear_bot_oauth_authorize_url = "https://open-inspect-linear-bot-codos.opencodos.workers.dev/oauth/authorize"
  + linear_bot_webhook_url         = "https://open-inspect-linear-bot-codos.opencodos.workers.dev/webhook"
  + linear_bot_worker_name         = "open-inspect-linear-bot-codos"
  + linear_kv_id                   = (known after apply)
  + modal_app_name                 = "open-inspect"
  + modal_health_url               = "https://codos--open-inspect-api-health.modal.run"
  + sandbox_provider               = "modal"
  + slack_bot_events_url           = "https://open-inspect-slack-bot-codos.opencodos.workers.dev/events"
  + slack_bot_interactions_url     = "https://open-inspect-slack-bot-codos.opencodos.workers.dev/interactions"
  + slack_bot_worker_name          = "open-inspect-slack-bot-codos"
  + slack_bot_worker_url           = "https://open-inspect-slack-bot-codos.opencodos.workers.dev"
  + slack_kv_id                    = (known after apply)
  + verification_commands          = <<-EOT
        # 1. Health check control plane
        curl https://open-inspect-control-plane-codos.opencodos.workers.dev/health
        
        # 2. Health check sandbox backend
        curl https://codos--open-inspect-api-health.modal.run
        
        # 3. Verify web app deployment
        curl https://open-inspect-web-codos.opencodos.workers.dev
        
        # 4. Test authenticated endpoint (should return 401)
        curl https://open-inspect-control-plane-codos.opencodos.workers.dev/sessions
    EOT
  + web_app_platform               = "cloudflare"
  + web_app_url                    = "https://open-inspect-web-codos.opencodos.workers.dev"

Warning: Resource Destruction Considerations

  with module.control_plane_worker.cloudflare_workers_cron_trigger.this[0],
  on ../../modules/cloudflare-worker/main.tf line 173, in resource "cloudflare_workers_cron_trigger" "this":
 173: resource "cloudflare_workers_cron_trigger" "this" {

This resource cannot be destroyed from Terraform. If you create this
resource, it will be present in the API until manually deleted.

─────────────────────────────────────────────────────────────────────────────

Saved the plan to: tfplan

To perform exactly these actions, run the following command to apply:
    terraform apply "tfplan"

Pushed by: @rhlsthrm

@codos-reviewer codos-reviewer Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Blocking: 1 · Non-blocking: 0

The workflow change matches the backend's fixed bucket, but the state migration needs to freeze all writers, not just merges. A manual Terraform apply on main after the copy and before this merge can advance the old state while the new bucket remains stale. Expand the cutover procedure to prevent manual dispatches and direct applies during that window, and verify the source/destination state serial and lineage again immediately before merging (recopy if either changed). The current PR plan's 48 creates reflect the not-yet-migrated destination; they are not safe to apply.

Comment thread .github/workflows/terraform.yml

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Caution

Some comments are outside the diff and can’t be posted inline due to GitHub limitations.

⚠️ Outside diff range comments (1)

🟠 Major · Preserve the configured state bucket until migration is complete. · terraform.yml:197-200

.github/workflows/terraform.yml:197-200
🗄️ Data Integrity & Integration | 🟠 Major | ⚡ Quick win

Preserve the configured state bucket until migration is complete.

The base workflow used R2_BUCKET, which can point to a different existing state bucket. The head workflow always uses open-inspect-terraform-state. If that bucket has an empty valid state, the state step does not check for managed resources, and terraform apply -auto-approve can recreate them.

Keep the configurable bucket, with the fixed bucket as its default, until all existing state has been migrated.

Suggested fix
         env:
           CLOUDFLARE_ACCOUNT_ID: ${{ vars.CLOUDFLARE_ACCOUNT_ID || secrets.CLOUDFLARE_ACCOUNT_ID }}
+          R2_BUCKET: ${{ vars.R2_BUCKET || secrets.R2_BUCKET || 'open-inspect-terraform-state' }}
         run: |
           terraform init \
             -backend-config="access_key=${{ secrets.R2_ACCESS_KEY_ID }}" \
             -backend-config="secret_key=${{ secrets.R2_SECRET_ACCESS_KEY }}" \
+            -backend-config="bucket=${R2_BUCKET}" \
             -backend-config="endpoints={s3=\"https://${CLOUDFLARE_ACCOUNT_ID}.r2.cloudflarestorage.com\"}"

Apply the same change to the apply job.


ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Team

Run ID: cf2917b4-bb5b-4402-8afd-26e144837f62

📥 Commits

Reviewing files that changed from the base of the PR and between 242e66d and f9c78b0.

📒 Files selected for processing (1)
  • .github/workflows/terraform.yml

Included review availability: This review used your included allowance. 9 included reviews remain after this review. Your included PR review attempts over the past 7 days set your current allowance at 10 reviews per hour. Your free on-demand review promotion remains active until October 9, 2026 at 6:00 PM UTC.

@rhlsthrm

Copy link
Copy Markdown
Collaborator Author

Cutover freeze applied as requested: the Terraform workflow is disabled (gh workflow disable), so neither pushes, workflow_dispatch nor this merge can apply. No run was in flight. State copied: sha256 identical, serial 221 / lineage e4fbc209 on both. terraform init -reconfigure against the new bucket: state list = 49 resources, state pull serial 221. The 48 creates in this PR's plan came from the empty destination before the copy. Next: merge with the workflow disabled, re-check both serials, re-enable, then workflow_dispatch on main.

@rhlsthrm
rhlsthrm merged commit 328e444 into main Sep 26, 2026
7 checks passed
@rhlsthrm
rhlsthrm deleted the fork/converge-state-bucket branch September 26, 2026 12:46
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant