@hubspot/cli 8.0.10-experimental.3 → 8.0.10-experimental.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/api/migrate.js +5 -1
- package/commands/account/auth.js +5 -15
- package/commands/account/use.js +4 -14
- package/commands/app/migrate.js +2 -2
- package/commands/auth.js +6 -10
- package/commands/cms/__tests__/upload.test.js +4 -0
- package/commands/hubdb/clear.js +0 -4
- package/commands/hubdb/delete.js +0 -4
- package/commands/hubdb/fetch.js +0 -4
- package/commands/init.js +0 -4
- package/commands/project/__tests__/migrate.test.js +2 -2
- package/commands/project/dev/index.js +19 -29
- package/commands/project/download.js +1 -5
- package/commands/project/migrate.js +6 -6
- package/commands/sandbox/__tests__/create.test.js +48 -1
- package/commands/sandbox/create.js +30 -3
- package/commands/testAccount/create.js +0 -4
- package/lang/en.d.ts +2 -3
- package/lang/en.js +2 -3
- package/lib/__tests__/buildAccount.test.js +52 -1
- package/lib/__tests__/sandboxSync.test.d.ts +1 -0
- package/lib/__tests__/sandboxSync.test.js +147 -0
- package/lib/__tests__/sandboxes.test.js +29 -1
- package/lib/accountAuth.js +0 -4
- package/lib/buildAccount.d.ts +6 -1
- package/lib/buildAccount.js +42 -9
- package/lib/constants.d.ts +2 -0
- package/lib/constants.js +2 -0
- package/lib/projects/__tests__/components.test.js +0 -14
- package/lib/projects/components.js +2 -12
- package/lib/projects/localDev/AppDevModeInterface.js +0 -4
- package/lib/projects/localDev/LocalDevManager_DEPRECATED.js +0 -4
- package/lib/projects/localDev/helpers/account.js +11 -5
- package/lib/prompts/downloadProjectPrompt.js +10 -11
- package/lib/prompts/installAppPrompt.js +2 -3
- package/lib/prompts/personalAccessKeyPrompt.js +2 -3
- package/lib/prompts/projectDevTargetAccountPrompt.js +16 -13
- package/lib/prompts/selectHubDBTablePrompt.js +4 -8
- package/lib/prompts/selectPublicAppForMigrationPrompt.js +6 -12
- package/lib/sandboxSync.d.ts +4 -0
- package/lib/sandboxSync.js +102 -0
- package/lib/sandboxes.d.ts +9 -1
- package/lib/sandboxes.js +21 -0
- package/lib/theme/__tests__/migrate.test.js +6 -11
- package/lib/theme/migrate.d.ts +1 -1
- package/lib/theme/migrate.js +1 -5
- package/package.json +3 -3
- package/lib/errors/PromptExitError.d.ts +0 -4
- package/lib/errors/PromptExitError.js +0 -8
package/api/migrate.js
CHANGED
|
@@ -23,7 +23,11 @@ function mapPlatformVersionToEnum(platformVersion) {
|
|
|
23
23
|
if (platformVersion === PLATFORM_VERSIONS.unstable) {
|
|
24
24
|
return PLATFORM_VERSIONS.unstable.toUpperCase();
|
|
25
25
|
}
|
|
26
|
-
|
|
26
|
+
const reformattedPlatformVersion = platformVersion
|
|
27
|
+
.replace('.', '_')
|
|
28
|
+
.replace('-', '_')
|
|
29
|
+
.toUpperCase();
|
|
30
|
+
return `V${reformattedPlatformVersion}`;
|
|
27
31
|
}
|
|
28
32
|
export async function initializeAppMigration(accountId, applicationId, platformVersion) {
|
|
29
33
|
return http.post(accountId, {
|
package/commands/account/auth.js
CHANGED
|
@@ -10,7 +10,6 @@ import { makeYargsBuilder } from '../../lib/yargsUtils.js';
|
|
|
10
10
|
import { commands } from '../../lang/en.js';
|
|
11
11
|
import { uiLogger } from '../../lib/ui/logger.js';
|
|
12
12
|
import { authenticateNewAccount } from '../../lib/accountAuth.js';
|
|
13
|
-
import { PromptExitError } from '../../lib/errors/PromptExitError.js';
|
|
14
13
|
const TRACKING_STATUS = {
|
|
15
14
|
STARTED: 'started',
|
|
16
15
|
ERROR: 'error',
|
|
@@ -36,20 +35,11 @@ async function handler(args) {
|
|
|
36
35
|
await trackAuthAction('account-auth', authType, TRACKING_STATUS.STARTED);
|
|
37
36
|
}
|
|
38
37
|
handleExit(deleteConfigFileIfEmpty);
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
accountId: parsedUserProvidedAccountId,
|
|
45
|
-
});
|
|
46
|
-
}
|
|
47
|
-
catch (e) {
|
|
48
|
-
if (e instanceof PromptExitError) {
|
|
49
|
-
process.exit(e.exitCode);
|
|
50
|
-
}
|
|
51
|
-
throw e;
|
|
52
|
-
}
|
|
38
|
+
const updatedConfig = await authenticateNewAccount({
|
|
39
|
+
env: args.qa ? ENVIRONMENTS.QA : ENVIRONMENTS.PROD,
|
|
40
|
+
providedPersonalAccessKey,
|
|
41
|
+
accountId: parsedUserProvidedAccountId,
|
|
42
|
+
});
|
|
53
43
|
if (!updatedConfig) {
|
|
54
44
|
if (!disableTracking) {
|
|
55
45
|
await trackAuthAction('account-auth', authType, TRACKING_STATUS.ERROR);
|
package/commands/account/use.js
CHANGED
|
@@ -7,7 +7,6 @@ import { uiLogger } from '../../lib/ui/logger.js';
|
|
|
7
7
|
import { selectAccountFromConfig, AUTHENTICATE_NEW_ACCOUNT_VALUE, } from '../../lib/prompts/accountsPrompt.js';
|
|
8
8
|
import { makeYargsBuilder } from '../../lib/yargsUtils.js';
|
|
9
9
|
import { authenticateNewAccount } from '../../lib/accountAuth.js';
|
|
10
|
-
import { PromptExitError } from '../../lib/errors/PromptExitError.js';
|
|
11
10
|
import { EXIT_CODES } from '../../lib/enums/exitCodes.js';
|
|
12
11
|
const command = 'use [account]';
|
|
13
12
|
const describe = commands.account.subcommands.use.describe;
|
|
@@ -25,19 +24,10 @@ async function handler(args) {
|
|
|
25
24
|
}
|
|
26
25
|
}
|
|
27
26
|
if (newDefaultAccount === AUTHENTICATE_NEW_ACCOUNT_VALUE) {
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
setAsDefaultAccount: true,
|
|
33
|
-
});
|
|
34
|
-
}
|
|
35
|
-
catch (e) {
|
|
36
|
-
if (e instanceof PromptExitError) {
|
|
37
|
-
process.exit(e.exitCode);
|
|
38
|
-
}
|
|
39
|
-
throw e;
|
|
40
|
-
}
|
|
27
|
+
const updatedConfig = await authenticateNewAccount({
|
|
28
|
+
env: args.qa ? ENVIRONMENTS.QA : ENVIRONMENTS.PROD,
|
|
29
|
+
setAsDefaultAccount: true,
|
|
30
|
+
});
|
|
41
31
|
if (!updatedConfig) {
|
|
42
32
|
process.exit(EXIT_CODES.ERROR);
|
|
43
33
|
}
|
package/commands/app/migrate.js
CHANGED
|
@@ -8,7 +8,7 @@ import { EXIT_CODES } from '../../lib/enums/exitCodes.js';
|
|
|
8
8
|
import { migrateApp } from '../../lib/app/migrate.js';
|
|
9
9
|
import { getIsInProject } from '../../lib/projects/config.js';
|
|
10
10
|
import { makeYargsBuilder } from '../../lib/yargsUtils.js';
|
|
11
|
-
const { v2025_2 } = PLATFORM_VERSIONS;
|
|
11
|
+
const { v2025_2, v2026_03_beta } = PLATFORM_VERSIONS;
|
|
12
12
|
const command = 'migrate';
|
|
13
13
|
const describe = commands.project.migrateApp.describe;
|
|
14
14
|
export function handlerGenerator(commandTrackingName) {
|
|
@@ -67,7 +67,7 @@ function appMigrateBuilder(yargs) {
|
|
|
67
67
|
},
|
|
68
68
|
'platform-version': {
|
|
69
69
|
type: 'string',
|
|
70
|
-
choices: [v2025_2],
|
|
70
|
+
choices: [v2025_2, v2026_03_beta],
|
|
71
71
|
default: v2025_2,
|
|
72
72
|
},
|
|
73
73
|
unstable: {
|
package/commands/auth.js
CHANGED
|
@@ -16,7 +16,6 @@ import { authenticateWithOauth } from '../lib/oauth.js';
|
|
|
16
16
|
import { EXIT_CODES } from '../lib/enums/exitCodes.js';
|
|
17
17
|
import { uiFeatureHighlight } from '../lib/ui/index.js';
|
|
18
18
|
import { logError } from '../lib/errorHandlers/index.js';
|
|
19
|
-
import { PromptExitError } from '../lib/errors/PromptExitError.js';
|
|
20
19
|
import { commands } from '../lang/en.js';
|
|
21
20
|
import { uiLogger } from '../lib/ui/logger.js';
|
|
22
21
|
import { parseStringToNumber } from '../lib/parsing.js';
|
|
@@ -91,21 +90,18 @@ async function handler(args) {
|
|
|
91
90
|
}
|
|
92
91
|
break;
|
|
93
92
|
case PERSONAL_ACCESS_KEY_AUTH_METHOD.value:
|
|
93
|
+
const { personalAccessKey } = providedPersonalAccessKey
|
|
94
|
+
? { personalAccessKey: providedPersonalAccessKey }
|
|
95
|
+
: await personalAccessKeyPrompt({
|
|
96
|
+
env,
|
|
97
|
+
account: parsedUserProvidedAccountId,
|
|
98
|
+
});
|
|
94
99
|
try {
|
|
95
|
-
const { personalAccessKey } = providedPersonalAccessKey
|
|
96
|
-
? { personalAccessKey: providedPersonalAccessKey }
|
|
97
|
-
: await personalAccessKeyPrompt({
|
|
98
|
-
env,
|
|
99
|
-
account: parsedUserProvidedAccountId,
|
|
100
|
-
});
|
|
101
100
|
token = await getAccessToken(personalAccessKey, env);
|
|
102
101
|
defaultName = token.hubName ? toKebabCase(token.hubName) : undefined;
|
|
103
102
|
updatedConfig = await updateConfigWithAccessToken(token, personalAccessKey, env);
|
|
104
103
|
}
|
|
105
104
|
catch (e) {
|
|
106
|
-
if (e instanceof PromptExitError) {
|
|
107
|
-
process.exit(e.exitCode);
|
|
108
|
-
}
|
|
109
105
|
logError(e);
|
|
110
106
|
}
|
|
111
107
|
if (!updatedConfig) {
|
|
@@ -8,6 +8,7 @@ import * as modulesLib from '@hubspot/local-dev-lib/cms/modules';
|
|
|
8
8
|
import * as ignoreRulesLib from '@hubspot/local-dev-lib/ignoreRules';
|
|
9
9
|
import * as themesLib from '@hubspot/local-dev-lib/cms/themes';
|
|
10
10
|
import * as configLib from '@hubspot/local-dev-lib/config';
|
|
11
|
+
import * as handleFieldsJSLib from '@hubspot/local-dev-lib/cms/handleFieldsJS';
|
|
11
12
|
import { uiLogger } from '../../../lib/ui/logger.js';
|
|
12
13
|
import * as errorHandlers from '../../../lib/errorHandlers/index.js';
|
|
13
14
|
import * as commonOpts from '../../../lib/commonOpts.js';
|
|
@@ -26,6 +27,7 @@ vi.mock('@hubspot/local-dev-lib/cms/modules');
|
|
|
26
27
|
vi.mock('@hubspot/local-dev-lib/ignoreRules');
|
|
27
28
|
vi.mock('@hubspot/local-dev-lib/cms/themes');
|
|
28
29
|
vi.mock('@hubspot/local-dev-lib/config');
|
|
30
|
+
vi.mock('@hubspot/local-dev-lib/cms/handleFieldsJS');
|
|
29
31
|
vi.mock('../../../lib/errorHandlers/index.js');
|
|
30
32
|
vi.mock('../../../lib/commonOpts.js');
|
|
31
33
|
vi.mock('../../../lib/prompts/uploadPrompt.js');
|
|
@@ -53,6 +55,7 @@ const hasUploadErrorsSpy = vi.spyOn(uploadFolderLib, 'hasUploadErrors');
|
|
|
53
55
|
const processExitSpy = vi.spyOn(process, 'exit');
|
|
54
56
|
const logErrorSpy = vi.spyOn(errorHandlers, 'logError');
|
|
55
57
|
const getConfigAccountIfExistsSpy = vi.spyOn(configLib, 'getConfigAccountIfExists');
|
|
58
|
+
const isConvertableFieldJsSpy = vi.spyOn(handleFieldsJSLib, 'isConvertableFieldJs');
|
|
56
59
|
describe('commands/cms/upload', () => {
|
|
57
60
|
beforeEach(() => {
|
|
58
61
|
// @ts-expect-error Mock implementation
|
|
@@ -67,6 +70,7 @@ describe('commands/cms/upload', () => {
|
|
|
67
70
|
getThemePreviewUrlSpy.mockReturnValue(undefined);
|
|
68
71
|
// Mock config to prevent reading actual config file in CI
|
|
69
72
|
getConfigAccountIfExistsSpy.mockReturnValue(undefined);
|
|
73
|
+
isConvertableFieldJsSpy.mockReturnValue(false);
|
|
70
74
|
});
|
|
71
75
|
describe('command', () => {
|
|
72
76
|
it('should have the correct command structure', () => {
|
package/commands/hubdb/clear.js
CHANGED
|
@@ -1,6 +1,5 @@
|
|
|
1
1
|
import { uiLogger } from '../../lib/ui/logger.js';
|
|
2
2
|
import { logError } from '../../lib/errorHandlers/index.js';
|
|
3
|
-
import { PromptExitError } from '../../lib/errors/PromptExitError.js';
|
|
4
3
|
import { clearHubDbTableRows } from '@hubspot/local-dev-lib/hubdb';
|
|
5
4
|
import { publishTable } from '@hubspot/local-dev-lib/api/hubdb';
|
|
6
5
|
import { selectHubDBTablePrompt } from '../../lib/prompts/selectHubDBTablePrompt.js';
|
|
@@ -30,9 +29,6 @@ async function handler(args) {
|
|
|
30
29
|
}
|
|
31
30
|
}
|
|
32
31
|
catch (e) {
|
|
33
|
-
if (e instanceof PromptExitError) {
|
|
34
|
-
process.exit(e.exitCode);
|
|
35
|
-
}
|
|
36
32
|
logError(e);
|
|
37
33
|
}
|
|
38
34
|
}
|
package/commands/hubdb/delete.js
CHANGED
|
@@ -1,6 +1,5 @@
|
|
|
1
1
|
import { uiLogger } from '../../lib/ui/logger.js';
|
|
2
2
|
import { logError } from '../../lib/errorHandlers/index.js';
|
|
3
|
-
import { PromptExitError } from '../../lib/errors/PromptExitError.js';
|
|
4
3
|
import { deleteTable } from '@hubspot/local-dev-lib/api/hubdb';
|
|
5
4
|
import { trackCommandUsage } from '../../lib/usageTracking.js';
|
|
6
5
|
import { selectHubDBTablePrompt } from '../../lib/prompts/selectHubDBTablePrompt.js';
|
|
@@ -35,9 +34,6 @@ async function handler(args) {
|
|
|
35
34
|
process.exit(EXIT_CODES.SUCCESS);
|
|
36
35
|
}
|
|
37
36
|
catch (e) {
|
|
38
|
-
if (e instanceof PromptExitError) {
|
|
39
|
-
process.exit(e.exitCode);
|
|
40
|
-
}
|
|
41
37
|
uiLogger.error(commands.hubdb.subcommands.delete.errors.delete(args.tableId || ''));
|
|
42
38
|
logError(e);
|
|
43
39
|
}
|
package/commands/hubdb/fetch.js
CHANGED
|
@@ -1,6 +1,5 @@
|
|
|
1
1
|
import { uiLogger } from '../../lib/ui/logger.js';
|
|
2
2
|
import { logError } from '../../lib/errorHandlers/index.js';
|
|
3
|
-
import { PromptExitError } from '../../lib/errors/PromptExitError.js';
|
|
4
3
|
import { downloadHubDbTable } from '@hubspot/local-dev-lib/hubdb';
|
|
5
4
|
import { selectHubDBTablePrompt } from '../../lib/prompts/selectHubDBTablePrompt.js';
|
|
6
5
|
import { trackCommandUsage } from '../../lib/usageTracking.js';
|
|
@@ -23,9 +22,6 @@ async function handler(args) {
|
|
|
23
22
|
uiLogger.success(commands.hubdb.subcommands.fetch.success.fetch(tableId, filePath));
|
|
24
23
|
}
|
|
25
24
|
catch (e) {
|
|
26
|
-
if (e instanceof PromptExitError) {
|
|
27
|
-
process.exit(e.exitCode);
|
|
28
|
-
}
|
|
29
25
|
logError(e);
|
|
30
26
|
}
|
|
31
27
|
}
|
package/commands/init.js
CHANGED
|
@@ -12,7 +12,6 @@ import { setCLILogLevel } from '../lib/commonOpts.js';
|
|
|
12
12
|
import { makeYargsBuilder } from '../lib/yargsUtils.js';
|
|
13
13
|
import { handleExit } from '../lib/process.js';
|
|
14
14
|
import { debugError, logError } from '../lib/errorHandlers/index.js';
|
|
15
|
-
import { PromptExitError } from '../lib/errors/PromptExitError.js';
|
|
16
15
|
import { trackCommandUsage, trackAuthAction } from '../lib/usageTracking.js';
|
|
17
16
|
import { promptUser } from '../lib/prompts/promptUtils.js';
|
|
18
17
|
import { OAUTH_FLOW, personalAccessKeyPrompt, } from '../lib/prompts/personalAccessKeyPrompt.js';
|
|
@@ -151,9 +150,6 @@ async function handler(args) {
|
|
|
151
150
|
process.exit(EXIT_CODES.SUCCESS);
|
|
152
151
|
}
|
|
153
152
|
catch (err) {
|
|
154
|
-
if (err instanceof PromptExitError) {
|
|
155
|
-
process.exit(err.exitCode);
|
|
156
|
-
}
|
|
157
153
|
logError(err);
|
|
158
154
|
if (!disableTracking) {
|
|
159
155
|
await trackAuthAction('init', authType, TRACKING_STATUS.ERROR, parsedUserProvidedAccountId);
|
|
@@ -9,7 +9,7 @@ import { uiBetaTag, uiCommandReference } from '../../../lib/ui/index.js';
|
|
|
9
9
|
vi.mock('../../../lib/app/migrate');
|
|
10
10
|
vi.mock('../../../lib/projects/config');
|
|
11
11
|
vi.mock('../../../lib/ui');
|
|
12
|
-
const { v2025_2 } = PLATFORM_VERSIONS;
|
|
12
|
+
const { v2025_2, v2026_03_beta, v2026_03 } = PLATFORM_VERSIONS;
|
|
13
13
|
describe('commands/project/migrate', () => {
|
|
14
14
|
const yargsMock = yargs;
|
|
15
15
|
const optionsSpy = vi.spyOn(yargsMock, 'option').mockReturnValue(yargsMock);
|
|
@@ -48,7 +48,7 @@ describe('commands/project/migrate', () => {
|
|
|
48
48
|
migrateCommand.builder(yargsMock);
|
|
49
49
|
expect(optionsSpy).toHaveBeenCalledWith('platform-version', {
|
|
50
50
|
type: 'string',
|
|
51
|
-
choices: [v2025_2],
|
|
51
|
+
choices: [v2025_2, v2026_03_beta, v2026_03],
|
|
52
52
|
default: v2025_2,
|
|
53
53
|
});
|
|
54
54
|
expect(optionsSpy).toHaveBeenCalledWith('unstable', {
|
|
@@ -12,7 +12,6 @@ import { loadProfile } from '../../../lib/projects/projectProfiles.js';
|
|
|
12
12
|
import { commands } from '../../../lang/en.js';
|
|
13
13
|
import { uiLogger } from '../../../lib/ui/logger.js';
|
|
14
14
|
import { logError } from '../../../lib/errorHandlers/index.js';
|
|
15
|
-
import { PromptExitError } from '../../../lib/errors/PromptExitError.js';
|
|
16
15
|
import path from 'path';
|
|
17
16
|
import { listPrompt } from '../../../lib/prompts/promptUtils.js';
|
|
18
17
|
const command = 'dev';
|
|
@@ -100,35 +99,26 @@ async function handler(args) {
|
|
|
100
99
|
}
|
|
101
100
|
}
|
|
102
101
|
trackCommandUsage('project-dev', {}, targetProjectAccountId);
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
});
|
|
116
|
-
}
|
|
117
|
-
else {
|
|
118
|
-
await deprecatedProjectDevFlow({
|
|
119
|
-
args,
|
|
120
|
-
accountId: targetProjectAccountId,
|
|
121
|
-
projectConfig,
|
|
122
|
-
projectDir,
|
|
123
|
-
});
|
|
124
|
-
}
|
|
102
|
+
if (isV2Project(projectConfig.platformVersion)) {
|
|
103
|
+
const targetTestingAccountId = testingAccount
|
|
104
|
+
? getConfigAccountIfExists(testingAccount)?.accountId
|
|
105
|
+
: undefined;
|
|
106
|
+
await unifiedProjectDevFlow({
|
|
107
|
+
args,
|
|
108
|
+
targetProjectAccountId,
|
|
109
|
+
providedTargetTestingAccountId: targetTestingAccountId,
|
|
110
|
+
projectConfig,
|
|
111
|
+
projectDir,
|
|
112
|
+
profileConfig: profile,
|
|
113
|
+
});
|
|
125
114
|
}
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
115
|
+
else {
|
|
116
|
+
await deprecatedProjectDevFlow({
|
|
117
|
+
args,
|
|
118
|
+
accountId: targetProjectAccountId,
|
|
119
|
+
projectConfig,
|
|
120
|
+
projectDir,
|
|
121
|
+
});
|
|
132
122
|
}
|
|
133
123
|
}
|
|
134
124
|
function projectDevBuilder(yargs) {
|
|
@@ -3,7 +3,6 @@ import { getCwd, sanitizeFileName } from '@hubspot/local-dev-lib/path';
|
|
|
3
3
|
import { extractZipArchive } from '@hubspot/local-dev-lib/archive';
|
|
4
4
|
import { downloadProject, fetchProjectBuilds, } from '@hubspot/local-dev-lib/api/projects';
|
|
5
5
|
import { logError, ApiErrorContext } from '../../lib/errorHandlers/index.js';
|
|
6
|
-
import { PromptExitError } from '../../lib/errors/PromptExitError.js';
|
|
7
6
|
import { getProjectConfig } from '../../lib/projects/config.js';
|
|
8
7
|
import { downloadProjectPrompt } from '../../lib/prompts/downloadProjectPrompt.js';
|
|
9
8
|
import { commands } from '../../lang/en.js';
|
|
@@ -20,10 +19,10 @@ async function handler(args) {
|
|
|
20
19
|
process.exit(EXIT_CODES.ERROR);
|
|
21
20
|
}
|
|
22
21
|
const { dest, build, derivedAccountId } = args;
|
|
22
|
+
const { project: projectName } = await downloadProjectPrompt(args);
|
|
23
23
|
let buildNumberToDownload = build;
|
|
24
24
|
trackCommandUsage('project-download', undefined, derivedAccountId);
|
|
25
25
|
try {
|
|
26
|
-
const { project: projectName } = await downloadProjectPrompt(args);
|
|
27
26
|
if (!buildNumberToDownload) {
|
|
28
27
|
const { data: projectBuildsResult } = await fetchProjectBuilds(derivedAccountId, projectName);
|
|
29
28
|
const { results: projectBuilds } = projectBuildsResult;
|
|
@@ -46,9 +45,6 @@ async function handler(args) {
|
|
|
46
45
|
process.exit(EXIT_CODES.SUCCESS);
|
|
47
46
|
}
|
|
48
47
|
catch (e) {
|
|
49
|
-
if (e instanceof PromptExitError) {
|
|
50
|
-
process.exit(e.exitCode);
|
|
51
|
-
}
|
|
52
48
|
logError(e, new ApiErrorContext({
|
|
53
49
|
accountId: derivedAccountId,
|
|
54
50
|
request: 'project download',
|
|
@@ -9,11 +9,11 @@ import { commands, lib } from '../../lang/en.js';
|
|
|
9
9
|
import { uiLogger } from '../../lib/ui/logger.js';
|
|
10
10
|
import { renderInline } from '../../ui/render.js';
|
|
11
11
|
import { getWarningBox } from '../../ui/components/StatusMessageBoxes.js';
|
|
12
|
-
import { getHasMigratableThemes,
|
|
12
|
+
import { getHasMigratableThemes, migrateThemesV2, } from '../../lib/theme/migrate.js';
|
|
13
13
|
import { hasFeature } from '../../lib/hasFeature.js';
|
|
14
14
|
import { FEATURES } from '../../lib/constants.js';
|
|
15
15
|
import { trackCommandMetadataUsage, trackCommandUsage, } from '../../lib/usageTracking.js';
|
|
16
|
-
const { v2025_2 } = PLATFORM_VERSIONS;
|
|
16
|
+
const { v2025_2, v2026_03_beta, v2026_03 } = PLATFORM_VERSIONS;
|
|
17
17
|
const command = 'migrate';
|
|
18
18
|
const describe = commands.project.migrate.describe;
|
|
19
19
|
async function handler(args) {
|
|
@@ -26,8 +26,8 @@ async function handler(args) {
|
|
|
26
26
|
}
|
|
27
27
|
if (projectConfig?.projectConfig) {
|
|
28
28
|
await renderInline(getWarningBox({
|
|
29
|
-
title: lib.migrate.projectMigrationWarningTitle,
|
|
30
|
-
message: lib.migrate.projectMigrationWarning,
|
|
29
|
+
title: lib.migrate.projectMigrationWarningTitle(platformVersion),
|
|
30
|
+
message: lib.migrate.projectMigrationWarning(platformVersion),
|
|
31
31
|
}));
|
|
32
32
|
}
|
|
33
33
|
try {
|
|
@@ -38,7 +38,7 @@ async function handler(args) {
|
|
|
38
38
|
uiLogger.error(commands.project.migrate.errors.noThemeMigrationAccess(derivedAccountId));
|
|
39
39
|
return process.exit(EXIT_CODES.ERROR);
|
|
40
40
|
}
|
|
41
|
-
await
|
|
41
|
+
await migrateThemesV2(derivedAccountId, {
|
|
42
42
|
...args,
|
|
43
43
|
platformVersion: unstable
|
|
44
44
|
? PLATFORM_VERSIONS.unstable
|
|
@@ -67,7 +67,7 @@ function projectMigrateBuilder(yargs) {
|
|
|
67
67
|
yargs
|
|
68
68
|
.option('platform-version', {
|
|
69
69
|
type: 'string',
|
|
70
|
-
choices: [v2025_2],
|
|
70
|
+
choices: [v2025_2, v2026_03_beta, v2026_03],
|
|
71
71
|
default: v2025_2,
|
|
72
72
|
})
|
|
73
73
|
.option('unstable', {
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import yargs from 'yargs';
|
|
2
2
|
import { addAccountOptions, addConfigOptions, addUseEnvironmentOptions, addTestingOptions, } from '../../../lib/commonOpts.js';
|
|
3
3
|
import sandboxCreateCommand from '../create.js';
|
|
4
|
+
import { hasFeature } from '../../../lib/hasFeature.js';
|
|
4
5
|
import * as sandboxPrompts from '../../../lib/prompts/sandboxesPrompt.js';
|
|
5
6
|
import * as accountNamePrompt from '../../../lib/prompts/accountNamePrompt.js';
|
|
6
7
|
import * as configUtils from '@hubspot/local-dev-lib/config';
|
|
@@ -11,6 +12,7 @@ import * as buildAccount from '../../../lib/buildAccount.js';
|
|
|
11
12
|
import { EXIT_CODES } from '../../../lib/enums/exitCodes.js';
|
|
12
13
|
import { uiLogger } from '../../../lib/ui/logger.js';
|
|
13
14
|
import * as sandboxesLib from '../../../lib/sandboxes.js';
|
|
15
|
+
import * as sandboxSync from '../../../lib/sandboxSync.js';
|
|
14
16
|
import { vi } from 'vitest';
|
|
15
17
|
import { ENVIRONMENTS } from '@hubspot/local-dev-lib/constants/environments';
|
|
16
18
|
vi.mock('@hubspot/local-dev-lib/config');
|
|
@@ -27,10 +29,15 @@ const getConfigAccountByIdSpy = vi.spyOn(configUtils, 'getConfigAccountById');
|
|
|
27
29
|
const promptUserSpy = vi.spyOn(promptUtils, 'promptUser');
|
|
28
30
|
const sandboxTypePromptSpy = vi.spyOn(sandboxPrompts, 'sandboxTypePrompt');
|
|
29
31
|
const processExitSpy = vi.spyOn(process, 'exit');
|
|
32
|
+
const buildSandboxSpy = vi.spyOn(buildAccount, 'buildSandbox');
|
|
30
33
|
const buildV2SandboxSpy = vi.spyOn(buildAccount, 'buildV2Sandbox');
|
|
31
34
|
const getConfigAccountEnvironmentSpy = vi.spyOn(configUtils, 'getConfigAccountEnvironment');
|
|
35
|
+
const getAvailableSyncTypesSpy = vi.spyOn(sandboxesLib, 'getAvailableSyncTypes');
|
|
36
|
+
const syncSandboxSpy = vi.spyOn(sandboxSync, 'syncSandbox');
|
|
32
37
|
const validateSandboxUsageLimitsSpy = vi.spyOn(sandboxesLib, 'validateSandboxUsageLimits');
|
|
33
38
|
const hubspotAccountNamePromptSpy = vi.spyOn(accountNamePrompt, 'hubspotAccountNamePrompt');
|
|
39
|
+
const mockedHasFeatureV2Sandboxes = hasFeature;
|
|
40
|
+
const mockedHasFeatureV2Cli = hasFeature;
|
|
34
41
|
describe('commands/sandbox/create', () => {
|
|
35
42
|
const yargsMock = yargs;
|
|
36
43
|
describe('command', () => {
|
|
@@ -96,10 +103,22 @@ describe('commands/sandbox/create', () => {
|
|
|
96
103
|
contactRecordsSyncPrompt: false,
|
|
97
104
|
});
|
|
98
105
|
validateSandboxUsageLimitsSpy.mockResolvedValue(undefined);
|
|
106
|
+
mockedHasFeatureV2Sandboxes.mockResolvedValue(false);
|
|
107
|
+
mockedHasFeatureV2Cli.mockResolvedValue(false);
|
|
99
108
|
getConfigAccountEnvironmentSpy.mockReturnValue(ENVIRONMENTS.PROD);
|
|
109
|
+
buildSandboxSpy.mockResolvedValue({
|
|
110
|
+
sandbox: mockSandbox,
|
|
111
|
+
personalAccessKey: 'mock-personal-access-key',
|
|
112
|
+
name: sandboxNameFromPrompt,
|
|
113
|
+
});
|
|
100
114
|
buildV2SandboxSpy.mockResolvedValue({
|
|
101
115
|
sandbox: { ...mockSandbox, version: 'V2' },
|
|
102
116
|
});
|
|
117
|
+
getAvailableSyncTypesSpy.mockResolvedValue([
|
|
118
|
+
{ type: 'object-schemas' },
|
|
119
|
+
{ type: 'workflows' },
|
|
120
|
+
]);
|
|
121
|
+
syncSandboxSpy.mockResolvedValue(undefined);
|
|
103
122
|
// Spy on process.exit so our tests don't close when it's called
|
|
104
123
|
// @ts-expect-error Doesn't match the actual signature because then the linter complains about unused variables
|
|
105
124
|
processExitSpy.mockImplementation(() => { });
|
|
@@ -148,7 +167,35 @@ describe('commands/sandbox/create', () => {
|
|
|
148
167
|
});
|
|
149
168
|
expect(promptUserSpy).toHaveBeenCalledTimes(1);
|
|
150
169
|
});
|
|
151
|
-
it('should build a v2
|
|
170
|
+
it('should build a v1 sandbox if the parent account is not ungated for sandboxes:v2:enabled and not ungated for sandboxes:v2:cliEnabled', async () => {
|
|
171
|
+
await sandboxCreateCommand.handler(args);
|
|
172
|
+
expect(buildSandboxSpy).toHaveBeenCalledTimes(1);
|
|
173
|
+
expect(buildSandboxSpy).toHaveBeenCalledWith(sandboxNameFromPrompt, {
|
|
174
|
+
accountId: 1234567890,
|
|
175
|
+
accountType: HUBSPOT_ACCOUNT_TYPES.STANDARD,
|
|
176
|
+
env: 'prod',
|
|
177
|
+
}, HUBSPOT_ACCOUNT_TYPES.DEVELOPMENT_SANDBOX, 'prod', undefined // force
|
|
178
|
+
);
|
|
179
|
+
expect(getAvailableSyncTypesSpy).toHaveBeenCalledTimes(1);
|
|
180
|
+
expect(syncSandboxSpy).toHaveBeenCalledTimes(1);
|
|
181
|
+
});
|
|
182
|
+
it('should build a v1 sandbox if the parent account is ungated for sandboxes:v2:enabled but not ungated for sandboxes:v2:cliEnabled', async () => {
|
|
183
|
+
mockedHasFeatureV2Sandboxes.mockResolvedValue(true);
|
|
184
|
+
mockedHasFeatureV2Cli.mockResolvedValue(false);
|
|
185
|
+
await sandboxCreateCommand.handler(args);
|
|
186
|
+
expect(buildSandboxSpy).toHaveBeenCalledTimes(1);
|
|
187
|
+
expect(buildSandboxSpy).toHaveBeenCalledWith(sandboxNameFromPrompt, {
|
|
188
|
+
accountId: 1234567890,
|
|
189
|
+
accountType: HUBSPOT_ACCOUNT_TYPES.STANDARD,
|
|
190
|
+
env: 'prod',
|
|
191
|
+
}, HUBSPOT_ACCOUNT_TYPES.DEVELOPMENT_SANDBOX, 'prod', undefined // force
|
|
192
|
+
);
|
|
193
|
+
expect(getAvailableSyncTypesSpy).toHaveBeenCalledTimes(1);
|
|
194
|
+
expect(syncSandboxSpy).toHaveBeenCalledTimes(1);
|
|
195
|
+
});
|
|
196
|
+
it('should build a v2 sandbox if the parent account is ungated for both sandboxes:v2:enabled and sandboxes:v2:cliEnabled', async () => {
|
|
197
|
+
mockedHasFeatureV2Sandboxes.mockResolvedValue(true);
|
|
198
|
+
mockedHasFeatureV2Cli.mockResolvedValue(true);
|
|
152
199
|
await sandboxCreateCommand.handler(args);
|
|
153
200
|
expect(buildV2SandboxSpy).toHaveBeenCalledTimes(1);
|
|
154
201
|
expect(buildV2SandboxSpy).toHaveBeenCalledWith(sandboxNameFromPrompt, {
|
|
@@ -6,14 +6,17 @@ import { HUBSPOT_ACCOUNT_TYPES, HUBSPOT_ACCOUNT_TYPE_STRINGS, } from '@hubspot/l
|
|
|
6
6
|
import { commands, lib } from '../../lang/en.js';
|
|
7
7
|
import { EXIT_CODES } from '../../lib/enums/exitCodes.js';
|
|
8
8
|
import { uiFeatureHighlight, uiBetaTag } from '../../lib/ui/index.js';
|
|
9
|
-
import { SANDBOX_TYPE_MAP, validateSandboxUsageLimits, } from '../../lib/sandboxes.js';
|
|
9
|
+
import { SANDBOX_TYPE_MAP, getAvailableSyncTypes, SYNC_TYPES, validateSandboxUsageLimits, } from '../../lib/sandboxes.js';
|
|
10
10
|
import { trackCommandUsage } from '../../lib/usageTracking.js';
|
|
11
11
|
import { sandboxTypePrompt } from '../../lib/prompts/sandboxesPrompt.js';
|
|
12
12
|
import { promptUser } from '../../lib/prompts/promptUtils.js';
|
|
13
|
+
import { syncSandbox } from '../../lib/sandboxSync.js';
|
|
13
14
|
import { logError } from '../../lib/errorHandlers/index.js';
|
|
14
|
-
import { buildV2Sandbox } from '../../lib/buildAccount.js';
|
|
15
|
+
import { buildSandbox, buildV2Sandbox } from '../../lib/buildAccount.js';
|
|
15
16
|
import { hubspotAccountNamePrompt } from '../../lib/prompts/accountNamePrompt.js';
|
|
16
17
|
import { makeYargsBuilder } from '../../lib/yargsUtils.js';
|
|
18
|
+
import { hasFeature } from '../../lib/hasFeature.js';
|
|
19
|
+
import { FEATURES } from '../../lib/constants.js';
|
|
17
20
|
const command = 'create';
|
|
18
21
|
const describe = uiBetaTag(commands.sandbox.subcommands.create.describe, false);
|
|
19
22
|
async function handler(args) {
|
|
@@ -95,14 +98,38 @@ async function handler(args) {
|
|
|
95
98
|
contactRecordsSyncPromptResult = contactRecordsSyncPrompt;
|
|
96
99
|
}
|
|
97
100
|
}
|
|
101
|
+
// Check if parent portal is ungated for v2 sandboxes
|
|
102
|
+
const isUngatedForV2Cli = await hasFeature(derivedAccountId, FEATURES.SANDBOXES_V2_CLI);
|
|
103
|
+
const isUngatedForV2Sandboxes = await hasFeature(derivedAccountId, FEATURES.SANDBOXES_V2);
|
|
104
|
+
const canCreateV2Sandbox = isUngatedForV2Sandboxes && isUngatedForV2Cli;
|
|
98
105
|
try {
|
|
99
|
-
|
|
106
|
+
let result;
|
|
107
|
+
if (canCreateV2Sandbox) {
|
|
108
|
+
result = await buildV2Sandbox(sandboxName, accountConfig, sandboxType, contactRecordsSyncPromptResult, env, force);
|
|
109
|
+
}
|
|
110
|
+
else {
|
|
111
|
+
result = await buildSandbox(sandboxName, accountConfig, sandboxType, env, force);
|
|
112
|
+
}
|
|
100
113
|
const sandboxAccountConfig = getConfigAccountById(result.sandbox.sandboxHubId);
|
|
101
114
|
// Check if sandbox account config exists
|
|
102
115
|
if (!sandboxAccountConfig) {
|
|
103
116
|
uiLogger.error(commands.sandbox.subcommands.create.failure.noSandboxAccountConfig(result.sandbox.sandboxHubId));
|
|
104
117
|
process.exit(EXIT_CODES.ERROR);
|
|
105
118
|
}
|
|
119
|
+
if (result && !canCreateV2Sandbox) {
|
|
120
|
+
// For v1 sandboxes, keep sync here. Once we migrate to v2, this will be handled by BE automatically
|
|
121
|
+
try {
|
|
122
|
+
let availableSyncTasks = await getAvailableSyncTypes(accountConfig, sandboxAccountConfig);
|
|
123
|
+
if (!contactRecordsSyncPromptResult) {
|
|
124
|
+
availableSyncTasks = availableSyncTasks.filter(t => t.type !== SYNC_TYPES.OBJECT_RECORDS);
|
|
125
|
+
}
|
|
126
|
+
await syncSandbox(sandboxAccountConfig, accountConfig, env, availableSyncTasks);
|
|
127
|
+
}
|
|
128
|
+
catch (err) {
|
|
129
|
+
logError(err);
|
|
130
|
+
throw err;
|
|
131
|
+
}
|
|
132
|
+
}
|
|
106
133
|
const highlightItems = ['accountsUseCommand', 'projectCreateCommand'];
|
|
107
134
|
if (sandboxType === HUBSPOT_ACCOUNT_TYPES.DEVELOPMENT_SANDBOX) {
|
|
108
135
|
highlightItems.push('projectDevCommand');
|
|
@@ -12,7 +12,6 @@ import { fileExists } from '../../lib/validation.js';
|
|
|
12
12
|
import { commands } from '../../lang/en.js';
|
|
13
13
|
import { createDeveloperTestAccountConfigPrompt } from '../../lib/prompts/createDeveloperTestAccountConfigPrompt.js';
|
|
14
14
|
import { debugError, logError } from '../../lib/errorHandlers/index.js';
|
|
15
|
-
import { PromptExitError } from '../../lib/errors/PromptExitError.js';
|
|
16
15
|
import SpinniesManager from '../../lib/ui/SpinniesManager.js';
|
|
17
16
|
import { createDeveloperTestAccountV2, saveAccountToConfig, } from '../../lib/buildAccount.js';
|
|
18
17
|
import { ACCOUNT_LEVEL_CHOICES, ACCOUNT_LEVELS } from '../../lib/constants.js';
|
|
@@ -131,9 +130,6 @@ async function handler(args) {
|
|
|
131
130
|
}
|
|
132
131
|
}
|
|
133
132
|
catch (e) {
|
|
134
|
-
if (e instanceof PromptExitError) {
|
|
135
|
-
process.exit(e.exitCode);
|
|
136
|
-
}
|
|
137
133
|
debugError(e);
|
|
138
134
|
uiLogger.error(commands.testAccount.create.errors.saveAccountToConfigFailure(testAccountConfig.accountName));
|
|
139
135
|
process.exit(EXIT_CODES.ERROR);
|
package/lang/en.d.ts
CHANGED
|
@@ -3474,7 +3474,6 @@ export declare const lib: {
|
|
|
3474
3474
|
invalidOauthClientSecretCopy: string;
|
|
3475
3475
|
invalidPersonalAccessKey: string;
|
|
3476
3476
|
invalidPersonalAccessKeyCopy: string;
|
|
3477
|
-
authCancelled: string;
|
|
3478
3477
|
};
|
|
3479
3478
|
};
|
|
3480
3479
|
createTemplatePrompt: {
|
|
@@ -3920,8 +3919,8 @@ export declare const lib: {
|
|
|
3920
3919
|
componentsToBeMigrated: (components: string) => string;
|
|
3921
3920
|
componentsThatWillNotBeMigrated: (components: string) => string;
|
|
3922
3921
|
sourceContentsMoved: (newLocation: string) => string;
|
|
3923
|
-
projectMigrationWarningTitle: string;
|
|
3924
|
-
projectMigrationWarning: string;
|
|
3922
|
+
projectMigrationWarningTitle: (platformVersion: string) => string;
|
|
3923
|
+
projectMigrationWarning: (platformVersion: string) => string;
|
|
3925
3924
|
exitWithoutMigrating: string;
|
|
3926
3925
|
success: {
|
|
3927
3926
|
downloadedProject: (projectName: string, projectDest: string) => string;
|
package/lang/en.js
CHANGED
|
@@ -3497,7 +3497,6 @@ export const lib = {
|
|
|
3497
3497
|
invalidOauthClientSecretCopy: 'Please copy the actual OAuth2 client secret rather than the asterisks that mask it.',
|
|
3498
3498
|
invalidPersonalAccessKey: 'You did not enter a valid access key. Please try again.',
|
|
3499
3499
|
invalidPersonalAccessKeyCopy: 'Please copy the actual access key rather than the bullets that mask it.',
|
|
3500
|
-
authCancelled: 'Authentication cancelled.',
|
|
3501
3500
|
},
|
|
3502
3501
|
},
|
|
3503
3502
|
createTemplatePrompt: {
|
|
@@ -3943,8 +3942,8 @@ export const lib = {
|
|
|
3943
3942
|
componentsToBeMigrated: (components) => `The following features will be migrated: ${components}`,
|
|
3944
3943
|
componentsThatWillNotBeMigrated: (components) => `[NOTE] These features are not yet supported for migration but will be available later: ${components}`,
|
|
3945
3944
|
sourceContentsMoved: (newLocation) => `The contents of your old source directory have been moved to ${newLocation}, move any required files to the new source directory.`,
|
|
3946
|
-
projectMigrationWarningTitle:
|
|
3947
|
-
projectMigrationWarning: uiBetaTag(`Running the ${uiCommandReference('hs project migrate')} command will permanently upgrade your project to platformVersion
|
|
3945
|
+
projectMigrationWarningTitle: (platformVersion) => `Important: Migrating to platformVersion ${platformVersion} is irreversible`,
|
|
3946
|
+
projectMigrationWarning: (platformVersion) => uiBetaTag(`Running the ${uiCommandReference('hs project migrate')} command will permanently upgrade your project to platformVersion ${platformVersion}. This action cannot be undone. To ensure you have access to your original files, they will be copied to a new directory (archive) for safekeeping.\n\nThis command will guide you through the process, prompting you to enter the required fields and will download the new project source code into your project source directory.`, false),
|
|
3948
3947
|
exitWithoutMigrating: 'Exiting without migrating',
|
|
3949
3948
|
success: {
|
|
3950
3949
|
downloadedProject: (projectName, projectDest) => `Saved ${projectName} to ${projectDest}`,
|