Skip to content

Commit 3e8e89b

Browse files
yardend-wixclaude
andcommitted
feat(deploy): migrate unified deploy to per-function API
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
1 parent 83f723c commit 3e8e89b

3 files changed

Lines changed: 41 additions & 40 deletions

File tree

packages/cli/src/cli/commands/project/deploy.ts

Lines changed: 18 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -5,12 +5,8 @@ import {
55
promptOAuthFlows,
66
} from "@/cli/commands/connectors/oauth-prompt.js";
77
import type { CLIContext } from "@/cli/types.js";
8-
import {
9-
getDashboardUrl,
10-
runCommand,
11-
runTask,
12-
theme,
13-
} from "@/cli/utils/index.js";
8+
import { formatDeployResult } from "@/cli/utils/formatDeployResult.js";
9+
import { getDashboardUrl, runCommand, theme } from "@/cli/utils/index.js";
1410
import type { RunCommandResult } from "@/cli/utils/runCommand.js";
1511
import {
1612
deployAll,
@@ -80,16 +76,24 @@ export async function deployAction(
8076
log.info(`Deploying:\n${summaryLines.join("\n")}`);
8177
}
8278

83-
const result = await runTask(
84-
"Deploying your app...",
85-
async () => {
86-
return await deployAll(projectData);
79+
// Deploy resources with per-function progress
80+
let functionCompleted = 0;
81+
const functionTotal = functions.length;
82+
83+
const result = await deployAll(projectData, {
84+
onFunctionStart: (names) => {
85+
const label = names.length === 1 ? names[0] : `${names.length} functions`;
86+
log.step(
87+
theme.styles.dim(
88+
`[${functionCompleted + 1}/${functionTotal}] Deploying ${label}...`,
89+
),
90+
);
8791
},
88-
{
89-
successMessage: theme.colors.base44Orange("Deployment completed"),
90-
errorMessage: "Deployment failed",
92+
onFunctionResult: (r) => {
93+
functionCompleted++;
94+
formatDeployResult(r);
9195
},
92-
);
96+
});
9397

9498
// Handle connector OAuth flows
9599
const needsOAuth = filterPendingOAuth(result.connectorResults ?? []);

packages/cli/src/core/project/deploy.ts

Lines changed: 15 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,10 @@ import {
66
pushConnectors,
77
} from "@/core/resources/connector/index.js";
88
import { entityResource } from "@/core/resources/entity/index.js";
9-
import { functionResource } from "@/core/resources/function/index.js";
9+
import {
10+
deployFunctionsSequentially,
11+
type SingleFunctionDeployResult,
12+
} from "@/core/resources/function/deploy.js";
1013
import { deploySite } from "@/core/site/index.js";
1114

1215
/**
@@ -40,19 +43,29 @@ interface DeployAllResult {
4043
connectorResults?: ConnectorSyncResult[];
4144
}
4245

46+
export interface DeployAllOptions {
47+
onFunctionStart?: (names: string[]) => void;
48+
onFunctionResult?: (result: SingleFunctionDeployResult) => void;
49+
}
50+
4351
/**
4452
* Deploys all project resources (entities, functions, agents, connectors, and site) to Base44.
4553
*
4654
* @param projectData - The project configuration and resources to deploy
55+
* @param options - Optional progress callbacks for resource deployment
4756
* @returns The deployment result including app URL if site was deployed
4857
*/
4958
export async function deployAll(
5059
projectData: ProjectData,
60+
options?: DeployAllOptions,
5161
): Promise<DeployAllResult> {
5262
const { project, entities, functions, agents, connectors } = projectData;
5363

5464
await entityResource.push(entities);
55-
await functionResource.push(functions);
65+
await deployFunctionsSequentially(functions, {
66+
onStart: options?.onFunctionStart,
67+
onResult: options?.onFunctionResult,
68+
});
5669
await agentResource.push(agents);
5770
const { results: connectorResults } = await pushConnectors(connectors);
5871

packages/cli/tests/cli/deploy.spec.ts

Lines changed: 8 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,6 @@ describe("deploy command (unified)", () => {
3535
const result = await t.run("deploy", "-y");
3636

3737
t.expectResult(result).toSucceed();
38-
t.expectResult(result).toContain("Deployment completed");
3938
t.expectResult(result).toContain("App deployed successfully");
4039
});
4140

@@ -52,62 +51,53 @@ describe("deploy command (unified)", () => {
5251
const result = await t.run("deploy", "--yes");
5352

5453
t.expectResult(result).toSucceed();
55-
t.expectResult(result).toContain("Deployment completed");
54+
t.expectResult(result).toContain("App deployed successfully");
5655
});
5756

5857
it("deploys entities and functions together", async () => {
5958
await t.givenLoggedInWithProject(fixture("with-functions-and-entities"));
6059
t.api.mockEntitiesPush({ created: ["Order"], updated: [], deleted: [] });
61-
t.api.mockFunctionsPush({
62-
deployed: ["process-order"],
63-
deleted: [],
64-
errors: null,
65-
});
60+
t.api.mockSingleFunctionDeploy({ status: "deployed" });
6661
t.api.mockAgentsPush({ created: [], updated: [], deleted: [] });
6762
t.api.mockConnectorsList({ integrations: [] });
6863

6964
const result = await t.run("deploy", "-y");
7065

7166
t.expectResult(result).toSucceed();
72-
t.expectResult(result).toContain("Deployment completed");
67+
t.expectResult(result).toContain("App deployed successfully");
7368
});
7469

7570
it("deploys zero-config functions (path-based names) with unified deploy", async () => {
7671
await t.givenLoggedInWithProject(fixture("with-zero-config-functions"));
7772
t.api.mockEntitiesPush({ created: [], updated: [], deleted: [] });
78-
t.api.mockFunctionsPush({
79-
deployed: ["foo/bar", "foo/kfir/hello", "stam", "custom-name"],
80-
deleted: [],
81-
errors: null,
82-
});
73+
t.api.mockSingleFunctionDeploy({ status: "deployed" });
8374
t.api.mockAgentsPush({ created: [], updated: [], deleted: [] });
8475
t.api.mockConnectorsList({ integrations: [] });
8576

8677
const result = await t.run("deploy", "-y");
8778

8879
t.expectResult(result).toSucceed();
89-
t.expectResult(result).toContain("Deployment completed");
80+
t.expectResult(result).toContain("App deployed successfully");
9081
});
9182

9283
it("deploys entities, functions, and site together", async () => {
9384
await t.givenLoggedInWithProject(fixture("full-project"));
9485
t.api.mockEntitiesPush({ created: ["Task"], updated: [], deleted: [] });
95-
t.api.mockFunctionsPush({ deployed: ["hello"], deleted: [], errors: null });
86+
t.api.mockSingleFunctionDeploy({ status: "deployed" });
9687
t.api.mockAgentsPush({ created: [], updated: [], deleted: [] });
9788
t.api.mockConnectorsList({ integrations: [] });
9889
t.api.mockSiteDeploy({ app_url: "https://full-project.base44.app" });
9990

10091
const result = await t.run("deploy", "-y");
10192

10293
t.expectResult(result).toSucceed();
103-
t.expectResult(result).toContain("Deployment completed");
94+
t.expectResult(result).toContain("App deployed successfully");
10495
t.expectResult(result).toContain("https://full-project.base44.app");
10596
});
10697

10798
it("deploys agents successfully with -y flag", async () => {
10899
await t.givenLoggedInWithProject(fixture("with-agents"));
109100
t.api.mockEntitiesPush({ created: [], updated: [], deleted: [] });
110-
t.api.mockFunctionsPush({ deployed: [], deleted: [], errors: null });
111101
t.api.mockAgentsPush({
112102
created: ["customer_support", "order_assistant", "data_analyst"],
113103
updated: [],
@@ -118,14 +108,12 @@ describe("deploy command (unified)", () => {
118108
const result = await t.run("deploy", "-y");
119109

120110
t.expectResult(result).toSucceed();
121-
t.expectResult(result).toContain("Deployment completed");
122111
t.expectResult(result).toContain("App deployed successfully");
123112
});
124113

125114
it("deploys agents and entities together", async () => {
126115
await t.givenLoggedInWithProject(fixture("with-agents"));
127116
t.api.mockEntitiesPush({ created: [], updated: [], deleted: [] });
128-
t.api.mockFunctionsPush({ deployed: [], deleted: [], errors: null });
129117
t.api.mockAgentsPush({
130118
created: ["customer_support"],
131119
updated: ["order_assistant"],
@@ -136,13 +124,12 @@ describe("deploy command (unified)", () => {
136124
const result = await t.run("deploy", "-y");
137125

138126
t.expectResult(result).toSucceed();
139-
t.expectResult(result).toContain("Deployment completed");
127+
t.expectResult(result).toContain("App deployed successfully");
140128
});
141129

142130
it("deploys connectors successfully with -y flag", async () => {
143131
await t.givenLoggedInWithProject(fixture("with-connectors"));
144132
t.api.mockEntitiesPush({ created: [], updated: [], deleted: [] });
145-
t.api.mockFunctionsPush({ deployed: [], deleted: [], errors: null });
146133
t.api.mockAgentsPush({ created: [], updated: [], deleted: [] });
147134
t.api.mockConnectorsList({ integrations: [] });
148135
t.api.mockConnectorSet({
@@ -154,14 +141,12 @@ describe("deploy command (unified)", () => {
154141
const result = await t.run("deploy", "-y");
155142

156143
t.expectResult(result).toSucceed();
157-
t.expectResult(result).toContain("Deployment completed");
158144
t.expectResult(result).toContain("3 connectors");
159145
});
160146

161147
it("shows OAuth info when connectors need authorization with -y flag", async () => {
162148
await t.givenLoggedInWithProject(fixture("with-connectors"));
163149
t.api.mockEntitiesPush({ created: [], updated: [], deleted: [] });
164-
t.api.mockFunctionsPush({ deployed: [], deleted: [], errors: null });
165150
t.api.mockAgentsPush({ created: [], updated: [], deleted: [] });
166151
t.api.mockConnectorsList({ integrations: [] });
167152
t.api.mockConnectorSet({
@@ -173,7 +158,6 @@ describe("deploy command (unified)", () => {
173158
const result = await t.run("deploy", "-y");
174159

175160
t.expectResult(result).toSucceed();
176-
t.expectResult(result).toContain("Deployment completed");
177161
t.expectResult(result).toContain("require authorization");
178162
t.expectResult(result).toContain("base44 connectors push");
179163
});

0 commit comments

Comments
 (0)