Configure Fastly during deploy via generated route snippets#374
Open
cwillisf wants to merge 5 commits into
Open
Configure Fastly during deploy via generated route snippets#374cwillisf wants to merge 5 commits into
cwillisf wants to merge 5 commits into
Conversation
configure-fastly.js created a Fastly condition, response object, and header per route. That accumulates objects on every change and spends a synthetic status code per redirect (the live service was already carrying 900-906). Render routes.json into three snippets instead: two lookup tables plus the recv/error logic, overwritten by name on every run, with one reused internal status (700) driving every 301. Redirects and section rewrites become table lookups, so the route config stops accumulating. - routes-to-vcl.js: pure routes.json to snippet-spec generator, with a test wired into npm test / test:unit - fastly-extended.js: add setSnippet (delete-then-create, since fastly-js updateSnippet sends no body) and validateVersion; drop the now-unused condition and response-object helpers - configure-fastly.js: build and write the snippets, validate the version before activating, drop the per-route machinery and unused lodash.defaults
The origin Host header is owned by the backend's Override host setting in the Fastly service config, which cloning a version preserves. Setting it again from configure-fastly duplicated that responsibility and, with a CloudFront-fronted origin, set the wrong value (an S3 bucket name rather than a host CloudFront routes on). Drop setBucketNameHeader and the now-unused header helper; the script manages only the route snippets.
Run the Fastly configuration script as its own job in the deploy workflow, scoped to the resolved GitHub Environment so staging and production each use their own Fastly service and activation setting. Now that route config renders to a few overwritten snippets instead of accumulating per-route objects, running it on every deploy no longer piles up Fastly objects. Activation is gated per environment by the FASTLY_ACTIVATE_CHANGES variable: set it in an environment to activate and purge after configuring, leave it unset to configure and validate only.
There was a problem hiding this comment.
Pull request overview
This PR makes Fastly route configuration deploy-safe and idempotent by generating a small fixed set of VCL snippets from src/routes.json (instead of creating per-route Fastly objects), then wiring the configuration step into the deploy workflow with optional per-environment activation.
Changes:
- Add
bin/lib/routes-to-vcl.jsto renderroutes.jsonintoinit/recv/errorVCL snippets, plus a unit test validating the generated VCL. - Update Fastly tooling to manage VCL snippets (delete+create) and validate generated VCL before activation.
- Add a
configure-fastlyGitHub Actions job and adjust npm scripts/deps (newtest:unit, droplodash.defaults, removeS3_BUCKET_NAMEdocs).
Reviewed changes
Copilot reviewed 4 out of 8 changed files in this pull request and generated 2 comments.
Show a summary per file
| File | Description |
|---|---|
bin/lib/routes-to-vcl.js |
New routes.json → VCL snippet generator (tables + recv/error logic). |
bin/lib/fastly-extended.js |
Switch helper surface to snippet management + version validation. |
bin/configure-fastly.js |
Configure snippets on a working version and validate VCL before optional activation/purge. |
test/routes-to-vcl.test.js |
New unit test for snippet generation and redirect-pattern validation. |
.github/workflows/deploy.yml |
Add configure-fastly job scoped to the resolved Environment. |
package.json / package-lock.json |
Add unit-test script; remove unused dependency. |
README.md |
Remove S3_BUCKET_NAME from env var docs. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
The learn section was renamed to explore, but the live config still routed /learn to a now-missing learn page. Redirect /learn and its sub-paths to the matching /explore path (preserving the tail), plus an exact /learn.html redirect, so existing links keep working. Add a "prefix" redirect kind to the generator, rendered as a regex rewrite in recv (a table lookup can only match an exact path).
The configure-fastly job ran in parallel with the S3 upload, so with activation on it could flip Fastly to new routing before the new content was uploaded -- and this PR adds new routes. Depend on the deploy job so content lands first, and so a failed upload skips the Fastly step. FASTLY_ACTIVATE_CHANGES was a presence check, so the string "false" (or any non-empty value) still activated. Require exactly "true".
cwillisf
force-pushed
the
refactor/fastly-config-generated-snippets
branch
from
July 16, 2026 22:24
97d811c to
cbfa1f6
Compare
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.
Proposed Changes
src/routes.jsoninto a small, fixed set of VCL snippets (aninitsnippet withredirects/sectionslookup tables, arecvsnippet, and anerrorsnippet) via a newbin/lib/routes-to-vcl.js, instead of creating one Fastly condition + response object + header per route. The snippets are overwritten by name on every run.table.lookups; a single reused internal status code (700) drives every 301, rather than burning one synthetic status per redirect.validateServiceVersion) before anything activates.Hostheader in code. The backend Override host in the service config owns it, and cloning a version preserves it. With CloudFront fronting the origin, the previousHost = S3 bucket namewas also the wrong value (CloudFront routes on its distribution domain / alternate domain names, not a bucket name).configure-fastlyjob to the deploy workflow, scoped to the resolved GitHub Environment so staging and production each use their own Fastly service. Activation is gated per-environment by theFASTLY_ACTIVATE_CHANGESvariable (set it to activate + purge, leave it unset to configure + validate only).Reason for Changes
The previous
configure-fastlywas additive: each run created fresh conditions, response objects, and headers per route, and consumed a synthetic status code per redirect. Re-running it — as a deploy step would — accumulated objects and eventually exhausted status codes, which is why it was never wired into the deploy. Rendering routes to a handful of overwrite-by-name snippets makes the operation idempotent, so it is safe to run on every deploy. This PR both makes that change and wires the step in.Test Coverage
test/routes-to-vcl.test.js(wired intonpm test/npm run test:unit) asserts the generated snippet set: the lookup-table contents, therecvlookups plus root handling and the redirect sentinel, theerrorsnippet's 301 conversion, and that a non-literal redirect pattern is rejected rather than silently mishandled.configure-fastlycompile-checks the cloned version before activating.developruns the job against the staging service; the legacy per-route objects (and any lingeringBucket nameheader) should be cleared from the staging service first, so the run tests the snippet path alone.