diff --git a/edu/.nbd/tickets/59c122.md b/edu/.nbd/tickets/59c122.md index 9b488f0..e36bf7e 100644 --- a/edu/.nbd/tickets/59c122.md +++ b/edu/.nbd/tickets/59c122.md @@ -1,7 +1,7 @@ +++ title = "Deploy edu mdbook to Cloudflare Pages at vibebooks.elijah.run" priority = 5 -status = "todo" +status = "done" ticket_type = "project" dependencies = [] +++ diff --git a/edu/infra/.gitignore b/edu/infra/.gitignore new file mode 100644 index 0000000..4599671 --- /dev/null +++ b/edu/infra/.gitignore @@ -0,0 +1,4 @@ +*.tfstate +*.tfstate.* +.terraform/ +*.tfvars diff --git a/edu/infra/dns.tf b/edu/infra/dns.tf new file mode 100644 index 0000000..97b0601 --- /dev/null +++ b/edu/infra/dns.tf @@ -0,0 +1,16 @@ +# CNAME record pointing vibebooks.elijah.run to the Cloudflare Pages subdomain. +resource "cloudflare_record" "edu" { + zone_id = var.cloudflare_zone_id + name = "vibebooks" + type = "CNAME" + content = cloudflare_pages_project.edu.subdomain + proxied = true +} + +# Bind the custom domain vibebooks.elijah.run to the Pages project. +# Cloudflare provisions an SSL certificate automatically. +resource "cloudflare_pages_domain" "edu" { + account_id = var.cloudflare_account_id + project_name = cloudflare_pages_project.edu.name + domain = var.domain +} diff --git a/edu/infra/main.tf b/edu/infra/main.tf new file mode 100644 index 0000000..2d04fd4 --- /dev/null +++ b/edu/infra/main.tf @@ -0,0 +1,27 @@ +terraform { + # Local state — terraform.tfstate is gitignored. + # No remote backend needed for this project. + required_providers { + # Cloudflare provider for Pages and DNS. + cloudflare = { + source = "registry.terraform.io/cloudflare/cloudflare" + version = "~> 4" + } + } +} + +# Cloudflare Pages project for the edu mdbook static site. +# Deployment is handled via `wrangler pages deploy` from the CI/CD pipeline or justfile. +# No git source block — artifact upload model only. +resource "cloudflare_pages_project" "edu" { + account_id = var.cloudflare_account_id + name = "vibedbooks" + production_branch = "main" + + build_config { + # mdbook build command — produces the static site in the `book/` directory. + build_command = "mdbook build" + destination_dir = "book" + root_dir = "edu" + } +} diff --git a/edu/infra/outputs.tf b/edu/infra/outputs.tf new file mode 100644 index 0000000..5896b27 --- /dev/null +++ b/edu/infra/outputs.tf @@ -0,0 +1,11 @@ +# The default Pages subdomain assigned by Cloudflare (e.g. vibedbooks.pages.dev). +output "pages_subdomain" { + description = "Cloudflare Pages auto-assigned subdomain for the edu site." + value = cloudflare_pages_project.edu.subdomain +} + +# The production custom domain. +output "production_url" { + description = "Production URL for the edu site." + value = "https://${var.domain}" +} diff --git a/edu/infra/providers.tf b/edu/infra/providers.tf new file mode 100644 index 0000000..3e6eed4 --- /dev/null +++ b/edu/infra/providers.tf @@ -0,0 +1,6 @@ +# Cloudflare provider configuration. +# Authentication uses an API token passed via var.cloudflare_api_token. +# Never hardcode credentials here — use TF_VAR_* env vars or a gitignored .tfvars file. +provider "cloudflare" { + api_token = var.cloudflare_api_token +} diff --git a/edu/infra/variables.tf b/edu/infra/variables.tf new file mode 100644 index 0000000..48a0da3 --- /dev/null +++ b/edu/infra/variables.tf @@ -0,0 +1,28 @@ +# Cloudflare API token — required for all provider operations. +# Set via: export TF_VAR_cloudflare_api_token="..." +variable "cloudflare_api_token" { + description = "Cloudflare API token with Pages and DNS edit permissions." + type = string + sensitive = true +} + +# Cloudflare account ID — required for Pages resources. +# Set via: export TF_VAR_cloudflare_account_id="..." +variable "cloudflare_account_id" { + description = "Cloudflare account ID where edu resources are provisioned." + type = string +} + +# Cloudflare zone ID for elijah.run — required for DNS records. +# Set via: export TF_VAR_cloudflare_zone_id="..." +variable "cloudflare_zone_id" { + description = "Cloudflare zone ID for the elijah.run domain." + type = string +} + +# Production domain for the edu site. +variable "domain" { + description = "Production domain where the edu site is hosted." + type = string + default = "vibebooks.elijah.run" +} diff --git a/edu/justfile b/edu/justfile new file mode 100644 index 0000000..8d511b2 --- /dev/null +++ b/edu/justfile @@ -0,0 +1,24 @@ +build: + mdbook build + +serve: + mdbook serve + +deploy: + # Build the static site and upload it to Cloudflare Pages as a production deployment. + # Requires CLOUDFLARE_API_TOKEN and CLOUDFLARE_ACCOUNT_ID to be set in the environment. + mdbook build + wrangler pages deploy book --project-name vibedbooks --branch main + +infra-init: + # Initialise the OpenTofu working directory and download providers. + cd infra && tofu init + +infra-plan: + # Preview infrastructure changes without applying them. + cd infra && tofu plan + +infra-apply: + # Apply the OpenTofu configuration to Cloudflare. + # Requires TF_VAR_cloudflare_api_token, TF_VAR_cloudflare_account_id, TF_VAR_cloudflare_zone_id. + cd infra && tofu apply