Skip to content

feat(deploy): ship every site through the deployments API, and publish it - #643

Merged
netanelgilad merged 13 commits into
mainfrom
claude/inspiring-edison-55ncqj
Oct 21, 2026
Merged

netanelgilad merged 13 commits into
mainfrom
claude/inspiring-edison-55ncqj

Conversation

@netanelgilad

@netanelgilad netanelgilad commented Sep 28, 2026 •

Copy link
Copy Markdown
Contributor

Note

Description

base44 deploy --git-hash <hash> now ships a project's built site through the deployments API — the same lane base44 site deploy uses — 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 its BASE44_DEPLOYMENTS_API env gate: --git-hash / --concurrency are always registered on site deploy, and the flag itself — never the checkout's HEAD, never an env var — is what selects the lane. Without --git-hash both 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-hash publishes what it shipped, because the archive upload it replaces always left the app live. base44 site deploy --git-hash does not — --publish asks 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

  • Bug fix (non-breaking change which fixes an issue)
  • New feature (non-breaking change which adds functionality)
  • Breaking change (fix or feature that would cause existing functionality to not work as expected)
  • Documentation update
  • Refactoring (no functional changes)
  • Other (please describe):

Changes Made

  • base44 deploy --git-hash <hash> ships the site through the deployments API — deployAll() routes the site step through a new deployProjectSite() / shipCommit() pair that calls deployToDeployments() and then publishes the result, so a full-stack build's worker is deployed instead of dropped. --concurrency deliberately stays off this command: the caller uploading at scale is site deploy.
  • The flag, and only the flag, selects the lane. resolveGitHash() is gone — git rev-parse HEAD is 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).
  • New publishDeployment() in core/site/api.ts — POST /deploy with { git_commit_hash }, then reads the app URL from published-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.
  • Builder-managed apps degrade gracefully: a publish rejected with error.code git_commit_hash_not_supported is 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.
  • A worker build with no commit fails before anything ships (workerBuildNeedsGitHashError()): deployAll() checks for a .wrangler/deploy/config.json artifact up front, and site deploy checks before its first request. A static build without --git-hash keeps deploySite(), which publishes as it always did.
  • site deploy reports when it did not publish — "Not published: production still serves its previous build. Run again with --publish to serve this one." — and its --json document gains published: boolean alongside deploymentId and gitHash; 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 no site.outputDirectory is no longer reported as "No resources found to deploy".
  • Env gate removed: deploymentsApiEnabled() and BASE44_DEPLOYMENTS_API are gone from core/site/deployment.ts, and site deploy's deployTarball() branch becomes the no-commit deployArchive() fallback.
  • Shared --git-hash parser: parseGitHash() extracted to cli/utils/git-hash.ts and reused by site deploy, deploy, and the versions commands' gitHashOption(), replacing duplicate copies of the same 7–64-hex-char check.
  • New PublishedDeployment type (gitHash, appUrl) in core/site/schema.ts.
  • Tests: new testkit mocks mockDeploymentPublish() / mockDeploymentPublishError() with a publishedCommits capture; mockSiteDeployError() renamed to mockDeploymentCreateError() and repointed at /deployments; a new "shipping the site" suite in deploy.spec.ts covering publish, builder-managed apps, publish failure and the no-commit archive fallback; site_deploy.spec.ts gains not-published / --publish / archive-fallback cases; fullstack_deploy.spec.ts asserts deploy -y refuses a worker build before any request; gate-off tests and givenEnv({ BASE44_DEPLOYMENTS_API }) dropped across the deploy specs.
  • Docs: docs/deployments.md rewritten around deploy-vs-publish, the --git-hash opt-in, the remaining callers of the archive upload and the updated command UX; CHANGELOG.md entries added.

Testing

  • I have tested these changes locally
  • I have added/updated tests as needed
  • All tests pass (npm test)

Checklist

  • My code follows the project's style guidelines
  • I have performed a self-review of my own code
  • I have commented my code, particularly in hard-to-understand areas
  • I have made corresponding changes to the documentation (if applicable)
  • My changes generate no new warnings
  • I have updated docs/ (AGENTS.md) if I made architectural changes

Additional Notes

  • Behavior changes worth calling out: BASE44_DEPLOYMENTS_API becomes a no-op, so a caller that set it to reach the lane must now pass --git-hash instead; base44 deploy --git-hash publishes with no way to opt out (an earlier --no-publish was 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-hash on both commands instead of silently shipping a worker-less archive.
  • Stale references left in the tree: docs/resources.md:116-121 still documents deploymentsApiEnabled() as an export of the site module, docs/versions.md:98,147 still describe resolveGitHash and BASE44_DEPLOYMENTS_API as live, core/version/gate.ts:8 repeats the latter, and tests/cli/static_site_deployments.spec.ts:296 still sets it (now a harmless no-op).
  • The testing checkboxes are unchecked because the suite was not executed in this environment — dependencies are not installed here and the default npm test mode needs a build first. Coverage for the new paths lives in deploy.spec.ts, site_deploy.spec.ts, static_site_deployments.spec.ts, fullstack_deploy.spec.ts and build.spec.ts.

🤖 Generated by Claude | 2026-10-21 12:12 UTC | d4dd71d

netanelgilad and others added 2 commits September 22, 2026 12:19
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
@netanelgilad netanelgilad self-assigned this Sep 28, 2026
…on-55ncqj

# Conflicts:
#	CHANGELOG.md
#	docs/deployments.md
#	packages/cli/src/cli/dev/serve-command-runner.ts
@github-actions

github-actions Bot commented Oct 3, 2026 •

Copy link
Copy Markdown
Contributor

🚀 Package Preview Available!


Install this PR's preview build with npm:

npm i @base44-preview/cli@0.1.33-pr.643.706636f

Prefer not to change any import paths? Install using npm alias so your code still imports base44:

npm i "base44@npm:@base44-preview/cli@0.1.33-pr.643.706636f"

Or add it to your package.json dependencies:

{
  "dependencies": {
    "base44": "npm:@base44-preview/cli@0.1.33-pr.643.706636f"
  }
}

Preview published to npm registry — try new features instantly!

claude added 2 commits October 3, 2026 06:35
…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
@netanelgilad
netanelgilad marked this pull request as draft October 11, 2026 12:23

Copy link
Copy Markdown
Contributor Author

Parking this as a draft.

  • The base44 dev front door is split out to feat(dev): front the app's dev server with the SDK header contract #659. This PR now has only the deploy routing (deployments API as the default lane, env gate removed, deploy publishes).
  • Full-stack deploys look set to go through Yury's base44-dev/apper#29340 (build/deploy pipeline) plus the CLI's base44 publish (versions API, behind BASE44_VERSIONS_API).
  • Once #29340 lands, this PR will probably shrink to having base44 deploy use publish for worker builds.

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
@netanelgilad
netanelgilad marked this pull request as ready for review October 21, 2026 10:00

netanelgilad commented Oct 21, 2026 •

Copy link
Copy Markdown
Contributor Author

Back in review. The server side is settled: base44-dev/apper#26921 makes POST /deploy accept git_commit_hash for apps whose source Base44 doesn't manage, and it's in the merge queue. This PR now calls that instead of the dropped deployments/{git_hash}/publish route, and reads the app URL from published-url afterwards.

Merge order matters: release this only after #26921 is deployed. A server without it ignores the unknown git_commit_hash field and runs an ordinary latest-version publish instead of publishing the uploaded commit.


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
@netanelgilad
netanelgilad force-pushed the claude/inspiring-edison-55ncqj branch from 344a4f5 to d52a878 Compare October 21, 2026 11:11

Copy link
Copy Markdown
Contributor Author

test (ubuntu-latest, npm) failed on d52a878 in tests/cli/dev-security.spec.ts (the beforeEach that starts base44 dev): Port 40477 is already in use. This PR's last push only removes --no-publish from base44 deploy and doesn't touch the dev server or that spec. The same suite passed on all four matrix legs on the previous head, 6d264e8. The error comes from a port the dev server picks being taken by another process before it binds (dev-server/main.ts), not from anything this PR changes. No fix exists yet. I'll re-run the job once when the rest of the workflow finishes.


Generated by Claude Code

base44-os-gremlins[bot]
base44-os-gremlins Bot previously approved these changes Oct 21, 2026
…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
@netanelgilad
netanelgilad merged commit 0cb08aa into main Oct 21, 2026
15 checks passed
@netanelgilad
netanelgilad deleted the claude/inspiring-edison-55ncqj branch October 21, 2026 12:30
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.

2 participants