CI hygiene + Terraform remote state on R2 - #6
Merged
Merged
Conversation
node 20 -> 24, pnpm/action-setup v2 -> v6 (pnpm 10), checkout v7, setup-node v7 with pnpm store cache. pages-action repo is gone (404); deploy.yml switches to cloudflare/wrangler-action v4 'pages deploy' (same secrets).
State moves to R2 (bucket pywire-tfstate, native S3 locking, TF >= 1.10): - backend block in main.tf; .terraform.lock.hcl now committed (was gitignored — CI needs reproducible provider pins) - infra.auto.tfvars carries the non-secret values for CI (public repo; token + emails stay in secrets) - infra-plan.yml: PR plan comments (never fail the PR), push-to-main drift enforcement, weekly scheduled drift check -> issue - infra-apply.yml: workflow_dispatch on main, self-verifying (plan must be empty after apply); both serialize on the terraform lock group - infra/README.md: daily flow, local setup, secrets, break-glass Follow-up (bootstrap): local 'terraform init -migrate-state' moves the existing state into R2; the first apply also converges the known pywire-docs Pages drift (repo renamed pywire-core -> pywire).
The GitHub connections on both Pages projects are vestigial (builds disabled since 53218bf; deploys are GitHub Actions direct uploads), so the config now matches reality: no source, just production_branch. The live connections cannot be detached in place — the API silently ignores PATCH source:null (verified) — so converging requires delete+recreate, documented in infra/README.md. Provider stays pinned at 5.16 by the lock: 5.24+ can't read this state's email_routing_settings.
Terraform plan — 📝 changescloudflare_email_routing_address.destinations["[email]"]: Refreshing state... [id=b465118aef02490e9f584146ce54f65b]
cloudflare_email_routing_address.destinations["[email]"]: Refreshing state... [id=ba2224a5004345f19ad66c5a71343976]
cloudflare_r2_bucket.cdn: Refreshing state... [id=pywire-cdn]
cloudflare_email_routing_settings.main: Refreshing state... [id=9f1c5e8c997d29163e81a24f7b26dc35]
cloudflare_dns_record.vscode_verification: Refreshing state... [id=8ac56f7d47b8d2401df848706ad88cdd]
cloudflare_pages_project.docs: Refreshing state... [id=pywire-docs]
cloudflare_ruleset.allow_llm_crawlers: Refreshing state... [id=375241922de4405a8b77b812e6e25730]
cloudflare_pages_project.landing: Refreshing state... [id=pywire-landing]
cloudflare_dns_record.nightly: Refreshing state... [id=6a2d6b6878cc8452e1e29dd1f30c6dd1]
cloudflare_email_routing_rule.maintainers_group: Refreshing state... [id=2f20558a55f04e10959ead934f397730]
cloudflare_email_routing_rule.individual_aliases["reece"]: Refreshing state... [id=d14078e65aea4106adf662dc46d54710]
cloudflare_email_routing_rule.individual_aliases["hello"]: Refreshing state... [id=1ff2a9d2add740089d4c7952455e4b56]
cloudflare_workers_script.router: Refreshing state... [id=pywire-router]
cloudflare_workers_route.catch_all: Refreshing state... [id=a70946d772474786ada02b87b9fd05cf]
cloudflare_workers_route.nightly: Refreshing state... [id=874d0be239a04de79226d64cd941e9bf]
Terraform used the selected providers to generate the following execution
plan. Resource actions are indicated with the following symbols:
~ update in-place
Terraform will perform the following actions:
# cloudflare_pages_project.docs will be updated in-place
~ resource "cloudflare_pages_project" "docs" {
~ build_config = {
+ build_caching = (known after apply)
+ web_analytics_tag = (known after apply)
+ web_analytics_token = (sensitive value)
# (3 unchanged attributes hidden)
}
id = "pywire-docs"
name = "pywire-docs"
- source = {
- config = {
- deployments_enabled = true -> null
- owner = "pywire" -> null
- owner_id = "256868786" -> null
- path_includes = [
- "*",
] -> null
- pr_comments_enabled = true -> null
- preview_branch_includes = [
- "*",
] -> null
- preview_deployment_setting = "none" -> null
- production_branch = "main" -> null
- production_deployments_enabled = false -> null
- repo_id = "1141348112" -> null
- repo_name = "pywire-core" -> null
} -> null
- type = "github" -> null
} -> null
# (13 unchanged attributes hidden)
}
# cloudflare_pages_project.landing will be updated in-place
~ resource "cloudflare_pages_project" "landing" {
~ build_config = {
+ build_caching = (known after apply)
+ web_analytics_tag = (known after apply)
+ web_analytics_token = (sensitive value)
# (3 unchanged attributes hidden)
}
id = "pywire-landing"
name = "pywire-landing"
- source = {
- config = {
- deployments_enabled = true -> null
- owner = "pywire" -> null
- owner_id = "256868786" -> null
- path_includes = [
- "*",
] -> null
- pr_comments_enabled = true -> null
- preview_branch_includes = [
- "*",
] -> null
- preview_deployment_setting = "none" -> null
- production_branch = "main" -> null
- production_deployments_enabled = false -> null
- repo_id = "1141395619" -> null
- repo_name = "pywire.dev" -> null
} -> null
- type = "github" -> null
} -> null
# (13 unchanged attributes hidden)
}
Plan: 0 to add, 2 to change, 0 to destroy. |
reecelikesramen
added a commit
that referenced
this pull request
Sep 23, 2026
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Parts B+C of the infra/CI plan.
cloudflare/pages-actionis gone (repo 404s — the astro-v7 push failures) — nowcloudflare/wrangler-action@v4pages deploy.pywire-tfstatebucket, native S3 locking): backend block, committed lockfile,infra.auto.tfvarsfor non-secret vars (repo is public — emails/token stay secrets),infra/README.mdrunbook.infra-plan.yml(PR plan comments, push-to-main drift enforcement, weekly drift issue) andinfra-apply.yml(dispatch-only, self-verifying).Verified locally: migration to R2 done, plan identical pre/post migration, state pull from R2, locking works. First push to main after merge runs the drift job; dispatch Infra apply once to see the full loop.