Serve Rspack-built assets in production behind a feature flag

What

Adds a feature flag (rspack_production_assets, default off) that lets Rails serve frontend assets built by Rspack instead of Webpack in production, so the Rspack cutover can be rolled out gradually and reverted instantly.

  • Rspack now emits manifest.rspack.json next to Webpack's manifest.json in the same /assets/webpack/ folder. Production assets are content-hashed, so both bundlers' files coexist without collisions, and nginx already serves /assets/webpack/**.
  • Gitlab::Webpack::Manifest accepts a manifest_filename: (memoized per filename, the default is unchanged), and WebpackHelper selects the Rspack manifest per request (actor: current user) when the flag is on. Public path is unchanged.

Design notes

The flag is evaluated at render time in the helper, not baked into the class-level manifest cache (per the feature-flag guidance against load-time flags). Keeping both manifests in one folder means no nginx change.

CI & packaging

To actually serve Rspack assets in production, the deployed assets image needs to contain both bundlers' output, not just Webpack's.

  • A new build-assets-image job merges the Webpack and Rspack compile jobs into a single production assets image, containing both manifests and all content-hashed chunks (they overlay cleanly with no collisions). This applies to both EE (gitlab-assets-ee) and CE (gitlab-assets-ce, via as-if-foss). compile-production-assets keeps compiling Webpack and still emits the image tag/hash artifacts that Omnibus, CNG, and release-environments rely on — it just no longer pushes the image itself; build-assets-image pushes the combined image under the same tag, so downstream consumers are unaffected.
  • The Webpack and Rspack builds now use separate CI caches (a dedicated ENABLE_RSPACK assets-hash job plus a GLCI_ASSETS_BUNDLER cache token), so the two builds can no longer clobber or skip each other via a shared cache key.
  • Trade-off: while the flag is active, the production assets payload is roughly double its usual size, since both bundlers' output ships together. This is temporary and goes away once Webpack is fully removed.

Feature flag

  • rspack_production_assets, type gitlab_com_derisk, disabled by default.
  • Disabled by default in the test env: the test asset pipeline builds Webpack only, so manifest.rspack.json is not present there.

Verify

  1. Build both bundlers into public/assets/webpack/ (Webpack manifest.json, Rspack manifest.rspack.json).
  2. With the flag off, pages load Webpack assets (unchanged). Enable rspack_production_assets, reload, and pages load the Rspack assets with no console errors.

Rollout

This is the production leg of the Rspack cutover:

  • Builds on !243058 (merged), which introduced the ENABLE_RSPACK mechanism and manifest.rspack.json selection this flag reuses. This MR adds the CI/packaging work needed to ship both bundlers in the same production assets image.
  • The dev cutover is gitlab-development-kit!6068 (merged) (Rspack as the default GDK bundler with zero gdk.yml changes, verified end to end). This MR gates production serving separately behind rspack_production_assets so dev and production roll out independently.
  • If prod validation runs beyond the gitlab_com_derisk 2-month window, convert the flag to ops (with a runbook and a feature-flags-list entry).

Epic: &22569 · Proposal: #604382 (closed)

Edited by Stanislav Lashmanov

Merge request reports

Loading
Loading