Skip to content

Bootstrap CodePress Live Dev Server recipe + dev Dockerfile - #41

Open
codepress-dev[bot] wants to merge 1 commit into
masterfrom
codepress/dev@codepress.dev/bootstrap-live-dev-server-artifacts-2e04b9fe
Open

Bootstrap CodePress Live Dev Server recipe + dev Dockerfile#41
codepress-dev[bot] wants to merge 1 commit into
masterfrom
codepress/dev@codepress.dev/bootstrap-live-dev-server-artifacts-2e04b9fe

Conversation

@codepress-dev

@codepress-dev codepress-dev Bot commented Jul 26, 2026

Copy link
Copy Markdown

Summary

This sets up live previews for the React app in web/. Until now, CodePress had to guess how to boot your frontend each time; this PR adds two small committed files that spell it out explicitly, so a preview starts the same way every time and hot reload works in the browser.

The repo's React app is a 2017-era create-react-app (react-scripts 1.0.10), which needed a couple of specific accommodations to run behind a hosted preview URL — those are baked into the files here. Nothing about how you run the app locally changes: yarn start in web/ behaves exactly as before.

Two things were checked and deliberately left alone. The Django API in server/ is served on the same origin as the React build (Django's catch-all route renders the built index.html), it has no CORS allowlist, and the frontend makes no cross-origin API calls — so there was no CORS change to make. And no staging backend URL exists anywhere in the repo, so none was recorded rather than guessing one.

Technical details

Adds .codepress/dev-server/recipe.json (schema_version 1) and .codepress/dev-server/Dockerfile.web. The recipe is the backend's source of truth for building one content-addressed dev image per frontend.

Discovery. One runnable frontend: web/ — CRA, start: react-scripts start, yarn (web/yarn.lock), default port 3000. samples/auth-web/ is not a frontend: no package.json, and its files import modules (../../redux/auth, ../../../config/settings) and npm packages (query-string, react-spinkit, secure-random) that don't exist in web/ — it's unreachable tutorial reference code. server/ is a Django backend (detected, not made into a frontend entry).

Recipe fields. working_dir: "web", framework: "cra", package_manager: "yarn", install_command: "yarn install --frozen-lockfile", dependency_manifests: ["web/yarn.lock"] (per-app lockfile, not a root one), dev_port: 3000, system_packages: ["procps"], size: "medium". No prebuild_command — the postbuild script only runs after a production build, so nothing needs pre-building for dev.

system_packages is just the procps baseline: I grepped web/yarn.lock for source-built native modules (node-sass, node-gyp, bcrypt, canvas, sharp, natives, fibers, sqlite3) and found none. fsevents@1.1.2 is present but is os: ["darwin"], so yarn skips it on linux.

The non-obvious part — why DANGEROUSLY_DISABLE_HOST_CHECK=true. I verified against the actual pinned versions rather than react-scripts' declared ranges: web/yarn.lock resolves react-scripts 1.0.10 → webpack 2.6.1, webpack-dev-server 2.5.0, react-dev-utils 3.0.2. webpack-dev-server 2.5.0's Server.js rejects any request whose Host header it doesn't recognize with res.send("Invalid Host header") (and applies the same check to the sockjs connection), which would break every request through the preview proxy's unique per-session hostname. react-scripts 1.0.10 sets disableHostCheck: !proxy || process.env.DANGEROUSLY_DISABLE_HOST_CHECK === 'true' — so today it is off only because web/package.json happens to have no proxy field. Setting the env var explicitly makes previews robust if someone adds proxy later.

HMR needs no client-port wiring. react-dev-utils 3.0.2's webpackHotDevClient.js builds its SockJS URL from window.location (protocol + hostname + port) plus /sockjs-node, so the websocket rides the page origin and works behind the proxy unchanged. WDS_SOCKET_PORT is still mapped from CODEPRESS_HMR_CLIENT_PORT in the CMD for the uniform runtime contract and for a future react-scripts upgrade that honors it.

Base image: node:18-bookworm (full, from the public-ECR mirror — never -slim, which strips the node-gyp toolchain the runtime install may need). 18 is the oldest bookworm Node tag and the closest fit for this toolchain. Notably no --openssl-legacy-provider is needed: the ERR_OSSL_EVP_UNSUPPORTED md4 problem starts at webpack 4, and webpack 2 hashes with md5, which OpenSSL 3 still provides. yarn 1.22.22 is pinned via corepack prepare --activate with a build-time yarn --version assertion, so a cold start never downloads a package manager.

The image is thin per contract: no baked node_modules, no source COPY, no CodePress binary. It binds via CODEPRESS_BIND_HOST (default 0.0.0.0) and declares PORT / CODEPRESS_HMR_CLIENT_PORT / CODEPRESS_HMR_PROTOCOL; deps arrive at runtime on the checkout bind-mounted at /app. BROWSER=none stops CRA trying to open a browser in the container; CI=false keeps dev warnings from being escalated.

Not done, with reasons. No staging_backend_url: there is no .env*, no .github/workflows, no vercel.json, and no .codepress/verify-staging/recipe.json — nothing to detect, and a guess is worse than nothing. No preview-CORS edit: the Django backend has no corsheaders, no CORS middleware, no CORS_* settings, and no cross-origin caller (zero fetch/axios/absolute API base URL in web/src) — it is a same-origin architecture, so there is no CORS surface to extend. It also has only a single settings.py with no staging module, so even if a surface existed this would have been report-only rather than an auto-edit. No preview social-auth work: the runnable frontend has no social sign-in, and the backend installs allauth.account only (no allauth.socialaccount, no providers).

Changes

  • Add .codepress/dev-server/recipe.json — schema_version 1 with a single web frontend entry (CRA / yarn / port 3000), fingerprinted on web/yarn.lock
  • Add .codepress/dev-server/Dockerfile.web — thin dev-mode image on full node:18-bookworm, binds 0.0.0.0 via CODEPRESS_BIND_HOST, pins yarn 1.22.22, installs procps
  • Set DANGEROUSLY_DISABLE_HOST_CHECK=true in the dev image so webpack-dev-server 2.5.0 accepts the preview proxy's per-session hostname instead of returning Invalid Host header
  • Set BROWSER=none and CI=false so react-scripts start behaves correctly headless in a container
  • No changes to application code, web/package.json, or the Django backend

Test plan

  • Nothing here affects local development — confirm cd web && yarn start still behaves exactly as before.
  • Confirm the recipe is valid and self-consistent: python3 -m json.tool .codepress/dev-server/recipe.json, then check the single frontend's dockerfile_path exists, working_dir is web, and dev_port is 3000.
  • Build the dev image from the repo root (build context is the root, Dockerfile passed with -f): docker build -f .codepress/dev-server/Dockerfile.web -t drx-web-dev . — it should finish in well under a minute since no dependencies are baked, and the yarn --version step should print 1.22.22.
  • Install deps on the checkout the way the runtime does: cd web && yarn install --frozen-lockfile (the exact command in the recipe). It should not report that the lockfile needs updating.
  • Run the image against that checkout from the repo root: docker run --rm -p 3000:3000 -v "$PWD":/app drx-web-dev. Expect react-scripts start to compile and print the "Compiled successfully!" banner, binding all interfaces rather than loopback.
  • Open http://localhost:3000 — the React logo page renders.
  • Confirm hot reload: with the container still running, edit the heading text in web/src/containers/home/Home.js and save. The browser should update without a manual refresh, with no sockjs-node connection errors in the browser console.
  • Confirm the host check no longer blocks a foreign hostname (the preview-proxy case): curl -s -H 'Host: abc123.preview.codepress.dev' http://localhost:3000/ | head -5. It should return the app's HTML, NOT the string Invalid Host header.
  • Open a CodePress preview for this branch and confirm the page loads at the preview URL and that editing a file live-updates the preview.

Open items

  • Not runtime-verified: no Docker daemon was available in this bootstrap environment, so the image was never actually built or booted. Version-specific behavior was verified by reading the exact pinned sources (webpack-dev-server 2.5.0's checkHost, react-scripts 1.0.10's webpackDevServer.config.js, react-dev-utils 3.0.2's webpackHotDevClient.js) rather than by running them. Step 5 of the test plan is the real check.
  • Node 18 against a 2017 toolchain is the main risk. react-scripts 1.0.10 targeted Node 6/8. The usual modern-Node blockers were checked and do not apply here (webpack 2 uses md5, not md4, so no OpenSSL 3 failure; no native modules to compile), but this combination is untested in practice. If the install or dev server fails on Node 18, the fix is a one-line base-image change in Dockerfile.web.
  • The preview proxy must forward /sockjs-node (websocket upgrade and xhr-streaming) for hot reload to connect. Standard for the platform, but worth watching at step 7 of the test plan if HMR is silent.
  • The frontend and Django API are not wired together in dev: web/src makes no API calls and there is no proxy field in web/package.json, so a preview serves the React app alone and the server/ API is not reachable from it. That is the repo's current state, not something this PR changed. If cross-origin API calls are added later, preview CORS on the Django side becomes relevant and this bootstrap can be re-run to handle it.

Authors

Generated with CodePress · View the chat session

Co-authored-by: dev@codepress.dev dev@codepress.dev

Adds the repo-owned Live Dev Server artifacts so previews boot from committed
files instead of a runtime heuristic.

- .codepress/dev-server/recipe.json: one frontend entry for `web/` (CRA,
  yarn, port 3000, `yarn install --frozen-lockfile`, fingerprinted on
  web/yarn.lock).
- .codepress/dev-server/Dockerfile.web: thin dev-mode image on the full
  node:18-bookworm base (no baked node_modules, no source COPY, no CodePress
  binary). Binds 0.0.0.0 via CODEPRESS_BIND_HOST and sets
  DANGEROUSLY_DISABLE_HOST_CHECK so webpack-dev-server 2.5.0 accepts the
  preview proxy's per-session hostname instead of returning
  "Invalid Host header".

No staging backend URL was detected, so none is persisted. The Django API in
server/ is served same-origin (its catch-all URL renders the React build), has
no CORS allowlist and no staging settings module, so no preview-CORS change was
needed. The runnable frontend has no social sign-in.

Co-authored-by: dev@codepress.dev <dev@codepress.dev>
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.

0 participants