Skip to content

Deploy 2026-08-20 - #377

Merged
cwillisf merged 26 commits into
masterfrom
develop
Aug 20, 2026
Merged

Deploy 2026-08-20#377
cwillisf merged 26 commits into
masterfrom
develop

Conversation

@cwillisf

Copy link
Copy Markdown
Contributor

Proposed Changes

All of the updates since July 2026:

  • update and modernize libraries and build
  • updates from DevTech for research page and curriculum and professional development

cwillisf and others added 26 commits July 14, 2026 09:55
node-sass 7.0.1 ships native bindings that don't run on current Node
(the build failed with "Node Sass does not yet support your current
environment" on Node 24), leaving the Sass build stuck on Node 17.

Swap it for Dart Sass (sass) and bump sass-loader 6 -> 7 (the last line
that supports webpack 3 while accepting an `implementation` option), wired
via `implementation: sass` in the webpack config. Also modernize the one
deprecated `darken()` call to `color.adjust()`, which produces identical
output (#7f7f7f -> #666).

Incidental cleanups on the touched files: fix a dead-selector typo
(#disscussion-tweets) and drop a spurious executable bit on
webpack.config.js, both surfaced by pre-commit hooks.

npm regenerated package-lock.json into the current lockfile format as a
side effect of touching dependencies on modern npm.
bin/configure-fastly.js was the only consumer of `async` (1.5.2). Rewrite
its async.auto / async.forEachOf flow with native async/await and
util.promisify over the callback-style fastly-extended helpers, preserving
the same dependency ordering and concurrency. Remove the `async` dependency.
Node 17 was never an LTS line and is long past end-of-life; the upcoming
build-tooling upgrade needs Node 18+. Point .nvmrc and the GitHub Actions
workflow at Node 22 (current LTS).

Also remove .travis.yml: CI moved to GitHub Actions, and the file still
referenced Travis, the old LLK org, and Node 17, so it was dead and
misleading.
Move the build off the end-of-life webpack 3 / Babel 6 toolchain, which
was pinned in place because every loader and the Babel packages had to
move together.

- webpack 3 -> 5 (+ webpack-cli, which webpack 5 needs as a separate dep)
- Babel 6 -> 7: @babel/core, @babel/preset-env, @babel/preset-react,
  babel-loader 10; object-rest-spread now comes from preset-env, so its
  standalone plugin is gone. babel-eslint 8 -> 10 keeps ESLint 4 working
  against the Babel 7 parser.
- Loaders bumped for webpack 5: css-loader, style-loader, sass-loader
  (modern Sass API, which clears the legacy-js-api deprecation noise),
  postcss-loader (plugins move under postcssOptions), html-loader,
  html-webpack-plugin, copy-webpack-plugin, webpack-dev-middleware.
- Drop file-loader and url-loader in favor of built-in asset modules
  (type: 'asset'), which inline assets under 8 KiB and emit larger ones
  as cacheable files. Drop json-loader (webpack 5 handles JSON natively)
  and the unused webpack-dev-server.
- autoprefixer's inline `browsers` option was removed upstream; targets
  now live in .browserslistrc, shared with @babel/preset-env.
- css/scss url() and html <img> sources: resolve fragment-relative
  assets, but leave root-absolute /images/... (served from static) alone.
- html-webpack-plugin 5 removed htmlWebpackPlugin.files.chunks; the
  template now reads files.js[0], with chunks scoped per route.
- Build in production mode with a root-absolute publicPath so bundles and
  emitted assets resolve from any route.

Output verified: 13 pages build, per-route script tags resolve, small
images inline while larger ones emit to /assets, and root-absolute static
references stay literal.

Incidental: drop a stray executable bit on dev-server/index.js, surfaced
by a pre-commit hook.
The build ran in a single hardcoded production mode. Drive webpack's mode
off NODE_ENV instead (defaulting to production, so any build that doesn't
opt down stays deploy-safe), which also flips React's dev/prod build to
match since webpack derives the bundle's process.env.NODE_ENV from mode.

- dev server: defaults NODE_ENV to development for readable, source-mapped
  local builds.
- CI: a small setup job resolves the deploy environment once and derives
  NODE_ENV from it, so the two travel together. Staging (develop) ships a
  development build for easier debugging; production (master) ships a
  production build. Pull-request builds run in development; production mode
  is still exercised by the master build before the production deploy.
Modernize the build toolchain: Dart Sass, webpack 5/Babel 7, Node 22, drop async
Replace the unmaintained fastly@2.2.0 (thisandagain) client with the
official fastly@15 (fastly-js), which is promise-native and covers the
full API. fastly-extended.js now wraps the per-resource API classes and
keeps promise-returning, upsert-by-name helper signatures, so
configure-fastly.js keeps the same behavior: the same conditions,
headers, and response objects are created.

Also modernized to const/let, arrow functions, and template literals.

One behavior change: authenticate() fails fast at startup when the API
key is unset, rather than deferring the error to the first API call.
…y-js-client

build: migrate Fastly config script to the official fastly-js client
The Makefile was a task runner duplicating what npm scripts already do,
with package.json scripts just delegating back to it (npm -> make ->
command). Convert the targets to scripts directly and drop the Makefile.

- build/clean/lint/start/test become package.json scripts
- add a sync-fastly script (was the make sync-fastly target)
- drop the stop/watch/dev scripts: they referenced make targets that no
  longer existed, and the dev server already watches via
  webpack-dev-middleware
- point CI and the README at npm run lint / npm run build
sass-lint ./src/**/*.scss already matches the whole tree, so the separate
./src/*.scss invocation was redundant (and matches nothing, since there
are no top-level src/*.scss files).
…ile-with-npm-scripts

build: replace Makefile with package.json scripts
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.
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".
Prepend a provenance header naming bin/configure-fastly.js to each generated
snippet, so it is clear in the Fastly UI that they are generated and how to
regenerate them.

Use the default snippet priority (100) for app-routes-recv instead of 10. It is
the only generated recv snippet, so it needs no special ordering, and keeping it
at the default leaves lower numbers free for manually-managed snippets that must
sort ahead of it.
…-generated-snippets

Configure Fastly during deploy via generated route snippets
@cwillisf
cwillisf requested a review from devtechrg August 20, 2026 15:54

@devtechrg devtechrg left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks good

@cwillisf
cwillisf merged commit fdbb4be into master Aug 20, 2026
10 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants