Repository navigation
feat(deploy): ship every site through the deployments API, and publish it - #643
Conversation
base44 dev now serves the whole app on one origin. /api/apps stays with the local backend; everything else — the app's own /api routes included — is forwarded to its dev server carrying Base44-App-Id, Base44-Api-Url and a service-role credential, with the caller's copies of the platform-owned headers dropped first. Full-stack apps can build a client with createClientFromRequest locally, the way they do in production, instead of 500ing on every SSR loader and server function. The dev server's port is read off the URL it announces at startup (the front door's own port skipped, since the Vite plugin logs it as its proxy target), and a request that comes back marked as forwarded answers 508 naming the Vite proxy rather than looping. Websocket upgrades follow the same split, so HMR reaches the app and the entity-events socket stays local. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Tmb4PCrrV2SLRpBjkGAgXJ
…h it `BASE44_DEPLOYMENTS_API` is gone. The lane it gated is now the only transport `site deploy` has, and `--git-hash` / `--concurrency` are always registered rather than appearing only when an env var was set. Setting the variable does nothing; the sandbox that sets it needs no change. `base44 deploy` ships its site through the same lane instead of the archive upload, so a full-stack project's worker is no longer silently dropped, and a build carries the commit it was made at. Because that lane only builds, the deploy then publishes what it deployed (`--no-publish` opts out) — the archive upload it replaces always left the app live, and a deploy that quietly stopped doing so would be a worse default than a flag. `site deploy` deliberately does NOT publish, and gains `--publish` instead: the platform's own build step runs it against builder-managed apps and repoints production itself, so publishing there would fail exactly those apps. When the server does refuse a publish for that reason (`app_publishes_from_builder`), the deploy reports it and still succeeds — its resources and its build did land. The archive upload keeps the two callers that have no commit to be addressed by: `base44 create`'s scaffold deploy, and a `base44 deploy` in a project with no git history, which warns and falls back. A worker build has no archive form, so there a missing commit is fatal. Depends on base44-dev/apper#26921 for the publish endpoint. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Tmb4PCrrV2SLRpBjkGAgXJ
…on-55ncqj # Conflicts: # CHANGELOG.md # docs/deployments.md # packages/cli/src/cli/dev/serve-command-runner.ts
🚀 Package Preview Available!Install this PR's preview build with npm: npm i @base44-preview/cli@0.1.33-pr.643.706636fPrefer not to change any import paths? Install using npm alias so your code still imports npm i "base44@npm:@base44-preview/cli@0.1.33-pr.643.706636f"Or add it to your {
"dependencies": {
"base44": "npm:@base44-preview/cli@0.1.33-pr.643.706636f"
}
}
Preview published to npm registry — try new features instantly! |
…ards them too The compiled binary answered every HMR upgrade with an error: under Bun, bytes written to the socket a node:http `upgrade` listener receives never reach the wire, so neither http-proxy's tunnel nor a raw-socket pipe can complete a handshake from it. `ws` is the one path Bun maps onto its native websockets — it is how Socket.IO's realtime socket already worked there — so the front door now accepts the browser's socket with it and relays frames to a `ws` client connected to the dev server, carrying the same SDK headers the HTTP path does. The two paths now share one `sdkHeaders()` definition. The HMR test asserted only that the socket opened, which a relay satisfies on its own, so it now waits for the dev server's own frame and checks what that frame says the upgrade arrived with. The fixture also tolerates a reset socket, as a real dev server does: Bun's client resets rather than closing, and the unhandled error killed the fixture and with it the dev environment. Adds `ws` (8.21.0, already locked through miniflare) and `@types/ws` as devDependencies. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Tmb4PCrrV2SLRpBjkGAgXJ
Resolves site/deploy.ts against #649's defaulted site block: siteOutputDir() now always returns a path, so the "No site configuration found" guard this branch had re-added is unreachable and goes. The output directory is resolved before the commit, so a defaulted block is reported before anything can fail; #649's own test passes --git-hash so it still fails on the missing ./dist it was written for. bun.lock is main's plus this branch's ws / @types/ws. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Tmb4PCrrV2SLRpBjkGAgXJ
The dev-server front door and its tests now live in #659. This branch keeps only the deploy routing. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Tmb4PCrrV2SLRpBjkGAgXJ
|
Parking this as a draft.
Generated by Claude Code |
The publish moved server-side (base44-dev/apper#26921): instead of
`POST deployments/{git_hash}/publish`, the CLI calls the builder's own
`POST /deploy` with `{ git_commit_hash }`, then reads the app's URL from
`published-url` (the deploy response carries none). A builder-managed app
now refuses with `error.code` `git_commit_hash_not_supported`, which is
what "production left where it was" keys on.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Tmb4PCrrV2SLRpBjkGAgXJ
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Tmb4PCrrV2SLRpBjkGAgXJ
|
Back in review. The server side is settled: base44-dev/apper#26921 makes Merge order matters: release this only after #26921 is deployed. A server without it ignores the unknown Generated by Claude Code |
`POST /deploy` answers a logged-in user (a `base44 login` token) with the editor's full app, and only an API key gets the slim record carrying `git_commit_hash` and `deployed_at`. Requiring those fields failed `base44 deploy` after the publish had already succeeded. The CLI knows the commit it sent and reads the URL from `published-url`, so it no longer parses the body. The testkit's `/deploy` mock now answers with the full-app shape. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Tmb4PCrrV2SLRpBjkGAgXJ
`deploy`, `site deploy` and the versions commands each carried the same argParser; it now lives in `cli/utils/git-hash.ts`. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Tmb4PCrrV2SLRpBjkGAgXJ
It read as covering every resource deploy ships, but only the site waits on a publish. site deploy keeps --publish as its opt-in. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Tmb4PCrrV2SLRpBjkGAgXJ
344a4f5 to
d52a878
Compare
|
Generated by Claude Code |
…itless static build as before Without a commit, a static build goes through the archive upload and goes live, as site deploy did before; a worker build still needs one. When it leaves production alone, it points at --publish. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Tmb4PCrrV2SLRpBjkGAgXJ
No commit is inferred from the local checkout any more. Without --git-hash, deploy and site deploy keep the archive upload and go live as they always did; a full-stack build, which that upload cannot carry, fails asking for the flag, and deploy checks that before shipping any resource. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Tmb4PCrrV2SLRpBjkGAgXJ
Note
Description
base44 deploy --git-hash <hash>now ships a project's built site through the deployments API — the same lanebase44 site deployuses — addressed by that commit, and publishes it, so a full-stack build's Cloudflare worker ships instead of being silently dropped by the unified deploy. The lane also loses itsBASE44_DEPLOYMENTS_APIenv gate:--git-hash/--concurrencyare always registered onsite deploy, and the flag itself — never the checkout's HEAD, never an env var — is what selects the lane. Without--git-hashboth commands keep the legacy tar.gz archive upload, which goes live as before; a worker build, which that upload cannot carry, now fails asking for the flag before anything ships.Deploying and publishing stay separate acts.
base44 deploy --git-hashpublishes what it shipped, because the archive upload it replaces always left the app live.base44 site deploy --git-hashdoes not —--publishasks for it — because its main caller is the platform's build step running against builder-managed apps, which are published from the builder; the command now says so, pointing at--publish, when it leaves production alone.Related Issue
None
Type of Change
Changes Made
base44 deploy --git-hash <hash>ships the site through the deployments API —deployAll()routes the site step through a newdeployProjectSite()/shipCommit()pair that callsdeployToDeployments()and then publishes the result, so a full-stack build's worker is deployed instead of dropped.--concurrencydeliberately stays off this command: the caller uploading at scale issite deploy.resolveGitHash()is gone —git rev-parse HEADis never consulted for a deployment, so nothing is inferred from the local checkout. Versions keep reading HEAD as provenance (resolveProvenanceCommit(), which now does its own hex check).publishDeployment()incore/site/api.ts—POST /deploywith{ git_commit_hash }, then reads the app URL frompublished-url. The response body is deliberately not parsed: it differs between a logged-in user and an API key and carries nothing the CLI doesn't already know.error.codegit_commit_hash_not_supportedis not a failed deploy — the resources and the build did ship, so the CLI reports that production was left where it was and succeeds. Any other publish error still fails the command.workerBuildNeedsGitHashError()):deployAll()checks for a.wrangler/deploy/config.jsonartifact up front, andsite deploychecks before its first request. A static build without--git-hashkeepsdeploySite(), which publishes as it always did.site deployreports when it did not publish — "Not published: production still serves its previous build. Run again with --publish to serve this one." — and its--jsondocument gainspublished: booleanalongsidedeploymentIdandgitHash; the archive fallback now emits{ appUrl, published: true }.hasResourcesToDeploy()is now async and counts a detected full-stack artifact as a site, so a worker-only project with nosite.outputDirectoryis no longer reported as "No resources found to deploy".deploymentsApiEnabled()andBASE44_DEPLOYMENTS_APIare gone fromcore/site/deployment.ts, andsite deploy'sdeployTarball()branch becomes the no-commitdeployArchive()fallback.--git-hashparser:parseGitHash()extracted tocli/utils/git-hash.tsand reused bysite deploy,deploy, and the versions commands'gitHashOption(), replacing duplicate copies of the same 7–64-hex-char check.PublishedDeploymenttype (gitHash,appUrl) incore/site/schema.ts.mockDeploymentPublish()/mockDeploymentPublishError()with apublishedCommitscapture;mockSiteDeployError()renamed tomockDeploymentCreateError()and repointed at/deployments; a new "shipping the site" suite indeploy.spec.tscovering publish, builder-managed apps, publish failure and the no-commit archive fallback;site_deploy.spec.tsgains not-published /--publish/ archive-fallback cases;fullstack_deploy.spec.tsassertsdeploy -yrefuses a worker build before any request; gate-off tests andgivenEnv({ BASE44_DEPLOYMENTS_API })dropped across the deploy specs.docs/deployments.mdrewritten around deploy-vs-publish, the--git-hashopt-in, the remaining callers of the archive upload and the updated command UX;CHANGELOG.mdentries added.Testing
npm test)Checklist
docs/(AGENTS.md) if I made architectural changesAdditional Notes
BASE44_DEPLOYMENTS_APIbecomes a no-op, so a caller that set it to reach the lane must now pass--git-hashinstead;base44 deploy --git-hashpublishes with no way to opt out (an earlier--no-publishwas dropped before merge, since it read as covering every resource the deploy ships when only the site waits on a publish); and a full-stack build now fails asking for--git-hashon both commands instead of silently shipping a worker-less archive.docs/resources.md:116-121still documentsdeploymentsApiEnabled()as an export of the site module,docs/versions.md:98,147still describeresolveGitHashandBASE44_DEPLOYMENTS_APIas live,core/version/gate.ts:8repeats the latter, andtests/cli/static_site_deployments.spec.ts:296still sets it (now a harmless no-op).deploy.spec.ts,site_deploy.spec.ts,static_site_deployments.spec.ts,fullstack_deploy.spec.tsandbuild.spec.ts.🤖 Generated by Claude | 2026-10-21 12:12 UTC | d4dd71d