Migrate the asset pipeline from Sprockets to Propshaft - #535
Merged
cycomachead merged 1 commit intoAug 6, 2026
Merged
Conversation
superconductor-for-github
Bot
force-pushed
the
cycomachead/211-upgrade-css-compilation/1
branch
from
August 5, 2026 22:23
e1889f9 to
9388a3a
Compare
Follow-on to the dartsass-sprockets migration. Same Dart Sass compiler,
different plumbing: Propshaft digests and serves what is on disk and does no
transformation, so Dart Sass writes app/assets/builds/application.css and
Propshaft takes it from there.
sprockets-rails -> propshaft
dartsass-sprockets -> dartsass-rails
What goes away: app/assets/config/manifest.js, the config.assets.precompile
list, and five Sprockets-only environment settings (assets.debug, .digest,
.quiet, .compile, .css_compressor).
Four things needed handling:
1. Font Awesome webfonts. font-awesome-sass emits font-path() only when
Sprockets' Sass helpers are loaded, and otherwise falls back to
"#{$fa-font-path}/#{$file}" -- i.e. ../webfonts/..., which 404s. Set
$fa-font-path to the gem's logical asset path and let Propshaft rewrite
the url() to a digested one.
2. Gem Sass sources leaking into public/assets. Propshaft has a single load
path and serves everything on it, and the bootstrap and font-awesome-sass
gems put their Sass sources there so application.scss can @import them --
110 .scss partials were being digested into public/assets. They cannot
simply be excluded, because dartsass-rails derives its --load-path list
from config.assets.paths and Propshaft's excluded_paths removes entries
from that same array. Pass the two directories to Dart Sass explicitly and
then take them off Propshaft's path.
3. The CSS is a build artifact now. A checkout without it raises
Propshaft::MissingAssetError rather than rendering unstyled, so bin/setup
builds it and the three CI workflows that run rspec/cucumber directly get
a Build CSS step. Procfile.dev gains a css process, so `make dev` keeps it
fresh.
4. Dev output. dartsass-rails defaults to --style=compressed --no-source-map
in every environment, where sassc-rails only compressed outside
development. Build expanded with source maps in development.
Verified: rspec 584/0; the a11y suite 40/0 with all 40 screenshots
pixel-compared against the Sprockets build (20 byte-identical, the rest
differing only where generated timestamps change column widths); cucumber
unchanged at the same 5 pre-existing failures. Production and development
boot with CSS, webfonts and all 27 local importmap assets returning 200. The
compiled CSS is token-for-token identical to the Sprockets output apart from
minifier cosmetics (nested calc(), leading zeros).
Co-Authored-By: Claude <noreply@anthropic.com>
superconductor-for-github
Bot
force-pushed
the
cycomachead/211-propshaft-spike
branch
from
August 5, 2026 22:28
108729a to
85b5404
Compare
cycomachead
marked this pull request as ready for review
August 6, 2026 02:16
cycomachead
merged commit Aug 6, 2026
9c923ec
into
cycomachead/211-upgrade-css-compilation/1
14 checks passed
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.
Follow-on to the Dart Sass migration in #527. Same compiler, different plumbing: Propshaft digests and serves what is on disk and does no transformation of its own, so Dart Sass writes
app/assets/builds/application.cssand Propshaft takes it from there.Propshaft has been the default asset pipeline for new Rails apps since Rails 8.
What goes away
app/assets/config/manifest.jsconfig.assets.precompilelist inconfig/initializers/assets.rbassets.debug,assets.digest,assets.quiet,assets.compile,assets.css_compressorFour things that needed handling
1. Font Awesome webfonts 404'd.
font-awesome-sassemitsfont-path()only when Sprockets' Sass helpers are loaded, and otherwise falls back to#{$fa-font-path}/#{$file}— i.e.../webfonts/…, which resolves to nothing. Fixed by setting$fa-font-pathto the gem's logical asset path and letting Propshaft rewrite theurl()to a digested one.2. Gem Sass sources leaked into
public/assets. Propshaft has a single load path and serves everything on it, and thebootstrapandfont-awesome-sassgems put their Sass sources there soapplication.scsscan@importthem — so the first precompile digested 110.scsspartials intopublic/assets(11 MB → 9.2 MB once fixed).They can't simply be excluded:
dartsass-railsderives its--load-pathlist fromconfig.assets.paths, and Propshaft'sexcluded_pathsremoves entries from that same array, so excluding them breaks the build.config/initializers/dartsass.rbtherefore passes the two directories to Dart Sass explicitly as--load-pathand then takes them off Propshaft's path. (font-awesome-sass/assets/fontshas to stay — that's where the webfonts live.)3. The CSS is a build artifact now. A checkout without
app/assets/builds/application.cssraisesPropshaft::MissingAssetErrorrather than rendering unstyled, which is the right failure mode but means:bin/setupbuilds it, so a fresh clone doesn't 500 on first page loadProcfile.devgains acssprocess (rails dartsass:watch), somake devkeeps it freshrspec/cucumberdirectly get a Build CSS step4. Dev output regressed.
dartsass-railsdefaults to--style=compressed --no-source-mapin every environment, wheresassc-railsonly compressed outside development. Development now builds expanded, with source maps pointing back at the.scsssources.Verification
rspec— 584 examples, 0 failuresrspec --tag a11y— 40 examples, 0 failures, and all 40 screenshots pixel-compared against the Sprockets build: 20 byte-identical, the rest differing only where generated timestamps change column widths. No styling changes.cucumber— unchanged at the same 5 pre-existing failures (enrollments.feature,requests.feature; these fail onmaintoo).scssinpublic/assetscalc()and leading zeros. 351,895 vs 358,871 bytes..platform/.ebextensionsforgzip_static: nothing there, so losing Sprockets'.gzfiles is a non-issue.Relationship to #527
#527 has been rebased onto
mainand is nowmergeable: clean. Resolving it kept the SCSS approach, deletedpublic/datatables/and dropped the two<link>tags — and went further, per review: the vendored DataTables copies are gone entirely, replaced by@imports out ofnode_modulespinned to the same versions as the JavaScript inconfig/importmap.rb.That means
node_modulesis a prerequisite for compiling CSS, and #527 already adds the Node setup to the Docker build stage, the three test workflows andbin/setup. This PR carries the dartsass-rails equivalent of that load-path config.Merge #527 first; this one is stacked on it.
Please check
Blazer (
/admin/blazer) and Faultline (/admin/errors). Both engines boot and their assets precompile, and Blazer 3.4 ships a Propshaft-aware layout branch that lists each file individually instead of relying on//= require— but I couldn't authenticate to click through either UI.