@glash/cli 0.2.29 → 0.2.30
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/commands/deploy.mjs +63 -10
- package/package.json +1 -1
package/commands/deploy.mjs
CHANGED
|
@@ -134,10 +134,11 @@ export async function deploy({ flags }) {
|
|
|
134
134
|
const commitHash = flags.commit || (remoteDeploy ? undefined : currentCommit());
|
|
135
135
|
const gitBranch = flags.branch || currentBranch();
|
|
136
136
|
const target = flags.prod || flags.production ? 'production' : (flags.target || 'production');
|
|
137
|
+
const requestedAssetMode = assetMode(flags);
|
|
137
138
|
|
|
138
139
|
let dep;
|
|
139
140
|
let envSummary = { count: 0, skipped: Boolean(flags['no-env']) };
|
|
140
|
-
let assetSummary = { mode:
|
|
141
|
+
let assetSummary = { mode: requestedAssetMode, status: requestedAssetMode === 'inline' ? 'inline' : 'pending', offloaded: false, count: 0, bytes: 0, skipped: 0 };
|
|
141
142
|
let currentPhase = 'doing';
|
|
142
143
|
const start = Date.now();
|
|
143
144
|
const indicator = createDeployIndicator();
|
|
@@ -177,8 +178,11 @@ export async function deploy({ flags }) {
|
|
|
177
178
|
assetSummary = await offloadPublicAssetsBeforeDeploy(projectId, flags);
|
|
178
179
|
report.assetSummary = assetSummary;
|
|
179
180
|
} catch (e) {
|
|
181
|
+
const failedAssetSummary = e.assetSummary ?? { ...assetSummary, status: 'failed', error: e.message };
|
|
182
|
+
assetSummary = failedAssetSummary;
|
|
183
|
+
report.assetSummary = failedAssetSummary;
|
|
180
184
|
indicator.fail('failed');
|
|
181
|
-
printDeployReport({ ...report, assetSummary, status: 'failed', error: `Public asset offload failed: ${e.message}` });
|
|
185
|
+
printDeployReport({ ...report, assetSummary: failedAssetSummary, status: 'failed', error: `Public asset offload failed: ${e.message}` });
|
|
182
186
|
return fail(`Public asset offload failed: ${e.message}`);
|
|
183
187
|
}
|
|
184
188
|
} else if (assetSummary.mode !== 'inline') {
|
|
@@ -335,16 +339,16 @@ async function syncEnvBeforeDeploy(projectId, target, flags) {
|
|
|
335
339
|
|
|
336
340
|
async function offloadPublicAssetsBeforeDeploy(projectId, flags) {
|
|
337
341
|
const mode = assetMode(flags);
|
|
338
|
-
const empty = { mode, offloaded: false, count: 0, bytes: 0, skipped: 0 };
|
|
342
|
+
const empty = { mode, status: mode === 'inline' ? 'inline' : 'pending', offloaded: false, count: 0, bytes: 0, skipped: 0 };
|
|
339
343
|
if (mode === 'inline') return empty;
|
|
340
344
|
|
|
341
345
|
const files = await publicAssetFiles(process.cwd());
|
|
342
|
-
if (!files.length) return empty;
|
|
346
|
+
if (!files.length) return { ...empty, status: 'no-public-assets' };
|
|
343
347
|
|
|
344
348
|
const totalBytes = files.reduce((sum, file) => sum + file.sizeBytes, 0);
|
|
345
349
|
const threshold = assetOffloadThresholdBytes(flags);
|
|
346
350
|
if (mode === 'auto' && totalBytes < threshold) {
|
|
347
|
-
return { ...empty, bytes: totalBytes };
|
|
351
|
+
return { ...empty, status: 'below-threshold', count: files.length, bytes: totalBytes, thresholdBytes: threshold };
|
|
348
352
|
}
|
|
349
353
|
|
|
350
354
|
const assets = [];
|
|
@@ -360,7 +364,12 @@ async function offloadPublicAssetsBeforeDeploy(projectId, flags) {
|
|
|
360
364
|
let uploaded = 0;
|
|
361
365
|
let skipped = 0;
|
|
362
366
|
for (const batch of chunk(assets, 100)) {
|
|
363
|
-
|
|
367
|
+
let prepared;
|
|
368
|
+
try {
|
|
369
|
+
prepared = await apiPost(`/projects/${projectId}/public-assets/prepare`, { assets: batch });
|
|
370
|
+
} catch (e) {
|
|
371
|
+
throw assetOffloadFailure(e, { mode, status: 'prepare-failed', offloaded: false, count: assets.length, uploaded, skipped, bytes: totalBytes });
|
|
372
|
+
}
|
|
364
373
|
for (const asset of prepared.assets ?? []) {
|
|
365
374
|
if (asset.skipped) {
|
|
366
375
|
skipped += 1;
|
|
@@ -377,15 +386,23 @@ async function offloadPublicAssetsBeforeDeploy(projectId, flags) {
|
|
|
377
386
|
});
|
|
378
387
|
if (!res.ok) {
|
|
379
388
|
const detail = await res.text().catch(() => '');
|
|
380
|
-
throw
|
|
389
|
+
throw assetOffloadFailure(
|
|
390
|
+
new Error(`Upload ${asset.path} failed with HTTP ${res.status}${detail ? `: ${detail.slice(0, 200)}` : ''}`),
|
|
391
|
+
{ mode, status: 'upload-failed', offloaded: false, count: assets.length, uploaded, skipped, bytes: totalBytes },
|
|
392
|
+
);
|
|
381
393
|
}
|
|
382
394
|
uploaded += 1;
|
|
383
395
|
}
|
|
384
|
-
|
|
396
|
+
try {
|
|
397
|
+
await apiPost(`/projects/${projectId}/public-assets/complete`, { assets: batch });
|
|
398
|
+
} catch (e) {
|
|
399
|
+
throw assetOffloadFailure(e, { mode, status: 'complete-failed', offloaded: false, count: assets.length, uploaded, skipped, bytes: totalBytes });
|
|
400
|
+
}
|
|
385
401
|
}
|
|
386
402
|
|
|
387
403
|
return {
|
|
388
404
|
mode,
|
|
405
|
+
status: 'offloaded',
|
|
389
406
|
offloaded: true,
|
|
390
407
|
count: assets.length,
|
|
391
408
|
uploaded,
|
|
@@ -394,6 +411,26 @@ async function offloadPublicAssetsBeforeDeploy(projectId, flags) {
|
|
|
394
411
|
};
|
|
395
412
|
}
|
|
396
413
|
|
|
414
|
+
function assetOffloadFailure(error, assetSummary) {
|
|
415
|
+
const message = friendlyAssetOffloadError(error);
|
|
416
|
+
const wrapped = new Error(message);
|
|
417
|
+
wrapped.cause = error;
|
|
418
|
+
wrapped.status = error?.status;
|
|
419
|
+
wrapped.assetSummary = { ...assetSummary, error: message };
|
|
420
|
+
return wrapped;
|
|
421
|
+
}
|
|
422
|
+
|
|
423
|
+
function friendlyAssetOffloadError(error) {
|
|
424
|
+
const message = error?.message || String(error);
|
|
425
|
+
if (error?.status === 404 && /public-assets\/prepare|public-assets/i.test(message)) {
|
|
426
|
+
return 'This GlashDB API does not support public asset offload yet. Deploy the backend public-assets route and router update, then retry.';
|
|
427
|
+
}
|
|
428
|
+
if (error?.status === 503 && /Public asset storage is not configured|storage is not configured/i.test(message)) {
|
|
429
|
+
return 'Public asset storage is not configured on this GlashDB API. Set the R2 asset storage secrets, redeploy the backend, then retry.';
|
|
430
|
+
}
|
|
431
|
+
return message;
|
|
432
|
+
}
|
|
433
|
+
|
|
397
434
|
function assetMode(flags) {
|
|
398
435
|
const raw = String(flags['asset-mode'] ?? flags.assets ?? flags['public-assets'] ?? 'inline').toLowerCase();
|
|
399
436
|
if (['inline', 'offload', 'auto'].includes(raw)) return raw;
|
|
@@ -635,6 +672,7 @@ function printDeployReport(report) {
|
|
|
635
672
|
const liveUrl = report.liveUrl ? terminalLink(report.liveUrl) : '-';
|
|
636
673
|
const rows = [
|
|
637
674
|
['Status', formatReportStatus(report.status)],
|
|
675
|
+
['Project ID', report.projectId || '-'],
|
|
638
676
|
['Deployment ID', report.deploymentId || '-'],
|
|
639
677
|
['Target', report.target || 'production'],
|
|
640
678
|
['Source', report.source || '-'],
|
|
@@ -673,14 +711,29 @@ function formatPackagesUploaded(report) {
|
|
|
673
711
|
}
|
|
674
712
|
|
|
675
713
|
function formatAssetSummary(assetSummary) {
|
|
676
|
-
if (!assetSummary || assetSummary.mode === 'inline') return 'inline';
|
|
714
|
+
if (!assetSummary || assetSummary.mode === 'inline') return 'inline (bundled with source)';
|
|
715
|
+
if (assetSummary.status === 'pending') return `${assetSummary.mode} requested`;
|
|
716
|
+
if (assetSummary.status === 'no-public-assets') return `${assetSummary.mode}, no public assets found`;
|
|
717
|
+
if (assetSummary.status === 'below-threshold') {
|
|
718
|
+
return `auto kept inline (${formatBytes(assetSummary.bytes)} below ${formatBytes(assetSummary.thresholdBytes)})`;
|
|
719
|
+
}
|
|
720
|
+
if (assetSummary.status === 'prepare-failed') {
|
|
721
|
+
return `prepare failed (${(assetSummary.count ?? 0).toLocaleString()} assets, ${formatBytes(assetSummary.bytes)})`;
|
|
722
|
+
}
|
|
723
|
+
if (assetSummary.status === 'upload-failed') {
|
|
724
|
+
return `upload failed (${(assetSummary.uploaded ?? 0).toLocaleString()}/${(assetSummary.count ?? 0).toLocaleString()} uploaded, ${formatBytes(assetSummary.bytes)})`;
|
|
725
|
+
}
|
|
726
|
+
if (assetSummary.status === 'complete-failed') {
|
|
727
|
+
return `activation failed (${(assetSummary.uploaded ?? 0).toLocaleString()} uploaded, ${formatBytes(assetSummary.bytes)})`;
|
|
728
|
+
}
|
|
729
|
+
if (assetSummary.status === 'failed') return `failed${assetSummary.error ? ` (${assetSummary.error})` : ''}`;
|
|
677
730
|
if (!assetSummary.offloaded) {
|
|
678
731
|
const size = assetSummary.bytes ? ` (${formatBytes(assetSummary.bytes)})` : '';
|
|
679
732
|
return `${assetSummary.mode}, no offload${size}`;
|
|
680
733
|
}
|
|
681
734
|
const uploaded = assetSummary.uploaded ?? Math.max(0, (assetSummary.count ?? 0) - (assetSummary.skipped ?? 0));
|
|
682
735
|
const skipped = assetSummary.skipped ? `, ${assetSummary.skipped.toLocaleString()} reused` : '';
|
|
683
|
-
return `${assetSummary.count.toLocaleString()} offloaded (${formatBytes(assetSummary.bytes)}; ${uploaded.toLocaleString()} uploaded${skipped})`;
|
|
736
|
+
return `${assetSummary.count.toLocaleString()} assets offloaded (${formatBytes(assetSummary.bytes)}; ${uploaded.toLocaleString()} uploaded${skipped})`;
|
|
684
737
|
}
|
|
685
738
|
|
|
686
739
|
function formatDeploymentSize(report) {
|
package/package.json
CHANGED