From 7f7ecde4fc9958b50b1a8d73f385742d962bd1ea Mon Sep 17 00:00:00 2001 From: Claude Date: Wed, 21 Oct 2026 13:46:50 +0000 Subject: [PATCH] fix(deploy): don't fail a successful publish when the app URL lookup is refused The URL lookup after publishing goes through a platform route that refuses a workspace API key, so `base44 deploy --git-hash` with BASE44_API_KEY exited 1 after putting the commit live. The lookup is now best effort. Co-Authored-By: Claude Opus 5.5 Claude-Session: https://claude.ai/code/session_01Tmb4PCrrV2SLRpBjkGAgXJ --- CHANGELOG.md | 1 + packages/cli/src/cli/commands/site/deploy.ts | 4 ++-- packages/cli/src/core/site/api.ts | 5 ++++- packages/cli/src/core/site/schema.ts | 4 ++-- packages/cli/tests/cli/deploy.spec.ts | 21 ++++++++++++++++++++ 5 files changed, 30 insertions(+), 5 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 2d58f01b..31fb8eec 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -29,6 +29,7 @@ ### Fixed +- `base44 deploy --git-hash` (and `site deploy --publish`) no longer fails after a successful publish when the app's URL can't be looked up, as happened with a workspace API key (`BASE44_API_KEY`). It reports the deploy and leaves out the URL. - An app id passed with `--app-id` or `BASE44_APP_ID` no longer discards the project the command runs in. Commands that need a project — `site dev`, `dev`, `connectors pull`/`push` — refused as if there were none. A checkout with `base44/config.jsonc` but no `.app.jsonc`, such as a hosted sandbox, can now run them. - The `backend-and-client` template names its entity file `Task.jsonc`, matching the entity's `name`; the Base44 platform derives the entity from the filename, so `task.jsonc` read as a second entity. - `base44 link` now lists editor-created apps; previously only apps created by the CLI could be linked. diff --git a/packages/cli/src/cli/commands/site/deploy.ts b/packages/cli/src/cli/commands/site/deploy.ts index dcc187ed..3a35d89d 100644 --- a/packages/cli/src/cli/commands/site/deploy.ts +++ b/packages/cli/src/cli/commands/site/deploy.ts @@ -130,11 +130,11 @@ async function deployToDeploymentsApi( ) : undefined; - if (published) { + if (published?.appUrl) { log.message( `${theme.styles.header("App URL")}: ${theme.colors.links(published.appUrl)}`, ); - } else { + } else if (!published) { log.info( "Not published: production still serves its previous build. Run again with --publish to serve this one.", ); diff --git a/packages/cli/src/core/site/api.ts b/packages/cli/src/core/site/api.ts index 4cb96112..92c37331 100644 --- a/packages/cli/src/core/site/api.ts +++ b/packages/cli/src/core/site/api.ts @@ -181,5 +181,8 @@ export async function publishDeployment( throw await ApiError.fromHttpError(error, "publishing deployment"); } - return { gitHash, appUrl: await getSiteUrl() }; + // Best effort: the publish already went through, and the URL lookup is a + // platform route a workspace API key is refused on. + const appUrl = await getSiteUrl().catch(() => undefined); + return { gitHash, appUrl }; } diff --git a/packages/cli/src/core/site/schema.ts b/packages/cli/src/core/site/schema.ts index 2b6e9e68..5f4a7110 100644 --- a/packages/cli/src/core/site/schema.ts +++ b/packages/cli/src/core/site/schema.ts @@ -203,8 +203,8 @@ export type FinalizeDeploymentResponse = z.infer< typeof FinalizeDeploymentResponseSchema >; -/** A commit's build now serving production, and where. */ +/** A commit's build now serving production, and where when it could be looked up. */ export interface PublishedDeployment { gitHash: string; - appUrl: string; + appUrl?: string; } diff --git a/packages/cli/tests/cli/deploy.spec.ts b/packages/cli/tests/cli/deploy.spec.ts index 33b75d4a..ed7fbbc0 100644 --- a/packages/cli/tests/cli/deploy.spec.ts +++ b/packages/cli/tests/cli/deploy.spec.ts @@ -342,6 +342,27 @@ describe("deploy command (unified)", () => { t.expectResult(result).toContain("published from the Base44 builder"); }); + it("still succeeds when the app URL can't be looked up after publishing", async () => { + // A workspace API key is refused on the published-url route. + await t.givenLoggedInWithProject(fixture("full-project")); + mockProjectResources(); + mockDeployment(); + t.api.mockError( + "get", + `/api/apps/platform/${t.api.appId}/published-url`, + { + status: 401, + body: { message: "Unauthorized", detail: "Object not found" }, + }, + ); + t.api.mockDeploymentPublish(); + + const result = await t.run("deploy", "-y", "--git-hash", GIT_HASH); + + t.expectResult(result).toSucceed(); + expect(t.api.publishedCommits).toEqual([GIT_HASH]); + }); + it("fails when the publish itself fails", async () => { await t.givenLoggedInWithProject(fixture("full-project")); mockProjectResources();