feat(collab): pre-warm pnpm store with frontend + api deps at build time#43
Merged
Conversation
Speed win V1 — the single biggest user-visible latency in the collab
flow. A cold `pnpm install` inside a fresh frontend workspace takes
~10 min (3,500-package tree, network-bound). pnpm's store is
content-addressed and shared across installs, so populating it at BUILD
time turns every runtime session install into a near-offline store-link
("Done in ~30 s") instead of a 10-min download.
Dockerfile: new "warm-store" RUN after the config bake. Uses
`pnpm fetch` (reads ONLY the lockfile, downloads to the store, links no
node_modules — exactly a store warm) for unleashlive/frontend +
unleashlive/api. Runs with HOME=/home/opencode so pnpm's default store
path matches what the runtime preview install (uid 10001) reads; the
existing final-stage `chown -R 10001:10001 /home/opencode` hands the
warmed store to the runtime user.
Auth: cloning these PRIVATE repos at build time needs a cross-repo read
token, passed as a BuildKit secret (id=github_token) — mounted only for
that RUN, never baked into a layer. deploy-collab.yml wires it from a
new repo secret WARM_STORE_GITHUB_TOKEN.
SAFE TO MERGE BEFORE THE SECRET EXISTS: when the secret is unset,
build-push-action passes an empty value, the Dockerfile's `[ ! -s ]`
guard skips the warm, and the image builds normally — runtime installs
just stay cold until a warmed image ships. Clone/fetch failures are
caught per-repo (no pipe-masked exit codes) and never abort the build.
Tradeoff: cold image build grows ~5-10 min (the warm can't use a build
cache mount — the store must be baked into the image, not a throwaway
cache). Worth it: one operator's build-time pain per deploy vs. every
participant's 10-min wait on every fresh session.
Staleness is fine — a lockfile that drifts post-build means pnpm
fetches only the delta at runtime; the unchanged thousands are store
hits.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
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.
Speed win V1 (PR 4 of 5) — the single biggest user-visible latency in the collab flow.
The problem
A cold
pnpm installinside a fresh frontend workspace takes ~10 min (3,500-package tree, network-bound). Every participant pays it on every fresh session.The fix
pnpm's store is content-addressed and shared across installs, so populating it at build time turns every runtime session install into a near-offline store-link (~30 s) instead of a 10-min download.
New
warm-storeRUN in the Dockerfile usespnpm fetch(reads only the lockfile, downloads to the store, links nonode_modules— exactly a store warm) forunleashlive/frontend+unleashlive/api. Runs withHOME=/home/opencodeso pnpm's default store path matches what the runtime preview install (uid 10001) reads; the existing final-stagechown -R 10001:10001 /home/opencodehands the warmed store to the runtime user.Auth — one operator action required to activate
Cloning these private repos at build time needs a cross-repo read token, passed as a BuildKit secret (
id=github_token), mounted only for that RUN — never baked into a layer.To turn it on: provision a fine-grained PAT (or GitHub App installation token) with Contents: read on
unleashlive/frontend+unleashlive/api, store it as the repo secretWARM_STORE_GITHUB_TOKEN.deploy-collab.ymlalready wires it through.Safe to merge before the secret exists
When the secret is unset,
build-push-actionpasses an empty value, the Dockerfile's[ ! -s ]guard skips the warm, and the image builds normally — runtime installs just stay cold until a warmed image ships. Clone/fetch failures are caught per-repo (no pipe-masked exit codes) and never abort the build.Tradeoff
Cold image build grows ~5-10 min (the warm can't use a build cache mount — the store must be baked into the image, not a throwaway cache). Worth it: one operator's build-time pain per deploy vs. every participant's 10-min wait on every fresh session.
Staleness is fine — a lockfile that drifts post-build means pnpm fetches only the delta at runtime; the unchanged thousands are store hits.
Merge-order note
Touches
Dockerfilein a different region than PR #38 (Headroom MCP edits apt-get + the config printf; this adds a RUN after the config bake). Should auto-merge; if not, trivial rebase.Test plan
WARM_STORE_GITHUB_TOKEN, deploy[warm-store] pnpm fetch for frontend…+…for api…+pre-warm completeDone in …)no github_token secret — SKIPPING, image builds fineWARNING clone failed, build still succeeds🤖 Generated with Claude Code