onveloz 0.0.0-beta.15 → 0.0.0-beta.16
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.
- package/dist/index.mjs +474 -82
- package/package.json +1 -1
package/dist/index.mjs
CHANGED
|
@@ -29,7 +29,6 @@ const BUILD_TIMEOUT_MS = 600 * 1e3;
|
|
|
29
29
|
const DEPLOY_TIMEOUT_MS = 300 * 1e3;
|
|
30
30
|
const DATABASE_PROVISION_TIMEOUT_MS = 300 * 1e3;
|
|
31
31
|
const DATABASE_WAITING_ON_PROVIDER_AFTER_MS = 60 * 1e3;
|
|
32
|
-
const DATABASE_HEALTH_POLL_INTERVAL_MS = 60 * 1e3;
|
|
33
32
|
const DATABASE_ENGINES = [
|
|
34
33
|
"postgresql",
|
|
35
34
|
"mysql",
|
|
@@ -52,7 +51,6 @@ const DATABASE_ENGINE_DEFAULTS = {
|
|
|
52
51
|
defaultVersion: "7"
|
|
53
52
|
}
|
|
54
53
|
};
|
|
55
|
-
const DEFAULT_SLEEP_CHECK_INTERVAL_MS = 300 * 1e3;
|
|
56
54
|
|
|
57
55
|
//#endregion
|
|
58
56
|
//#region ../../packages/config/veloz-config.ts
|
|
@@ -111,6 +109,20 @@ const VolumeConfigSchema = z$1.object({
|
|
|
111
109
|
].some((p) => value === p || value.startsWith(p + "/")), "Caminho de montagem não permitido por segurança"),
|
|
112
110
|
sizeGb: z$1.number().int().min(10).max(100).optional().default(10)
|
|
113
111
|
});
|
|
112
|
+
const DatabaseResourcesSchema = z$1.object({
|
|
113
|
+
cpu: z$1.string().regex(/^[0-9]+(\.[0-9]+)?|[0-9]+m$/).default("500m").optional(),
|
|
114
|
+
memory: z$1.string().regex(/^[0-9]+(Mi|Gi)$/).default("512Mi").optional()
|
|
115
|
+
});
|
|
116
|
+
const PoolerConfigSchema = z$1.object({
|
|
117
|
+
enabled: z$1.boolean().default(false),
|
|
118
|
+
poolMode: z$1.enum([
|
|
119
|
+
"transaction",
|
|
120
|
+
"session",
|
|
121
|
+
"statement"
|
|
122
|
+
]).default("transaction").optional(),
|
|
123
|
+
defaultPoolSize: z$1.number().int().min(1).max(200).default(20).optional(),
|
|
124
|
+
maxClientConn: z$1.number().int().min(1).max(1e4).default(100).optional()
|
|
125
|
+
});
|
|
114
126
|
const DatabaseConfigSchema = z$1.object({
|
|
115
127
|
id: z$1.string().optional(),
|
|
116
128
|
name: z$1.string().optional(),
|
|
@@ -121,6 +133,8 @@ const DatabaseConfigSchema = z$1.object({
|
|
|
121
133
|
]),
|
|
122
134
|
version: z$1.string().optional(),
|
|
123
135
|
storage: z$1.string().regex(/^[0-9]+(Gi)$/).default("10Gi").optional(),
|
|
136
|
+
resources: DatabaseResourcesSchema.optional(),
|
|
137
|
+
pooler: PoolerConfigSchema.optional(),
|
|
124
138
|
fromTemplate: z$1.string().optional()
|
|
125
139
|
});
|
|
126
140
|
const ServiceConfigSchema = z$1.object({
|
|
@@ -542,7 +556,9 @@ async function pollForToken(authClient, deviceCode, interval) {
|
|
|
542
556
|
let pollingInterval = interval;
|
|
543
557
|
const maxAttempts = Math.ceil(300 / pollingInterval);
|
|
544
558
|
for (let i = 0; i < maxAttempts; i++) {
|
|
545
|
-
await new Promise((r) =>
|
|
559
|
+
await new Promise((r) => {
|
|
560
|
+
setTimeout(r, pollingInterval * 1e3);
|
|
561
|
+
});
|
|
546
562
|
try {
|
|
547
563
|
const { data, error } = await authClient.device.token({
|
|
548
564
|
grant_type: "urn:ietf:params:oauth:grant-type:device_code",
|
|
@@ -838,6 +854,7 @@ async function resolveService(serviceFlag) {
|
|
|
838
854
|
}
|
|
839
855
|
async function resolveServiceId(serviceFlag) {
|
|
840
856
|
const { service } = await resolveService(serviceFlag);
|
|
857
|
+
if (!service.id) throw new Error(`Serviço "${service.name}" não possui ID. Execute 'veloz deploy' para vincular o serviço.`);
|
|
841
858
|
return service.id;
|
|
842
859
|
}
|
|
843
860
|
function resolveAllServices(serviceFlag) {
|
|
@@ -1847,7 +1864,10 @@ dbGroup.command("create", {
|
|
|
1847
1864
|
name: z.string().optional().describe("Nome do banco de dados"),
|
|
1848
1865
|
engine: z.string().optional().describe("Engine (postgresql, mysql, redis)"),
|
|
1849
1866
|
engineVersion: z.string().optional().describe("Versão do engine"),
|
|
1850
|
-
storage: z.string().optional().describe("Armazenamento (ex: 10Gi, 20Gi)")
|
|
1867
|
+
storage: z.string().optional().describe("Armazenamento (ex: 10Gi, 20Gi)"),
|
|
1868
|
+
cpu: z.string().optional().describe("Limite de CPU (ex: 500m, 1)"),
|
|
1869
|
+
memory: z.string().optional().describe("Limite de memória (ex: 512Mi, 1Gi)"),
|
|
1870
|
+
pooler: z.boolean().optional().describe("Habilitar PgBouncer (apenas PostgreSQL)")
|
|
1851
1871
|
}),
|
|
1852
1872
|
async run(c) {
|
|
1853
1873
|
const projectId = getProjectId$1();
|
|
@@ -1896,7 +1916,10 @@ dbGroup.command("create", {
|
|
|
1896
1916
|
name,
|
|
1897
1917
|
engine: validEngine,
|
|
1898
1918
|
engineVersion: version,
|
|
1899
|
-
storage
|
|
1919
|
+
storage,
|
|
1920
|
+
cpuLimit: c.options.cpu,
|
|
1921
|
+
memoryLimit: c.options.memory,
|
|
1922
|
+
poolerEnabled: c.options.pooler
|
|
1900
1923
|
})
|
|
1901
1924
|
});
|
|
1902
1925
|
success(`Banco de dados ${chalk.bold(db.name)} criado! Provisionando...`);
|
|
@@ -1908,7 +1931,12 @@ dbGroup.command("create", {
|
|
|
1908
1931
|
id: db.id,
|
|
1909
1932
|
engine: validEngine,
|
|
1910
1933
|
version: version ?? void 0,
|
|
1911
|
-
storage: storage ?? void 0
|
|
1934
|
+
storage: storage ?? void 0,
|
|
1935
|
+
...c.options.cpu || c.options.memory ? { resources: {
|
|
1936
|
+
...c.options.cpu && { cpu: c.options.cpu },
|
|
1937
|
+
...c.options.memory && { memory: c.options.memory }
|
|
1938
|
+
} } : {},
|
|
1939
|
+
...c.options.pooler ? { pooler: { enabled: true } } : {}
|
|
1912
1940
|
};
|
|
1913
1941
|
config.databases = updatedDatabases;
|
|
1914
1942
|
config.updated = (/* @__PURE__ */ new Date()).toISOString();
|
|
@@ -2453,7 +2481,7 @@ function analyzeRepo(files) {
|
|
|
2453
2481
|
name: appName,
|
|
2454
2482
|
path: appPath,
|
|
2455
2483
|
framework: appFramework,
|
|
2456
|
-
usesNodeFs: sourceEntries.some(([
|
|
2484
|
+
usesNodeFs: sourceEntries.some(([srcPath, fileContent]) => srcPath.startsWith(`${appPath}/`) && usesNodeFs(fileContent))
|
|
2457
2485
|
});
|
|
2458
2486
|
}
|
|
2459
2487
|
return {
|
|
@@ -2610,10 +2638,14 @@ async function withRetry(fn, maxRetries = 3) {
|
|
|
2610
2638
|
const rateLimit = isRateLimitError(error);
|
|
2611
2639
|
if (rateLimit) {
|
|
2612
2640
|
const waitMs = Math.min(rateLimit.retryAfterMs, 3e4);
|
|
2613
|
-
await new Promise((r) =>
|
|
2641
|
+
await new Promise((r) => {
|
|
2642
|
+
setTimeout(r, waitMs);
|
|
2643
|
+
});
|
|
2614
2644
|
} else {
|
|
2615
2645
|
const delay = Math.min(1e3 * Math.pow(2, attempt), 1e4);
|
|
2616
|
-
await new Promise((r) =>
|
|
2646
|
+
await new Promise((r) => {
|
|
2647
|
+
setTimeout(r, delay);
|
|
2648
|
+
});
|
|
2617
2649
|
}
|
|
2618
2650
|
}
|
|
2619
2651
|
throw new Error("Max retries exceeded");
|
|
@@ -2623,7 +2655,7 @@ async function withRetry(fn, maxRetries = 3) {
|
|
|
2623
2655
|
//#region src/lib/deploy-constants.ts
|
|
2624
2656
|
const statusLabels = {
|
|
2625
2657
|
QUEUED: "Na fila",
|
|
2626
|
-
BUILDING: "
|
|
2658
|
+
BUILDING: "Compilando",
|
|
2627
2659
|
BUILD_FAILED: "Falha na construção",
|
|
2628
2660
|
DEPLOYING: "Implantando",
|
|
2629
2661
|
LIVE: "Ativo",
|
|
@@ -2724,8 +2756,7 @@ function renderProgress(progressMap, prevLineCount) {
|
|
|
2724
2756
|
if (nonEmptyLines.length > 0) {
|
|
2725
2757
|
const tail = nonEmptyLines.slice(-3);
|
|
2726
2758
|
for (const line of tail) {
|
|
2727
|
-
|
|
2728
|
-
process.stdout.write(` ${chalk.dim(truncated)}\n`);
|
|
2759
|
+
process.stdout.write(` ${chalk.dim(line)}\n`);
|
|
2729
2760
|
lineCount++;
|
|
2730
2761
|
}
|
|
2731
2762
|
} else if (progress.status === "BUILDING") {
|
|
@@ -2811,7 +2842,9 @@ async function deployServicesInParallel(services) {
|
|
|
2811
2842
|
if (isTTY) lineCount = renderProgress(progressMap, lineCount);
|
|
2812
2843
|
const streamPromises = activeDeployments.map(async ({ service, deploymentId }) => {
|
|
2813
2844
|
try {
|
|
2814
|
-
await new Promise((resolve$1) =>
|
|
2845
|
+
await new Promise((resolve$1) => {
|
|
2846
|
+
setTimeout(resolve$1, 1e3);
|
|
2847
|
+
});
|
|
2815
2848
|
const stream = await client.logs.streamBuildLogs({ deploymentId });
|
|
2816
2849
|
for await (const event of stream) {
|
|
2817
2850
|
const progress = progressMap.get(service.serviceId);
|
|
@@ -2960,6 +2993,310 @@ function getFailureHints(status) {
|
|
|
2960
2993
|
default: return ["Execute 'veloz logs -f' para mais detalhes."];
|
|
2961
2994
|
}
|
|
2962
2995
|
}
|
|
2996
|
+
/** Raw BuildKit line: `#N content` */
|
|
2997
|
+
const BUILDKIT_PREFIX_RE = /^#(\d+)\s+(.*)/;
|
|
2998
|
+
/** Docker build step: `[stage step/total] COMMAND` */
|
|
2999
|
+
const DOCKER_STEP_RE = /^\[(\S+)\s+(\d+)\/(\d+)\]\s+(.+)$/;
|
|
3000
|
+
/** Platform timestamp: `[2026-03-23T02:37:13.795Z] message` */
|
|
3001
|
+
const TIMESTAMP_RE = /^\[(\d{4}-\d{2}-\d{2}T[\d:.]+Z)\]\s+(.+)$/;
|
|
3002
|
+
/** DONE marker: `DONE 4.2s` */
|
|
3003
|
+
const DONE_RE = /^DONE\s+([\d.]+s?)$/;
|
|
3004
|
+
/** Timing prefix: `0.543 actual content` */
|
|
3005
|
+
const TIMING_RE = /^(\d+\.\d+)\s+(.*)/;
|
|
3006
|
+
/** Infrastructure / deploy orchestration messages to hide from user output */
|
|
3007
|
+
const HIDDEN_MESSAGES = [
|
|
3008
|
+
/^Ensuring namespace\b/i,
|
|
3009
|
+
/^Updating deployment\b/i,
|
|
3010
|
+
/^Syncing ingress\b/i,
|
|
3011
|
+
/^Waiting for rollout\b/i,
|
|
3012
|
+
/^Deploy complete\b/i,
|
|
3013
|
+
/^→\s+https?:\/\//,
|
|
3014
|
+
/\bnamespace\b.*\bsvc\.cluster\.local\b/i,
|
|
3015
|
+
/\bpod\b/i,
|
|
3016
|
+
/\bkubernetes\b/i,
|
|
3017
|
+
/\bk8s\b/i,
|
|
3018
|
+
/\brollout\b/i
|
|
3019
|
+
];
|
|
3020
|
+
function isHiddenMessage(text) {
|
|
3021
|
+
return HIDDEN_MESSAGES.some((p) => p.test(text));
|
|
3022
|
+
}
|
|
3023
|
+
/**
|
|
3024
|
+
* Try to extract a human-readable message from a structured JSON log line.
|
|
3025
|
+
* Returns null if the line is not JSON or has no message.
|
|
3026
|
+
*/
|
|
3027
|
+
function parseJsonLog(text) {
|
|
3028
|
+
if (!text.startsWith("{")) return null;
|
|
3029
|
+
try {
|
|
3030
|
+
const parsed = JSON.parse(text);
|
|
3031
|
+
if (typeof parsed === "object" && parsed !== null && typeof parsed.msg === "string") return parsed.msg;
|
|
3032
|
+
} catch {}
|
|
3033
|
+
return null;
|
|
3034
|
+
}
|
|
3035
|
+
/** Clean a display line — parse JSON, filter infra */
|
|
3036
|
+
function cleanDisplayLine(text) {
|
|
3037
|
+
if (isHiddenMessage(text)) return null;
|
|
3038
|
+
const jsonMsg = parseJsonLog(text);
|
|
3039
|
+
if (jsonMsg !== null) {
|
|
3040
|
+
if (isHiddenMessage(jsonMsg)) return null;
|
|
3041
|
+
return jsonMsg;
|
|
3042
|
+
}
|
|
3043
|
+
return text;
|
|
3044
|
+
}
|
|
3045
|
+
function parseBuildLine(raw) {
|
|
3046
|
+
const trimmed = raw.trim();
|
|
3047
|
+
const bkMatch = BUILDKIT_PREFIX_RE.exec(trimmed);
|
|
3048
|
+
if (bkMatch) {
|
|
3049
|
+
const stepNum = parseInt(bkMatch[1], 10);
|
|
3050
|
+
const content = bkMatch[2];
|
|
3051
|
+
if (content.trim() === "CACHED") return {
|
|
3052
|
+
kind: "cached",
|
|
3053
|
+
stepNum
|
|
3054
|
+
};
|
|
3055
|
+
const doneMatch = DONE_RE.exec(content);
|
|
3056
|
+
if (doneMatch) return {
|
|
3057
|
+
kind: "done",
|
|
3058
|
+
stepNum,
|
|
3059
|
+
duration: doneMatch[1]
|
|
3060
|
+
};
|
|
3061
|
+
const stepMatch = DOCKER_STEP_RE.exec(content);
|
|
3062
|
+
if (stepMatch) return {
|
|
3063
|
+
kind: "step",
|
|
3064
|
+
stepNum,
|
|
3065
|
+
stage: stepMatch[1],
|
|
3066
|
+
step: parseInt(stepMatch[2], 10),
|
|
3067
|
+
total: parseInt(stepMatch[3], 10),
|
|
3068
|
+
command: stepMatch[4]
|
|
3069
|
+
};
|
|
3070
|
+
const timingMatch = TIMING_RE.exec(content);
|
|
3071
|
+
if (timingMatch) {
|
|
3072
|
+
const text = timingMatch[2];
|
|
3073
|
+
if (text.trim()) return {
|
|
3074
|
+
kind: "output",
|
|
3075
|
+
stepNum,
|
|
3076
|
+
text
|
|
3077
|
+
};
|
|
3078
|
+
return {
|
|
3079
|
+
kind: "other",
|
|
3080
|
+
text: ""
|
|
3081
|
+
};
|
|
3082
|
+
}
|
|
3083
|
+
if (content.trim()) return {
|
|
3084
|
+
kind: "output",
|
|
3085
|
+
stepNum,
|
|
3086
|
+
text: content
|
|
3087
|
+
};
|
|
3088
|
+
return {
|
|
3089
|
+
kind: "other",
|
|
3090
|
+
text: ""
|
|
3091
|
+
};
|
|
3092
|
+
}
|
|
3093
|
+
const tsMatch = TIMESTAMP_RE.exec(trimmed);
|
|
3094
|
+
if (tsMatch) return {
|
|
3095
|
+
kind: "platform",
|
|
3096
|
+
message: tsMatch[2]
|
|
3097
|
+
};
|
|
3098
|
+
return {
|
|
3099
|
+
kind: "other",
|
|
3100
|
+
text: trimmed
|
|
3101
|
+
};
|
|
3102
|
+
}
|
|
3103
|
+
const BAR_WIDTH = 20;
|
|
3104
|
+
const BRAND = chalk.rgb(255, 77, 0);
|
|
3105
|
+
function renderProgressBar(filled, total, allCached, allDone) {
|
|
3106
|
+
const ratio = total > 0 ? filled / total : 0;
|
|
3107
|
+
const filledChars = Math.round(ratio * BAR_WIDTH);
|
|
3108
|
+
const emptyChars = BAR_WIDTH - filledChars;
|
|
3109
|
+
const counter = `${filled}/${total}`;
|
|
3110
|
+
if (allCached) return `${BRAND("━".repeat(BAR_WIDTH))} ${BRAND(`${counter} ◆ cached`)}`;
|
|
3111
|
+
if (allDone) return `${chalk.green("━".repeat(BAR_WIDTH))} ${chalk.green(`${counter} ✓`)}`;
|
|
3112
|
+
return chalk.cyan("━".repeat(filledChars)) + chalk.dim("─".repeat(emptyChars)) + ` ${chalk.dim(counter)}`;
|
|
3113
|
+
}
|
|
3114
|
+
const SPINNER_FRAMES = [
|
|
3115
|
+
"⠋",
|
|
3116
|
+
"⠙",
|
|
3117
|
+
"⠹",
|
|
3118
|
+
"⠸",
|
|
3119
|
+
"⠼",
|
|
3120
|
+
"⠴",
|
|
3121
|
+
"⠦",
|
|
3122
|
+
"⠧",
|
|
3123
|
+
"⠇",
|
|
3124
|
+
"⠏"
|
|
3125
|
+
];
|
|
3126
|
+
/**
|
|
3127
|
+
* Dashboard-style renderer for compact TTY mode.
|
|
3128
|
+
* Redraws the entire build progress block on each update.
|
|
3129
|
+
* Includes an integrated spinner that animates via setInterval.
|
|
3130
|
+
*/
|
|
3131
|
+
var BuildProgressRenderer = class {
|
|
3132
|
+
stages = /* @__PURE__ */ new Map();
|
|
3133
|
+
stageOrder = [];
|
|
3134
|
+
platformMessages = [];
|
|
3135
|
+
renderLineCount = 0;
|
|
3136
|
+
phase = "waiting";
|
|
3137
|
+
runtimeHeaderPrinted = false;
|
|
3138
|
+
serviceName;
|
|
3139
|
+
spinnerFrame = 0;
|
|
3140
|
+
spinnerInterval = null;
|
|
3141
|
+
spinnerText = "Aguardando início do build...";
|
|
3142
|
+
/** External ora spinner reference — used to pause/resume during runtime log output */
|
|
3143
|
+
externalSpinner = null;
|
|
3144
|
+
constructor(serviceName) {
|
|
3145
|
+
this.serviceName = serviceName;
|
|
3146
|
+
this.startSpinner();
|
|
3147
|
+
}
|
|
3148
|
+
setExternalSpinner(spinner$1) {
|
|
3149
|
+
this.externalSpinner = spinner$1;
|
|
3150
|
+
}
|
|
3151
|
+
startSpinner() {
|
|
3152
|
+
if (this.spinnerInterval) return;
|
|
3153
|
+
this.spinnerInterval = setInterval(() => {
|
|
3154
|
+
this.spinnerFrame = (this.spinnerFrame + 1) % SPINNER_FRAMES.length;
|
|
3155
|
+
this.render();
|
|
3156
|
+
}, 80);
|
|
3157
|
+
}
|
|
3158
|
+
stopSpinner() {
|
|
3159
|
+
if (this.spinnerInterval) {
|
|
3160
|
+
clearInterval(this.spinnerInterval);
|
|
3161
|
+
this.spinnerInterval = null;
|
|
3162
|
+
}
|
|
3163
|
+
}
|
|
3164
|
+
setBuilding() {
|
|
3165
|
+
this.phase = "building";
|
|
3166
|
+
this.spinnerText = "Compilando...";
|
|
3167
|
+
}
|
|
3168
|
+
switchToRuntime() {
|
|
3169
|
+
if (this.phase === "runtime") return;
|
|
3170
|
+
for (const stage of this.stages.values()) if (stage.steps.size > 0 && stage.steps.size < stage.total) stage.total = stage.steps.size;
|
|
3171
|
+
this.stopSpinner();
|
|
3172
|
+
this.render();
|
|
3173
|
+
this.renderLineCount = 0;
|
|
3174
|
+
this.phase = "runtime";
|
|
3175
|
+
}
|
|
3176
|
+
processLine(raw) {
|
|
3177
|
+
const trimmed = raw.trim();
|
|
3178
|
+
if (!trimmed) return;
|
|
3179
|
+
if (this.phase === "runtime") {
|
|
3180
|
+
this.printRuntimeLine(trimmed);
|
|
3181
|
+
return;
|
|
3182
|
+
}
|
|
3183
|
+
if (this.phase === "waiting") {
|
|
3184
|
+
this.phase = "building";
|
|
3185
|
+
this.spinnerText = "Compilando...";
|
|
3186
|
+
}
|
|
3187
|
+
const parsed = parseBuildLine(trimmed);
|
|
3188
|
+
switch (parsed.kind) {
|
|
3189
|
+
case "step": {
|
|
3190
|
+
let stage = this.stages.get(parsed.stage);
|
|
3191
|
+
if (!stage) {
|
|
3192
|
+
stage = {
|
|
3193
|
+
name: parsed.stage,
|
|
3194
|
+
total: parsed.total,
|
|
3195
|
+
steps: /* @__PURE__ */ new Map(),
|
|
3196
|
+
stepNumMap: /* @__PURE__ */ new Map(),
|
|
3197
|
+
cachedStepNums: /* @__PURE__ */ new Set(),
|
|
3198
|
+
doneStepNums: /* @__PURE__ */ new Set()
|
|
3199
|
+
};
|
|
3200
|
+
this.stages.set(parsed.stage, stage);
|
|
3201
|
+
this.stageOrder.push(parsed.stage);
|
|
3202
|
+
}
|
|
3203
|
+
stage.steps.set(parsed.step, parsed.command);
|
|
3204
|
+
stage.stepNumMap.set(parsed.stepNum, parsed.step);
|
|
3205
|
+
stage.total = Math.max(stage.total, parsed.total);
|
|
3206
|
+
break;
|
|
3207
|
+
}
|
|
3208
|
+
case "cached":
|
|
3209
|
+
for (const stage of this.stages.values()) if (stage.stepNumMap.has(parsed.stepNum)) {
|
|
3210
|
+
stage.cachedStepNums.add(parsed.stepNum);
|
|
3211
|
+
break;
|
|
3212
|
+
}
|
|
3213
|
+
break;
|
|
3214
|
+
case "done":
|
|
3215
|
+
for (const stage of this.stages.values()) if (stage.stepNumMap.has(parsed.stepNum)) {
|
|
3216
|
+
stage.doneStepNums.add(parsed.stepNum);
|
|
3217
|
+
break;
|
|
3218
|
+
}
|
|
3219
|
+
break;
|
|
3220
|
+
case "platform": {
|
|
3221
|
+
const cleaned = cleanDisplayLine(parsed.message);
|
|
3222
|
+
if (cleaned) this.platformMessages.push(cleaned);
|
|
3223
|
+
break;
|
|
3224
|
+
}
|
|
3225
|
+
case "output":
|
|
3226
|
+
case "other": break;
|
|
3227
|
+
}
|
|
3228
|
+
this.render();
|
|
3229
|
+
}
|
|
3230
|
+
printRuntimeLine(line) {
|
|
3231
|
+
const parsed = parseBuildLine(line);
|
|
3232
|
+
let text = null;
|
|
3233
|
+
if (parsed.kind === "platform") text = parsed.message;
|
|
3234
|
+
else if (parsed.kind === "other" && parsed.text) text = parsed.text;
|
|
3235
|
+
else if (parsed.kind === "output") text = parsed.text;
|
|
3236
|
+
if (!text) return;
|
|
3237
|
+
if (isHiddenMessage(text)) return;
|
|
3238
|
+
if (this.externalSpinner) this.externalSpinner.clear();
|
|
3239
|
+
if (!this.runtimeHeaderPrinted) {
|
|
3240
|
+
this.runtimeHeaderPrinted = true;
|
|
3241
|
+
console.log(chalk.cyan.bold(`\n RUNTIME`));
|
|
3242
|
+
}
|
|
3243
|
+
console.log(` ${text}`);
|
|
3244
|
+
if (this.externalSpinner) this.externalSpinner.render();
|
|
3245
|
+
}
|
|
3246
|
+
isStageComplete(stage) {
|
|
3247
|
+
return stage.steps.size >= stage.total;
|
|
3248
|
+
}
|
|
3249
|
+
render() {
|
|
3250
|
+
if (this.renderLineCount > 0) process.stdout.write(`\x1b[${this.renderLineCount}A\x1b[J`);
|
|
3251
|
+
let lines = 0;
|
|
3252
|
+
const label = this.serviceName ? `BUILD ${chalk.dim(`(${this.serviceName})`)}` : "BUILD";
|
|
3253
|
+
process.stdout.write(`${chalk.cyan.bold(` ${label}`)}\n`);
|
|
3254
|
+
lines++;
|
|
3255
|
+
for (const msg of this.platformMessages) {
|
|
3256
|
+
process.stdout.write(` ${msg}\n`);
|
|
3257
|
+
lines++;
|
|
3258
|
+
}
|
|
3259
|
+
if (this.stageOrder.length > 0) {
|
|
3260
|
+
process.stdout.write("\n");
|
|
3261
|
+
lines++;
|
|
3262
|
+
}
|
|
3263
|
+
const maxNameLen = Math.max(...this.stageOrder.map((n) => n.length), 4);
|
|
3264
|
+
for (let i = 0; i < this.stageOrder.length; i++) {
|
|
3265
|
+
const stageName = this.stageOrder[i];
|
|
3266
|
+
const stage = this.stages.get(stageName);
|
|
3267
|
+
const complete = this.isStageComplete(stage);
|
|
3268
|
+
const allCached = complete && stage.cachedStepNums.size === stage.steps.size;
|
|
3269
|
+
const allDone = complete && !allCached;
|
|
3270
|
+
const bar = renderProgressBar(stage.steps.size, stage.total, allCached, allDone);
|
|
3271
|
+
const paddedName = chalk.bold(stageName.padEnd(maxNameLen));
|
|
3272
|
+
process.stdout.write(` ${paddedName} ${bar}\n`);
|
|
3273
|
+
lines++;
|
|
3274
|
+
const sortedSteps = [...stage.steps.entries()].sort((a, b) => a[0] - b[0]);
|
|
3275
|
+
for (const [stepNum, command] of sortedSteps) {
|
|
3276
|
+
let stepStatus = "";
|
|
3277
|
+
for (const [bkNum, dockerStep] of stage.stepNumMap.entries()) if (dockerStep === stepNum) {
|
|
3278
|
+
if (stage.cachedStepNums.has(bkNum)) stepStatus = ` ${BRAND("◆")}`;
|
|
3279
|
+
else if (stage.doneStepNums.has(bkNum)) stepStatus = ` ${chalk.green("✓")}`;
|
|
3280
|
+
break;
|
|
3281
|
+
}
|
|
3282
|
+
process.stdout.write(` ${command}${stepStatus}\n`);
|
|
3283
|
+
lines++;
|
|
3284
|
+
}
|
|
3285
|
+
if (i < this.stageOrder.length - 1) {
|
|
3286
|
+
process.stdout.write("\n");
|
|
3287
|
+
lines++;
|
|
3288
|
+
}
|
|
3289
|
+
}
|
|
3290
|
+
if (this.spinnerInterval) {
|
|
3291
|
+
if (!(this.stageOrder.length > 0 && this.stageOrder.every((name) => this.isStageComplete(this.stages.get(name))))) {
|
|
3292
|
+
const frame = SPINNER_FRAMES[this.spinnerFrame];
|
|
3293
|
+
process.stdout.write(`\n ${chalk.cyan(frame)} ${this.spinnerText}\n`);
|
|
3294
|
+
lines += 2;
|
|
3295
|
+
}
|
|
3296
|
+
}
|
|
3297
|
+
this.renderLineCount = lines;
|
|
3298
|
+
}
|
|
3299
|
+
};
|
|
2963
3300
|
async function streamDeploymentLogs(deploymentId, serviceId, serviceName) {
|
|
2964
3301
|
const client = await getClient();
|
|
2965
3302
|
const isVerbose = process.env.VELOZ_VERBOSE === "true";
|
|
@@ -2968,12 +3305,10 @@ async function streamDeploymentLogs(deploymentId, serviceId, serviceName) {
|
|
|
2968
3305
|
const isGHA = !mcp && process.env.GITHUB_ACTIONS === "true";
|
|
2969
3306
|
const allLogLines = [];
|
|
2970
3307
|
let buildSpinner = null;
|
|
3308
|
+
let renderer = null;
|
|
2971
3309
|
if (mcp) log(serviceName ? `[deploy] Build: ${serviceName}` : "[deploy] Build iniciando...");
|
|
2972
3310
|
else if (isGHA) startGroup(serviceName ? `Build: ${serviceName}` : "Build");
|
|
2973
|
-
else if (isTTY && !isVerbose)
|
|
2974
|
-
text: "Aguardando início do build...",
|
|
2975
|
-
color: "cyan"
|
|
2976
|
-
}).start();
|
|
3311
|
+
else if (isTTY && !isVerbose) renderer = new BuildProgressRenderer(serviceName);
|
|
2977
3312
|
else if (isTTY) {
|
|
2978
3313
|
const header = serviceName ? `Build: ${chalk.bold(serviceName)}` : "Build";
|
|
2979
3314
|
console.log(chalk.cyan(`\n${header}`));
|
|
@@ -2989,19 +3324,24 @@ async function streamDeploymentLogs(deploymentId, serviceId, serviceName) {
|
|
|
2989
3324
|
const label = statusLabels[event.content] ?? event.content;
|
|
2990
3325
|
finalStatus = event.content;
|
|
2991
3326
|
if (mcp) log(`[deploy] Status: ${label}`);
|
|
2992
|
-
else if (
|
|
2993
|
-
if (
|
|
2994
|
-
|
|
2995
|
-
|
|
2996
|
-
|
|
2997
|
-
|
|
2998
|
-
|
|
2999
|
-
|
|
3000
|
-
|
|
3001
|
-
|
|
3327
|
+
else if (renderer) {
|
|
3328
|
+
if (event.content === "BUILDING") renderer.setBuilding();
|
|
3329
|
+
else if (event.content === "DEPLOYING") {
|
|
3330
|
+
renderer.switchToRuntime();
|
|
3331
|
+
buildSpinner = ora({
|
|
3332
|
+
text: "Publicando...",
|
|
3333
|
+
color: "cyan"
|
|
3334
|
+
}).start();
|
|
3335
|
+
renderer.setExternalSpinner(buildSpinner);
|
|
3336
|
+
} else if (event.content === "LIVE") {
|
|
3337
|
+
if (buildSpinner) {
|
|
3338
|
+
renderer.setExternalSpinner(null);
|
|
3002
3339
|
buildSpinner.succeed("Publicado");
|
|
3003
3340
|
buildSpinner = null;
|
|
3004
|
-
}
|
|
3341
|
+
}
|
|
3342
|
+
} else if (TERMINAL_STATUSES.has(event.content) && event.content !== "LIVE") {
|
|
3343
|
+
if (buildSpinner) {
|
|
3344
|
+
renderer.setExternalSpinner(null);
|
|
3005
3345
|
buildSpinner.fail(label);
|
|
3006
3346
|
buildSpinner = null;
|
|
3007
3347
|
}
|
|
@@ -3015,16 +3355,11 @@ async function streamDeploymentLogs(deploymentId, serviceId, serviceName) {
|
|
|
3015
3355
|
allLogLines.push(...lines);
|
|
3016
3356
|
if (mcp) {
|
|
3017
3357
|
for (const line of lines) if (line.trim()) log(`[build] ${line.trim()}`);
|
|
3018
|
-
} else if (
|
|
3019
|
-
const trimmed = line.trim();
|
|
3020
|
-
if (trimmed) {
|
|
3021
|
-
const display = trimmed.length > 60 ? trimmed.substring(0, 57) + "..." : trimmed;
|
|
3022
|
-
if (buildSpinner) buildSpinner.text = display;
|
|
3023
|
-
}
|
|
3024
|
-
}
|
|
3358
|
+
} else if (renderer) for (const line of lines) renderer.processLine(line);
|
|
3025
3359
|
else for (const line of lines) if (line.trim()) process.stdout.write(` ${line}\n`);
|
|
3026
3360
|
}
|
|
3027
3361
|
} catch {
|
|
3362
|
+
if (renderer) renderer.stopSpinner();
|
|
3028
3363
|
if (buildSpinner) {
|
|
3029
3364
|
buildSpinner.stop();
|
|
3030
3365
|
buildSpinner = null;
|
|
@@ -3038,6 +3373,7 @@ async function streamDeploymentLogs(deploymentId, serviceId, serviceName) {
|
|
|
3038
3373
|
} catch {}
|
|
3039
3374
|
}
|
|
3040
3375
|
if (isGHA) endGroup();
|
|
3376
|
+
if (renderer) renderer.stopSpinner();
|
|
3041
3377
|
const urls = finalStatus === "LIVE" ? await fetchDeployUrls(client, serviceId) : [];
|
|
3042
3378
|
if (finalStatus === "LIVE") {
|
|
3043
3379
|
if (buildSpinner) {
|
|
@@ -3056,7 +3392,7 @@ async function streamDeploymentLogs(deploymentId, serviceId, serviceName) {
|
|
|
3056
3392
|
if (mcp) {
|
|
3057
3393
|
log(`✗ Deploy finalizou: ${label}`);
|
|
3058
3394
|
for (const hint of hints) log(` → ${hint}`);
|
|
3059
|
-
} else if (isTTY && allLogLines.length > 0) {
|
|
3395
|
+
} else if (isTTY && !renderer && allLogLines.length > 0) {
|
|
3060
3396
|
console.log();
|
|
3061
3397
|
console.log(chalk.red(` ${"─".repeat(50)}`));
|
|
3062
3398
|
console.log(chalk.red.bold(" Logs de build:"));
|
|
@@ -3150,7 +3486,7 @@ const LOGO_LINES = [
|
|
|
3150
3486
|
];
|
|
3151
3487
|
const BRAND_COLOR = "#FF4D00";
|
|
3152
3488
|
function getVersion() {
|
|
3153
|
-
return "0.0.0-beta.
|
|
3489
|
+
return "0.0.0-beta.16";
|
|
3154
3490
|
}
|
|
3155
3491
|
function printBanner(subtitle) {
|
|
3156
3492
|
const version = getVersion();
|
|
@@ -3622,7 +3958,7 @@ async function autoUpdate() {
|
|
|
3622
3958
|
if (process.env.VELOZ_MCP === "true") return;
|
|
3623
3959
|
const pm = detectPackageManager();
|
|
3624
3960
|
if (!pm) return;
|
|
3625
|
-
const currentVersion = "0.0.0-beta.
|
|
3961
|
+
const currentVersion = "0.0.0-beta.16";
|
|
3626
3962
|
const latestVersion = await fetchLatestVersion();
|
|
3627
3963
|
if (!latestVersion || latestVersion === currentVersion) return;
|
|
3628
3964
|
const installCmd = getInstallCommand(pm, latestVersion);
|
|
@@ -3698,7 +4034,13 @@ async function provisionDatabases(config, opts) {
|
|
|
3698
4034
|
name: key,
|
|
3699
4035
|
engine: dbConfig.engine,
|
|
3700
4036
|
engineVersion: dbConfig.version,
|
|
3701
|
-
storage: dbConfig.storage
|
|
4037
|
+
storage: dbConfig.storage,
|
|
4038
|
+
cpuLimit: dbConfig.resources?.cpu,
|
|
4039
|
+
memoryLimit: dbConfig.resources?.memory,
|
|
4040
|
+
poolerEnabled: dbConfig.pooler?.enabled,
|
|
4041
|
+
poolerPoolMode: dbConfig.pooler?.poolMode,
|
|
4042
|
+
poolerDefaultPoolSize: dbConfig.pooler?.defaultPoolSize,
|
|
4043
|
+
poolerMaxClientConn: dbConfig.pooler?.maxClientConn
|
|
3702
4044
|
})
|
|
3703
4045
|
});
|
|
3704
4046
|
success(`Banco de dados "${key}" criado (provisionando...).`);
|
|
@@ -3733,9 +4075,13 @@ function getDatabaseUrlHints(config) {
|
|
|
3733
4075
|
const databases = config.databases ?? {};
|
|
3734
4076
|
const entries = Object.entries(databases);
|
|
3735
4077
|
if (entries.length === 0) return [];
|
|
3736
|
-
|
|
3737
|
-
|
|
3738
|
-
|
|
4078
|
+
const hints = [];
|
|
4079
|
+
for (const [key, db] of entries) if (db.engine === "postgresql" || db.engine === "mysql") {
|
|
4080
|
+
const prefix = key.toUpperCase().replace(/-/g, "_");
|
|
4081
|
+
hints.push(`${prefix}_DATABASE_URL será injetado automaticamente de "${key}"`);
|
|
4082
|
+
if (db.pooler?.enabled && db.engine === "postgresql") hints.push(`${prefix}_POOLER_URL será injetado automaticamente (PgBouncer)`);
|
|
4083
|
+
}
|
|
4084
|
+
return hints;
|
|
3739
4085
|
}
|
|
3740
4086
|
|
|
3741
4087
|
//#endregion
|
|
@@ -3789,21 +4135,18 @@ async function computeExtraFilesForServices(services) {
|
|
|
3789
4135
|
return results;
|
|
3790
4136
|
}
|
|
3791
4137
|
async function triggerDeploy(serviceId, serviceName, preDetection) {
|
|
3792
|
-
const spinUpload = spinner(serviceName ? `Fazendo upload ${chalk.bold(serviceName)}...` : "Fazendo upload do código...");
|
|
3793
4138
|
const sizeInBytes = await calculateDirectorySize(process.cwd());
|
|
3794
4139
|
const sizeMB = Math.round(sizeInBytes / (1024 * 1024) * 10) / 10;
|
|
3795
|
-
if (sizeMB > 5) spinUpload.text = `Fazendo upload (${sizeMB} MB)...`;
|
|
3796
4140
|
const client = await getClient();
|
|
3797
|
-
const
|
|
4141
|
+
const velozConfig = loadConfig();
|
|
4142
|
+
const serviceConf = resolveServiceConf(velozConfig, serviceId);
|
|
3798
4143
|
const detection = preDetection ?? detectLocalRepo();
|
|
3799
4144
|
warnIfEphemeralFsDetected(detection, serviceConf, serviceName ?? void 0);
|
|
3800
4145
|
const extraFiles = prepareExtraFiles(detection, serviceConf);
|
|
3801
4146
|
const warnings = runPreDeployChecks(serviceConf?.rootDirectory || ".");
|
|
3802
|
-
if (warnings.length > 0)
|
|
3803
|
-
|
|
3804
|
-
|
|
3805
|
-
spinUpload.start();
|
|
3806
|
-
}
|
|
4147
|
+
if (warnings.length > 0) printDeployWarnings(warnings);
|
|
4148
|
+
const spinUpload = spinner(serviceName ? `Fazendo upload ${chalk.bold(serviceName)}...` : "Fazendo upload do código...");
|
|
4149
|
+
if (sizeMB > 5) spinUpload.text = `Fazendo upload (${sizeMB} MB)...`;
|
|
3807
4150
|
spinUpload.text = "Iniciando deploy...";
|
|
3808
4151
|
const deployment = await withRetry(() => client.deployments.create({
|
|
3809
4152
|
serviceId,
|
|
@@ -3816,7 +4159,12 @@ async function triggerDeploy(serviceId, serviceName, preDetection) {
|
|
|
3816
4159
|
setupSigintHandler();
|
|
3817
4160
|
trackDeployment(deployment.id);
|
|
3818
4161
|
try {
|
|
3819
|
-
|
|
4162
|
+
const result = await streamDeploymentLogs(deployment.id, serviceId, serviceName);
|
|
4163
|
+
if (result.status === "LIVE" && velozConfig?.project?.id) {
|
|
4164
|
+
const dashUrl = `${process.env.VELOZ_WEB_URL || "https://app.onveloz.com"}/projetos/${velozConfig.project.id}`;
|
|
4165
|
+
info(`Dashboard: ${chalk.dim(dashUrl)}`);
|
|
4166
|
+
}
|
|
4167
|
+
return result;
|
|
3820
4168
|
} finally {
|
|
3821
4169
|
untrackDeployment(deployment.id);
|
|
3822
4170
|
}
|
|
@@ -3876,15 +4224,56 @@ async function maybeConfigurePersistentVolume(serviceConfig, detection, opts, se
|
|
|
3876
4224
|
async function findServicesFromConfig() {
|
|
3877
4225
|
const config = loadConfig();
|
|
3878
4226
|
if (!config) return [];
|
|
4227
|
+
const missingIds = Object.entries(config.services).filter(([_, svc]) => !svc.id);
|
|
4228
|
+
if (missingIds.length > 0 && config.project.id) {
|
|
4229
|
+
const client = await getClient();
|
|
4230
|
+
const remoteServices = await withRetry(() => client.services.list({ projectId: config.project.id }));
|
|
4231
|
+
let configUpdated = false;
|
|
4232
|
+
for (const [key, serviceConfig] of missingIds) {
|
|
4233
|
+
const match = remoteServices.find((rs) => rs.name.toLowerCase() === serviceConfig.name.toLowerCase() || rs.name.toLowerCase() === key.toLowerCase());
|
|
4234
|
+
if (match) {
|
|
4235
|
+
serviceConfig.id = match.id;
|
|
4236
|
+
config.services[key] = serviceConfig;
|
|
4237
|
+
configUpdated = true;
|
|
4238
|
+
info(`Serviço "${serviceConfig.name}" vinculado (ID: ${match.id})`);
|
|
4239
|
+
} else {
|
|
4240
|
+
info(`Serviço "${serviceConfig.name}" não encontrado no projeto.`);
|
|
4241
|
+
if (await promptConfirm(`Criar serviço "${serviceConfig.name}" no projeto "${config.project.name}"?`)) {
|
|
4242
|
+
const branch = getGitBranch();
|
|
4243
|
+
const serviceType = serviceConfig.type?.toUpperCase() ?? "WEB";
|
|
4244
|
+
serviceConfig.id = (await withSpinner({
|
|
4245
|
+
text: `Criando serviço "${serviceConfig.name}"...`,
|
|
4246
|
+
fn: () => withRetry(() => client.services.create({
|
|
4247
|
+
projectId: config.project.id,
|
|
4248
|
+
name: serviceConfig.name,
|
|
4249
|
+
type: serviceType,
|
|
4250
|
+
branch,
|
|
4251
|
+
rootDirectory: serviceConfig.root ?? "."
|
|
4252
|
+
}))
|
|
4253
|
+
})).id;
|
|
4254
|
+
config.services[key] = serviceConfig;
|
|
4255
|
+
configUpdated = true;
|
|
4256
|
+
success(`Serviço "${serviceConfig.name}" criado.`);
|
|
4257
|
+
}
|
|
4258
|
+
}
|
|
4259
|
+
}
|
|
4260
|
+
if (configUpdated) {
|
|
4261
|
+
saveConfig(config);
|
|
4262
|
+
info(`Arquivo ${getConfigFileName()} atualizado com IDs dos serviços.`);
|
|
4263
|
+
}
|
|
4264
|
+
}
|
|
3879
4265
|
const services = [];
|
|
3880
|
-
for (const [key, serviceConfig] of Object.entries(config.services))
|
|
3881
|
-
|
|
3882
|
-
|
|
3883
|
-
|
|
3884
|
-
|
|
3885
|
-
|
|
3886
|
-
|
|
3887
|
-
|
|
4266
|
+
for (const [key, serviceConfig] of Object.entries(config.services)) {
|
|
4267
|
+
if (!serviceConfig.id) continue;
|
|
4268
|
+
services.push({
|
|
4269
|
+
path: resolve(process.cwd(), serviceConfig.root ?? "."),
|
|
4270
|
+
serviceId: serviceConfig.id,
|
|
4271
|
+
projectId: config.project.id,
|
|
4272
|
+
serviceName: serviceConfig.name,
|
|
4273
|
+
projectName: config.project.name,
|
|
4274
|
+
key
|
|
4275
|
+
});
|
|
4276
|
+
}
|
|
3888
4277
|
return services;
|
|
3889
4278
|
}
|
|
3890
4279
|
function readLocalFile(path) {
|
|
@@ -4640,7 +5029,8 @@ async function cliDeployFlow(opts) {
|
|
|
4640
5029
|
const available = configuredServices.map((s) => ` • ${s.key} (${s.serviceName})`).join("\n");
|
|
4641
5030
|
throw new Error(`Serviço '${opts.service}' não encontrado.\n\nServiços disponíveis:\n${available}`);
|
|
4642
5031
|
}
|
|
4643
|
-
|
|
5032
|
+
await triggerDeploy(found.serviceId, found.serviceName);
|
|
5033
|
+
return;
|
|
4644
5034
|
}
|
|
4645
5035
|
if (opts.all || opts.yes || configuredServices.length === 1) {
|
|
4646
5036
|
if (configuredServices.length > 1 && !opts.yes) {
|
|
@@ -4655,25 +5045,25 @@ async function cliDeployFlow(opts) {
|
|
|
4655
5045
|
return;
|
|
4656
5046
|
}
|
|
4657
5047
|
}
|
|
4658
|
-
if (configuredServices.length === 1)
|
|
4659
|
-
else
|
|
4660
|
-
|
|
4661
|
-
|
|
4662
|
-
|
|
4663
|
-
|
|
4664
|
-
|
|
4665
|
-
|
|
4666
|
-
|
|
4667
|
-
|
|
4668
|
-
}
|
|
4669
|
-
|
|
4670
|
-
|
|
4671
|
-
|
|
4672
|
-
|
|
4673
|
-
|
|
4674
|
-
if (selectedServices.length === 1) return await triggerDeploy(selectedServices[0].serviceId, selectedServices[0].serviceName);
|
|
4675
|
-
else return await deployServicesInParallel(await computeExtraFilesForServices(selectedServices));
|
|
5048
|
+
if (configuredServices.length === 1) await triggerDeploy(configuredServices[0].serviceId, configuredServices[0].serviceName);
|
|
5049
|
+
else await deployServicesInParallel(await computeExtraFilesForServices(configuredServices));
|
|
5050
|
+
return;
|
|
5051
|
+
}
|
|
5052
|
+
console.log(chalk.bold("\nServiços disponíveis:\n"));
|
|
5053
|
+
const selectedServiceIds = await promptMultiSelect("Quais serviços deseja fazer deploy?", configuredServices.map((s) => {
|
|
5054
|
+
const relPath = relative(process.cwd(), s.path) || ".";
|
|
5055
|
+
return {
|
|
5056
|
+
label: `${s.serviceName} ${chalk.dim(`(${relPath})`)}`,
|
|
5057
|
+
value: s.serviceId
|
|
5058
|
+
};
|
|
5059
|
+
}));
|
|
5060
|
+
const selectedServices = configuredServices.filter((s) => selectedServiceIds.includes(s.serviceId));
|
|
5061
|
+
if (selectedServices.length === 0) {
|
|
5062
|
+
info("Nenhum serviço selecionado.");
|
|
5063
|
+
return;
|
|
4676
5064
|
}
|
|
5065
|
+
if (selectedServices.length === 1) await triggerDeploy(selectedServices[0].serviceId, selectedServices[0].serviceName);
|
|
5066
|
+
else await deployServicesInParallel(await computeExtraFilesForServices(selectedServices));
|
|
4677
5067
|
}
|
|
4678
5068
|
if (!isGitRepo()) throw new Error("Este diretório não é um repositório git. Inicialize com `git init` e adicione um remote.");
|
|
4679
5069
|
info("Detectando repositório git...");
|
|
@@ -4728,7 +5118,8 @@ async function cliDeployFlow(opts) {
|
|
|
4728
5118
|
info(`Arquivo ${getConfigFileName()} criado na raiz do projeto.`);
|
|
4729
5119
|
const freshConfig = loadConfig();
|
|
4730
5120
|
if (freshConfig) await provisionDatabases(freshConfig, { yes: opts.yes ?? false });
|
|
4731
|
-
|
|
5121
|
+
await triggerDeploy(serviceId$1, void 0, detection);
|
|
5122
|
+
return;
|
|
4732
5123
|
}
|
|
4733
5124
|
if (project && project.services.length === 0) {
|
|
4734
5125
|
info(`Projeto encontrado: ${chalk.bold(project.name)}`);
|
|
@@ -4739,7 +5130,8 @@ async function cliDeployFlow(opts) {
|
|
|
4739
5130
|
});
|
|
4740
5131
|
const freshConfig = loadConfig();
|
|
4741
5132
|
if (freshConfig) await provisionDatabases(freshConfig, { yes: opts.yes ?? false });
|
|
4742
|
-
|
|
5133
|
+
await triggerDeploy(serviceId$1);
|
|
5134
|
+
return;
|
|
4743
5135
|
}
|
|
4744
5136
|
info("Projeto não encontrado. Vamos criar um novo.");
|
|
4745
5137
|
const projectName = opts.yes ? remote.repo : await prompt(`Nome do projeto: ${chalk.dim(`(${remote.repo})`)}`) || remote.repo;
|
|
@@ -4758,7 +5150,7 @@ async function cliDeployFlow(opts) {
|
|
|
4758
5150
|
});
|
|
4759
5151
|
const newConfig = loadConfig();
|
|
4760
5152
|
if (newConfig) await provisionDatabases(newConfig, { yes: opts.yes ?? false });
|
|
4761
|
-
|
|
5153
|
+
await triggerDeploy(serviceId);
|
|
4762
5154
|
}
|
|
4763
5155
|
|
|
4764
5156
|
//#endregion
|
|
@@ -5072,7 +5464,7 @@ async function pruneRemovedEntries(config, existingConfig, services, databases)
|
|
|
5072
5464
|
//#region src/index.ts
|
|
5073
5465
|
if (process.argv.includes("--mcp")) process.env.VELOZ_MCP = "true";
|
|
5074
5466
|
const cli = Cli.create("veloz", {
|
|
5075
|
-
version: "0.0.0-beta.
|
|
5467
|
+
version: "0.0.0-beta.16",
|
|
5076
5468
|
description: "CLI da plataforma Veloz — deploy rápido para o Brasil",
|
|
5077
5469
|
env: z.object({ VELOZ_ENV: z.string().optional().describe("Ambiente alvo (ex: preview, staging)") })
|
|
5078
5470
|
});
|