@hubspot/cli 8.2.0 → 8.3.0-beta.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/api/migrate.js +5 -1
- package/commands/account/auth.js +15 -5
- package/commands/account/use.js +14 -4
- package/commands/app/__tests__/migrate.test.js +2 -2
- package/commands/app/migrate.js +3 -3
- package/commands/auth.js +10 -6
- package/commands/cms/__tests__/upload.test.js +4 -0
- package/commands/getStarted.js +1 -1
- package/commands/hubdb/clear.js +4 -0
- package/commands/hubdb/delete.js +4 -0
- package/commands/hubdb/fetch.js +4 -0
- package/commands/init.js +4 -0
- package/commands/project/__tests__/create.test.js +2 -2
- package/commands/project/__tests__/migrate.test.js +3 -3
- package/commands/project/create.js +3 -3
- package/commands/project/dev/index.js +29 -19
- package/commands/project/download.js +5 -1
- package/commands/project/migrate.js +7 -7
- package/commands/sandbox/__tests__/create.test.js +1 -48
- package/commands/sandbox/create.js +3 -30
- package/commands/testAccount/create.js +4 -0
- package/lang/en.d.ts +3 -3
- package/lang/en.js +4 -4
- package/lib/__tests__/buildAccount.test.js +1 -52
- package/lib/__tests__/sandboxes.test.js +1 -29
- package/lib/accountAuth.js +4 -0
- package/lib/app/__tests__/migrate.test.js +1 -1
- package/lib/app/migrate.js +11 -6
- package/lib/buildAccount.d.ts +1 -6
- package/lib/buildAccount.js +9 -42
- package/lib/constants.d.ts +0 -2
- package/lib/constants.js +0 -2
- package/lib/errors/PromptExitError.d.ts +4 -0
- package/lib/errors/PromptExitError.js +8 -0
- package/lib/getStartedV2Actions.js +1 -1
- package/lib/projects/__tests__/components.test.js +14 -0
- package/lib/projects/components.js +12 -2
- package/lib/projects/localDev/AppDevModeInterface.js +4 -0
- package/lib/projects/localDev/LocalDevManager_DEPRECATED.js +4 -0
- package/lib/projects/localDev/helpers/account.js +5 -11
- package/lib/prompts/downloadProjectPrompt.js +11 -10
- package/lib/prompts/installAppPrompt.js +3 -2
- package/lib/prompts/personalAccessKeyPrompt.js +3 -2
- package/lib/prompts/projectDevTargetAccountPrompt.js +13 -16
- package/lib/prompts/selectHubDBTablePrompt.js +8 -4
- package/lib/prompts/selectPublicAppForMigrationPrompt.js +12 -6
- package/lib/sandboxes.d.ts +1 -9
- package/lib/sandboxes.js +0 -21
- package/lib/theme/__tests__/migrate.test.js +7 -16
- package/lib/theme/migrate.d.ts +1 -1
- package/lib/theme/migrate.js +1 -5
- package/lib/ui/SpinniesManager.js +2 -0
- package/mcp-server/tools/project/AddFeatureToProjectTool.d.ts +1 -0
- package/mcp-server/tools/project/CreateProjectTool.d.ts +1 -0
- package/mcp-server/tools/project/CreateProjectTool.js +2 -1
- package/mcp-server/tools/project/__tests__/CreateProjectTool.test.js +0 -1
- package/mcp-server/tools/project/constants.d.ts +1 -0
- package/mcp-server/tools/project/constants.js +2 -1
- package/package.json +2 -2
- package/lib/__tests__/sandboxSync.test.d.ts +0 -1
- package/lib/__tests__/sandboxSync.test.js +0 -147
- package/lib/sandboxSync.d.ts +0 -4
- package/lib/sandboxSync.js +0 -102
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
|
+
.replaceAll('.', '_')
|
|
28
|
+
.replaceAll('-', '_')
|
|
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,6 +10,7 @@ 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';
|
|
13
14
|
const TRACKING_STATUS = {
|
|
14
15
|
STARTED: 'started',
|
|
15
16
|
ERROR: 'error',
|
|
@@ -35,11 +36,20 @@ async function handler(args) {
|
|
|
35
36
|
await trackAuthAction('account-auth', authType, TRACKING_STATUS.STARTED);
|
|
36
37
|
}
|
|
37
38
|
handleExit(deleteConfigFileIfEmpty);
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
39
|
+
let updatedConfig;
|
|
40
|
+
try {
|
|
41
|
+
updatedConfig = await authenticateNewAccount({
|
|
42
|
+
env: args.qa ? ENVIRONMENTS.QA : ENVIRONMENTS.PROD,
|
|
43
|
+
providedPersonalAccessKey,
|
|
44
|
+
accountId: parsedUserProvidedAccountId,
|
|
45
|
+
});
|
|
46
|
+
}
|
|
47
|
+
catch (e) {
|
|
48
|
+
if (e instanceof PromptExitError) {
|
|
49
|
+
process.exit(e.exitCode);
|
|
50
|
+
}
|
|
51
|
+
throw e;
|
|
52
|
+
}
|
|
43
53
|
if (!updatedConfig) {
|
|
44
54
|
if (!disableTracking) {
|
|
45
55
|
await trackAuthAction('account-auth', authType, TRACKING_STATUS.ERROR);
|
package/commands/account/use.js
CHANGED
|
@@ -7,6 +7,7 @@ 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';
|
|
10
11
|
import { EXIT_CODES } from '../../lib/enums/exitCodes.js';
|
|
11
12
|
const command = 'use [account]';
|
|
12
13
|
const describe = commands.account.subcommands.use.describe;
|
|
@@ -24,10 +25,19 @@ async function handler(args) {
|
|
|
24
25
|
}
|
|
25
26
|
}
|
|
26
27
|
if (newDefaultAccount === AUTHENTICATE_NEW_ACCOUNT_VALUE) {
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
28
|
+
let updatedConfig;
|
|
29
|
+
try {
|
|
30
|
+
updatedConfig = await authenticateNewAccount({
|
|
31
|
+
env: args.qa ? ENVIRONMENTS.QA : ENVIRONMENTS.PROD,
|
|
32
|
+
setAsDefaultAccount: true,
|
|
33
|
+
});
|
|
34
|
+
}
|
|
35
|
+
catch (e) {
|
|
36
|
+
if (e instanceof PromptExitError) {
|
|
37
|
+
process.exit(e.exitCode);
|
|
38
|
+
}
|
|
39
|
+
throw e;
|
|
40
|
+
}
|
|
31
41
|
if (!updatedConfig) {
|
|
32
42
|
process.exit(EXIT_CODES.ERROR);
|
|
33
43
|
}
|
|
@@ -89,7 +89,7 @@ describe('commands/app/migrate', () => {
|
|
|
89
89
|
}),
|
|
90
90
|
'platform-version': expect.objectContaining({
|
|
91
91
|
type: 'string',
|
|
92
|
-
default: '
|
|
92
|
+
default: '2026.03',
|
|
93
93
|
}),
|
|
94
94
|
}));
|
|
95
95
|
});
|
|
@@ -97,7 +97,7 @@ describe('commands/app/migrate', () => {
|
|
|
97
97
|
await migrateCommand.builder(mockYargs);
|
|
98
98
|
expect(optionsSpy).toHaveBeenCalledWith(expect.objectContaining({
|
|
99
99
|
'platform-version': expect.objectContaining({
|
|
100
|
-
default: '
|
|
100
|
+
default: '2026.03',
|
|
101
101
|
}),
|
|
102
102
|
}));
|
|
103
103
|
});
|
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, v2026_03 } = PLATFORM_VERSIONS;
|
|
12
12
|
const command = 'migrate';
|
|
13
13
|
const describe = commands.project.migrateApp.describe;
|
|
14
14
|
export function handlerGenerator(commandTrackingName) {
|
|
@@ -67,8 +67,8 @@ function appMigrateBuilder(yargs) {
|
|
|
67
67
|
},
|
|
68
68
|
'platform-version': {
|
|
69
69
|
type: 'string',
|
|
70
|
-
choices: [v2025_2],
|
|
71
|
-
default:
|
|
70
|
+
choices: [v2025_2, v2026_03_beta, v2026_03],
|
|
71
|
+
default: v2026_03,
|
|
72
72
|
},
|
|
73
73
|
unstable: {
|
|
74
74
|
type: 'boolean',
|
package/commands/auth.js
CHANGED
|
@@ -16,6 +16,7 @@ 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';
|
|
19
20
|
import { commands } from '../lang/en.js';
|
|
20
21
|
import { uiLogger } from '../lib/ui/logger.js';
|
|
21
22
|
import { parseStringToNumber } from '../lib/parsing.js';
|
|
@@ -90,18 +91,21 @@ async function handler(args) {
|
|
|
90
91
|
}
|
|
91
92
|
break;
|
|
92
93
|
case PERSONAL_ACCESS_KEY_AUTH_METHOD.value:
|
|
93
|
-
const { personalAccessKey } = providedPersonalAccessKey
|
|
94
|
-
? { personalAccessKey: providedPersonalAccessKey }
|
|
95
|
-
: await personalAccessKeyPrompt({
|
|
96
|
-
env,
|
|
97
|
-
account: parsedUserProvidedAccountId,
|
|
98
|
-
});
|
|
99
94
|
try {
|
|
95
|
+
const { personalAccessKey } = providedPersonalAccessKey
|
|
96
|
+
? { personalAccessKey: providedPersonalAccessKey }
|
|
97
|
+
: await personalAccessKeyPrompt({
|
|
98
|
+
env,
|
|
99
|
+
account: parsedUserProvidedAccountId,
|
|
100
|
+
});
|
|
100
101
|
token = await getAccessToken(personalAccessKey, env);
|
|
101
102
|
defaultName = token.hubName ? toKebabCase(token.hubName) : undefined;
|
|
102
103
|
updatedConfig = await updateConfigWithAccessToken(token, personalAccessKey, env);
|
|
103
104
|
}
|
|
104
105
|
catch (e) {
|
|
106
|
+
if (e instanceof PromptExitError) {
|
|
107
|
+
process.exit(e.exitCode);
|
|
108
|
+
}
|
|
105
109
|
logError(e);
|
|
106
110
|
}
|
|
107
111
|
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/getStarted.js
CHANGED
|
@@ -115,7 +115,7 @@ async function handler(args) {
|
|
|
115
115
|
// 4. Clone the project template from GitHub
|
|
116
116
|
try {
|
|
117
117
|
await cloneGithubRepo(HUBSPOT_PROJECT_COMPONENTS_GITHUB_PATH, projectDest, {
|
|
118
|
-
sourceDir: '
|
|
118
|
+
sourceDir: '2026.03/private-app-get-started-template',
|
|
119
119
|
hideLogs: true,
|
|
120
120
|
});
|
|
121
121
|
await trackCommandMetadataUsage('get-started', {
|
package/commands/hubdb/clear.js
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
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';
|
|
3
4
|
import { clearHubDbTableRows } from '@hubspot/local-dev-lib/hubdb';
|
|
4
5
|
import { publishTable } from '@hubspot/local-dev-lib/api/hubdb';
|
|
5
6
|
import { selectHubDBTablePrompt } from '../../lib/prompts/selectHubDBTablePrompt.js';
|
|
@@ -29,6 +30,9 @@ async function handler(args) {
|
|
|
29
30
|
}
|
|
30
31
|
}
|
|
31
32
|
catch (e) {
|
|
33
|
+
if (e instanceof PromptExitError) {
|
|
34
|
+
process.exit(e.exitCode);
|
|
35
|
+
}
|
|
32
36
|
logError(e);
|
|
33
37
|
}
|
|
34
38
|
}
|
package/commands/hubdb/delete.js
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
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';
|
|
3
4
|
import { deleteTable } from '@hubspot/local-dev-lib/api/hubdb';
|
|
4
5
|
import { trackCommandUsage } from '../../lib/usageTracking.js';
|
|
5
6
|
import { selectHubDBTablePrompt } from '../../lib/prompts/selectHubDBTablePrompt.js';
|
|
@@ -34,6 +35,9 @@ async function handler(args) {
|
|
|
34
35
|
process.exit(EXIT_CODES.SUCCESS);
|
|
35
36
|
}
|
|
36
37
|
catch (e) {
|
|
38
|
+
if (e instanceof PromptExitError) {
|
|
39
|
+
process.exit(e.exitCode);
|
|
40
|
+
}
|
|
37
41
|
uiLogger.error(commands.hubdb.subcommands.delete.errors.delete(args.tableId || ''));
|
|
38
42
|
logError(e);
|
|
39
43
|
}
|
package/commands/hubdb/fetch.js
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
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';
|
|
3
4
|
import { downloadHubDbTable } from '@hubspot/local-dev-lib/hubdb';
|
|
4
5
|
import { selectHubDBTablePrompt } from '../../lib/prompts/selectHubDBTablePrompt.js';
|
|
5
6
|
import { trackCommandUsage } from '../../lib/usageTracking.js';
|
|
@@ -22,6 +23,9 @@ async function handler(args) {
|
|
|
22
23
|
uiLogger.success(commands.hubdb.subcommands.fetch.success.fetch(tableId, filePath));
|
|
23
24
|
}
|
|
24
25
|
catch (e) {
|
|
26
|
+
if (e instanceof PromptExitError) {
|
|
27
|
+
process.exit(e.exitCode);
|
|
28
|
+
}
|
|
25
29
|
logError(e);
|
|
26
30
|
}
|
|
27
31
|
}
|
package/commands/init.js
CHANGED
|
@@ -12,6 +12,7 @@ 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';
|
|
15
16
|
import { trackCommandUsage, trackAuthAction } from '../lib/usageTracking.js';
|
|
16
17
|
import { promptUser } from '../lib/prompts/promptUtils.js';
|
|
17
18
|
import { OAUTH_FLOW, personalAccessKeyPrompt, } from '../lib/prompts/personalAccessKeyPrompt.js';
|
|
@@ -150,6 +151,9 @@ async function handler(args) {
|
|
|
150
151
|
process.exit(EXIT_CODES.SUCCESS);
|
|
151
152
|
}
|
|
152
153
|
catch (err) {
|
|
154
|
+
if (err instanceof PromptExitError) {
|
|
155
|
+
process.exit(err.exitCode);
|
|
156
|
+
}
|
|
153
157
|
logError(err);
|
|
154
158
|
if (!disableTracking) {
|
|
155
159
|
await trackAuthAction('init', authType, TRACKING_STATUS.ERROR, parsedUserProvidedAccountId);
|
|
@@ -50,8 +50,8 @@ describe('commands/project/create', () => {
|
|
|
50
50
|
expect(optionsCall['platform-version']).toEqual(expect.objectContaining({
|
|
51
51
|
describe: 'The target platform version for the new project.',
|
|
52
52
|
type: 'string',
|
|
53
|
-
choices: ['2025.1', '2025.2', '2026.03-beta'],
|
|
54
|
-
default: '
|
|
53
|
+
choices: ['2025.1', '2025.2', '2026.03-beta', '2026.03'],
|
|
54
|
+
default: '2026.03',
|
|
55
55
|
}));
|
|
56
56
|
});
|
|
57
57
|
it('should define project base option with correct choices', () => {
|
|
@@ -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,8 +48,8 @@ describe('commands/project/migrate', () => {
|
|
|
48
48
|
migrateCommand.builder(yargsMock);
|
|
49
49
|
expect(optionsSpy).toHaveBeenCalledWith('platform-version', {
|
|
50
50
|
type: 'string',
|
|
51
|
-
choices: [v2025_2],
|
|
52
|
-
default:
|
|
51
|
+
choices: [v2025_2, v2026_03_beta, v2026_03],
|
|
52
|
+
default: v2026_03,
|
|
53
53
|
});
|
|
54
54
|
expect(optionsSpy).toHaveBeenCalledWith('unstable', {
|
|
55
55
|
type: 'boolean',
|
|
@@ -20,7 +20,7 @@ import { updateHsMetaFilesWithAutoGeneratedFields } from '../../lib/projects/com
|
|
|
20
20
|
import SpinniesManager from '../../lib/ui/SpinniesManager.js';
|
|
21
21
|
const command = ['create', 'init'];
|
|
22
22
|
const describe = commands.project.create.describe;
|
|
23
|
-
const { v2025_1, v2025_2, v2026_03_beta } = PLATFORM_VERSIONS;
|
|
23
|
+
const { v2025_1, v2025_2, v2026_03_beta, v2026_03 } = PLATFORM_VERSIONS;
|
|
24
24
|
async function handler(args) {
|
|
25
25
|
const { derivedAccountId, platformVersion, templateSource } = args;
|
|
26
26
|
if (templateSource && !templateSource.includes('/')) {
|
|
@@ -125,8 +125,8 @@ function projectCreateBuilder(yargs) {
|
|
|
125
125
|
'platform-version': {
|
|
126
126
|
describe: commands.project.create.options.platformVersion.describe,
|
|
127
127
|
type: 'string',
|
|
128
|
-
choices: [v2025_1, v2025_2, v2026_03_beta],
|
|
129
|
-
default:
|
|
128
|
+
choices: [v2025_1, v2025_2, v2026_03_beta, v2026_03],
|
|
129
|
+
default: v2026_03,
|
|
130
130
|
},
|
|
131
131
|
'project-base': {
|
|
132
132
|
describe: commands.project.create.options.projectBase.describe,
|
|
@@ -12,6 +12,7 @@ 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';
|
|
15
16
|
import path from 'path';
|
|
16
17
|
import { listPrompt } from '../../../lib/prompts/promptUtils.js';
|
|
17
18
|
const command = 'dev';
|
|
@@ -99,26 +100,35 @@ async function handler(args) {
|
|
|
99
100
|
}
|
|
100
101
|
}
|
|
101
102
|
trackCommandUsage('project-dev', {}, targetProjectAccountId);
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
103
|
+
try {
|
|
104
|
+
if (isV2Project(projectConfig.platformVersion)) {
|
|
105
|
+
const targetTestingAccountId = testingAccount
|
|
106
|
+
? getConfigAccountIfExists(testingAccount)?.accountId
|
|
107
|
+
: undefined;
|
|
108
|
+
await unifiedProjectDevFlow({
|
|
109
|
+
args,
|
|
110
|
+
targetProjectAccountId,
|
|
111
|
+
providedTargetTestingAccountId: targetTestingAccountId,
|
|
112
|
+
projectConfig,
|
|
113
|
+
projectDir,
|
|
114
|
+
profileConfig: profile,
|
|
115
|
+
});
|
|
116
|
+
}
|
|
117
|
+
else {
|
|
118
|
+
await deprecatedProjectDevFlow({
|
|
119
|
+
args,
|
|
120
|
+
accountId: targetProjectAccountId,
|
|
121
|
+
projectConfig,
|
|
122
|
+
projectDir,
|
|
123
|
+
});
|
|
124
|
+
}
|
|
114
125
|
}
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
});
|
|
126
|
+
catch (e) {
|
|
127
|
+
if (e instanceof PromptExitError) {
|
|
128
|
+
process.exit(e.exitCode);
|
|
129
|
+
}
|
|
130
|
+
logError(e);
|
|
131
|
+
process.exit(EXIT_CODES.ERROR);
|
|
122
132
|
}
|
|
123
133
|
}
|
|
124
134
|
function projectDevBuilder(yargs) {
|
|
@@ -3,6 +3,7 @@ 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';
|
|
6
7
|
import { getProjectConfig } from '../../lib/projects/config.js';
|
|
7
8
|
import { downloadProjectPrompt } from '../../lib/prompts/downloadProjectPrompt.js';
|
|
8
9
|
import { commands } from '../../lang/en.js';
|
|
@@ -19,10 +20,10 @@ async function handler(args) {
|
|
|
19
20
|
process.exit(EXIT_CODES.ERROR);
|
|
20
21
|
}
|
|
21
22
|
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);
|
|
26
27
|
if (!buildNumberToDownload) {
|
|
27
28
|
const { data: projectBuildsResult } = await fetchProjectBuilds(derivedAccountId, projectName);
|
|
28
29
|
const { results: projectBuilds } = projectBuildsResult;
|
|
@@ -45,6 +46,9 @@ async function handler(args) {
|
|
|
45
46
|
process.exit(EXIT_CODES.SUCCESS);
|
|
46
47
|
}
|
|
47
48
|
catch (e) {
|
|
49
|
+
if (e instanceof PromptExitError) {
|
|
50
|
+
process.exit(e.exitCode);
|
|
51
|
+
}
|
|
48
52
|
logError(e, new ApiErrorContext({
|
|
49
53
|
accountId: derivedAccountId,
|
|
50
54
|
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,8 +67,8 @@ function projectMigrateBuilder(yargs) {
|
|
|
67
67
|
yargs
|
|
68
68
|
.option('platform-version', {
|
|
69
69
|
type: 'string',
|
|
70
|
-
choices: [v2025_2],
|
|
71
|
-
default:
|
|
70
|
+
choices: [v2025_2, v2026_03_beta, v2026_03],
|
|
71
|
+
default: v2026_03,
|
|
72
72
|
})
|
|
73
73
|
.option('unstable', {
|
|
74
74
|
type: 'boolean',
|
|
@@ -1,7 +1,6 @@
|
|
|
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';
|
|
5
4
|
import * as sandboxPrompts from '../../../lib/prompts/sandboxesPrompt.js';
|
|
6
5
|
import * as accountNamePrompt from '../../../lib/prompts/accountNamePrompt.js';
|
|
7
6
|
import * as configUtils from '@hubspot/local-dev-lib/config';
|
|
@@ -12,7 +11,6 @@ import * as buildAccount from '../../../lib/buildAccount.js';
|
|
|
12
11
|
import { EXIT_CODES } from '../../../lib/enums/exitCodes.js';
|
|
13
12
|
import { uiLogger } from '../../../lib/ui/logger.js';
|
|
14
13
|
import * as sandboxesLib from '../../../lib/sandboxes.js';
|
|
15
|
-
import * as sandboxSync from '../../../lib/sandboxSync.js';
|
|
16
14
|
import { vi } from 'vitest';
|
|
17
15
|
import { ENVIRONMENTS } from '@hubspot/local-dev-lib/constants/environments';
|
|
18
16
|
vi.mock('@hubspot/local-dev-lib/config');
|
|
@@ -29,15 +27,10 @@ const getConfigAccountByIdSpy = vi.spyOn(configUtils, 'getConfigAccountById');
|
|
|
29
27
|
const promptUserSpy = vi.spyOn(promptUtils, 'promptUser');
|
|
30
28
|
const sandboxTypePromptSpy = vi.spyOn(sandboxPrompts, 'sandboxTypePrompt');
|
|
31
29
|
const processExitSpy = vi.spyOn(process, 'exit');
|
|
32
|
-
const buildSandboxSpy = vi.spyOn(buildAccount, 'buildSandbox');
|
|
33
30
|
const buildV2SandboxSpy = vi.spyOn(buildAccount, 'buildV2Sandbox');
|
|
34
31
|
const getConfigAccountEnvironmentSpy = vi.spyOn(configUtils, 'getConfigAccountEnvironment');
|
|
35
|
-
const getAvailableSyncTypesSpy = vi.spyOn(sandboxesLib, 'getAvailableSyncTypes');
|
|
36
|
-
const syncSandboxSpy = vi.spyOn(sandboxSync, 'syncSandbox');
|
|
37
32
|
const validateSandboxUsageLimitsSpy = vi.spyOn(sandboxesLib, 'validateSandboxUsageLimits');
|
|
38
33
|
const hubspotAccountNamePromptSpy = vi.spyOn(accountNamePrompt, 'hubspotAccountNamePrompt');
|
|
39
|
-
const mockedHasFeatureV2Sandboxes = hasFeature;
|
|
40
|
-
const mockedHasFeatureV2Cli = hasFeature;
|
|
41
34
|
describe('commands/sandbox/create', () => {
|
|
42
35
|
const yargsMock = yargs;
|
|
43
36
|
describe('command', () => {
|
|
@@ -103,22 +96,10 @@ describe('commands/sandbox/create', () => {
|
|
|
103
96
|
contactRecordsSyncPrompt: false,
|
|
104
97
|
});
|
|
105
98
|
validateSandboxUsageLimitsSpy.mockResolvedValue(undefined);
|
|
106
|
-
mockedHasFeatureV2Sandboxes.mockResolvedValue(false);
|
|
107
|
-
mockedHasFeatureV2Cli.mockResolvedValue(false);
|
|
108
99
|
getConfigAccountEnvironmentSpy.mockReturnValue(ENVIRONMENTS.PROD);
|
|
109
|
-
buildSandboxSpy.mockResolvedValue({
|
|
110
|
-
sandbox: mockSandbox,
|
|
111
|
-
personalAccessKey: 'mock-personal-access-key',
|
|
112
|
-
name: sandboxNameFromPrompt,
|
|
113
|
-
});
|
|
114
100
|
buildV2SandboxSpy.mockResolvedValue({
|
|
115
101
|
sandbox: { ...mockSandbox, version: 'V2' },
|
|
116
102
|
});
|
|
117
|
-
getAvailableSyncTypesSpy.mockResolvedValue([
|
|
118
|
-
{ type: 'object-schemas' },
|
|
119
|
-
{ type: 'workflows' },
|
|
120
|
-
]);
|
|
121
|
-
syncSandboxSpy.mockResolvedValue(undefined);
|
|
122
103
|
// Spy on process.exit so our tests don't close when it's called
|
|
123
104
|
// @ts-expect-error Doesn't match the actual signature because then the linter complains about unused variables
|
|
124
105
|
processExitSpy.mockImplementation(() => { });
|
|
@@ -167,35 +148,7 @@ describe('commands/sandbox/create', () => {
|
|
|
167
148
|
});
|
|
168
149
|
expect(promptUserSpy).toHaveBeenCalledTimes(1);
|
|
169
150
|
});
|
|
170
|
-
it('should build a
|
|
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);
|
|
151
|
+
it('should build a v2 sandbox', async () => {
|
|
199
152
|
await sandboxCreateCommand.handler(args);
|
|
200
153
|
expect(buildV2SandboxSpy).toHaveBeenCalledTimes(1);
|
|
201
154
|
expect(buildV2SandboxSpy).toHaveBeenCalledWith(sandboxNameFromPrompt, {
|
|
@@ -6,17 +6,14 @@ 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,
|
|
9
|
+
import { SANDBOX_TYPE_MAP, 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';
|
|
14
13
|
import { logError } from '../../lib/errorHandlers/index.js';
|
|
15
|
-
import {
|
|
14
|
+
import { buildV2Sandbox } from '../../lib/buildAccount.js';
|
|
16
15
|
import { hubspotAccountNamePrompt } from '../../lib/prompts/accountNamePrompt.js';
|
|
17
16
|
import { makeYargsBuilder } from '../../lib/yargsUtils.js';
|
|
18
|
-
import { hasFeature } from '../../lib/hasFeature.js';
|
|
19
|
-
import { FEATURES } from '../../lib/constants.js';
|
|
20
17
|
const command = 'create';
|
|
21
18
|
const describe = uiBetaTag(commands.sandbox.subcommands.create.describe, false);
|
|
22
19
|
async function handler(args) {
|
|
@@ -98,38 +95,14 @@ async function handler(args) {
|
|
|
98
95
|
contactRecordsSyncPromptResult = contactRecordsSyncPrompt;
|
|
99
96
|
}
|
|
100
97
|
}
|
|
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;
|
|
105
98
|
try {
|
|
106
|
-
|
|
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
|
-
}
|
|
99
|
+
const result = await buildV2Sandbox(sandboxName, accountConfig, sandboxType, contactRecordsSyncPromptResult, env, force);
|
|
113
100
|
const sandboxAccountConfig = getConfigAccountById(result.sandbox.sandboxHubId);
|
|
114
101
|
// Check if sandbox account config exists
|
|
115
102
|
if (!sandboxAccountConfig) {
|
|
116
103
|
uiLogger.error(commands.sandbox.subcommands.create.failure.noSandboxAccountConfig(result.sandbox.sandboxHubId));
|
|
117
104
|
process.exit(EXIT_CODES.ERROR);
|
|
118
105
|
}
|
|
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
|
-
}
|
|
133
106
|
const highlightItems = ['accountsUseCommand', 'projectCreateCommand'];
|
|
134
107
|
if (sandboxType === HUBSPOT_ACCOUNT_TYPES.DEVELOPMENT_SANDBOX) {
|
|
135
108
|
highlightItems.push('projectDevCommand');
|
|
@@ -12,6 +12,7 @@ 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';
|
|
15
16
|
import SpinniesManager from '../../lib/ui/SpinniesManager.js';
|
|
16
17
|
import { createDeveloperTestAccountV2, saveAccountToConfig, } from '../../lib/buildAccount.js';
|
|
17
18
|
import { ACCOUNT_LEVEL_CHOICES, ACCOUNT_LEVELS } from '../../lib/constants.js';
|
|
@@ -130,6 +131,9 @@ async function handler(args) {
|
|
|
130
131
|
}
|
|
131
132
|
}
|
|
132
133
|
catch (e) {
|
|
134
|
+
if (e instanceof PromptExitError) {
|
|
135
|
+
process.exit(e.exitCode);
|
|
136
|
+
}
|
|
133
137
|
debugError(e);
|
|
134
138
|
uiLogger.error(commands.testAccount.create.errors.saveAccountToConfigFailure(testAccountConfig.accountName));
|
|
135
139
|
process.exit(EXIT_CODES.ERROR);
|