gencow 0.1.258 → 0.1.259
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
|
@@ -83,10 +83,16 @@ const SAFE_LOCAL_ERROR_CODES = new Map([
|
|
|
83
83
|
[
|
|
84
84
|
"CODEGEN_SOURCE_LOAD_FAILED",
|
|
85
85
|
{
|
|
86
|
-
message: "Deploy stopped before upload: a backend source file
|
|
86
|
+
message: "Deploy stopped before upload: a backend source file could not be loaded.",
|
|
87
87
|
stage: "codegen",
|
|
88
|
-
action:
|
|
89
|
-
|
|
88
|
+
action: (caught) => {
|
|
89
|
+
const cause =
|
|
90
|
+
"the top-level code that throws during module load, or an import path that does not resolve (check the path and that the imported package is installed)";
|
|
91
|
+
if (safeSourceLocation(caught)) {
|
|
92
|
+
return `Retryable: yes, after the source at the location above is fixed (${cause}) — run \`npx gencow@latest codegen\` to confirm it passes, then rerun your deploy command.`;
|
|
93
|
+
}
|
|
94
|
+
return `Retryable: yes, after ${cause} is fixed — run \`npx gencow@latest codegen\` to confirm it passes, then rerun your deploy command. The failing location is not available; check the backend files that run code at import time and the paths they import.`;
|
|
95
|
+
},
|
|
90
96
|
},
|
|
91
97
|
],
|
|
92
98
|
[
|
|
@@ -33,6 +33,18 @@ function toProjectRelativePath(rawPath, cwd, processCwd) {
|
|
|
33
33
|
return normalized;
|
|
34
34
|
}
|
|
35
35
|
|
|
36
|
+
// esbuild prints one diagnostic per line (`<file>:<line>:<col>: ERROR: <text>`) and the
|
|
37
|
+
// Bun loader prints `Could not resolve`/`Cannot find module` for an import path that does
|
|
38
|
+
// not exist. Those are load failures: the source is well-formed, its import is not.
|
|
39
|
+
const DIAGNOSTIC_LINE_PATTERN = /ERROR: |Could not resolve "|Cannot find (?:module|package)\b/u;
|
|
40
|
+
const UNRESOLVED_MODULE_PATTERN = /Could not resolve "|Cannot find (?:module|package)\b/u;
|
|
41
|
+
|
|
42
|
+
// The first diagnostic decides the class, matching how the first location is reported.
|
|
43
|
+
function isUnresolvedModuleFailure(message) {
|
|
44
|
+
const firstDiagnostic = message.split("\n").find((line) => DIAGNOSTIC_LINE_PATTERN.test(line)) ?? message;
|
|
45
|
+
return UNRESOLVED_MODULE_PATTERN.test(firstDiagnostic);
|
|
46
|
+
}
|
|
47
|
+
|
|
36
48
|
// Returns the first project-relative source location found in a raw message, or null.
|
|
37
49
|
// Absolute paths, stacks and the raw text are never returned.
|
|
38
50
|
function findProjectSourceLocation(message, { cwd, processCwd }) {
|
|
@@ -53,11 +65,14 @@ export function classifyDeployCodegenFailure(error, { cwd, backendRoot = "gencow
|
|
|
53
65
|
const message = (error instanceof Error ? error.message : String(error ?? "")).slice(0, MAX_CLASSIFIED_MESSAGE_LENGTH);
|
|
54
66
|
if (message.includes("Transpile error:")) {
|
|
55
67
|
const location = findProjectSourceLocation(message, { cwd, processCwd });
|
|
68
|
+
const backendLocation = location && location.sourceFile.startsWith(`${backendRoot}/`) ? location : null;
|
|
69
|
+
if (isUnresolvedModuleFailure(message)) {
|
|
70
|
+
return { code: CODEGEN_FAILURE_CODES.SOURCE_LOAD_FAILED, ...(backendLocation ?? {}) };
|
|
71
|
+
}
|
|
56
72
|
if (location) {
|
|
57
|
-
const inBackend = location.sourceFile.startsWith(`${backendRoot}/`);
|
|
58
73
|
return {
|
|
59
74
|
code: CODEGEN_FAILURE_CODES.SOURCE_SYNTAX_ERROR,
|
|
60
|
-
...(
|
|
75
|
+
...(backendLocation ?? {}),
|
|
61
76
|
};
|
|
62
77
|
}
|
|
63
78
|
}
|
|
@@ -0,0 +1,144 @@
|
|
|
1
|
+
import { existsSync, readFileSync, writeFileSync } from "fs";
|
|
2
|
+
|
|
3
|
+
import { CONFIRMATION_MODE, resolveConfirmationMode } from "./confirm-gate.mjs";
|
|
4
|
+
import { confirmFirstProductionDeploy } from "./deploy-first-prod-confirm.mjs";
|
|
5
|
+
import { reconcileProductionAppTarget } from "./deploy-app-target.mjs";
|
|
6
|
+
import { readJsonObjectResponse } from "./http-response-json.mjs";
|
|
7
|
+
import { BOLD, DIM, GREEN, RED, RESET, error, info, log, success } from "./output.mjs";
|
|
8
|
+
import { prodCreateFailureMessage } from "./prod-create-failure.mjs";
|
|
9
|
+
|
|
10
|
+
export async function ensureProductionDeployTarget({
|
|
11
|
+
appId,
|
|
12
|
+
displayName,
|
|
13
|
+
createCloudAppIfNeeded,
|
|
14
|
+
createInterfaceImpl,
|
|
15
|
+
creds,
|
|
16
|
+
deployArgs,
|
|
17
|
+
gencowJsonPath,
|
|
18
|
+
platformFetchImpl,
|
|
19
|
+
processEnv = process.env,
|
|
20
|
+
readFileSyncImpl = readFileSync,
|
|
21
|
+
setTimeoutImpl = setTimeout,
|
|
22
|
+
successImpl = success,
|
|
23
|
+
errorImpl = error,
|
|
24
|
+
infoImpl = info,
|
|
25
|
+
logImpl = log,
|
|
26
|
+
writeFileSyncImpl = writeFileSync,
|
|
27
|
+
exitImpl = (code) => process.exit(code),
|
|
28
|
+
prodAppId = null,
|
|
29
|
+
region = null,
|
|
30
|
+
prepareLocalSource = null,
|
|
31
|
+
}) {
|
|
32
|
+
const confirmMode = resolveConfirmationMode({
|
|
33
|
+
allowCiBypass: true,
|
|
34
|
+
args: deployArgs,
|
|
35
|
+
createInterfaceImpl,
|
|
36
|
+
processEnv,
|
|
37
|
+
});
|
|
38
|
+
// Without a recorded prod app, the first create always needs confirmation, so a
|
|
39
|
+
// non-interactive run fails before any platform lookup or create request.
|
|
40
|
+
if (!prodAppId && confirmMode === CONFIRMATION_MODE.NON_INTERACTIVE) {
|
|
41
|
+
await confirmFirstProductionDeploy({ appId, errorImpl, infoImpl, logImpl, mode: confirmMode, region });
|
|
42
|
+
exitImpl(1);
|
|
43
|
+
return null;
|
|
44
|
+
}
|
|
45
|
+
const reconciledTarget = await reconcileProductionAppTarget({
|
|
46
|
+
appId,
|
|
47
|
+
prodAppId,
|
|
48
|
+
creds,
|
|
49
|
+
displayName,
|
|
50
|
+
gencowJsonPath,
|
|
51
|
+
platformFetchImpl,
|
|
52
|
+
createCloudAppIfNeeded,
|
|
53
|
+
infoImpl,
|
|
54
|
+
errorImpl,
|
|
55
|
+
exitImpl,
|
|
56
|
+
});
|
|
57
|
+
if (!reconciledTarget) return null;
|
|
58
|
+
const productionParentAppId = reconciledTarget.appId;
|
|
59
|
+
const existingProdAppId = reconciledTarget.prodAppId;
|
|
60
|
+
if (existingProdAppId) return existingProdAppId;
|
|
61
|
+
if (!productionParentAppId) return null;
|
|
62
|
+
|
|
63
|
+
const proceed = await confirmFirstProductionDeploy({
|
|
64
|
+
appId: productionParentAppId,
|
|
65
|
+
createInterfaceImpl,
|
|
66
|
+
errorImpl,
|
|
67
|
+
infoImpl,
|
|
68
|
+
logImpl,
|
|
69
|
+
mode: confirmMode,
|
|
70
|
+
region,
|
|
71
|
+
});
|
|
72
|
+
if (!proceed) {
|
|
73
|
+
exitImpl(1);
|
|
74
|
+
return null;
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
// Local source validation must pass before any remote create: a rejected source
|
|
78
|
+
// (for example a codegen failure) must not leave an empty production app behind.
|
|
79
|
+
// The callback renders its own diagnosis and returns false when it aborts.
|
|
80
|
+
if (prepareLocalSource && !(await prepareLocalSource())) return null;
|
|
81
|
+
|
|
82
|
+
infoImpl("Creating prod app...");
|
|
83
|
+
const createProdRes = await platformFetchImpl(
|
|
84
|
+
creds,
|
|
85
|
+
`/platform/apps/${productionParentAppId}/create-prod`,
|
|
86
|
+
{
|
|
87
|
+
method: "POST",
|
|
88
|
+
headers: { "Content-Type": "application/json" },
|
|
89
|
+
body: JSON.stringify(region ? { region } : {}),
|
|
90
|
+
},
|
|
91
|
+
);
|
|
92
|
+
|
|
93
|
+
if (!createProdRes.ok) {
|
|
94
|
+
const errData = await readJsonObjectResponse(createProdRes);
|
|
95
|
+
if (createProdRes.status === 403) {
|
|
96
|
+
logImpl("");
|
|
97
|
+
logImpl(` ${RED}⛔ Production Deploy — Startup or Enterprise required${RESET}`);
|
|
98
|
+
logImpl("");
|
|
99
|
+
logImpl(` ${DIM}gencow deploy --prod is for production deployment.${RESET}`);
|
|
100
|
+
logImpl("");
|
|
101
|
+
logImpl(` ${BOLD}💡 Dev environment deploy:${RESET}`);
|
|
102
|
+
logImpl(` ${GREEN}gencow deploy${RESET} ${DIM}← Deploy backend to dev${RESET}`);
|
|
103
|
+
logImpl(` ${GREEN}gencow dev${RESET} ${DIM}← Real-time backend + live logs${RESET}`);
|
|
104
|
+
logImpl(` ${GREEN}gencow static dist/${RESET} ${DIM}← Static file (dist/) deploy${RESET}`);
|
|
105
|
+
logImpl("");
|
|
106
|
+
logImpl(` ${BOLD}🚀 Unlock production deploy:${RESET}`);
|
|
107
|
+
logImpl(` ${GREEN}gencow upgrade${RESET} ${DIM}← Upgrade to Startup${RESET}`);
|
|
108
|
+
logImpl("");
|
|
109
|
+
} else {
|
|
110
|
+
errorImpl(prodCreateFailureMessage(errData, createProdRes));
|
|
111
|
+
}
|
|
112
|
+
exitImpl(1);
|
|
113
|
+
return null;
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
const createProdData = await readJsonObjectResponse(createProdRes);
|
|
117
|
+
if (!createProdData) {
|
|
118
|
+
errorImpl("Prod app creation failed: server returned an invalid JSON response");
|
|
119
|
+
exitImpl(1);
|
|
120
|
+
return null;
|
|
121
|
+
}
|
|
122
|
+
const nextProdAppId = createProdData.prodApp;
|
|
123
|
+
const gencowJson = existsSync(gencowJsonPath) ? JSON.parse(readFileSyncImpl(gencowJsonPath, "utf8")) : {};
|
|
124
|
+
gencowJson.prodApp = nextProdAppId;
|
|
125
|
+
writeFileSyncImpl(gencowJsonPath, JSON.stringify(gencowJson, null, 2));
|
|
126
|
+
|
|
127
|
+
if (createProdData.alreadyExists) {
|
|
128
|
+
infoImpl(`Prod app verified: ${nextProdAppId}`);
|
|
129
|
+
if (region && createProdData.region && createProdData.region !== region) {
|
|
130
|
+
infoImpl(
|
|
131
|
+
`Region unchanged: existing prod app is in ${createProdData.region}; --region only applies to first prod creation.`,
|
|
132
|
+
);
|
|
133
|
+
}
|
|
134
|
+
} else {
|
|
135
|
+
successImpl(`Prod app created: ${nextProdAppId}`);
|
|
136
|
+
infoImpl(`URL: ${createProdData.url}`);
|
|
137
|
+
if (createProdData.region) {
|
|
138
|
+
infoImpl(`Region: ${createProdData.region} (${createProdData.regionSource || "selected"})`);
|
|
139
|
+
}
|
|
140
|
+
await new Promise((resolveDelay) => setTimeoutImpl(resolveDelay, 3000));
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
return nextProdAppId;
|
|
144
|
+
}
|
package/lib/deploy-runtime.mjs
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { existsSync, readFileSync, unlinkSync, writeFileSync } from "fs";
|
|
2
2
|
import { basename, resolve } from "path";
|
|
3
3
|
import { readProjectDisplayName } from "./cli-project-runtime.mjs";
|
|
4
|
-
import { BOLD, CYAN, DIM,
|
|
4
|
+
import { BOLD, CYAN, DIM, RESET, YELLOW, error, info, log, success, warn } from "./output.mjs";
|
|
5
5
|
import { createDeployPackageRuntime } from "./deploy-package-runtime.mjs";
|
|
6
6
|
import {
|
|
7
7
|
getCliInvocationSelection,
|
|
@@ -17,11 +17,8 @@ import {
|
|
|
17
17
|
} from "./deploy-static-fullstack-runtime.mjs";
|
|
18
18
|
import { detectStaticDeployBackend, preflightStaticDeployArtifact } from "./static-deploy-command.mjs";
|
|
19
19
|
import { formatCommandError } from "./cli-command-runner.mjs";
|
|
20
|
-
import { prodCreateFailureMessage } from "./prod-create-failure.mjs";
|
|
21
20
|
import { readJsonObjectResponse } from "./http-response-json.mjs";
|
|
22
|
-
import {
|
|
23
|
-
import { CONFIRMATION_MODE, resolveConfirmationMode } from "./confirm-gate.mjs";
|
|
24
|
-
import { confirmFirstProductionDeploy } from "./deploy-first-prod-confirm.mjs";
|
|
21
|
+
import { ensureProductionDeployTarget } from "./deploy-prod-target.mjs";
|
|
25
22
|
|
|
26
23
|
export function parseDeployArgs(deployArgs) {
|
|
27
24
|
const parsed = {
|
|
@@ -186,134 +183,12 @@ function showUnknownDeployArgument(arg, { errorImpl = error, infoImpl = info, lo
|
|
|
186
183
|
infoImpl(" gencow static [dir] Deploy static files only");
|
|
187
184
|
}
|
|
188
185
|
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
deployArgs,
|
|
196
|
-
gencowJsonPath,
|
|
197
|
-
platformFetchImpl,
|
|
198
|
-
processEnv = process.env,
|
|
199
|
-
readFileSyncImpl = readFileSync,
|
|
200
|
-
setTimeoutImpl = setTimeout,
|
|
201
|
-
successImpl = success,
|
|
202
|
-
errorImpl = error,
|
|
203
|
-
infoImpl = info,
|
|
204
|
-
logImpl = log,
|
|
205
|
-
writeFileSyncImpl = writeFileSync,
|
|
206
|
-
exitImpl = (code) => process.exit(code),
|
|
207
|
-
prodAppId = null,
|
|
208
|
-
region = null,
|
|
209
|
-
}) {
|
|
210
|
-
const confirmMode = resolveConfirmationMode({
|
|
211
|
-
allowCiBypass: true,
|
|
212
|
-
args: deployArgs,
|
|
213
|
-
createInterfaceImpl,
|
|
214
|
-
processEnv,
|
|
215
|
-
});
|
|
216
|
-
// Without a recorded prod app, the first create always needs confirmation, so a
|
|
217
|
-
// non-interactive run fails before any platform lookup or create request.
|
|
218
|
-
if (!prodAppId && confirmMode === CONFIRMATION_MODE.NON_INTERACTIVE) {
|
|
219
|
-
await confirmFirstProductionDeploy({ appId, errorImpl, infoImpl, logImpl, mode: confirmMode, region });
|
|
220
|
-
exitImpl(1);
|
|
221
|
-
return null;
|
|
222
|
-
}
|
|
223
|
-
const reconciledTarget = await reconcileProductionAppTarget({
|
|
224
|
-
appId,
|
|
225
|
-
prodAppId,
|
|
226
|
-
creds,
|
|
227
|
-
displayName,
|
|
228
|
-
gencowJsonPath,
|
|
229
|
-
platformFetchImpl,
|
|
230
|
-
createCloudAppIfNeeded,
|
|
231
|
-
infoImpl,
|
|
232
|
-
errorImpl,
|
|
233
|
-
exitImpl,
|
|
234
|
-
});
|
|
235
|
-
if (!reconciledTarget) return null;
|
|
236
|
-
const productionParentAppId = reconciledTarget.appId;
|
|
237
|
-
const existingProdAppId = reconciledTarget.prodAppId;
|
|
238
|
-
if (existingProdAppId) return existingProdAppId;
|
|
239
|
-
if (!productionParentAppId) return null;
|
|
240
|
-
|
|
241
|
-
const proceed = await confirmFirstProductionDeploy({
|
|
242
|
-
appId: productionParentAppId,
|
|
243
|
-
createInterfaceImpl,
|
|
244
|
-
errorImpl,
|
|
245
|
-
infoImpl,
|
|
246
|
-
logImpl,
|
|
247
|
-
mode: confirmMode,
|
|
248
|
-
region,
|
|
249
|
-
});
|
|
250
|
-
if (!proceed) {
|
|
251
|
-
exitImpl(1);
|
|
252
|
-
return null;
|
|
253
|
-
}
|
|
254
|
-
|
|
255
|
-
infoImpl("Creating prod app...");
|
|
256
|
-
const createProdRes = await platformFetchImpl(
|
|
257
|
-
creds,
|
|
258
|
-
`/platform/apps/${productionParentAppId}/create-prod`,
|
|
259
|
-
{
|
|
260
|
-
method: "POST",
|
|
261
|
-
headers: { "Content-Type": "application/json" },
|
|
262
|
-
body: JSON.stringify(region ? { region } : {}),
|
|
263
|
-
},
|
|
264
|
-
);
|
|
265
|
-
|
|
266
|
-
if (!createProdRes.ok) {
|
|
267
|
-
const errData = await readJsonObjectResponse(createProdRes);
|
|
268
|
-
if (createProdRes.status === 403) {
|
|
269
|
-
logImpl("");
|
|
270
|
-
logImpl(` ${RED}⛔ Production Deploy — Startup or Enterprise required${RESET}`);
|
|
271
|
-
logImpl("");
|
|
272
|
-
logImpl(` ${DIM}gencow deploy --prod is for production deployment.${RESET}`);
|
|
273
|
-
logImpl("");
|
|
274
|
-
logImpl(` ${BOLD}💡 Dev environment deploy:${RESET}`);
|
|
275
|
-
logImpl(` ${GREEN}gencow deploy${RESET} ${DIM}← Deploy backend to dev${RESET}`);
|
|
276
|
-
logImpl(` ${GREEN}gencow dev${RESET} ${DIM}← Real-time backend + live logs${RESET}`);
|
|
277
|
-
logImpl(` ${GREEN}gencow static dist/${RESET} ${DIM}← Static file (dist/) deploy${RESET}`);
|
|
278
|
-
logImpl("");
|
|
279
|
-
logImpl(` ${BOLD}🚀 Unlock production deploy:${RESET}`);
|
|
280
|
-
logImpl(` ${GREEN}gencow upgrade${RESET} ${DIM}← Upgrade to Startup${RESET}`);
|
|
281
|
-
logImpl("");
|
|
282
|
-
} else {
|
|
283
|
-
errorImpl(prodCreateFailureMessage(errData, createProdRes));
|
|
284
|
-
}
|
|
285
|
-
exitImpl(1);
|
|
286
|
-
return null;
|
|
287
|
-
}
|
|
288
|
-
|
|
289
|
-
const createProdData = await readJsonObjectResponse(createProdRes);
|
|
290
|
-
if (!createProdData) {
|
|
291
|
-
errorImpl("Prod app creation failed: server returned an invalid JSON response");
|
|
292
|
-
exitImpl(1);
|
|
293
|
-
return null;
|
|
294
|
-
}
|
|
295
|
-
const nextProdAppId = createProdData.prodApp;
|
|
296
|
-
const gencowJson = existsSync(gencowJsonPath) ? JSON.parse(readFileSyncImpl(gencowJsonPath, "utf8")) : {};
|
|
297
|
-
gencowJson.prodApp = nextProdAppId;
|
|
298
|
-
writeFileSyncImpl(gencowJsonPath, JSON.stringify(gencowJson, null, 2));
|
|
299
|
-
|
|
300
|
-
if (createProdData.alreadyExists) {
|
|
301
|
-
infoImpl(`Prod app verified: ${nextProdAppId}`);
|
|
302
|
-
if (region && createProdData.region && createProdData.region !== region) {
|
|
303
|
-
infoImpl(
|
|
304
|
-
`Region unchanged: existing prod app is in ${createProdData.region}; --region only applies to first prod creation.`,
|
|
305
|
-
);
|
|
306
|
-
}
|
|
307
|
-
} else {
|
|
308
|
-
successImpl(`Prod app created: ${nextProdAppId}`);
|
|
309
|
-
infoImpl(`URL: ${createProdData.url}`);
|
|
310
|
-
if (createProdData.region) {
|
|
311
|
-
infoImpl(`Region: ${createProdData.region} (${createProdData.regionSource || "selected"})`);
|
|
312
|
-
}
|
|
313
|
-
await new Promise((resolveDelay) => setTimeoutImpl(resolveDelay, 3000));
|
|
314
|
-
}
|
|
315
|
-
|
|
316
|
-
return nextProdAppId;
|
|
186
|
+
// Returns the prepared backend source, or null when the capability check rejected it
|
|
187
|
+
// (the rejection is already rendered and exit is already requested).
|
|
188
|
+
async function prepareBackendSourceForProdCreate(runtime) {
|
|
189
|
+
if ((await runtime.preflightBackendCapability?.()) === false) return null;
|
|
190
|
+
const generatorVersion = await runtime.prepareDeployProjectSource?.({ existingBundleMode: false });
|
|
191
|
+
return { runtime, generatorVersion };
|
|
317
192
|
}
|
|
318
193
|
|
|
319
194
|
async function runRollbackDeploy({
|
|
@@ -618,28 +493,46 @@ export function createDeployCommand({
|
|
|
618
493
|
return;
|
|
619
494
|
}
|
|
620
495
|
|
|
496
|
+
// Backend source preparation (capability check, migration generation, codegen) runs
|
|
497
|
+
// before the production app is created, so a rejected source creates no remote app.
|
|
498
|
+
// It is prepared once here and handed to deployBackendPackage/runStaticDeployFlow.
|
|
499
|
+
let preparedSource = null;
|
|
621
500
|
if (parsed.envTarget === "prod" && appId && !parsed.existingBundleMode && !parsed.resumeRequested) {
|
|
622
|
-
|
|
623
|
-
|
|
624
|
-
|
|
625
|
-
|
|
626
|
-
|
|
627
|
-
|
|
628
|
-
|
|
629
|
-
|
|
630
|
-
|
|
631
|
-
|
|
632
|
-
|
|
633
|
-
|
|
634
|
-
|
|
635
|
-
|
|
636
|
-
|
|
637
|
-
|
|
638
|
-
|
|
639
|
-
|
|
640
|
-
|
|
641
|
-
|
|
642
|
-
|
|
501
|
+
// A static-only deploy has no backend source to prepare.
|
|
502
|
+
const preparesBackendSource = !parsed.staticDeploy || hasDetectedBackend;
|
|
503
|
+
let prodTarget = null;
|
|
504
|
+
try {
|
|
505
|
+
prodTarget = await ensureProductionDeployTarget({
|
|
506
|
+
appId: rootAppId,
|
|
507
|
+
displayName,
|
|
508
|
+
createCloudAppIfNeeded: backendDeployRuntime.createCloudAppIfNeeded,
|
|
509
|
+
createInterfaceImpl,
|
|
510
|
+
creds,
|
|
511
|
+
deployArgs,
|
|
512
|
+
gencowJsonPath,
|
|
513
|
+
platformFetchImpl,
|
|
514
|
+
processEnv,
|
|
515
|
+
readFileSyncImpl,
|
|
516
|
+
setTimeoutImpl,
|
|
517
|
+
successImpl,
|
|
518
|
+
errorImpl,
|
|
519
|
+
infoImpl,
|
|
520
|
+
logImpl,
|
|
521
|
+
writeFileSyncImpl,
|
|
522
|
+
exitImpl,
|
|
523
|
+
prodAppId,
|
|
524
|
+
region: parsed.region,
|
|
525
|
+
prepareLocalSource: preparesBackendSource
|
|
526
|
+
? async () => {
|
|
527
|
+
preparedSource = await prepareBackendSourceForProdCreate(backendDeployRuntime);
|
|
528
|
+
return preparedSource !== null;
|
|
529
|
+
}
|
|
530
|
+
: null,
|
|
531
|
+
});
|
|
532
|
+
} finally {
|
|
533
|
+
// Prepared codegen output is temporary; drop it when no production app was created.
|
|
534
|
+
if (!prodTarget) backendDeployRuntime.cleanupPreparedDeploySource?.();
|
|
535
|
+
}
|
|
643
536
|
if (!prodTarget) return;
|
|
644
537
|
appId = prodTarget;
|
|
645
538
|
infoImpl(`Deploy target: ${CYAN}${appId}${RESET} (production)`);
|
|
@@ -653,6 +546,7 @@ export function createDeployCommand({
|
|
|
653
546
|
if (parsed.staticDeploy) {
|
|
654
547
|
await runStaticDeployFlow({
|
|
655
548
|
appId,
|
|
549
|
+
preparedSource,
|
|
656
550
|
backendRoot: backendProjectRoot,
|
|
657
551
|
creds,
|
|
658
552
|
displayName,
|
|
@@ -740,6 +634,7 @@ export function createDeployCommand({
|
|
|
740
634
|
|
|
741
635
|
await deployBackendPackage({
|
|
742
636
|
activePackageRuntime,
|
|
637
|
+
preparedSource,
|
|
743
638
|
appId,
|
|
744
639
|
creds,
|
|
745
640
|
displayName,
|
|
@@ -72,6 +72,9 @@ export function createScopedDeployPackageRuntime({
|
|
|
72
72
|
|
|
73
73
|
export async function deployBackendPackage({
|
|
74
74
|
activePackageRuntime,
|
|
75
|
+
// Set when the backend source was prepared before the production app was created.
|
|
76
|
+
// Its runtime must be the one that packages (it holds the prepared codegen output).
|
|
77
|
+
preparedSource = null,
|
|
75
78
|
appId,
|
|
76
79
|
creds,
|
|
77
80
|
displayName,
|
|
@@ -122,10 +125,12 @@ export async function deployBackendPackage({
|
|
|
122
125
|
});
|
|
123
126
|
if (!activeAppId) return null;
|
|
124
127
|
} else {
|
|
125
|
-
if (
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
128
|
+
if (!preparedSource) {
|
|
129
|
+
if ((await activePackageRuntime.preflightBackendCapability?.()) === false) return null;
|
|
130
|
+
}
|
|
131
|
+
const preparedGeneratorVersion = preparedSource
|
|
132
|
+
? preparedSource.generatorVersion
|
|
133
|
+
: await activePackageRuntime.prepareDeployProjectSource?.({ existingBundleMode });
|
|
129
134
|
packageResult = await activePackageRuntime.packageDeployProject({
|
|
130
135
|
forceDeploy,
|
|
131
136
|
existingBundleMode,
|
|
@@ -262,6 +267,7 @@ export async function deployBackendPackage({
|
|
|
262
267
|
|
|
263
268
|
export async function runStaticDeployFlow({
|
|
264
269
|
appId,
|
|
270
|
+
preparedSource = null,
|
|
265
271
|
backendRoot,
|
|
266
272
|
creds,
|
|
267
273
|
displayName,
|
|
@@ -332,11 +338,13 @@ export async function runStaticDeployFlow({
|
|
|
332
338
|
logImpl("");
|
|
333
339
|
|
|
334
340
|
const runtime =
|
|
335
|
-
|
|
341
|
+
preparedSource?.runtime ??
|
|
342
|
+
(backendRoot === frontendCwd
|
|
336
343
|
? deployPackageRuntime
|
|
337
|
-
: createDeployPackageRuntimeForCwd(() => backendRoot);
|
|
344
|
+
: createDeployPackageRuntimeForCwd(() => backendRoot));
|
|
338
345
|
const deployed = await deployBackendPackage({
|
|
339
346
|
activePackageRuntime: runtime,
|
|
347
|
+
preparedSource,
|
|
340
348
|
appId: activeAppId,
|
|
341
349
|
creds,
|
|
342
350
|
displayName,
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "gencow",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.259",
|
|
4
4
|
"description": "Gencow — AI Backend Engine",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"bin": {
|
|
@@ -41,10 +41,10 @@
|
|
|
41
41
|
"better-auth": "1.7.11",
|
|
42
42
|
"postgres": "^3.4.9",
|
|
43
43
|
"@gencow/client": "0.2.8",
|
|
44
|
+
"@gencow/shared": "0.0.0",
|
|
44
45
|
"@gencow/core": "0.1.47",
|
|
45
|
-
"@gencow/react": "0.2.8",
|
|
46
46
|
"@gencow/server": "0.1.3",
|
|
47
|
-
"@gencow/
|
|
47
|
+
"@gencow/react": "0.2.8"
|
|
48
48
|
},
|
|
49
49
|
"scripts": {
|
|
50
50
|
"build": "node scripts/bundle-core.mjs && node scripts/bundle-shared.mjs && node scripts/bundle-server.mjs",
|