connectbase-client 3.44.0 → 3.45.0
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/cli.js +121 -13
- package/package.json +1 -1
package/dist/cli.js
CHANGED
|
@@ -429,7 +429,10 @@ async function offloadLargeBinaries(baseStorageUrl, headers, diff, _envLabel, ti
|
|
|
429
429
|
const presignFiles = largeBinaries.map((f) => ({
|
|
430
430
|
path: f.path,
|
|
431
431
|
size: Buffer.from(f.content, "base64").length,
|
|
432
|
-
mime_type: guessMimeType(f.path)
|
|
432
|
+
mime_type: guessMimeType(f.path),
|
|
433
|
+
// 신형 서버: hash 제공 시 content-addressed blob 키({id}/blobs/{hash})로 presign 되고,
|
|
434
|
+
// 동일 blob 이 이미 있으면 already_exists 로 업로드를 생략할 수 있다. 구버전 서버는 무시.
|
|
435
|
+
hash: f.hash
|
|
433
436
|
}));
|
|
434
437
|
let presigned;
|
|
435
438
|
try {
|
|
@@ -445,22 +448,30 @@ async function offloadLargeBinaries(baseStorageUrl, headers, diff, _envLabel, ti
|
|
|
445
448
|
warn(`\uB300\uC6A9\uB7C9 \uC5D0\uC14B\uC740 \uD30C\uC77C \uC2A4\uD1A0\uB9AC\uC9C0(${colors.cyan}cb.storage${colors.reset})\uC5D0 \uC62C\uB9AC\uACE0 URL \uB85C \uCC38\uC870\uD558\uAC70\uB098, \uC11C\uBC84 \uC5C5\uB370\uC774\uD2B8 \uD6C4 \uB2E4\uC2DC \uC2DC\uB3C4\uD558\uC138\uC694.`);
|
|
446
449
|
process.exit(1);
|
|
447
450
|
}
|
|
448
|
-
const
|
|
451
|
+
const entryByPath = new Map(presigned.map((p) => [p.path, p]));
|
|
449
452
|
info(`\uB300\uC6A9\uB7C9 \uBC14\uC774\uB108\uB9AC ${largeBinaries.length}\uAC1C\uB97C Object Storage \uB85C \uC9C1\uC811 \uC5C5\uB85C\uB4DC\uD569\uB2C8\uB2E4...`);
|
|
450
453
|
for (const f of largeBinaries) {
|
|
451
|
-
const
|
|
452
|
-
if (!
|
|
453
|
-
error(`presign \uC751\uB2F5\uC5D0 \
|
|
454
|
+
const entry = entryByPath.get(f.path);
|
|
455
|
+
if (!entry) {
|
|
456
|
+
error(`presign \uC751\uB2F5\uC5D0 \uD56D\uBAA9\uC774 \uC5C6\uC2B5\uB2C8\uB2E4: ${f.path}`);
|
|
454
457
|
process.exit(1);
|
|
455
458
|
}
|
|
456
459
|
const bytes = Buffer.from(f.content, "base64");
|
|
457
|
-
|
|
458
|
-
|
|
459
|
-
}
|
|
460
|
-
|
|
461
|
-
|
|
460
|
+
if (entry.already_exists) {
|
|
461
|
+
success(`\uC5C5\uB85C\uB4DC \uC0DD\uB7B5(\uAE30\uC874 blob \uC7AC\uC0AC\uC6A9): ${f.path} (${formatSize(bytes.length)})`);
|
|
462
|
+
} else {
|
|
463
|
+
if (!entry.upload_url) {
|
|
464
|
+
error(`presign \uC751\uB2F5\uC5D0 \uC5C5\uB85C\uB4DC URL \uC774 \uC5C6\uC2B5\uB2C8\uB2E4: ${f.path}`);
|
|
465
|
+
process.exit(1);
|
|
466
|
+
}
|
|
467
|
+
try {
|
|
468
|
+
await putToPresignedUrl(entry.upload_url, bytes, guessMimeType(f.path), timeoutMs);
|
|
469
|
+
} catch (err) {
|
|
470
|
+
error(`\uC5C5\uB85C\uB4DC \uC2E4\uD328 (${f.path}): ${err instanceof Error ? err.message : err}`);
|
|
471
|
+
process.exit(1);
|
|
472
|
+
}
|
|
473
|
+
success(`\uC9C1\uC811 \uC5C5\uB85C\uB4DC: ${f.path} (${formatSize(bytes.length)})`);
|
|
462
474
|
}
|
|
463
|
-
success(`\uC9C1\uC811 \uC5C5\uB85C\uB4DC: ${f.path} (${formatSize(bytes.length)})`);
|
|
464
475
|
f.pre_uploaded = true;
|
|
465
476
|
f.size = bytes.length;
|
|
466
477
|
f.content = "";
|
|
@@ -743,8 +754,25 @@ async function fullDeploy(baseStorageUrl, headers, files, envLabel, endpoint, ti
|
|
|
743
754
|
}
|
|
744
755
|
function handleDeployResponse(response, envLabel) {
|
|
745
756
|
if (response.status >= 200 && response.status < 300) {
|
|
746
|
-
success(`${envLabel} \uBC30\uD3EC \uC644\uB8CC!`);
|
|
747
757
|
const data = response.data;
|
|
758
|
+
const dep = data?.deployment ?? null;
|
|
759
|
+
if (dep?.deployment_id) {
|
|
760
|
+
if (dep.promoted) {
|
|
761
|
+
success(`${envLabel} \uBC30\uD3EC \uC644\uB8CC! (release #${dep.seq})`);
|
|
762
|
+
if (dep.production_url) log(`
|
|
763
|
+
${colors.cyan}Production: ${dep.production_url}${colors.reset}`);
|
|
764
|
+
if (dep.qa_url) log(`${colors.dim}QA: ${dep.qa_url}${colors.reset}
|
|
765
|
+
`);
|
|
766
|
+
} else {
|
|
767
|
+
success(`QA \uD658\uACBD\uC5D0 \uBC30\uD3EC\uB418\uC5C8\uC2B5\uB2C8\uB2E4 (release #${dep.seq}) \u2014 production \uC740 \uC544\uC9C1 \uC774\uC804 \uBC84\uC804\uC785\uB2C8\uB2E4`);
|
|
768
|
+
if (dep.qa_url) log(`
|
|
769
|
+
${colors.yellow}QA URL: ${dep.qa_url}${colors.reset}`);
|
|
770
|
+
log(`${colors.dim}\uAC80\uC99D \uD6C4 \uBC18\uC601: ${colors.cyan}npx connectbase promote${colors.reset}
|
|
771
|
+
`);
|
|
772
|
+
}
|
|
773
|
+
return;
|
|
774
|
+
}
|
|
775
|
+
success(`${envLabel} \uBC30\uD3EC \uC644\uB8CC!`);
|
|
748
776
|
if (data?.dev_url) {
|
|
749
777
|
log(`
|
|
750
778
|
${colors.yellow}Dev URL: ${data.dev_url}${colors.reset}
|
|
@@ -761,6 +789,64 @@ ${colors.cyan}URL: ${data.url}${colors.reset}
|
|
|
761
789
|
process.exit(1);
|
|
762
790
|
}
|
|
763
791
|
}
|
|
792
|
+
async function promote(config, deploymentId) {
|
|
793
|
+
const baseStorageUrl = `${config.baseUrl}/v1/public/storages/webs/${config.storageId}`;
|
|
794
|
+
const headers = { "X-Public-Key": config.publicKey };
|
|
795
|
+
info(deploymentId ? `release ${deploymentId} \uB97C production \uC73C\uB85C \uC2B9\uACA9\uD569\uB2C8\uB2E4...` : "\uCD5C\uC2E0 \uBC30\uD3EC\uB97C production \uC73C\uB85C \uC2B9\uACA9\uD569\uB2C8\uB2E4...");
|
|
796
|
+
const body = JSON.stringify(deploymentId ? { deployment_id: deploymentId } : {});
|
|
797
|
+
const res = await makeRequest(`${baseStorageUrl}/promote`, "POST", headers, body);
|
|
798
|
+
if (res.status === 404 && !res.data?.error) {
|
|
799
|
+
error("\uC11C\uBC84\uAC00 promote \uB97C \uC9C0\uC6D0\uD558\uC9C0 \uC54A\uC2B5\uB2C8\uB2E4 (\uAD6C\uBC84\uC804 \uC11C\uBC84) \u2014 \uC11C\uBC84 \uC5C5\uB370\uC774\uD2B8 \uD6C4 \uB2E4\uC2DC \uC2DC\uB3C4\uD558\uC138\uC694");
|
|
800
|
+
process.exit(1);
|
|
801
|
+
}
|
|
802
|
+
if (res.status < 200 || res.status >= 300) {
|
|
803
|
+
const data2 = res.data;
|
|
804
|
+
const msg = typeof data2 === "object" && data2 !== null ? data2.error || data2.message || JSON.stringify(data2) : `HTTP ${res.status}`;
|
|
805
|
+
error(`promote \uC2E4\uD328: ${msg}`);
|
|
806
|
+
process.exit(1);
|
|
807
|
+
}
|
|
808
|
+
const data = res.data;
|
|
809
|
+
success(`production \uC2B9\uACA9 \uC644\uB8CC! (release #${data.seq})`);
|
|
810
|
+
if (data.production_url) {
|
|
811
|
+
log(`
|
|
812
|
+
${colors.cyan}Production: ${data.production_url}${colors.reset}
|
|
813
|
+
`);
|
|
814
|
+
}
|
|
815
|
+
}
|
|
816
|
+
async function listReleases(config) {
|
|
817
|
+
const baseStorageUrl = `${config.baseUrl}/v1/public/storages/webs/${config.storageId}`;
|
|
818
|
+
const headers = { "X-Public-Key": config.publicKey };
|
|
819
|
+
const res = await makeRequest(`${baseStorageUrl}/deployments`, "GET", headers);
|
|
820
|
+
if (res.status < 200 || res.status >= 300) {
|
|
821
|
+
const data2 = res.data;
|
|
822
|
+
const msg = typeof data2 === "object" && data2 !== null ? data2.error || data2.message || JSON.stringify(data2) : `HTTP ${res.status}`;
|
|
823
|
+
error(`\uBC30\uD3EC \uC774\uB825 \uC870\uD68C \uC2E4\uD328: ${msg}`);
|
|
824
|
+
process.exit(1);
|
|
825
|
+
}
|
|
826
|
+
const data = res.data;
|
|
827
|
+
if (!data.deployments || data.deployments.length === 0) {
|
|
828
|
+
info("\uBC30\uD3EC \uC774\uB825\uC774 \uC5C6\uC2B5\uB2C8\uB2E4. \uBA3C\uC800 deploy \uD558\uC138\uC694.");
|
|
829
|
+
return;
|
|
830
|
+
}
|
|
831
|
+
log(`
|
|
832
|
+
${colors.cyan}\uBC30\uD3EC \uC774\uB825${colors.reset} (promote_mode: ${data.promote_mode || "auto"}, \uCD1D ${data.total}\uAC1C)
|
|
833
|
+
`);
|
|
834
|
+
for (const d of data.deployments) {
|
|
835
|
+
const markers = [];
|
|
836
|
+
if (d.is_production) markers.push(`${colors.green}production${colors.reset}`);
|
|
837
|
+
if (d.is_qa) markers.push(`${colors.yellow}qa${colors.reset}`);
|
|
838
|
+
const marker = markers.length > 0 ? ` \u2190 ${markers.join(", ")}` : "";
|
|
839
|
+
const statusColor = d.status === "ready" ? colors.green : d.status === "failed" ? colors.red : colors.yellow;
|
|
840
|
+
log(
|
|
841
|
+
` #${String(d.seq).padEnd(4)} ${statusColor}${d.status.padEnd(9)}${colors.reset} ${String(d.file_count).padStart(4)}\uAC1C ${formatSize(d.total_size).padStart(9)} ${d.created_at} ${colors.dim}${d.id}${colors.reset}${marker}`
|
|
842
|
+
);
|
|
843
|
+
}
|
|
844
|
+
log("");
|
|
845
|
+
if (data.production_url) log(`${colors.cyan}Production: ${data.production_url}${colors.reset}`);
|
|
846
|
+
if (data.qa_url) log(`${colors.yellow}QA: ${data.qa_url}${colors.reset}`);
|
|
847
|
+
log(`${colors.dim}\uB864\uBC31: npx connectbase promote <deployment_id>${colors.reset}
|
|
848
|
+
`);
|
|
849
|
+
}
|
|
764
850
|
function prompt(question) {
|
|
765
851
|
const rl = readline.createInterface({
|
|
766
852
|
input: process.stdin,
|
|
@@ -2786,6 +2872,9 @@ ${colors.yellow}\uBA85\uB839\uC5B4:${colors.reset}
|
|
|
2786
2872
|
docs SDK \uBB38\uC11C \uB2E4\uC6B4\uB85C\uB4DC/\uC5C5\uB370\uC774\uD2B8 (\uBAA8\uB178\uB808\uD3EC \uC790\uB3D9 \uAC10\uC9C0)
|
|
2787
2873
|
mcp MCP \uC11C\uBC84 \uC124\uC815 (.mcp.json \uC0DD\uC131/\uC5C5\uB370\uC774\uD2B8, \uBAA8\uB178\uB808\uD3EC \uC790\uB3D9 \uAC10\uC9C0)
|
|
2788
2874
|
deploy <directory> \uC6F9 \uC2A4\uD1A0\uB9AC\uC9C0\uC5D0 \uD30C\uC77C \uBC30\uD3EC (--dev: Dev \uD658\uACBD)
|
|
2875
|
+
promote_mode=manual \uC774\uBA74 QA \uD658\uACBD\uC5D0\uB9CC \uBC30\uD3EC\uB429\uB2C8\uB2E4
|
|
2876
|
+
promote [releaseId] QA \uAC80\uC99D \uD6C4 production \uBC18\uC601 (\uBBF8\uC9C0\uC815: \uCD5C\uC2E0 \uBC30\uD3EC, \uACFC\uAC70 id: \uB864\uBC31)
|
|
2877
|
+
releases \uBC30\uD3EC \uC774\uB825 \uC870\uD68C (release \uBC88\uD638/\uC0C1\uD0DC/production\xB7QA \uD3EC\uC778\uD130)
|
|
2789
2878
|
tunnel <port> \uB85C\uCEEC \uC11C\uBE44\uC2A4\uB97C \uC778\uD130\uB137\uC5D0 \uB178\uCD9C
|
|
2790
2879
|
|
|
2791
2880
|
${colors.yellow}\uCF54\uB4DC \uBD84\uC11D (MCP \uD1B5\uD574 \uC0AC\uC6A9):${colors.reset}
|
|
@@ -2842,7 +2931,12 @@ ${colors.yellow}\uBE60\uB978 \uC2DC\uC791:${colors.reset}
|
|
|
2842
2931
|
${colors.dim}# 4. Prod \uBC30\uD3EC${colors.reset}
|
|
2843
2932
|
npm run deploy
|
|
2844
2933
|
|
|
2845
|
-
${colors.dim}# 4.
|
|
2934
|
+
${colors.dim}# 4-1. QA \uC6CC\uD06C\uD50C\uB85C\uC6B0 (\uCF58\uC194\uC5D0\uC11C promote_mode=manual \uC124\uC815 \uC2DC)${colors.reset}
|
|
2935
|
+
npx connectbase deploy ./dist ${colors.dim}# QA \uD658\uACBD\uC5D0\uB9CC \uBC30\uD3EC${colors.reset}
|
|
2936
|
+
npx connectbase releases ${colors.dim}# \uBC30\uD3EC \uC774\uB825 \uD655\uC778${colors.reset}
|
|
2937
|
+
npx connectbase promote ${colors.dim}# QA \uAC80\uC99D \uD6C4 production \uBC18\uC601${colors.reset}
|
|
2938
|
+
|
|
2939
|
+
${colors.dim}# 4-2. Dev \uD658\uACBD \uBC30\uD3EC (\uB0B4\uBD80 \uD655\uC778\uC6A9)${colors.reset}
|
|
2846
2940
|
npx connectbase deploy ./dist --dev
|
|
2847
2941
|
|
|
2848
2942
|
${colors.dim}# 5. \uD130\uB110 (\uAE30\uBCF8)${colors.reset}
|
|
@@ -2996,6 +3090,20 @@ async function main() {
|
|
|
2996
3090
|
} catch {
|
|
2997
3091
|
}
|
|
2998
3092
|
}
|
|
3093
|
+
} else if (parsed.command === "promote" || parsed.command === "releases") {
|
|
3094
|
+
if (!config.publicKey) {
|
|
3095
|
+
error('Public Key\uAC00 \uD544\uC694\uD569\uB2C8\uB2E4. "npx connectbase init"\uC73C\uB85C \uC124\uC815\uD558\uAC70\uB098 -k \uC635\uC158\uC744 \uC0AC\uC6A9\uD558\uC138\uC694');
|
|
3096
|
+
process.exit(1);
|
|
3097
|
+
}
|
|
3098
|
+
if (!config.storageId) {
|
|
3099
|
+
error('\uC2A4\uD1A0\uB9AC\uC9C0 ID\uAC00 \uD544\uC694\uD569\uB2C8\uB2E4. "npx connectbase init"\uC73C\uB85C \uC124\uC815\uD558\uAC70\uB098 -s \uC635\uC158\uC744 \uC0AC\uC6A9\uD558\uC138\uC694');
|
|
3100
|
+
process.exit(1);
|
|
3101
|
+
}
|
|
3102
|
+
if (parsed.command === "promote") {
|
|
3103
|
+
await promote(config, parsed.args[0]);
|
|
3104
|
+
} else {
|
|
3105
|
+
await listReleases(config);
|
|
3106
|
+
}
|
|
2999
3107
|
} else if (parsed.command === "tunnel") {
|
|
3000
3108
|
const portStr = parsed.args[0];
|
|
3001
3109
|
if (!portStr) {
|