Skip to content

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

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

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

Conversation

@codepress-dev

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

Copy link
Copy Markdown

Summary

This sets up the files CodePress needs to run live, shareable previews of the React app in this repo. Previously the preview system had to guess how to start your frontend; now the repo itself declares it, so previews boot the same way every time and hot reload works while you edit.

The repo has one runnable frontend (web/, a create-react-app project) and a Django backend (server/). Only the frontend needs preview artifacts. Because the app is 2017-era (create-react-app 1.0.10), the interesting question was whether it still runs at all on a modern base image — so rather than assume, I installed its dependencies and actually booted the dev server to confirm it compiles, serves the page, and hot-reloads. It does.

Two things were deliberately left alone. The Django app serves the built React bundle from its own catch-all URL, so it is same-origin and has no cross-origin browser callers and no CORS configuration at all — there is nothing to open up for previews, and adding CORS would be inventing a problem. 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) with a single frontends[] entry for web/, plus .codepress/dev-server/Dockerfile.web. No application code is touched.

Recipe entry (web): working_dir: "web", framework: "cra", package_manager: "yarn", dev_port: 3000, install_command: "yarn install --frozen-lockfile", dependency_manifests: ["web/yarn.lock"] (the per-app lockfile — there is no root lockfile), system_packages: ["procps"], size: "medium". No prebuild_command: the postbuild script in web/package.json copies a production build into Django's static dir and is not a dev-server prerequisite.

Why the base image is node:18-bookworm and not current LTS. This is the load-bearing decision. sockjs@0.3.18faye-websocketwebsocket-driver@0.6.5 calls process.binding('http_parser'). I verified empirically that Node 18.20.8 still provides that binding (deprecated) while Node 25 does not — on Node 25 the dev server dies at boot with Error: No such module: http_parser. Node 18 is also the oldest Node with an official -bookworm variant, so it is simultaneously the most conservative choice available and the newest one that works. Pulled from the public.ecr.aws mirror; full -bookworm, not -slim, since deps install at runtime and may need node-gyp.

No --openssl-legacy-provider. Worth flagging because it is the usual reflex for a toolchain this old. It is not needed here: webpack@2.6.1 defaults output.hashFunction to md5 (WebpackOptionsDefaulter.js), not md4. md4 became the default in webpack 4, so a react-scripts upgrade to v2+ would need the flag on OpenSSL 3 — there is a comment in the Dockerfile saying exactly that, so a future upgrader is not left guessing.

Bind and HMR wiring. react-scripts@1.0.10 reads HOST and PORT from env (confirmed in its scripts/start.js), so the CMD maps HOST="${CODEPRESS_BIND_HOST:-0.0.0.0}" at runtime. This is deliberately not an ENV HOST=$CODEPRESS_BIND_HOST alias, which Docker would resolve at build time and freeze, discarding the runtime override.

There is no HMR client-port knob to set on this version, and that turns out to be fine: react-dev-utils@3.0.2's webpackHotDevClient builds its SockJS URL from window.location (protocol + hostname + port + /sockjs-node), so behind the preview proxy it dials wss://<preview-host>/sockjs-node on 443 by itself. WDS_SOCKET_PORT is wired from CODEPRESS_HMR_CLIENT_PORT anyway — inert on this version (it landed in react-scripts 3.4+), but it keeps the runtime contract uniform and survives an upgrade.

DANGEROUSLY_DISABLE_HOST_CHECK=true is set explicitly. react-scripts' webpackDevServer.config.js computes disableHostCheck: !proxy || DANGEROUSLY_DISABLE_HOST_CHECK === 'true', so with no proxy field in web/package.json the check is already off today — pinning it means adding a proxy field later cannot silently start rejecting per-session preview hostnames. BROWSER=none stops openBrowser from trying to launch a browser in a headless container.

Preview CORS: intentionally no change. The gate requires a backend that serves cross-origin browser clients. server/ fails it on every count: django-cors-headers is absent from Pipfile and INSTALLED_APPS, there is no CORS middleware or setting anywhere, web/src makes zero network calls (no fetch/axios, no proxy), and server/server/urls.py ends in a catch-all rendering index.html from the copied React build — i.e. same-origin by construction. Independently, there is no staging config path to scope such an edit to (a single settings.py with DEBUG = True, no settings package, no .env.staging, no CI/deploy config), and adding preview origins to production CORS is exactly what that scoping rule exists to prevent.

Social auth: not applicable. web/ has no sign-in flow; its only "auth" is a local redux action toggling a boolean with no network call. samples/auth-web/ does contain a GitHub OAuth link, but it is unreferenced sample code with no package.json and no entry point, and is not part of any runnable frontend.

Changes

  • Add .codepress/dev-server/recipe.json — schema_version 1, one frontends[] entry for the web/ create-react-app frontend
  • Add .codepress/dev-server/Dockerfile.web — thin, dev-mode image on node:18-bookworm (no baked node_modules, no source COPY, no CodePress binary)
  • Pin Node 18 with an inline rationale: websocket-driver@0.6.5 needs process.binding('http_parser'), which newer Node removes
  • Map CRA's HOST from CODEPRESS_BIND_HOST and WDS_SOCKET_PORT from CODEPRESS_HMR_CLIENT_PORT at runtime in the CMD
  • Set BROWSER=none and DANGEROUSLY_DISABLE_HOST_CHECK=true so the dev server runs headless and accepts per-session preview hostnames
  • Omit staging_backend_url — no staging URL exists in the repo, and a guess would risk pointing previews at the wrong backend
  • No backend CORS edit and no auth edit — see Technical details for why each was correctly a no-op

Test plan

  • Build the image from the repo root: docker build -f .codepress/dev-server/Dockerfile.web -t drx-web . — it should succeed and stay thin (no node_modules layer).
  • Install deps and boot the dev server the way the recipe declares: from web/, run yarn install --frozen-lockfile, then HOST=0.0.0.0 PORT=3000 BROWSER=none npx react-scripts start. Expect Compiled successfully! and HTTP 200 from curl http://127.0.0.1:3000/ (the HTML should contain <div id="root"></div> and /static/js/bundle.js).
  • Confirm it binds all interfaces, not just loopback: curl http://<container-or-host-ip>:3000/ returns HTTP 200 (ss -lnt should show 0.0.0.0:3000).
  • Confirm a preview hostname is accepted rather than rejected by the host check: curl -H 'Host: abc123-de116f94b7ab06ad.preview.codepress.dev' http://127.0.0.1:3000/ returns HTTP 200 and the app HTML.
  • Confirm the HMR transport is up: curl http://127.0.0.1:3000/sockjs-node/info returns HTTP 200 with "websocket":true.
  • Confirm hot reload: with the server running, edit a string in web/src/containers/home/Home.js and save. The log should print a second Compiled successfully!, and curl http://127.0.0.1:3000/static/js/bundle.js | grep <your-string> should find the edit.
  • Sanity-check the deliberate no-ops: git grep -i cors -- server/ returns nothing, and git grep -nE 'fetch\(|axios' -- web/src returns nothing — confirming the Django app is same-origin with no CORS surface to change.

Open items

  • The image itself was never docker build-ed: Docker is unavailable in this bootstrap environment. Everything the image does was verified directly on a real node:18.20.8 toolchain (install, compile, bind, host check, /sockjs-node, hot reload), but the first CodePress dev-image build is the first time the Dockerfile is executed as written.
  • Node 18 is EOL upstream (security support ended April 2025). It is the correct pin today because websocket-driver@0.6.5 needs process.binding('http_parser'), but the durable fix is to bump websocket-driver to >= 0.7 (which uses a pure-JS parser). The declared range is already open-ended (websocket-driver@>=0.5.1), so a lockfile refresh would satisfy it — after which a newer Node base becomes possible. Out of scope here since it changes application dependency resolution.
  • yarn install --frozen-lockfile was verified against the current web/yarn.lock and succeeded (897 packages). It depends on 2017-era tarballs remaining available on the registry, so an unpublished transitive package would surface as an install failure rather than a config problem.
  • If a staging deployment for this repo exists somewhere outside the codebase, its URL can be added as staging_backend_url in the recipe; nothing in the repo pointed to one.

Authors

Generated with CodePress · View the chat session

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

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

Discovered one runnable frontend: web/ (create-react-app 1.0.10, Yarn 1).
The Django app in server/ is same-origin only -- it serves the built React
bundle through its catch-all URL and has no CORS surface and no cross-origin
caller -- so no preview-CORS change was made. No staging backend URL was
found, so staging_backend_url is intentionally omitted.

Verified end to end on the pinned node:18-bookworm base:
  - yarn install --frozen-lockfile succeeds against the 2017 lockfile
  - react-scripts start compiles and serves HTTP 200
  - binds 0.0.0.0:3000 and answers on a non-loopback address
  - accepts a *.preview.codepress.dev Host header (host check off)
  - /sockjs-node/info reports websocket support
  - a source edit triggers a second successful compile (hot reload works)

Node 18 is pinned deliberately: this toolchain's websocket-driver 0.6.5 calls
process.binding('http_parser'), which Node 18 still provides but newer Node
removes -- the dev server crashes on boot there.

Co-authored-by: dev@codepress.dev <dev@codepress.dev>
@codepress-dev codepress-dev Bot added the cp:in-progress CodePress: still being worked on label Jul 28, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

cp:in-progress CodePress: still being worked on

Projects

None yet

Development

Successfully merging this pull request may close these issues.

0 participants