@hubspot/cli 7.6.0-beta.12 → 7.6.0-beta.14
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/commands/__tests__/getStarted.test.js +2 -2
- package/commands/__tests__/mcp.test.js +1 -1
- package/commands/__tests__/project.test.js +0 -3
- package/commands/app/__tests__/migrate.test.js +0 -1
- package/commands/app/migrate.js +4 -5
- package/commands/app/secret/add.js +2 -1
- package/commands/app/secret/delete.js +2 -1
- package/commands/app/secret/list.js +2 -1
- package/commands/app/secret/update.js +2 -1
- package/commands/app/secret.js +2 -1
- package/commands/app.js +2 -2
- package/commands/config/set.js +0 -1
- package/commands/getStarted.d.ts +0 -2
- package/commands/getStarted.js +2 -2
- package/commands/mcp/__tests__/setup.test.js +2 -2
- package/commands/mcp/setup.js +10 -2
- package/commands/mcp.js +2 -2
- package/commands/project/__tests__/create.test.js +6 -6
- package/commands/project/__tests__/deploy.test.js +0 -3
- package/commands/project/__tests__/devUnifiedFlow.test.js +2 -4
- package/commands/project/__tests__/logs.test.js +0 -3
- package/commands/project/__tests__/migrate.test.js +1 -2
- package/commands/project/__tests__/migrateApp.test.js +1 -2
- package/commands/project/__tests__/profile.test.js +1 -1
- package/commands/project/add.js +1 -5
- package/commands/project/create.js +3 -9
- package/commands/project/deploy.js +2 -2
- package/commands/project/dev/index.js +4 -3
- package/commands/project/dev/unifiedFlow.js +1 -2
- package/commands/project/download.js +1 -2
- package/commands/project/installDeps.js +1 -2
- package/commands/project/listBuilds.js +2 -2
- package/commands/project/logs.js +2 -2
- package/commands/project/migrate.js +41 -13
- package/commands/project/migrateApp.js +1 -2
- package/commands/project/open.js +1 -2
- package/commands/project/profile/add.js +3 -3
- package/commands/project/profile/delete.js +1 -2
- package/commands/project/profile.js +2 -3
- package/commands/project/upload.js +2 -2
- package/commands/project/validate.js +1 -1
- package/commands/project/watch.js +2 -2
- package/commands/project.js +1 -2
- package/lang/en.d.ts +14 -3
- package/lang/en.js +16 -4
- package/lib/app/__tests__/migrate.test.js +14 -51
- package/lib/app/migrate.d.ts +2 -8
- package/lib/app/migrate.js +5 -80
- package/lib/constants.d.ts +6 -0
- package/lib/constants.js +6 -0
- package/lib/links.d.ts +1 -0
- package/lib/links.js +10 -3
- package/lib/mcp/setup.js +1 -1
- package/lib/middleware/fireAlarmMiddleware.js +15 -5
- package/lib/projects/__tests__/LocalDevProcess.test.js +227 -16
- package/lib/projects/__tests__/LocalDevWebsocketServer.test.js +16 -21
- package/lib/projects/__tests__/deploy.test.js +71 -6
- package/lib/projects/create/__tests__/v3.test.js +4 -4
- package/lib/projects/create/v3.js +2 -2
- package/lib/projects/localDev/LocalDevLogger.d.ts +4 -0
- package/lib/projects/localDev/LocalDevLogger.js +22 -0
- package/lib/projects/localDev/LocalDevProcess.d.ts +7 -5
- package/lib/projects/localDev/LocalDevProcess.js +90 -19
- package/lib/projects/localDev/LocalDevState.d.ts +9 -8
- package/lib/projects/localDev/LocalDevState.js +18 -17
- package/lib/projects/localDev/LocalDevWebsocketServer.d.ts +2 -0
- package/lib/projects/localDev/LocalDevWebsocketServer.js +55 -23
- package/lib/projects/localDev/localDevWebsocketServerUtils.d.ts +4 -0
- package/lib/projects/localDev/localDevWebsocketServerUtils.js +10 -0
- package/lib/projects/pollProjectBuildAndDeploy.js +4 -4
- package/lib/prompts/projectAddPrompt.js +3 -1
- package/lib/prompts/promptUtils.js +3 -0
- package/lib/prompts/selectProjectTemplatePrompt.js +2 -0
- package/lib/theme/__tests__/migrate.test.d.ts +1 -0
- package/lib/theme/__tests__/migrate.test.js +233 -0
- package/lib/theme/migrate.d.ts +13 -0
- package/lib/theme/migrate.js +90 -0
- package/lib/usageTracking.js +2 -2
- package/mcp-server/tools/project/AddFeatureToProjectTool.d.ts +2 -2
- package/mcp-server/tools/project/CreateProjectTool.d.ts +2 -2
- package/mcp-server/tools/project/DocsSearchTool.d.ts +4 -1
- package/mcp-server/tools/project/DocsSearchTool.js +5 -5
- package/mcp-server/tools/project/GetConfigValuesTool.d.ts +4 -1
- package/mcp-server/tools/project/GetConfigValuesTool.js +10 -4
- package/mcp-server/tools/project/__tests__/DocsSearchTool.test.js +12 -10
- package/mcp-server/tools/project/__tests__/GetConfigValuesTool.test.js +8 -7
- package/mcp-server/utils/__tests__/cliConfig.test.d.ts +1 -0
- package/mcp-server/utils/__tests__/cliConfig.test.js +110 -0
- package/mcp-server/utils/cliConfig.d.ts +1 -0
- package/mcp-server/utils/cliConfig.js +12 -0
- package/package.json +4 -4
- package/types/LocalDev.d.ts +19 -3
- package/ui/index.js +1 -1
|
@@ -8,9 +8,14 @@ import { uiCommandReference } from '../../lib/ui/index.js';
|
|
|
8
8
|
import { commands, lib } from '../../lang/en.js';
|
|
9
9
|
import { uiLogger } from '../../lib/ui/logger.js';
|
|
10
10
|
import { logInBox } from '../../lib/ui/boxen.js';
|
|
11
|
+
import { renderInline } from '../../ui/index.js';
|
|
12
|
+
import { getWarningBox } from '../../ui/components/StatusMessageBoxes.js';
|
|
13
|
+
import { getHasMigratableThemes, migrateThemes2025_2, } from '../../lib/theme/migrate.js';
|
|
14
|
+
import { hasFeature } from '../../lib/hasFeature.js';
|
|
15
|
+
import { FEATURES } from '../../lib/constants.js';
|
|
11
16
|
const { v2025_2 } = PLATFORM_VERSIONS;
|
|
12
17
|
const command = 'migrate';
|
|
13
|
-
const describe =
|
|
18
|
+
const describe = commands.project.migrate.describe;
|
|
14
19
|
async function handler(args) {
|
|
15
20
|
const { platformVersion, unstable } = args;
|
|
16
21
|
const projectConfig = await getProjectConfig();
|
|
@@ -19,20 +24,44 @@ async function handler(args) {
|
|
|
19
24
|
return process.exit(EXIT_CODES.ERROR);
|
|
20
25
|
}
|
|
21
26
|
if (projectConfig?.projectConfig) {
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
27
|
+
if (!process.env.HUBSPOT_ENABLE_INK) {
|
|
28
|
+
await logInBox({
|
|
29
|
+
contents: lib.migrate.projectMigrationWarning,
|
|
30
|
+
options: { title: lib.migrate.projectMigrationWarningTitle },
|
|
31
|
+
});
|
|
32
|
+
}
|
|
33
|
+
else {
|
|
34
|
+
await renderInline(getWarningBox({
|
|
35
|
+
title: lib.migrate.projectMigrationWarningTitle,
|
|
36
|
+
message: lib.migrate.projectMigrationWarning,
|
|
37
|
+
}));
|
|
38
|
+
}
|
|
26
39
|
}
|
|
27
40
|
const { derivedAccountId } = args;
|
|
28
41
|
try {
|
|
29
|
-
await
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
42
|
+
const { hasMigratableThemes, migratableThemesCount } = await getHasMigratableThemes(projectConfig);
|
|
43
|
+
if (hasMigratableThemes) {
|
|
44
|
+
const hasThemeMigrationAccess = await hasFeature(derivedAccountId, FEATURES.THEME_MIGRATION_2025_2);
|
|
45
|
+
if (!hasThemeMigrationAccess) {
|
|
46
|
+
uiLogger.error(commands.project.migrate.errors.noThemeMigrationAccess(derivedAccountId));
|
|
47
|
+
return process.exit(EXIT_CODES.ERROR);
|
|
48
|
+
}
|
|
49
|
+
await migrateThemes2025_2(derivedAccountId, {
|
|
50
|
+
...args,
|
|
51
|
+
platformVersion: unstable
|
|
52
|
+
? PLATFORM_VERSIONS.unstable
|
|
53
|
+
: platformVersion,
|
|
54
|
+
}, migratableThemesCount, projectConfig);
|
|
55
|
+
}
|
|
56
|
+
else {
|
|
57
|
+
await migrateApp2025_2(derivedAccountId, {
|
|
58
|
+
...args,
|
|
59
|
+
name: projectConfig?.projectConfig?.name,
|
|
60
|
+
platformVersion: unstable
|
|
61
|
+
? PLATFORM_VERSIONS.unstable
|
|
62
|
+
: platformVersion,
|
|
63
|
+
}, projectConfig);
|
|
64
|
+
}
|
|
36
65
|
}
|
|
37
66
|
catch (error) {
|
|
38
67
|
logError(error);
|
|
@@ -46,7 +75,6 @@ function projectMigrateBuilder(yargs) {
|
|
|
46
75
|
type: 'string',
|
|
47
76
|
choices: [v2025_2],
|
|
48
77
|
default: v2025_2,
|
|
49
|
-
hidden: true,
|
|
50
78
|
})
|
|
51
79
|
.option('unstable', {
|
|
52
80
|
type: 'boolean',
|
package/commands/project/open.js
CHANGED
|
@@ -6,11 +6,10 @@ import { getProjectConfig } from '../../lib/projects/config.js';
|
|
|
6
6
|
import { ensureProjectExists } from '../../lib/projects/ensureProjectExists.js';
|
|
7
7
|
import { getProjectDetailUrl } from '../../lib/projects/urls.js';
|
|
8
8
|
import { projectNamePrompt } from '../../lib/prompts/projectNamePrompt.js';
|
|
9
|
-
import { uiBetaTag } from '../../lib/ui/index.js';
|
|
10
9
|
import { EXIT_CODES } from '../../lib/enums/exitCodes.js';
|
|
11
10
|
import { makeYargsBuilder } from '../../lib/yargsUtils.js';
|
|
12
11
|
const command = 'open';
|
|
13
|
-
const describe =
|
|
12
|
+
const describe = i18n(`commands.project.subcommands.open.describe`);
|
|
14
13
|
async function handler(args) {
|
|
15
14
|
const { project, derivedAccountId } = args;
|
|
16
15
|
trackCommandUsage('project-open', undefined, derivedAccountId);
|
|
@@ -5,7 +5,7 @@ import { getAccountIdentifier } from '@hubspot/local-dev-lib/config/getAccountId
|
|
|
5
5
|
import { getAllHsProfiles, getHsProfileFilename, loadHsProfileFile, } from '@hubspot/project-parsing-lib';
|
|
6
6
|
import { trackCommandUsage } from '../../../lib/usageTracking.js';
|
|
7
7
|
import { getProjectConfig } from '../../../lib/projects/config.js';
|
|
8
|
-
import {
|
|
8
|
+
import { uiAccountDescription } from '../../../lib/ui/index.js';
|
|
9
9
|
import { uiLogger } from '../../../lib/ui/logger.js';
|
|
10
10
|
import { EXIT_CODES } from '../../../lib/enums/exitCodes.js';
|
|
11
11
|
import { makeYargsBuilder } from '../../../lib/yargsUtils.js';
|
|
@@ -14,8 +14,8 @@ import { promptUser, listPrompt, confirmPrompt, } from '../../../lib/prompts/pro
|
|
|
14
14
|
import { fileExists } from '../../../lib/validation.js';
|
|
15
15
|
import { debugError } from '../../../lib/errorHandlers/index.js';
|
|
16
16
|
const command = 'add [name]';
|
|
17
|
-
const describe =
|
|
18
|
-
const verboseDescribe =
|
|
17
|
+
const describe = commands.project.profile.add.describe;
|
|
18
|
+
const verboseDescribe = commands.project.profile.add.verboseDescribe;
|
|
19
19
|
async function selectProfileToCopyVariablesFrom(existingProfiles) {
|
|
20
20
|
let profileToCopyVariablesFrom;
|
|
21
21
|
if (existingProfiles.length == 1) {
|
|
@@ -5,7 +5,6 @@ import { fetchProject, deleteProject, } from '@hubspot/local-dev-lib/api/project
|
|
|
5
5
|
import { getAccountConfig } from '@hubspot/local-dev-lib/config';
|
|
6
6
|
import { trackCommandUsage } from '../../../lib/usageTracking.js';
|
|
7
7
|
import { getProjectConfig } from '../../../lib/projects/config.js';
|
|
8
|
-
import { uiBetaTag } from '../../../lib/ui/index.js';
|
|
9
8
|
import { uiLogger } from '../../../lib/ui/logger.js';
|
|
10
9
|
import { EXIT_CODES } from '../../../lib/enums/exitCodes.js';
|
|
11
10
|
import { makeYargsBuilder } from '../../../lib/yargsUtils.js';
|
|
@@ -15,7 +14,7 @@ import { fileExists } from '../../../lib/validation.js';
|
|
|
15
14
|
import { debugError } from '../../../lib/errorHandlers/index.js';
|
|
16
15
|
import { isDeveloperTestAccount, isSandbox, } from '../../../lib/accountTypes.js';
|
|
17
16
|
const command = 'delete [name]';
|
|
18
|
-
const describe =
|
|
17
|
+
const describe = commands.project.profile.delete.describe;
|
|
19
18
|
async function handler(args) {
|
|
20
19
|
const { derivedAccountId } = args;
|
|
21
20
|
trackCommandUsage('project-profile-delete', undefined, derivedAccountId);
|
|
@@ -1,11 +1,10 @@
|
|
|
1
|
-
import { uiBetaTag } from '../../lib/ui/index.js';
|
|
2
1
|
import add from './profile/add.js';
|
|
3
2
|
import deleteProfile from './profile/delete.js';
|
|
4
3
|
import { makeYargsBuilder } from '../../lib/yargsUtils.js';
|
|
5
4
|
import { commands } from '../../lang/en.js';
|
|
6
5
|
const command = ['profile', 'profiles'];
|
|
7
|
-
const describe =
|
|
8
|
-
const verboseDescribe =
|
|
6
|
+
const describe = commands.project.profile.describe;
|
|
7
|
+
const verboseDescribe = commands.project.profile.verboseDescribe;
|
|
9
8
|
function projectProfileBuilder(yargs) {
|
|
10
9
|
yargs.command(add).command(deleteProfile).demandCommand(1, '');
|
|
11
10
|
return yargs;
|
|
@@ -3,7 +3,7 @@ import { logger } from '@hubspot/local-dev-lib/logger';
|
|
|
3
3
|
import { getAccountConfig } from '@hubspot/local-dev-lib/config';
|
|
4
4
|
import { isSpecifiedError } from '@hubspot/local-dev-lib/errors/index';
|
|
5
5
|
import { useV3Api } from '../../lib/projects/platformVersion.js';
|
|
6
|
-
import {
|
|
6
|
+
import { uiCommandReference } from '../../lib/ui/index.js';
|
|
7
7
|
import { trackCommandUsage } from '../../lib/usageTracking.js';
|
|
8
8
|
import { getProjectConfig, validateProjectConfig, } from '../../lib/projects/config.js';
|
|
9
9
|
import { logFeedbackMessage } from '../../lib/projects/ui.js';
|
|
@@ -17,7 +17,7 @@ import { EXIT_CODES } from '../../lib/enums/exitCodes.js';
|
|
|
17
17
|
import { makeYargsBuilder } from '../../lib/yargsUtils.js';
|
|
18
18
|
import { uiLogger } from '../../lib/ui/logger.js';
|
|
19
19
|
const command = 'upload';
|
|
20
|
-
const describe =
|
|
20
|
+
const describe = i18n(`commands.project.subcommands.upload.describe`);
|
|
21
21
|
async function handler(args) {
|
|
22
22
|
const { forceCreate, message, derivedAccountId, skipValidation, formatOutputAsJson, profile, } = args;
|
|
23
23
|
const jsonOutput = {};
|
|
@@ -11,7 +11,7 @@ import { commands } from '../../lang/en.js';
|
|
|
11
11
|
import { loadAndValidateProfile } from '../../lib/projectProfiles.js';
|
|
12
12
|
import { logError } from '../../lib/errorHandlers/index.js';
|
|
13
13
|
const command = 'validate';
|
|
14
|
-
const describe =
|
|
14
|
+
const describe = commands.project.validate.describe;
|
|
15
15
|
async function handler(args) {
|
|
16
16
|
const { derivedAccountId, profile } = args;
|
|
17
17
|
const { projectConfig, projectDir } = await getProjectConfig();
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { cancelStagedBuild, fetchProjectBuilds, } from '@hubspot/local-dev-lib/api/projects';
|
|
2
2
|
import { isSpecifiedError } from '@hubspot/local-dev-lib/errors/index';
|
|
3
3
|
import { useV3Api } from '../../lib/projects/platformVersion.js';
|
|
4
|
-
import { uiCommandReference, uiLink
|
|
4
|
+
import { uiCommandReference, uiLink } from '../../lib/ui/index.js';
|
|
5
5
|
import { i18n } from '../../lib/lang.js';
|
|
6
6
|
import { createWatcher } from '../../lib/projects/watch.js';
|
|
7
7
|
import { logError, ApiErrorContext } from '../../lib/errorHandlers/index.js';
|
|
@@ -16,7 +16,7 @@ import { EXIT_CODES } from '../../lib/enums/exitCodes.js';
|
|
|
16
16
|
import { handleKeypress, handleExit } from '../../lib/process.js';
|
|
17
17
|
import { makeYargsBuilder } from '../../lib/yargsUtils.js';
|
|
18
18
|
const command = 'watch';
|
|
19
|
-
const describe =
|
|
19
|
+
const describe = i18n(`commands.project.subcommands.watch.describe`);
|
|
20
20
|
async function handleBuildStatus(accountId, projectName, buildId) {
|
|
21
21
|
const { isAutoDeployEnabled, deployStatusTaskLocator } = await pollBuildStatus(accountId, projectName, buildId, null);
|
|
22
22
|
if (isAutoDeployEnabled && deployStatusTaskLocator) {
|
package/commands/project.js
CHANGED
|
@@ -1,5 +1,4 @@
|
|
|
1
1
|
import { i18n } from '../lib/lang.js';
|
|
2
|
-
import { uiBetaTag } from '../lib/ui/index.js';
|
|
3
2
|
import deploy from './project/deploy.js';
|
|
4
3
|
import create from './project/create.js';
|
|
5
4
|
import upload from './project/upload.js';
|
|
@@ -18,7 +17,7 @@ import profile from './project/profile.js';
|
|
|
18
17
|
import projectValidate from './project/validate.js';
|
|
19
18
|
import { makeYargsBuilder } from '../lib/yargsUtils.js';
|
|
20
19
|
const command = ['project', 'projects'];
|
|
21
|
-
const describe =
|
|
20
|
+
const describe = i18n(`commands.project.describe`);
|
|
22
21
|
function projectBuilder(yargs) {
|
|
23
22
|
yargs
|
|
24
23
|
.command(create)
|
package/lang/en.d.ts
CHANGED
|
@@ -10,6 +10,7 @@ export declare const commands: {
|
|
|
10
10
|
};
|
|
11
11
|
};
|
|
12
12
|
readonly getStarted: {
|
|
13
|
+
readonly describe: "A step-by-step command to get you started with a HubSpot project.";
|
|
13
14
|
readonly options: {
|
|
14
15
|
readonly dest: {
|
|
15
16
|
readonly describe: "Directory where the project should be created";
|
|
@@ -859,8 +860,9 @@ Global configuration replaces hubspot.config.yml, and you will be prompted to mi
|
|
|
859
860
|
readonly tailLogs: (functionPath: string, accountId: string) => string;
|
|
860
861
|
};
|
|
861
862
|
readonly mcp: {
|
|
862
|
-
readonly describe: "Commands for managing HubSpot MCP servers";
|
|
863
|
+
readonly describe: "Commands for managing HubSpot MCP servers.";
|
|
863
864
|
readonly setup: {
|
|
865
|
+
readonly describe: "Setup the HubSpot development MCP servers.";
|
|
864
866
|
readonly installingDocSearch: "Adding the docs-search mcp server";
|
|
865
867
|
readonly claudeCode: "Claude Code";
|
|
866
868
|
readonly cursor: "Cursor";
|
|
@@ -872,6 +874,7 @@ Global configuration replaces hubspot.config.yml, and you will be prompted to mi
|
|
|
872
874
|
};
|
|
873
875
|
readonly success: (derivedTargets: string[]) => string;
|
|
874
876
|
readonly errors: {
|
|
877
|
+
readonly needsMcpAccess: (accountId?: number) => string;
|
|
875
878
|
readonly needsNode20: "This feature requires node >=20";
|
|
876
879
|
readonly errorParsingJsonFIle: (filename: string, errorMessage: string) => string;
|
|
877
880
|
};
|
|
@@ -1008,7 +1011,7 @@ Profiles enable you to reference variables in your component configuration files
|
|
|
1008
1011
|
readonly dev: {
|
|
1009
1012
|
readonly describe: "Start local dev for the current project.";
|
|
1010
1013
|
readonly logs: {
|
|
1011
|
-
readonly
|
|
1014
|
+
readonly header: "HubSpot projects local development";
|
|
1012
1015
|
readonly placeholderAccountSelection: "Using default account as target account (for now)";
|
|
1013
1016
|
readonly accountTypeInformation: "Testing in a developer test account is strongly recommended, but you can use a sandbox account if your plan allows you to create one.";
|
|
1014
1017
|
readonly learnMoreMessageV3: `Learn more about ${string} | ${string}`;
|
|
@@ -1070,7 +1073,7 @@ ${string}`;
|
|
|
1070
1073
|
readonly describe: "Project name (cannot be changed)";
|
|
1071
1074
|
};
|
|
1072
1075
|
readonly template: {
|
|
1073
|
-
readonly describe: "The starting template";
|
|
1076
|
+
readonly describe: "The starting template. Only applies when platform version is less than 2025.2.";
|
|
1074
1077
|
};
|
|
1075
1078
|
readonly templateSource: {
|
|
1076
1079
|
readonly describe: "Path to custom GitHub repository from which to create project template";
|
|
@@ -1136,6 +1139,7 @@ ${string}`;
|
|
|
1136
1139
|
readonly describe: "Migrate an existing project to the new version of the projects framework.";
|
|
1137
1140
|
readonly errors: {
|
|
1138
1141
|
readonly noProjectConfig: (command: string) => string;
|
|
1142
|
+
readonly noThemeMigrationAccess: (accountId?: number) => string;
|
|
1139
1143
|
};
|
|
1140
1144
|
readonly examples: {
|
|
1141
1145
|
readonly default: "Migrate an existing project to the new version of the projects framework.";
|
|
@@ -2591,8 +2595,13 @@ export declare const lib: {
|
|
|
2591
2595
|
readonly LocalDevProcess: {
|
|
2592
2596
|
readonly projectConfigMismatch: `Unable to upload project. The project config has been modified since starting ${string}.`;
|
|
2593
2597
|
readonly uploadInitiated: "Project upload initiated from Local Dev UI.";
|
|
2598
|
+
readonly deployInitiated: "Project deploy initiated from Local Dev UI.";
|
|
2594
2599
|
readonly uploadFailed: "Project upload failed. To proceed with local development, fix any necessary errors, then re-upload your project.";
|
|
2600
|
+
readonly deployFailed: "Project deploy failed. To proceed with local development, fix any necessary errors, then re-deploy your project.";
|
|
2595
2601
|
readonly uploadSuccess: "Project upload completed successfully. Resuming local dev...";
|
|
2602
|
+
readonly uploadSuccessAutoDeployDisabled: "Project upload completed successfully, but auto-deploy is disabled for this project. Deploy your latest build to proceed with local development.";
|
|
2603
|
+
readonly deploySuccess: "Project deploy completed successfully. Resuming local dev...";
|
|
2604
|
+
readonly noBuildToDeploy: "Error deploying project. No build was found to deploy.";
|
|
2596
2605
|
};
|
|
2597
2606
|
readonly localDevHelpers: {
|
|
2598
2607
|
readonly project: {
|
|
@@ -3111,6 +3120,7 @@ Run ${string} to upgrade to version ${string}`;
|
|
|
3111
3120
|
};
|
|
3112
3121
|
readonly projectAddPrompt: {
|
|
3113
3122
|
readonly selectType: "[--type] Select an app feature to add: ";
|
|
3123
|
+
readonly selectFeatures: "[--features] Select an app feature to add: ";
|
|
3114
3124
|
readonly enterName: "[--name] Give your component a name: ";
|
|
3115
3125
|
readonly errors: {
|
|
3116
3126
|
readonly nameRequired: "A component name is required";
|
|
@@ -3388,6 +3398,7 @@ Run ${string} to upgrade to version ${string}`;
|
|
|
3388
3398
|
readonly sourceContentsMoved: (newLocation: string) => string;
|
|
3389
3399
|
readonly projectMigrationWarningTitle: "Important: Migrating to platformVersion 2025.2 is irreversible";
|
|
3390
3400
|
readonly projectMigrationWarning: string;
|
|
3401
|
+
readonly exitWithoutMigrating: "Exiting without migrating";
|
|
3391
3402
|
readonly success: {
|
|
3392
3403
|
readonly downloadedProject: (projectName: string, projectDest: string) => string;
|
|
3393
3404
|
readonly themesMigrationSuccess: (platformVersion: string) => string;
|
package/lang/en.js
CHANGED
|
@@ -5,6 +5,7 @@ import { PERSONAL_ACCESS_KEY_AUTH_METHOD } from '@hubspot/local-dev-lib/constant
|
|
|
5
5
|
import { ARCHIVED_HUBSPOT_CONFIG_YAML_FILE_NAME, GLOBAL_CONFIG_PATH, DEFAULT_HUBSPOT_CONFIG_YAML_FILE_NAME, } from '@hubspot/local-dev-lib/constants/config';
|
|
6
6
|
import { uiAccountDescription, uiBetaTag, uiCommandReference, uiLink, UI_COLORS, } from '../lib/ui/index.js';
|
|
7
7
|
import { getProjectDetailUrl, getProjectSettingsUrl, getLocalDevUiUrl, getAppAllowlistUrl, } from '../lib/projects/urls.js';
|
|
8
|
+
import { getProductUpdatesUrl } from '../lib/links.js';
|
|
8
9
|
import { APP_DISTRIBUTION_TYPES, APP_AUTH_TYPES, PROJECT_CONFIG_FILE, PROJECT_WITH_APP, } from '../lib/constants.js';
|
|
9
10
|
import { getAccountIdentifier } from '@hubspot/local-dev-lib/config/getAccountIdentifier';
|
|
10
11
|
export const commands = {
|
|
@@ -18,6 +19,7 @@ export const commands = {
|
|
|
18
19
|
},
|
|
19
20
|
},
|
|
20
21
|
getStarted: {
|
|
22
|
+
describe: 'A step-by-step command to get you started with a HubSpot project.',
|
|
21
23
|
options: {
|
|
22
24
|
dest: {
|
|
23
25
|
describe: 'Directory where the project should be created',
|
|
@@ -862,8 +864,9 @@ export const commands = {
|
|
|
862
864
|
tailLogs: (functionPath, accountId) => `Waiting for log entries for "${functionPath}" on account "${accountId}".\n`,
|
|
863
865
|
},
|
|
864
866
|
mcp: {
|
|
865
|
-
describe: 'Commands for managing HubSpot MCP servers',
|
|
867
|
+
describe: 'Commands for managing HubSpot MCP servers.',
|
|
866
868
|
setup: {
|
|
869
|
+
describe: 'Setup the HubSpot development MCP servers.',
|
|
867
870
|
installingDocSearch: 'Adding the docs-search mcp server',
|
|
868
871
|
claudeCode: 'Claude Code',
|
|
869
872
|
cursor: 'Cursor',
|
|
@@ -875,6 +878,7 @@ export const commands = {
|
|
|
875
878
|
},
|
|
876
879
|
success: (derivedTargets) => `You can now use the HubSpot CLI MCP Server in ${derivedTargets.join(', ')}. ${chalk.bold('You may need to restart these tools to apply the changes')}.`,
|
|
877
880
|
errors: {
|
|
881
|
+
needsMcpAccess: (accountId) => `You must opt in to the developer MCP beta to use this feature on ${uiAccountDescription(accountId)}. Try again with a different account or ${uiLink('join the beta now', getProductUpdatesUrl('239890', accountId))}`,
|
|
878
882
|
needsNode20: `This feature requires node >=20`,
|
|
879
883
|
errorParsingJsonFIle: (filename, errorMessage) => `Unable to update ${chalk.bold(filename)} due to invalid JSON: ${errorMessage}`,
|
|
880
884
|
},
|
|
@@ -1009,7 +1013,7 @@ export const commands = {
|
|
|
1009
1013
|
dev: {
|
|
1010
1014
|
describe: 'Start local dev for the current project.',
|
|
1011
1015
|
logs: {
|
|
1012
|
-
|
|
1016
|
+
header: 'HubSpot projects local development',
|
|
1013
1017
|
placeholderAccountSelection: 'Using default account as target account (for now)',
|
|
1014
1018
|
accountTypeInformation: 'Testing in a developer test account is strongly recommended, but you can use a sandbox account if your plan allows you to create one.',
|
|
1015
1019
|
learnMoreMessageV3: `Learn more about ${uiLink('HubSpot projects local dev', 'https://hubspot.mintlify.io/en-us/developer-tooling/local-development/hubspot-cli/project-commands#start-a-local-development-server')} | ${uiLink('HubSpot account types', 'https://developers.hubspot.com/docs/getting-started/account-types')}`,
|
|
@@ -1069,7 +1073,7 @@ export const commands = {
|
|
|
1069
1073
|
describe: 'Project name (cannot be changed)',
|
|
1070
1074
|
},
|
|
1071
1075
|
template: {
|
|
1072
|
-
describe: 'The starting template',
|
|
1076
|
+
describe: 'The starting template. Only applies when platform version is less than 2025.2.',
|
|
1073
1077
|
},
|
|
1074
1078
|
templateSource: {
|
|
1075
1079
|
describe: 'Path to custom GitHub repository from which to create project template',
|
|
@@ -1135,6 +1139,7 @@ export const commands = {
|
|
|
1135
1139
|
describe: 'Migrate an existing project to the new version of the projects framework.',
|
|
1136
1140
|
errors: {
|
|
1137
1141
|
noProjectConfig: (command) => `No project detected. Please run this command again from a project directory. If you are trying to migrate an app, run ${command}`,
|
|
1142
|
+
noThemeMigrationAccess: (accountId) => `This project contains a CMS theme. You must opt in to theme migration beta to continue updating it on ${uiAccountDescription(accountId)}. Try again with a different account or ${uiLink('join the beta now', getProductUpdatesUrl('253920', accountId))}`,
|
|
1138
1143
|
},
|
|
1139
1144
|
examples: {
|
|
1140
1145
|
default: 'Migrate an existing project to the new version of the projects framework.',
|
|
@@ -1192,7 +1197,7 @@ export const commands = {
|
|
|
1192
1197
|
maxExceeded: (maxCount) => `This project has the maximum allowed(${maxCount})`,
|
|
1193
1198
|
authTypeNotAllowed: (authType) => `Auth type '${authType}' not allowed.`,
|
|
1194
1199
|
distributionNotAllowed: (dist) => `Distribution '${dist}' not allowed.`,
|
|
1195
|
-
portalDoesNotHaveAccessToThisFeature: (accountId) => `The account ${uiAccountDescription(accountId)} does not have access to this feature
|
|
1200
|
+
portalDoesNotHaveAccessToThisFeature: (accountId) => `The account ${uiAccountDescription(accountId)} does not have access to this feature.`,
|
|
1196
1201
|
locationInProject: 'This command must be run from within a project directory.',
|
|
1197
1202
|
failedToFetchComponentList: 'Failed to fetch the list of available features. Please try again later.',
|
|
1198
1203
|
projectContainsPublicApp: 'This project contains a public app. This command is currently only compatible with projects that contain private apps.',
|
|
@@ -2588,8 +2593,13 @@ export const lib = {
|
|
|
2588
2593
|
LocalDevProcess: {
|
|
2589
2594
|
projectConfigMismatch: `Unable to upload project. The project config has been modified since starting ${uiCommandReference('hs project dev')}.`,
|
|
2590
2595
|
uploadInitiated: 'Project upload initiated from Local Dev UI.',
|
|
2596
|
+
deployInitiated: 'Project deploy initiated from Local Dev UI.',
|
|
2591
2597
|
uploadFailed: 'Project upload failed. To proceed with local development, fix any necessary errors, then re-upload your project.',
|
|
2598
|
+
deployFailed: 'Project deploy failed. To proceed with local development, fix any necessary errors, then re-deploy your project.',
|
|
2592
2599
|
uploadSuccess: 'Project upload completed successfully. Resuming local dev...',
|
|
2600
|
+
uploadSuccessAutoDeployDisabled: 'Project upload completed successfully, but auto-deploy is disabled for this project. Deploy your latest build to proceed with local development.',
|
|
2601
|
+
deploySuccess: 'Project deploy completed successfully. Resuming local dev...',
|
|
2602
|
+
noBuildToDeploy: 'Error deploying project. No build was found to deploy.',
|
|
2593
2603
|
},
|
|
2594
2604
|
localDevHelpers: {
|
|
2595
2605
|
project: {
|
|
@@ -3105,6 +3115,7 @@ export const lib = {
|
|
|
3105
3115
|
},
|
|
3106
3116
|
projectAddPrompt: {
|
|
3107
3117
|
selectType: '[--type] Select an app feature to add: ',
|
|
3118
|
+
selectFeatures: '[--features] Select an app feature to add: ',
|
|
3108
3119
|
enterName: '[--name] Give your component a name: ',
|
|
3109
3120
|
errors: {
|
|
3110
3121
|
nameRequired: 'A component name is required',
|
|
@@ -3382,6 +3393,7 @@ export const lib = {
|
|
|
3382
3393
|
sourceContentsMoved: (newLocation) => `The contents of your old source directory have been moved to ${newLocation}, move any required files to the new source directory.`,
|
|
3383
3394
|
projectMigrationWarningTitle: 'Important: Migrating to platformVersion 2025.2 is irreversible',
|
|
3384
3395
|
projectMigrationWarning: uiBetaTag(`Running the ${uiCommandReference('hs project migrate')} command will permanently upgrade your project to platformVersion 2025.2. 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),
|
|
3396
|
+
exitWithoutMigrating: 'Exiting without migrating',
|
|
3385
3397
|
success: {
|
|
3386
3398
|
downloadedProject: (projectName, projectDest) => `Saved ${projectName} to ${projectDest}`,
|
|
3387
3399
|
themesMigrationSuccess: (platformVersion) => `Successfully migrated project to platformVersion ${chalk.bold(platformVersion)}. Upload your project using ${uiCommandReference('hs project upload')}`,
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { logger } from '@hubspot/local-dev-lib/logger';
|
|
2
2
|
import { getCwd, sanitizeFileName } from '@hubspot/local-dev-lib/path';
|
|
3
3
|
import { extractZipArchive } from '@hubspot/local-dev-lib/archive';
|
|
4
|
-
import { validateUid
|
|
4
|
+
import { validateUid } from '@hubspot/project-parsing-lib';
|
|
5
5
|
import { UNMIGRATABLE_REASONS } from '@hubspot/local-dev-lib/constants/projects';
|
|
6
6
|
import { MIGRATION_STATUS } from '@hubspot/local-dev-lib/types/Migration';
|
|
7
7
|
import { downloadProject } from '@hubspot/local-dev-lib/api/projects';
|
|
@@ -12,7 +12,7 @@ import { poll } from '../../polling.js';
|
|
|
12
12
|
import { CLI_UNMIGRATABLE_REASONS, continueAppMigration, initializeAppMigration, listAppsForMigration, } from '../../../api/migrate.js';
|
|
13
13
|
import { lib } from '../../../lang/en.js';
|
|
14
14
|
import { hasUnfiedAppsAccess } from '../../hasFeature.js';
|
|
15
|
-
import { getUnmigratableReason, generateFilterAppsByProjectNameFunction, buildErrorMessageFromMigrationStatus, fetchMigrationApps, promptForAppToMigrate, selectAppToMigrate, handleMigrationSetup,
|
|
15
|
+
import { getUnmigratableReason, generateFilterAppsByProjectNameFunction, buildErrorMessageFromMigrationStatus, fetchMigrationApps, promptForAppToMigrate, selectAppToMigrate, handleMigrationSetup, beginAppMigration, pollMigrationStatus, finalizeAppMigration, downloadProjectFiles, migrateApp2025_2, logInvalidAccountError, validateMigrationApps, } from '../migrate.js';
|
|
16
16
|
vi.mock('@hubspot/local-dev-lib/logger');
|
|
17
17
|
vi.mock('@hubspot/local-dev-lib/path');
|
|
18
18
|
vi.mock('@hubspot/local-dev-lib/archive');
|
|
@@ -40,7 +40,6 @@ const mockedListPrompt = listPrompt;
|
|
|
40
40
|
const mockedEnsureProjectExists = ensureProjectExists;
|
|
41
41
|
const mockedPoll = poll;
|
|
42
42
|
const mockedListAppsForMigration = listAppsForMigration;
|
|
43
|
-
const mockedGetProjectThemeDetails = getProjectThemeDetails;
|
|
44
43
|
const mockedInitializeAppMigration = initializeAppMigration;
|
|
45
44
|
const mockedContinueAppMigration = continueAppMigration;
|
|
46
45
|
const mockedHasUnfiedAppsAccess = hasUnfiedAppsAccess;
|
|
@@ -205,58 +204,31 @@ describe('lib/app/migrate', () => {
|
|
|
205
204
|
expect(result.migratableApps[0].projectName).toBe(PROJECT_NAME);
|
|
206
205
|
});
|
|
207
206
|
});
|
|
208
|
-
describe('
|
|
209
|
-
it('should return false when no projectConfig is provided', async () => {
|
|
210
|
-
const result = await getHasMigratableThemes();
|
|
211
|
-
expect(result).toEqual({
|
|
212
|
-
hasMigratableThemes: false,
|
|
213
|
-
migratableThemesCount: 0,
|
|
214
|
-
});
|
|
215
|
-
});
|
|
216
|
-
it('should return true when there are migratable themes', async () => {
|
|
217
|
-
mockedGetProjectThemeDetails.mockResolvedValue({
|
|
218
|
-
legacyThemeDetails: [
|
|
219
|
-
{
|
|
220
|
-
configFilepath: 'src/theme.json',
|
|
221
|
-
themePath: 'src/theme',
|
|
222
|
-
themeConfig: {
|
|
223
|
-
secret_names: ['my-secret'],
|
|
224
|
-
},
|
|
225
|
-
},
|
|
226
|
-
],
|
|
227
|
-
legacyReactThemeDetails: [],
|
|
228
|
-
});
|
|
229
|
-
const projectConfig = createLoadedProjectConfig(PROJECT_NAME);
|
|
230
|
-
const result = await getHasMigratableThemes(projectConfig);
|
|
231
|
-
expect(result).toEqual({
|
|
232
|
-
hasMigratableThemes: true,
|
|
233
|
-
migratableThemesCount: 1,
|
|
234
|
-
});
|
|
235
|
-
});
|
|
236
|
-
});
|
|
237
|
-
describe('validateMigrationAppsAndThemes', () => {
|
|
207
|
+
describe('validateMigrationApps', () => {
|
|
238
208
|
const mockMigratableApp1 = createMockMigratableApp(1, 'App 1', PROJECT_NAME);
|
|
239
209
|
const mockMigratableApp2 = createMockMigratableApp(2, 'App 2', PROJECT_NAME);
|
|
240
210
|
it('should throw an error when multiple apps are found for a project', async () => {
|
|
241
211
|
const projectConfig = createLoadedProjectConfig(PROJECT_NAME);
|
|
242
|
-
await expect(
|
|
212
|
+
await expect(validateMigrationApps(APP_ID, ACCOUNT_ID, {
|
|
243
213
|
migratableApps: [mockMigratableApp1, mockMigratableApp2],
|
|
244
214
|
unmigratableApps: [],
|
|
245
|
-
},
|
|
246
|
-
});
|
|
247
|
-
it('should throw an error when cms themes are found for a project', async () => {
|
|
248
|
-
const projectConfig = createLoadedProjectConfig(PROJECT_NAME);
|
|
249
|
-
await expect(validateMigrationAppsAndThemes(APP_ID, ACCOUNT_ID, { migratableApps: [mockMigratableApp1], unmigratableApps: [] }, true, projectConfig)).rejects.toThrow(lib.migrate.errors.project.themesAndAppsNotAllowed);
|
|
215
|
+
}, projectConfig)).rejects.toThrow(lib.migrate.errors.project.multipleApps);
|
|
250
216
|
});
|
|
251
217
|
it('should throw an error when no apps are found for a project', async () => {
|
|
252
218
|
const projectConfig = createLoadedProjectConfig(PROJECT_NAME);
|
|
253
|
-
await expect(
|
|
219
|
+
await expect(validateMigrationApps(APP_ID, ACCOUNT_ID, { migratableApps: [], unmigratableApps: [] }, projectConfig)).rejects.toThrow(lib.migrate.errors.noAppsForProject(PROJECT_NAME));
|
|
254
220
|
});
|
|
255
221
|
it('should throw an error when no migratable apps are found', async () => {
|
|
256
|
-
await expect(
|
|
222
|
+
await expect(validateMigrationApps(APP_ID, ACCOUNT_ID, {
|
|
223
|
+
migratableApps: [],
|
|
224
|
+
unmigratableApps: mockUnmigratableApps,
|
|
225
|
+
})).rejects.toThrow(/No apps in account/);
|
|
257
226
|
});
|
|
258
227
|
it('should throw an error when appId is provided but not found', async () => {
|
|
259
|
-
await expect(
|
|
228
|
+
await expect(validateMigrationApps(APP_ID, ACCOUNT_ID, {
|
|
229
|
+
migratableApps: [],
|
|
230
|
+
unmigratableApps: [],
|
|
231
|
+
})).rejects.toThrow(/No apps in account/);
|
|
260
232
|
});
|
|
261
233
|
});
|
|
262
234
|
describe('promptForAppToMigrate', () => {
|
|
@@ -330,10 +302,6 @@ describe('lib/app/migrate', () => {
|
|
|
330
302
|
unmigratableApps: [],
|
|
331
303
|
},
|
|
332
304
|
});
|
|
333
|
-
mockedGetProjectThemeDetails.mockResolvedValue({
|
|
334
|
-
legacyThemeDetails: [],
|
|
335
|
-
legacyReactThemeDetails: [],
|
|
336
|
-
});
|
|
337
305
|
mockedListPrompt.mockResolvedValue({ appId: 1 });
|
|
338
306
|
mockedConfirmPrompt.mockResolvedValue(true);
|
|
339
307
|
mockedEnsureProjectExists.mockResolvedValue({ projectExists: false });
|
|
@@ -352,16 +320,11 @@ describe('lib/app/migrate', () => {
|
|
|
352
320
|
unmigratableApps: [],
|
|
353
321
|
},
|
|
354
322
|
});
|
|
355
|
-
mockedGetProjectThemeDetails.mockResolvedValueOnce({
|
|
356
|
-
legacyThemeDetails: [],
|
|
357
|
-
legacyReactThemeDetails: [],
|
|
358
|
-
});
|
|
359
323
|
const result = await handleMigrationSetup(ACCOUNT_ID, defaultOptions, projectConfig);
|
|
360
324
|
expect(result).toEqual({
|
|
361
325
|
appIdToMigrate: 1,
|
|
362
326
|
projectName: PROJECT_NAME,
|
|
363
327
|
projectDest: MOCK_PROJECT_DIR,
|
|
364
|
-
isThemesMigration: false,
|
|
365
328
|
});
|
|
366
329
|
});
|
|
367
330
|
it('should prompt for project name when not provided', async () => {
|
package/lib/app/migrate.d.ts
CHANGED
|
@@ -12,18 +12,14 @@ export type MigrateAppArgs = CommonArgs & AccountArgs & EnvironmentArgs & Config
|
|
|
12
12
|
export declare function getUnmigratableReason(reasonCode: string, projectName: string | undefined, accountId: number): string;
|
|
13
13
|
export declare function generateFilterAppsByProjectNameFunction(projectConfig?: LoadedProjectConfig): (app: MigrationApp) => boolean;
|
|
14
14
|
export declare function buildErrorMessageFromMigrationStatus(error: MigrationFailed): string;
|
|
15
|
-
export declare function getHasMigratableThemes(projectConfig?: LoadedProjectConfig): Promise<{
|
|
16
|
-
hasMigratableThemes: boolean;
|
|
17
|
-
migratableThemesCount: number;
|
|
18
|
-
}>;
|
|
19
15
|
export declare function fetchMigrationApps(derivedAccountId: number, platformVersion: string, projectConfig?: LoadedProjectConfig): Promise<{
|
|
20
16
|
migratableApps: MigratableApp[];
|
|
21
17
|
unmigratableApps: UnmigratableApp[];
|
|
22
18
|
}>;
|
|
23
|
-
export declare function
|
|
19
|
+
export declare function validateMigrationApps(appId: MigrateAppArgs['appId'], derivedAccountId: number, { migratableApps, unmigratableApps, }: {
|
|
24
20
|
migratableApps: MigratableApp[];
|
|
25
21
|
unmigratableApps: UnmigratableApp[];
|
|
26
|
-
},
|
|
22
|
+
}, projectConfig?: LoadedProjectConfig): Promise<void>;
|
|
27
23
|
export declare function promptForAppToMigrate(allApps: MigrationApp[], derivedAccountId: number): Promise<number>;
|
|
28
24
|
export declare function selectAppToMigrate(allApps: MigrationApp[], derivedAccountId: number, appId?: number): Promise<{
|
|
29
25
|
proceed: boolean;
|
|
@@ -33,9 +29,7 @@ export declare function handleMigrationSetup(derivedAccountId: number, options:
|
|
|
33
29
|
appIdToMigrate?: number | undefined;
|
|
34
30
|
projectName?: string;
|
|
35
31
|
projectDest?: string;
|
|
36
|
-
isThemesMigration?: boolean;
|
|
37
32
|
}>;
|
|
38
|
-
export declare function handleThemesMigration(projectConfig: LoadedProjectConfig, platformVersion: string): Promise<void>;
|
|
39
33
|
export declare function beginAppMigration(derivedAccountId: number, appId: number, platformVersion: string): Promise<{
|
|
40
34
|
migrationId: number;
|
|
41
35
|
uidMap: Record<string, string>;
|