@hs-x/cli 0.4.2 → 0.4.4
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/commands/control-plane-read.js +1 -1
- package/dist/commands/control-plane-read.js.map +1 -1
- package/dist/commands/deploy.d.ts.map +1 -1
- package/dist/commands/deploy.js +195 -84
- package/dist/commands/deploy.js.map +1 -1
- package/dist/commands/dev.d.ts.map +1 -1
- package/dist/commands/dev.js +25 -16
- package/dist/commands/dev.js.map +1 -1
- package/dist/commands/project-lifecycle.d.ts.map +1 -1
- package/dist/commands/project-lifecycle.js +143 -46
- package/dist/commands/project-lifecycle.js.map +1 -1
- package/dist/commands/project.d.ts.map +1 -1
- package/dist/commands/project.js +24 -10
- package/dist/commands/project.js.map +1 -1
- package/dist/constants.d.ts +1 -1
- package/dist/constants.js +1 -1
- package/dist/hubspot-developer-client.d.ts +1 -0
- package/dist/hubspot-developer-client.d.ts.map +1 -1
- package/dist/hubspot-developer-client.js +16 -1
- package/dist/hubspot-developer-client.js.map +1 -1
- package/dist/hubspot-project-lifecycle.d.ts +12 -0
- package/dist/hubspot-project-lifecycle.d.ts.map +1 -1
- package/dist/hubspot-project-lifecycle.js +28 -0
- package/dist/hubspot-project-lifecycle.js.map +1 -1
- package/package.json +2 -2
package/dist/commands/deploy.js
CHANGED
|
@@ -10,6 +10,7 @@ import { cloudflareResourceName, generateHubSpotRuntimeProject, generateProjectA
|
|
|
10
10
|
import { HttpClientRequest, Schema, schemas, signDeploymentAttestationGrant } from '@hs-x/types';
|
|
11
11
|
import { validateProject } from '@hs-x/validator';
|
|
12
12
|
import { isLegacyProject } from '@hubspot/project-parsing-lib/projects';
|
|
13
|
+
import { translate as translateHubSpotProject } from '@hubspot/project-parsing-lib/translate';
|
|
13
14
|
import { Effect, Option } from 'effect';
|
|
14
15
|
import { isLinked } from '../account-store.js';
|
|
15
16
|
import { exitWith } from '../cli-error.js';
|
|
@@ -27,7 +28,7 @@ import { resolveDeployTargets } from '../deploy-targets.js';
|
|
|
27
28
|
import { executeCliHttp } from '../effect-http.js';
|
|
28
29
|
import { DirectHubSpotAccountResolutionError, createDeployHubSpotDeveloperClient, resolveDirectHubSpotContext, } from '../hubspot-developer-client.js';
|
|
29
30
|
import { HubSpotProjectArchiveSourceError, fingerprintHubSpotArchiveSource, writeLocalHubSpotProjectArchive, } from '../hubspot-project-archive.js';
|
|
30
|
-
import { deployHubSpotProjectBuild, waitForHubSpotAutoDeploy, waitForHubSpotBuildStatus, } from '../hubspot-project-lifecycle.js';
|
|
31
|
+
import { deployHubSpotProjectBuild, readHubSpotSubbuildStatuses, waitForHubSpotAutoDeploy, waitForHubSpotBuildStatus, } from '../hubspot-project-lifecycle.js';
|
|
31
32
|
import { detectLocalProjectMode, readHubSpotProjectDescriptor, validateHubSpotProject, } from '../hubspot-project.js';
|
|
32
33
|
import { hydrateAncestorEnv } from '../load-env.js';
|
|
33
34
|
import { LocalProjectPickerCancelled, resolveLocalProject } from '../local-project-picker.js';
|
|
@@ -772,6 +773,15 @@ async function executeHubSpotDirectDeployCommand({ argv, root, json, }) {
|
|
|
772
773
|
force,
|
|
773
774
|
sourceFingerprint,
|
|
774
775
|
createClient: async () => context.createClient(),
|
|
776
|
+
...(!isLegacyProject(platformVersion)
|
|
777
|
+
? {
|
|
778
|
+
translateProject: () => context.runWithEnvironment((selectedAccount) => translateHubSpotProject({
|
|
779
|
+
projectSourceDir: join(root, descriptor.srcDir),
|
|
780
|
+
platformVersion,
|
|
781
|
+
accountId: Number(selectedAccount.id),
|
|
782
|
+
})),
|
|
783
|
+
}
|
|
784
|
+
: {}),
|
|
775
785
|
};
|
|
776
786
|
}
|
|
777
787
|
}
|
|
@@ -929,7 +939,6 @@ async function executeHubSpotDirectDeployCommand({ argv, root, json, }) {
|
|
|
929
939
|
});
|
|
930
940
|
}
|
|
931
941
|
try {
|
|
932
|
-
const step = machineOutput ? undefined : reporter.step('Uploading to HubSpot');
|
|
933
942
|
const uploaded = await executeHubSpotOnlyUpload({
|
|
934
943
|
argv,
|
|
935
944
|
root,
|
|
@@ -937,6 +946,7 @@ async function executeHubSpotDirectDeployCommand({ argv, root, json, }) {
|
|
|
937
946
|
persistBinding: false,
|
|
938
947
|
uploadOnly,
|
|
939
948
|
prepared: preparedUpload,
|
|
949
|
+
...(machineOutput ? {} : { progress: reporter }),
|
|
940
950
|
});
|
|
941
951
|
const buildState = readStatus(uploaded.buildStatus) ?? 'UNKNOWN';
|
|
942
952
|
const deployState = uploaded.deployStatus
|
|
@@ -944,7 +954,6 @@ async function executeHubSpotDirectDeployCommand({ argv, root, json, }) {
|
|
|
944
954
|
: undefined;
|
|
945
955
|
const deployUnverified = uploaded.autoDeployExpected && deployState === undefined;
|
|
946
956
|
const ok = buildState === 'SUCCESS' && !deployUnverified && (!deployState || deployState === 'SUCCESS');
|
|
947
|
-
step?.[ok ? 'ok' : 'fail'](`build #${uploaded.buildId} ${buildState}${deployState ? ` · deploy ${deployState}` : deployUnverified ? ' · deploy unverified' : ''}`);
|
|
948
957
|
const result = {
|
|
949
958
|
schema_version: 1,
|
|
950
959
|
ok,
|
|
@@ -970,7 +979,7 @@ async function executeHubSpotDirectDeployCommand({ argv, root, json, }) {
|
|
|
970
979
|
reporter.done(ok ? directDeployDoneMessage(uploaded) : 'HubSpot deploy failed', ok ? 0 : 1);
|
|
971
980
|
}
|
|
972
981
|
else {
|
|
973
|
-
streamHubSpotUploadOutcome(reporter, uploaded);
|
|
982
|
+
streamHubSpotUploadOutcome(reporter, uploaded, { componentMode: 'failures' });
|
|
974
983
|
if (deployUnverified) {
|
|
975
984
|
reporter.error('HSX_E_DEPLOY_HUBSPOT', 'HubSpot auto-deploy could not be verified before the timeout.', { hint: 'Check the project deploy status in HubSpot, then retry if it did not ship.' });
|
|
976
985
|
}
|
|
@@ -1455,31 +1464,22 @@ export async function deployCommand({ argv, root, json, }) {
|
|
|
1455
1464
|
// vs deploy-then-upload). A FAILED build resolves the step as a failure so
|
|
1456
1465
|
// the failing phase is visible in the transcript, not just the exit code.
|
|
1457
1466
|
const runHubSpotUploadPhase = async () => {
|
|
1458
|
-
const
|
|
1459
|
-
|
|
1460
|
-
|
|
1461
|
-
|
|
1462
|
-
|
|
1463
|
-
|
|
1464
|
-
|
|
1465
|
-
|
|
1466
|
-
|
|
1467
|
-
|
|
1468
|
-
|
|
1469
|
-
|
|
1470
|
-
|
|
1471
|
-
|
|
1472
|
-
}
|
|
1473
|
-
const buildState = readStatus(result.buildStatus) ?? 'UNKNOWN';
|
|
1474
|
-
const detail = `build #${result.buildId} ${buildState} (${elapsedSince(uploadStartedAt)})`;
|
|
1475
|
-
if (buildState === 'SUCCESS') {
|
|
1476
|
-
uploadStep?.ok(detail);
|
|
1477
|
-
}
|
|
1478
|
-
else {
|
|
1479
|
-
uploadStep?.fail(detail);
|
|
1480
|
-
}
|
|
1467
|
+
const result = await executeHubSpotOnlyUpload({
|
|
1468
|
+
argv,
|
|
1469
|
+
root,
|
|
1470
|
+
local: localHubspotUpload,
|
|
1471
|
+
uploadOnly: hubspotUploadOnly,
|
|
1472
|
+
...(progress
|
|
1473
|
+
? {
|
|
1474
|
+
progress: {
|
|
1475
|
+
step: (label) => progress.step(label),
|
|
1476
|
+
rows: (rows) => progress.reporter.rows(rows),
|
|
1477
|
+
},
|
|
1478
|
+
}
|
|
1479
|
+
: {}),
|
|
1480
|
+
});
|
|
1481
1481
|
if (echo)
|
|
1482
|
-
streamHubSpotUploadOutcome(echo, result);
|
|
1482
|
+
streamHubSpotUploadOutcome(echo, result, { componentMode: 'failures' });
|
|
1483
1483
|
return result;
|
|
1484
1484
|
};
|
|
1485
1485
|
if (uploadFirstOrigin && controlPlanePlan) {
|
|
@@ -1635,9 +1635,20 @@ export async function deployCommand({ argv, root, json, }) {
|
|
|
1635
1635
|
const configDrift = controlPlaneRecord.configDrift;
|
|
1636
1636
|
echo.warn('HSX_W_DEPLOY_CONFIG_DRIFT', `Worker binding/config changed since the previously recorded deploy (${configDrift.previousDeployId}): fingerprint ${configDrift.previousFingerprint} -> ${configDrift.currentFingerprint}. This is expected if you intentionally changed bindings; investigate if you did not.`);
|
|
1637
1637
|
}
|
|
1638
|
+
// A combined plan cannot know the Worker URL that a real Cloudflare deploy
|
|
1639
|
+
// will mint. Keep plan mode useful and non-provider-mutating by deferring
|
|
1640
|
+
// runtime-bound HubSpot metadata until the actual deploy. Callers may still
|
|
1641
|
+
// pass --runtime-origin when they intentionally want to preview that bundle.
|
|
1642
|
+
const hubspotBundleDeferred = planOnly &&
|
|
1643
|
+
hubspotUploadRequested &&
|
|
1644
|
+
needsHubSpotBundle &&
|
|
1645
|
+
!resolveFlag(argv, '--runtime-origin');
|
|
1646
|
+
if (hubspotBundleDeferred && echo) {
|
|
1647
|
+
echo.info('HubSpot runtime bundle: deferred until deploy resolves the Worker URL.');
|
|
1648
|
+
}
|
|
1638
1649
|
// Default order (skipped when we already uploaded first above): generate the
|
|
1639
1650
|
// HubSpot bundle from the deployed Worker URL, then upload.
|
|
1640
|
-
if (!uploadFirstOrigin && hubspotUploadRequested && validation.ok) {
|
|
1651
|
+
if (!uploadFirstOrigin && hubspotUploadRequested && validation.ok && !hubspotBundleDeferred) {
|
|
1641
1652
|
const bundleStep = needsHubSpotBundle ? progress?.step('Generating HubSpot bundle') : undefined;
|
|
1642
1653
|
try {
|
|
1643
1654
|
await ensureHubSpotRuntimeProjectArtifacts({
|
|
@@ -1720,6 +1731,7 @@ export async function deployCommand({ argv, root, json, }) {
|
|
|
1720
1731
|
},
|
|
1721
1732
|
hubspot: {
|
|
1722
1733
|
uploadRequested: hubspotUploadRequested,
|
|
1734
|
+
bundleDeferred: hubspotBundleDeferred,
|
|
1723
1735
|
},
|
|
1724
1736
|
},
|
|
1725
1737
|
root,
|
|
@@ -2069,12 +2081,13 @@ function buildDeploySummaryRows(input) {
|
|
|
2069
2081
|
* diagnosable from the terminal, not the HubSpot UI (run-008 build #2 failed
|
|
2070
2082
|
* silently here).
|
|
2071
2083
|
*/
|
|
2072
|
-
function streamHubSpotUploadOutcome(reporter, upload) {
|
|
2084
|
+
function streamHubSpotUploadOutcome(reporter, upload, options = {}) {
|
|
2085
|
+
const componentMode = options.componentMode ?? 'all';
|
|
2073
2086
|
for (const subbuild of readSubbuildStatuses(upload.buildStatus)) {
|
|
2074
|
-
if (subbuild.status === 'SUCCESS') {
|
|
2087
|
+
if (subbuild.status === 'SUCCESS' && componentMode === 'all') {
|
|
2075
2088
|
reporter.info(` [ok] ${subbuild.name}${subbuild.type ? ` (${subbuild.type})` : ''}`);
|
|
2076
2089
|
}
|
|
2077
|
-
else {
|
|
2090
|
+
else if (subbuild.status !== 'SUCCESS' && componentMode !== 'none') {
|
|
2078
2091
|
reporter.error('HSX_E_DEPLOY_HUBSPOT_BUILD_COMPONENT', `${subbuild.name}${subbuild.type ? ` (${subbuild.type})` : ''}: ${subbuild.errorMessage ?? subbuild.status}`);
|
|
2079
2092
|
}
|
|
2080
2093
|
}
|
|
@@ -4276,15 +4289,35 @@ function runCloudflareCommand(command, options) {
|
|
|
4276
4289
|
},
|
|
4277
4290
|
})));
|
|
4278
4291
|
}
|
|
4279
|
-
async function executeHubSpotOnlyUpload({ argv, root, local, persistBinding = true, uploadOnly, prepared, }) {
|
|
4292
|
+
async function executeHubSpotOnlyUpload({ argv, root, local, persistBinding = true, uploadOnly, prepared, progress, }) {
|
|
4280
4293
|
// The HubSpot project name must match the name baked into the generated
|
|
4281
4294
|
// bundle (hsproject.json + app-hsmeta uid), not the on-disk directory name.
|
|
4282
4295
|
// Using basename(root) breaks when the project lives in a generic dir (e.g.
|
|
4283
4296
|
// `app/`): the upload targets a project named "app" while the bundle declares
|
|
4284
4297
|
// the real project id, and HubSpot rejects the mismatch with an opaque 400.
|
|
4285
4298
|
const projectName = prepared?.projectName ?? (await resolveHubSpotProjectName(argv, root));
|
|
4286
|
-
|
|
4299
|
+
let intermediateRepresentation = prepared?.intermediateRepresentation ??
|
|
4287
4300
|
(await readHubSpotProjectIntermediateRepresentation(root));
|
|
4301
|
+
if (prepared?.translateProject) {
|
|
4302
|
+
const translateStartedAt = Date.now();
|
|
4303
|
+
const translateStep = progress?.step('Translating HubSpot components');
|
|
4304
|
+
try {
|
|
4305
|
+
const translated = await prepared.translateProject();
|
|
4306
|
+
if (translated.skippedHsMetaFiles.length > 0 && !prepared.force) {
|
|
4307
|
+
throw new Error(`HubSpot skipped ${translated.skippedHsMetaFiles.length} component metadata file(s): ${translated.skippedHsMetaFiles.join(', ')}. Review them or re-run with --force.`);
|
|
4308
|
+
}
|
|
4309
|
+
intermediateRepresentation = translated.intermediateRepresentation;
|
|
4310
|
+
const nodes = isRecord(intermediateRepresentation)
|
|
4311
|
+
? intermediateRepresentation.intermediateNodesIndexedByUid
|
|
4312
|
+
: undefined;
|
|
4313
|
+
const componentCount = isRecord(nodes) ? Object.keys(nodes).length : 0;
|
|
4314
|
+
translateStep?.ok(`${componentCount} components · ${elapsedSince(translateStartedAt)}`);
|
|
4315
|
+
}
|
|
4316
|
+
catch (error) {
|
|
4317
|
+
translateStep?.fail(`${firstErrorLine(error)} · ${elapsedSince(translateStartedAt)}`);
|
|
4318
|
+
throw error;
|
|
4319
|
+
}
|
|
4320
|
+
}
|
|
4288
4321
|
const appUid = prepared?.appUid ??
|
|
4289
4322
|
readAppUidFromIntermediateRepresentation(intermediateRepresentation) ??
|
|
4290
4323
|
(await readAppUidFromProjectSource(root));
|
|
@@ -4294,10 +4327,20 @@ async function executeHubSpotOnlyUpload({ argv, root, local, persistBinding = tr
|
|
|
4294
4327
|
const developerAccountId = prepared?.developerAccountId ??
|
|
4295
4328
|
resolveFlag(argv, '--developer-account-id') ??
|
|
4296
4329
|
process.env.HSX_HUBSPOT_DEVELOPER_ACCOUNT_ID;
|
|
4297
|
-
const
|
|
4298
|
-
|
|
4299
|
-
|
|
4300
|
-
|
|
4330
|
+
const packageStartedAt = Date.now();
|
|
4331
|
+
const packageStep = progress?.step('Packaging HubSpot project');
|
|
4332
|
+
let archivePath;
|
|
4333
|
+
try {
|
|
4334
|
+
archivePath = await writeLocalHubSpotProjectArchive(root, projectName, {
|
|
4335
|
+
sourceOnly: intermediateRepresentation !== undefined,
|
|
4336
|
+
...(prepared ? { expectedSourceFingerprint: prepared.sourceFingerprint } : {}),
|
|
4337
|
+
});
|
|
4338
|
+
packageStep?.ok(elapsedSince(packageStartedAt));
|
|
4339
|
+
}
|
|
4340
|
+
catch (error) {
|
|
4341
|
+
packageStep?.fail(`${firstErrorLine(error)} · ${elapsedSince(packageStartedAt)}`);
|
|
4342
|
+
throw error;
|
|
4343
|
+
}
|
|
4301
4344
|
const client = prepared
|
|
4302
4345
|
? await prepared.createClient()
|
|
4303
4346
|
: await createDeployHubSpotDeveloperClient({
|
|
@@ -4306,29 +4349,52 @@ async function executeHubSpotOnlyUpload({ argv, root, local, persistBinding = tr
|
|
|
4306
4349
|
allowedOperations: ['hubspot-project-upload'],
|
|
4307
4350
|
cwd: root,
|
|
4308
4351
|
});
|
|
4309
|
-
await client.projects.ensureProject({ projectName });
|
|
4310
4352
|
const hsproject = await readHubSpotProjectConfig(root);
|
|
4311
4353
|
const configuredPlatformVersion = typeof hsproject.platformVersion === 'string' ? hsproject.platformVersion : undefined;
|
|
4312
4354
|
const platformVersion = prepared?.platformVersion ??
|
|
4313
4355
|
resolveFlag(argv, '--platform-version') ??
|
|
4314
4356
|
configuredPlatformVersion ??
|
|
4315
4357
|
'2026.03';
|
|
4316
|
-
const upload = await client.projects.upload({
|
|
4317
|
-
projectName,
|
|
4318
|
-
archivePath,
|
|
4319
|
-
message: prepared?.buildMessage ?? resolveFlag(argv, '--message') ?? 'HS-X HubSpot-only deploy',
|
|
4320
|
-
platformVersion,
|
|
4321
|
-
intermediateRepresentation,
|
|
4322
|
-
skipAutoDeploy: uploadOnly,
|
|
4323
|
-
});
|
|
4324
4358
|
const timeoutMs = prepared?.buildTimeoutMs ?? Number(resolveFlag(argv, '--hubspot-build-timeout-ms') ?? '60000');
|
|
4325
|
-
const
|
|
4326
|
-
|
|
4327
|
-
|
|
4328
|
-
|
|
4329
|
-
|
|
4330
|
-
|
|
4331
|
-
|
|
4359
|
+
const uploadStartedAt = Date.now();
|
|
4360
|
+
const uploadStep = progress?.step('Uploading project');
|
|
4361
|
+
let upload;
|
|
4362
|
+
try {
|
|
4363
|
+
await client.projects.ensureProject({ projectName });
|
|
4364
|
+
upload = await client.projects.upload({
|
|
4365
|
+
projectName,
|
|
4366
|
+
archivePath,
|
|
4367
|
+
message: prepared?.buildMessage ?? resolveFlag(argv, '--message') ?? 'HS-X HubSpot-only deploy',
|
|
4368
|
+
platformVersion,
|
|
4369
|
+
intermediateRepresentation,
|
|
4370
|
+
skipAutoDeploy: uploadOnly,
|
|
4371
|
+
});
|
|
4372
|
+
uploadStep?.ok(`build #${upload.buildId} · ${elapsedSince(uploadStartedAt)}`);
|
|
4373
|
+
}
|
|
4374
|
+
catch (error) {
|
|
4375
|
+
uploadStep?.fail(`${firstErrorLine(error)} · ${elapsedSince(uploadStartedAt)}`);
|
|
4376
|
+
throw error;
|
|
4377
|
+
}
|
|
4378
|
+
const buildStartedAt = Date.now();
|
|
4379
|
+
const buildStep = progress?.step(`Building project (build #${upload.buildId})`);
|
|
4380
|
+
const reportedComponents = new Map();
|
|
4381
|
+
let buildStatus;
|
|
4382
|
+
try {
|
|
4383
|
+
buildStatus = await waitForHubSpotBuildStatus({
|
|
4384
|
+
client,
|
|
4385
|
+
projectName,
|
|
4386
|
+
buildId: upload.buildId,
|
|
4387
|
+
timeoutMs,
|
|
4388
|
+
intervalMs: 1000,
|
|
4389
|
+
onStatus: (status) => streamHubSpotComponentProgress(progress, status, reportedComponents, 'build'),
|
|
4390
|
+
});
|
|
4391
|
+
}
|
|
4392
|
+
catch (error) {
|
|
4393
|
+
buildStep?.fail(`${firstErrorLine(error)} · ${elapsedSince(buildStartedAt)}`);
|
|
4394
|
+
throw error;
|
|
4395
|
+
}
|
|
4396
|
+
const buildState = readStatus(buildStatus) ?? 'UNKNOWN';
|
|
4397
|
+
buildStep?.[buildState === 'SUCCESS' ? 'ok' : 'fail'](`${buildState} · ${elapsedSince(buildStartedAt)}`);
|
|
4332
4398
|
const autoDeployEnabled = readBooleanProperty(buildStatus, 'isAutoDeployEnabled');
|
|
4333
4399
|
if (uploadOnly || readStatus(buildStatus) !== 'SUCCESS' || autoDeployEnabled) {
|
|
4334
4400
|
const appId = readStatus(buildStatus) === 'SUCCESS'
|
|
@@ -4346,15 +4412,32 @@ async function executeHubSpotOnlyUpload({ argv, root, local, persistBinding = tr
|
|
|
4346
4412
|
// Normal uploads can auto-deploy; uploadOnly sends skipAutoDeploy and is
|
|
4347
4413
|
// therefore a strict build-only result.
|
|
4348
4414
|
const autoDeployExpected = !uploadOnly && autoDeployEnabled === true && readStatus(buildStatus) === 'SUCCESS';
|
|
4349
|
-
|
|
4350
|
-
|
|
4351
|
-
|
|
4352
|
-
|
|
4353
|
-
|
|
4354
|
-
|
|
4355
|
-
|
|
4356
|
-
|
|
4357
|
-
|
|
4415
|
+
let autoDeployStatus;
|
|
4416
|
+
if (autoDeployExpected) {
|
|
4417
|
+
const deployStartedAt = Date.now();
|
|
4418
|
+
const deployStep = progress?.step(`Auto-deploying project (build #${upload.buildId})`);
|
|
4419
|
+
const reportedDeployComponents = new Map();
|
|
4420
|
+
try {
|
|
4421
|
+
const autoDeploy = await waitForHubSpotAutoDeploy({
|
|
4422
|
+
client,
|
|
4423
|
+
projectName,
|
|
4424
|
+
buildId: upload.buildId,
|
|
4425
|
+
buildStatus,
|
|
4426
|
+
timeoutMs,
|
|
4427
|
+
onStatus: (status) => streamHubSpotComponentProgress(progress, status, reportedDeployComponents, 'deploy'),
|
|
4428
|
+
});
|
|
4429
|
+
autoDeployStatus = autoDeploy.status;
|
|
4430
|
+
const state = readStatus(autoDeployStatus) ?? 'UNKNOWN';
|
|
4431
|
+
if (state === 'SUCCESS') {
|
|
4432
|
+
streamHubSpotComponentProgress(progress, buildStatus, reportedDeployComponents, 'deploy');
|
|
4433
|
+
}
|
|
4434
|
+
deployStep?.[state === 'SUCCESS' ? 'ok' : 'fail'](`deploy #${autoDeploy.deployId} ${state} · ${elapsedSince(deployStartedAt)}`);
|
|
4435
|
+
}
|
|
4436
|
+
catch (error) {
|
|
4437
|
+
deployStep?.fail(`${firstErrorLine(error)} · ${elapsedSince(deployStartedAt)}`);
|
|
4438
|
+
throw error;
|
|
4439
|
+
}
|
|
4440
|
+
}
|
|
4358
4441
|
return {
|
|
4359
4442
|
projectName,
|
|
4360
4443
|
archivePath,
|
|
@@ -4370,15 +4453,31 @@ async function executeHubSpotOnlyUpload({ argv, root, local, persistBinding = tr
|
|
|
4370
4453
|
local,
|
|
4371
4454
|
};
|
|
4372
4455
|
}
|
|
4373
|
-
const
|
|
4374
|
-
|
|
4375
|
-
|
|
4376
|
-
|
|
4377
|
-
|
|
4378
|
-
|
|
4379
|
-
|
|
4380
|
-
|
|
4381
|
-
|
|
4456
|
+
const deployStartedAt = Date.now();
|
|
4457
|
+
const deployStep = progress?.step(`Deploying project (build #${upload.buildId})`);
|
|
4458
|
+
const reportedDeployComponents = new Map();
|
|
4459
|
+
let deployed;
|
|
4460
|
+
try {
|
|
4461
|
+
deployed = await deployHubSpotProjectBuild({
|
|
4462
|
+
client,
|
|
4463
|
+
projectName,
|
|
4464
|
+
buildId: upload.buildId,
|
|
4465
|
+
force: prepared?.force ?? argv.includes('--force'),
|
|
4466
|
+
legacy: isLegacyProject(platformVersion),
|
|
4467
|
+
timeoutMs,
|
|
4468
|
+
onStatus: (status) => streamHubSpotComponentProgress(progress, status, reportedDeployComponents, 'deploy'),
|
|
4469
|
+
executeDeploy: (request) => withTransientDeployRetry(() => client.projects.deployBuild(request)),
|
|
4470
|
+
});
|
|
4471
|
+
const state = readStatus(deployed.status) ?? 'UNKNOWN';
|
|
4472
|
+
if (state === 'SUCCESS') {
|
|
4473
|
+
streamHubSpotComponentProgress(progress, buildStatus, reportedDeployComponents, 'deploy');
|
|
4474
|
+
}
|
|
4475
|
+
deployStep?.[state === 'SUCCESS' ? 'ok' : 'fail'](`deploy #${deployed.deployId} ${state} · ${elapsedSince(deployStartedAt)}`);
|
|
4476
|
+
}
|
|
4477
|
+
catch (error) {
|
|
4478
|
+
deployStep?.fail(`${firstErrorLine(error)} · ${elapsedSince(deployStartedAt)}`);
|
|
4479
|
+
throw error;
|
|
4480
|
+
}
|
|
4382
4481
|
const deployId = deployed.deployId;
|
|
4383
4482
|
const deployStatus = deployed.status;
|
|
4384
4483
|
const appId = await optionalHubSpotAppId(client, projectName, appUid);
|
|
@@ -4592,18 +4691,30 @@ async function resolveHubSpotAppAuthUrl(argv, root) {
|
|
|
4592
4691
|
function readStatus(value) {
|
|
4593
4692
|
return isRecord(value) && typeof value.status === 'string' ? value.status : undefined;
|
|
4594
4693
|
}
|
|
4595
|
-
|
|
4596
|
-
|
|
4597
|
-
|
|
4598
|
-
|
|
4599
|
-
|
|
4600
|
-
|
|
4601
|
-
|
|
4602
|
-
|
|
4603
|
-
|
|
4604
|
-
|
|
4605
|
-
|
|
4606
|
-
|
|
4694
|
+
const readSubbuildStatuses = readHubSpotSubbuildStatuses;
|
|
4695
|
+
const HUBSPOT_COMPONENT_TERMINAL_STATES = new Set(['SUCCESS', 'FAILURE', 'ERROR', 'CANCELLED']);
|
|
4696
|
+
/** Emit each HubSpot component once, when it reaches a terminal build state. */
|
|
4697
|
+
function streamHubSpotComponentProgress(progress, buildStatus, reported, phase) {
|
|
4698
|
+
if (!progress)
|
|
4699
|
+
return;
|
|
4700
|
+
const rows = [];
|
|
4701
|
+
for (const component of readSubbuildStatuses(buildStatus)) {
|
|
4702
|
+
const state = component.status.toUpperCase();
|
|
4703
|
+
if (!HUBSPOT_COMPONENT_TERMINAL_STATES.has(state))
|
|
4704
|
+
continue;
|
|
4705
|
+
const key = `${component.name}\0${component.type ?? ''}`;
|
|
4706
|
+
if (reported.get(key) === state)
|
|
4707
|
+
continue;
|
|
4708
|
+
reported.set(key, state);
|
|
4709
|
+
rows.push({
|
|
4710
|
+
key: component.name,
|
|
4711
|
+
value: component.type ?? 'COMPONENT',
|
|
4712
|
+
status: state === 'SUCCESS' ? 'ok' : 'fail',
|
|
4713
|
+
detail: `${phase} ${state}`,
|
|
4714
|
+
...(component.errorMessage ? { hint: component.errorMessage } : {}),
|
|
4715
|
+
});
|
|
4716
|
+
}
|
|
4717
|
+
progress.rows(rows);
|
|
4607
4718
|
}
|
|
4608
4719
|
function hasFlag(argv, flag) {
|
|
4609
4720
|
return argv.includes(flag);
|