Skip to content

Commit de144b6

Browse files
JustAGhosTclaude
andauthored
docs(website): consolidate deployment guide, fix stale facts (#62)
website/docs/DEPLOYMENT.md and docs/WEBSITE_DEPLOYMENT.md were two separately-stale deployment docs for the same site, each with its own wrong set of resource names/branch/domain. Verified live reality against the Terraform stack and deploy-website.yml, rewrote docs/WEBSITE_DEPLOYMENT.md as the single canonical doc, and replaced website/docs/DEPLOYMENT.md with a pointer to it. Closes the doc-sweep follow-up flagged in CEL_MIGRATION_PLAN.md step 5. Co-authored-by: Claude Sonnet 5 <noreply@anthropic.com>
1 parent 98d61cc commit de144b6

3 files changed

Lines changed: 168 additions & 454 deletions

File tree

docs/WEBSITE_DEPLOYMENT.md

Lines changed: 137 additions & 107 deletions
Original file line numberDiff line numberDiff line change
@@ -1,33 +1,61 @@
1-
# CodeFlow Website Deployment Guide
1+
# CodeFlow Website Deployment Guide
22

3-
This document describes the Next.js website and Azure deployment setup for codeflow.io.
3+
This document describes the Next.js website and Azure deployment setup for
4+
`codeflow.celladoresystems.com`.
5+
6+
> **Canonical doc.** This is the single source of truth for website deployment.
7+
> `website/docs/DEPLOYMENT.md` is a pointer to this file — don't let the two
8+
> drift again; edit only this one.
49
510
## Overview
611

712
The CodeFlow Engine website is a Next.js application that provides:
813
- **Home Page**: Project promotion and key features
9-
- **Installation Guide**: Step-by-step installation instructions
14+
- **Installation Guide**: Step-by-step installation instructions
1015
- **Download Page**: Links to various download methods (GitHub, PyPI, Docker)
1116

1217
## Architecture
1318

1419
### Technology Stack
1520

16-
- **Framework**: Next.js 16+ with App Router
17-
- **Styling**: Tailwind CSS 4
21+
- **Framework**: Next.js (static export, `output: "export"`)
22+
- **Styling**: Tailwind CSS
1823
- **Deployment**: Azure Static Web Apps
19-
- **CI/CD**: GitHub Actions
24+
- **CI/CD**: GitHub Actions (`.github/workflows/deploy-website.yml`)
2025

2126
### Azure Resources
2227

23-
All resources follow the naming convention: `{env}-{resourcetype}-{region}-codeflow`
24-
25-
#### Production Resources
26-
27-
- **Static Web App**: `prod-stapp-san-codeflow`
28-
- **Resource Group**: `prod-rg-san-codeflow`
29-
- **Location**: `eastus2` (East US 2) - Static Web Apps are only available in: westus2, centralus, eastus2, westeurope, eastasia
30-
- **Custom Domain**: `codeflow.io`
28+
Live resources (celladore-sub, recreated 2026-08-19 from the old
29+
phoenixvc-owned `pvc-*` resources — see
30+
`orchestration/infrastructure/CEL_MIGRATION_PLAN.md` for the full cutover
31+
history):
32+
33+
- **Static Web App**: `cel-prod-codeflow-swa`
34+
- **Resource Group**: `cel-prod-codeflow-rg`
35+
- **Location**: `eastus2`
36+
- **SKU**: Free tier (supports up to 2 custom domains — sufficient for this site)
37+
- **Custom Domain**: `codeflow.celladoresystems.com`
38+
39+
There is no CDN or Application Insights component provisioned for this site —
40+
only the Static Web App itself. If you need those, they'd have to be added
41+
first, not just documented.
42+
43+
### Infrastructure as Code
44+
45+
The Terraform stack for this site lives at
46+
`orchestration/infrastructure/terraform/website/` (this repo's canonical live
47+
Terraform for the website — see the root `CLAUDE.md`). As of 2026-08-19 it has
48+
**never been successfully applied**: no backend is configured (only
49+
`backend.tf.example` exists) and no state tracks the live resources, which
50+
were created out-of-band via `az` CLI. A first real `terraform apply` needs
51+
`terraform import` for the resource group and the Static Web App first, or it
52+
will try to create duplicates. See that stack's own `README.md` for the exact
53+
apply sequence and known gaps — don't treat it as already the deployment
54+
mechanism; the GitHub Actions workflow below is what actually ships the site
55+
today.
56+
57+
There is no `infrastructure/bicep/` directory in this repo — ignore any prior
58+
references to Bicep-based deployment for this site.
3159

3260
## Local Development
3361

@@ -43,113 +71,109 @@ Visit `http://localhost:3000` to view the site.
4371

4472
### Automatic Deployment
4573

46-
The website is automatically deployed when:
47-
- Changes are pushed to the `main` branch in the `website/` directory
74+
`.github/workflows/deploy-website.yml` deploys automatically when:
75+
- Changes are pushed to the **`master`** branch touching `website/**` or the
76+
workflow file itself
4877
- The workflow is manually triggered via `workflow_dispatch`
4978

50-
### Manual Deployment
79+
A pull request touching those paths runs a separate build-only validation job
80+
(no deploy).
5181

52-
1. **Build the site**:
53-
```bash
54-
cd website
55-
npm run build
56-
```
82+
The deploy job builds with `npm ci` + `npm run build`, then uploads the static
83+
export in `website/out` via `Azure/static-web-apps-deploy@v1`
84+
(`skip_app_build: true` — the action does not rebuild, it just uploads what
85+
the workflow already built).
5786

58-
2. **Deploy using Azure CLI**:
59-
```bash
60-
npm install -g @azure/static-web-apps-cli
61-
swa deploy ./out --deployment-token <YOUR_TOKEN>
62-
```
63-
64-
### Infrastructure Deployment
65-
66-
To deploy the Azure infrastructure:
87+
### Manual Deployment
6788

6889
```bash
69-
# Create resource group first (if it doesn't exist)
70-
az group create \
71-
--name prod-rg-san-codeflow \
72-
--location "eastus2"
73-
74-
# Deploy the Static Web App
75-
az deployment group create \
76-
--resource-group prod-rg-san-codeflow \
77-
--template-file infrastructure/bicep/website.bicep \
78-
--parameters @infrastructure/bicep/website-parameters.json
79-
```
90+
cd website
91+
npm run build
8092

81-
**Note:** The resource group must exist before deploying the Static Web App. The Static Web App will be created in the specified resource group.
93+
npm install -g @azure/static-web-apps-cli
94+
swa deploy ./out --deployment-token <YOUR_TOKEN>
95+
```
8296

8397
## Configuration
8498

85-
### Required GitHub Secrets
99+
### Required GitHub Secret
86100

87-
- `AZURE_STATIC_WEB_APPS_API_TOKEN`: Deployment token from Azure Static Web App
88-
- `AZURE_SUBSCRIPTION_ID`: Azure subscription ID
89-
- `AZURE_CLIENT_ID`: Service principal client ID (for infrastructure deployment)
90-
- `AZURE_CLIENT_SECRET`: Service principal client secret
91-
- `AZURE_TENANT_ID`: Azure AD tenant ID
101+
- `AZURE_STATIC_WEB_APPS_API_TOKEN`: deployment token for `cel-prod-codeflow-swa`.
102+
If unset, the deploy step is skipped and the workflow just confirms the
103+
build succeeded (see the "Skip deploy when token is missing" step).
92104

93105
### Getting the Deployment Token
94106

95-
After creating the Static Web App, retrieve the deployment token:
96-
97107
```bash
98108
az staticwebapp secrets list \
99-
--name prod-stapp-san-codeflow \
100-
--resource-group prod-rg-san-codeflow \
109+
--name cel-prod-codeflow-swa \
110+
--resource-group cel-prod-codeflow-rg \
101111
--query "properties.apiKey" \
102112
--output tsv
103113
```
104114

105-
## Custom Domain Setup
115+
### Environment variables
106116

107-
1. **Deploy the infrastructure** (if not already done)
117+
There is no `NEXT_PUBLIC_API_URL` or other runtime env var wired into the
118+
site. `APP_URL` / `API_URL` are compile-time constants in
119+
`website/app/config/constants.ts` — edit that file directly and redeploy
120+
rather than looking for a `.env.production`.
108121

109-
2. **Get domain validation token**:
110-
```bash
111-
az staticwebapp hostname show \
112-
--name prod-stapp-san-codeflow \
113-
--resource-group prod-rg-san-codeflow \
114-
--hostname codeflow.io
115-
```
116-
117-
3. **Add DNS TXT record** to your domain provider:
118-
- Record type: TXT
119-
- Name: `asuid.codeflow.io` (or as specified by Azure)
120-
- Value: (validation token from step 2)
122+
## Custom Domain Setup
121123

122-
4. **Wait for validation** (usually 5-10 minutes)
124+
The custom domain is already live. This is the sequence that was actually
125+
used, for reference if it ever needs to be redone:
123126

124-
5. **Add CNAME record** (if not automatically created):
125-
- Record type: CNAME
126-
- Name: `codeflow.io` (or `www.codeflow.io`)
127-
- Value: `{static-web-app-name}.azurestaticapps.net`
127+
1. `celladore-org/infrastructure/dns` (Cloudflare, zone `celladoresystems.com`)
128+
owns the `codeflow.celladoresystems.com` CNAME pointing at the Static Web
129+
App's default hostname. This repo does not own that DNS record.
130+
2. Once the CNAME resolves, bind the custom domain on the Static Web App:
131+
```bash
132+
az staticwebapp hostname set \
133+
--name cel-prod-codeflow-swa \
134+
--resource-group cel-prod-codeflow-rg \
135+
--hostname codeflow.celladoresystems.com
136+
```
137+
3. Azure provisions the SSL certificate automatically (`cname-delegation`
138+
validation, no TXT record needed since DNS already points at the SWA
139+
default hostname).
140+
4. Verify:
141+
```bash
142+
az staticwebapp hostname show \
143+
--name cel-prod-codeflow-swa \
144+
--resource-group cel-prod-codeflow-rg \
145+
--hostname codeflow.celladoresystems.com
146+
```
147+
or simply `curl -I https://codeflow.celladoresystems.com/` and check for
148+
`200` with a valid cert.
128149

129150
## Project Structure
130151

131152
```
132153
website/
133-
├── app/
134-
│ ├── page.tsx # Home page
135-
│ ├── installation/
136-
│ │ └── page.tsx # Installation guide
137-
│ ├── download/
138-
│ │ └── page.tsx # Download page
139-
│ ├── layout.tsx # Root layout
140-
│ └── globals.css # Global styles
141-
├── public/ # Static assets
142-
├── next.config.ts # Next.js configuration
143-
├── package.json
144-
└── README.md
145-
146-
infrastructure/bicep/
147-
├── website.bicep # Azure infrastructure definition
148-
├── website-parameters.json # Deployment parameters
149-
└── README-WEBSITE.md # Infrastructure documentation
154+
├── app/
155+
│ ├── page.tsx # Home page
156+
│ ├── installation/
157+
│ │ └── page.tsx # Installation guide
158+
│ ├── download/
159+
│ │ └── page.tsx # Download page
160+
│ ├── config/
161+
│ │ └── constants.ts # APP_URL / API_URL etc. (compile-time)
162+
│ ├── layout.tsx # Root layout
163+
│ └── globals.css # Global styles
164+
├── public/ # Static assets
165+
├── next.config.ts # Next.js configuration
166+
├── package.json
167+
└── README.md
168+
169+
orchestration/infrastructure/terraform/website/
170+
├── main.tf # Resource group + Static Web App + custom domain
171+
├── variables.tf
172+
├── outputs.tf
173+
└── README.md # Apply sequence, known gaps (see above)
150174
151175
.github/workflows/
152-
└── deploy-website.yml # CI/CD pipeline
176+
└── deploy-website.yml # CI/CD pipeline
153177
```
154178

155179
## Next.js Configuration
@@ -171,41 +195,47 @@ This ensures compatibility with Azure Static Web Apps.
171195

172196
## Monitoring
173197

174-
- **Azure Portal**: Monitor Static Web App metrics, logs, and performance
175-
- **GitHub Actions**: View deployment status and logs
176-
- **Custom Domain**: Monitor DNS and SSL certificate status
198+
- **GitHub Actions**: deployment status and build logs
199+
- **Azure Portal**: Static Web App overview for basic traffic/availability
200+
(no Application Insights component is provisioned — don't expect
201+
app-level telemetry beyond that)
202+
- **Custom Domain**: DNS and SSL certificate status via
203+
`az staticwebapp hostname show` (see above)
177204

178205
## Troubleshooting
179206

180207
### Build Failures
181208

182-
- Check Node.js version (requires 20+)
209+
- Check Node.js version (workflow uses 20)
183210
- Verify all dependencies are installed
184211
- Review build logs in GitHub Actions
185212

186213
### Deployment Failures
187214

188-
- Verify `AZURE_STATIC_WEB_APPS_API_TOKEN` is correct
189-
- Check Azure Static Web App exists and is accessible
215+
- Verify `AZURE_STATIC_WEB_APPS_API_TOKEN` is set in repo secrets — if it's
216+
missing, the workflow silently skips the deploy step instead of failing
217+
- Check `cel-prod-codeflow-swa` exists and is accessible
190218
- Review deployment logs in GitHub Actions
191219

192220
### Custom Domain Issues
193221

194-
- Verify DNS records are correct
195-
- Check domain validation status in Azure Portal
196-
- Ensure SSL certificate is provisioned (automatic for Static Web Apps)
197-
198-
## Cost Estimation
222+
- Verify the CNAME in `celladore-org/infrastructure/dns` still resolves
223+
- Check domain validation status in Azure Portal or via
224+
`az staticwebapp hostname show`
225+
- A transient cert-mismatch right after DNS changes is expected — the CNAME
226+
can resolve before Azure finishes binding the custom domain
199227

200-
- **Static Web App (Standard)**: ~$9/month
201-
- **Custom Domain**: Included
202-
- **Bandwidth**: 100 GB included, then $0.08/GB
228+
## Cost
203229

204-
Total estimated monthly cost: ~$9-15 depending on traffic.
230+
Live SKU is **Free tier** — no static-web-app hosting cost for this site at
231+
current traffic. If usage ever requires moving to Standard, re-check current
232+
Azure pricing before quoting a number here; don't assume a stale figure.
205233

206234
## References
207235

208236
- [Next.js Documentation](https://nextjs.org/docs)
209237
- [Azure Static Web Apps Documentation](https://learn.microsoft.com/azure/static-web-apps/)
210-
- [GitHub Actions for Azure](https://github.com/azure/login)
211-
238+
- `orchestration/infrastructure/CEL_MIGRATION_PLAN.md` — full cutover history
239+
(DNS, custom domain, and the celladore-sub recreate-and-cutover)
240+
- `orchestration/infrastructure/terraform/website/README.md` — Terraform apply
241+
sequence and known gaps

orchestration/infrastructure/CEL_MIGRATION_PLAN.md

Lines changed: 23 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -482,26 +482,39 @@ block was actually read.)
482482
`docs/deployment/ENVIRONMENT_VARIABLES.md` (two `CORS_ALLOWED_ORIGINS` examples — did
483483
*not* add `app.codeflow` to that list, since its own `/api` calls are same-origin and
484484
don't need a CORS entry), and `docs/deployment/KUBERNETES_DEPLOYMENT.md` (generic Ingress
485-
YAML example). Deliberately left `website/docs/DEPLOYMENT.md` untouched — it's stale on
486-
far more than this one hostname (pre-cel-migration resource names, `www.codeflow.io`
487-
instructions, an `NEXT_PUBLIC_API_URL` env var confirmed dead — zero reads anywhere in
488-
`website/`, superseded by the hardcoded `constants.ts` value from step 4 above); patching
489-
one line would have made an otherwise-broadly-stale doc look current. Flagged in the PR
490-
body as needing its own fix-vs-archive pass — **still open as a follow-up**, not part of
491-
this phase.
485+
YAML example). Deliberately left `website/docs/DEPLOYMENT.md` untouched at the time — it
486+
was stale on far more than this one hostname, and patching one line would have made an
487+
otherwise-broadly-stale doc look current. Flagged in the PR body as needing its own
488+
fix-vs-archive pass.
489+
490+
**Follow-up closed out** (separate PR, `docs/website-deployment-cleanup`): reading
491+
`website/docs/DEPLOYMENT.md` in full turned up a second problem — `docs/WEBSITE_DEPLOYMENT.md`
492+
at the repo-root docs level covers the same ground with its *own*, independently wrong set
493+
of facts (a third naming convention, `{env}-{resourcetype}-{region}-codeflow`; `codeflow.io`
494+
as the domain; `main` as the branch; a nonexistent `infrastructure/bicep/website.bicep`).
495+
Two separately-drifting docs for one deploy path was the actual bug, not just one doc being
496+
stale. Fix: verified live reality against the Terraform stack (`orchestration/infrastructure/terraform/website/main.tf`
497+
only `azurerm_resource_group` + `azurerm_static_web_app` + `azurerm_static_web_app_custom_domain`,
498+
no CDN, no App Insights; SKU is Free tier per `variables.tf`, not the Standard the old doc
499+
assumed for its cost estimate) and against `.github/workflows/deploy-website.yml` (branch
500+
is `master`, workflow file is `deploy-website.yml`, PR runs build-only validation, deploy
501+
step silently no-ops if the token secret is unset), then rewrote `docs/WEBSITE_DEPLOYMENT.md`
502+
as the single canonical doc and replaced `website/docs/DEPLOYMENT.md` with a short pointer
503+
to it, instead of maintaining two copies that will just drift again.
492504

493505
**Done.** All six steps above closed out 2026-08-19: DNS applied and verified resolving,
494506
Azure hostname bound with a trusted managed cert, `codeflow-engine#59` merged (13:36:38Z,
495-
`deploy-website.yml` run green on `master`), `codeflow-engine#60` merged (13:38:21Z).
507+
`deploy-website.yml` run green on `master`), `codeflow-engine#60` merged (13:38:21Z), and the
508+
doc-sweep follow-up from step 5 closed out with a consolidated, verified deployment guide.
496509
`celladore-org#10` needed a rebase before merging — `celladore-org#11` merged and applied
497510
after `#10` was branched, and this DNS repo's "commit state back to `main` after apply"
498511
pattern (`terraform-dns-apply.yml`'s `git push` step) turned that into a real conflict, not
499512
just GitHub's async `mergeable: UNKNOWN` lag; rebase-resolve-force-push-reverify-merge is the
500513
repeatable fix for any future overlapping DNS PR here. One loose end: the classifier blocked
501514
a direct `gh pr merge` on #60 once (docs-only, zero CI) but let an identical retry through
502515
minutes later — inconsistency noted, not resolved, doesn't block anything. `app.codeflow.celladoresystems.com`
503-
is now the live app+API domain end-to-end; `website/docs/DEPLOYMENT.md`'s broader staleness
504-
remains an open follow-up (see step 5).
516+
is now the live app+API domain end-to-end, and the website deployment docs now match live
517+
reality instead of three different sets of fictional resource names.
505518

506519
## Not touched by this plan
507520

0 commit comments

Comments
 (0)