Skip to content

Latest commit

 

History

History
119 lines (96 loc) · 6.58 KB

File metadata and controls

119 lines (96 loc) · 6.58 KB

Deploy runbook — pixieengine.com static archive

Steps to take the generated static tree live on pixieengine.com. Plan: a greenfield AWS CDK stack (infra/, PixieStaticStack) stands up a new private S3 bucket + new CloudFront distribution + edge function. The legacy distribution E3BKYYG8EH9O0K is never touched. Cutover = move the pixieengine.com DNS alias to the new distribution; rollback = move it back. Companion to architecture.md; the canonical infra-as-code runbook is ../infra/README.md.

(Historical note: an earlier draft of this doc repointed E3BKYYG8EH9O0K's origin in place. That approach is superseded by the greenfield stack below — it's safer, fully reversible via DNS, and keeps the live distribution untouched until verification passes.)

Pieces (all created by the CDK stack in infra/)

  • New distribution: a fresh CloudFront distribution (separate from E3BKYYG8EH9O0K). index.html root object; 403/404 → /410/index.html served as 404 (CloudFront can't emit a 410 in custom-error responses); rewrite function on viewer-request.
  • New bucket: pixieengine-static — private, CloudFront OAC only, RETAIN removal policy.
  • Edge function: pixieengine-rewrite (infra/functions/rewrite.civet → compiled to dist/functions/rewrite.js; id/slug rewrite, ?tagged=/tags/ 301, dir→index). Unit-tested via npm test in infra/.
  • Custom domain (cutover only, -c withDomain=true): ACM cert (DNS-validated in zone ZPF3QICRGSLCF) + pixieengine.com alias + Route 53 A/AAAA → new dist.
  • Deploy IAM role: pixie-deploy (defined by the stack, assumed by the pixie-ops user): rw on pixieengine-static + cloudfront:CreateInvalidation on the dist + scoped backup-recovery access. Keyless — content deploys assume it for short-lived STS creds (nothing to rotate).

The initial upload used a hand-made staging bucket pixieengine-com-site (256,530 objects), server-side-copied into pixieengine-static. That bucket was deleted 2026-06-02; deploys now sync directly from ../static-site/public (below).

Prereqs

  • cdk deploy runs locally as the pixie-ops profile (scoped to assume the CDK bootstrap roles): npx cdk deploy --profile pixie-ops. Only the one-time cdk bootstrap needs admin (run from CloudShell) — already done for this account. cdk synth/diff and npm test need no creds; content aws s3 sync uses the keyless pixie-deploy profile (role-chained off pixie-ops); pixieengine-audit remains read-only.
  • infra/ is self-contained: Civet sources (bin/, lib/, functions/, build.civet, validate-template.civet) that npm install + cdk compile automatically via build.civet (esbuild) — no separate build step. Get it into CloudShell via git push + clone, or upload a zip.

A. Sanity-check locally (no creds)

cd ~/apps/pixieengine.com/infra
npm install
npm test            # rewrite-function unit tests (16 assertions)
npx cdk synth       # confirms the stack compiles to a valid template

B. Phase 1 — deploy the new stack (no alias; invisible to the live domain)

Locally (the account is already bootstrapped — bootstrap is CloudShell/admin-only and rarely needed):

cd infra && npm install
npx cdk deploy --profile pixie-ops    # creates bucket + dist + function; alias NOT attached

Note the DistributionId and DistributionDomain (dXXXX.cloudfront.net) outputs.

C. Populate + invalidate

Content is not managed by CDK (it's a deploy step). Sync the generated tree from local:

aws s3 sync ../static-site/public s3://pixieengine-static --delete --profile pixie-deploy
aws cloudfront create-invalidation --distribution-id <DistributionId> --paths "/*" --profile pixie-deploy

D. Verify on the dXXXX.cloudfront.net URL (before cutover)

  • https://dXXXX.cloudfront.net/ → editor shell (200); /sprites/ → gallery; /sprites/page/100/ → logarithmic pager.
  • /sprites/257680-ugly-killman-donkey-ass → sprite page (slug variant 200).
  • /sprites?tagged=mario301/tags/mario/.
  • A missing id (e.g. /sprites/999999999) → 404 (the /410/index.html gone page).
  • /sitemap.xml → 200.
  • A sprite with a replay → ▶ Watch replay plays in the editor.
  • A tune → ▶ Play in Composer loads in the composer.

E. Phase 2 — cutover (the only step that touches the live domain)

A CloudFront alias can live on only one distribution. So, only after verification passes:

  1. Remove the pixieengine.com alias (CNAME) from the legacy dist E3BKYYG8EH9O0K (console).
  2. Add it to the new dist + create cert + DNS:
    npx cdk deploy -c withDomain=true     # ACM cert, alias, Route 53 A/AAAA -> new dist
    (ACM DNS validation auto-creates a CNAME in the zone; the first deploy waits for it.)
  3. Verify https://pixieengine.com/ (rerun the section-D checklist on the apex domain).

Rollback

Re-point the pixieengine.com Route 53 alias back to the legacy dist E3BKYYG8EH9O0K (or re-add the alias there). Nothing in the legacy distribution was modified, so rollback is pure DNS.

Ongoing content deploys

aws s3 sync ../static-site/public s3://pixieengine-static --delete --profile pixie-deploy
aws cloudfront create-invalidation --distribution-id <DistributionId> --paths "/*" --profile pixie-deploy

Incremental regen (enriched titles, etc.) → re-generates3 sync → invalidate just the changed paths. Full tree is ~256k objects.

  • Moderation deploys (removals / adult-gates) ride the same path: generate.mjs honors static-site/{removed.tsv,adult.tsv}, so the regen drops removed pages and gates adult ones; the --delete is what makes a removed page 404. Full runbook: static-site/README.md "Removing content" + "Moderation review".
  • Use the default (mtime) sync, not --size-only. A fresh generate rewrites every file, so a default sync re-uploads all and is always correct. --size-only is faster but skips content changes that don't alter byte size — notably paginated listings whose page count changes digit-preserving (e.g. last page 41154108), which silently desyncs gallery/tag/profile "Last »" links. If you must use --size-only for speed, follow with a default sync of the listing trees.

Notes

  • Content-types are inferred from extensions (.html→text/html, .css→text/css, .xml→application/xml).
  • After launch: delete the temporary pixieengine-audit user. (The staging bucket pixieengine-com-site was deleted 2026-06-02.)