@hubspot/cli 7.7.20-experimental.0 → 7.7.21-experimental.1

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.
Files changed (62) hide show
  1. package/bin/cli.js +6 -2
  2. package/commands/__tests__/getStarted.test.js +0 -10
  3. package/commands/app/__tests__/install.test.d.ts +1 -0
  4. package/commands/app/__tests__/install.test.js +52 -0
  5. package/commands/app/install.d.ts +8 -0
  6. package/commands/app/install.js +127 -0
  7. package/commands/app.js +6 -1
  8. package/commands/getStarted.js +12 -27
  9. package/commands/mcp/setup.js +1 -0
  10. package/commands/mcp/start.d.ts +4 -1
  11. package/commands/mcp/start.js +8 -3
  12. package/commands/project/__tests__/deploy.test.js +27 -25
  13. package/commands/project/__tests__/devUnifiedFlow.test.js +20 -16
  14. package/commands/project/create.js +1 -1
  15. package/commands/project/deploy.d.ts +3 -2
  16. package/commands/project/deploy.js +61 -55
  17. package/commands/project/dev/unifiedFlow.js +7 -6
  18. package/commands/testAccount/__tests__/createConfig.test.js +0 -3
  19. package/commands/testAccount/create.js +14 -24
  20. package/commands/testAccount/createConfig.d.ts +0 -2
  21. package/commands/testAccount/createConfig.js +7 -8
  22. package/lang/en.d.ts +61 -23
  23. package/lang/en.js +62 -24
  24. package/lang/en.lyaml +0 -26
  25. package/lib/__tests__/buildAccount.test.js +30 -2
  26. package/lib/__tests__/usageTracking.test.js +8 -14
  27. package/lib/buildAccount.d.ts +7 -1
  28. package/lib/buildAccount.js +54 -4
  29. package/lib/mcp/setup.js +8 -3
  30. package/lib/projects/add/legacyAddComponent.js +1 -1
  31. package/lib/projects/add/v3AddComponent.js +1 -1
  32. package/lib/projects/localDev/DevServerManager.js +0 -2
  33. package/lib/projects/localDev/DevServerManagerV2.js +0 -2
  34. package/lib/projects/localDev/helpers.d.ts +1 -1
  35. package/lib/projects/localDev/helpers.js +2 -2
  36. package/lib/projects/structure.d.ts +2 -2
  37. package/lib/projects/upload.d.ts +2 -1
  38. package/lib/projects/upload.js +2 -1
  39. package/lib/prompts/createDeveloperTestAccountConfigPrompt.d.ts +11 -10
  40. package/lib/prompts/createDeveloperTestAccountConfigPrompt.js +73 -31
  41. package/lib/prompts/promptUtils.js +66 -56
  42. package/lib/ui/index.js +1 -1
  43. package/lib/usageTracking.d.ts +11 -0
  44. package/lib/usageTracking.js +67 -73
  45. package/mcp-server/tools/project/AddFeatureToProject.js +4 -1
  46. package/mcp-server/tools/project/CreateProjectTool.d.ts +2 -2
  47. package/mcp-server/tools/project/CreateProjectTool.js +4 -1
  48. package/mcp-server/tools/project/DeployProject.js +4 -1
  49. package/mcp-server/tools/project/GuidedWalkthroughTool.js +4 -1
  50. package/mcp-server/tools/project/UploadProjectTools.js +4 -1
  51. package/mcp-server/tools/project/ValidateProjectTool.js +4 -1
  52. package/mcp-server/tools/project/__tests__/AddFeatureToProject.test.js +1 -0
  53. package/mcp-server/tools/project/__tests__/CreateProjectTool.test.js +1 -0
  54. package/mcp-server/tools/project/__tests__/DeployProject.test.js +1 -0
  55. package/mcp-server/tools/project/__tests__/GuidedWalkthroughTool.test.js +1 -0
  56. package/mcp-server/tools/project/__tests__/UploadProjectTools.test.js +1 -0
  57. package/mcp-server/tools/project/__tests__/ValidateProjectTool.test.js +1 -0
  58. package/mcp-server/utils/__tests__/project.test.js +9 -6
  59. package/mcp-server/utils/project.js +3 -0
  60. package/mcp-server/utils/toolUsageTracking.d.ts +1 -0
  61. package/mcp-server/utils/toolUsageTracking.js +25 -0
  62. package/package.json +4 -4
package/bin/cli.js CHANGED
@@ -135,8 +135,12 @@ const argv = yargs_1.default
135
135
  .wrap(getTerminalWidth())
136
136
  .strict().argv;
137
137
  if ('help' in argv && argv.help !== undefined) {
138
- (0, usageTracking_1.trackHelpUsage)((0, commonOpts_1.getCommandName)(argv));
138
+ (async () => {
139
+ await (0, usageTracking_1.trackHelpUsage)((0, commonOpts_1.getCommandName)(argv));
140
+ })();
139
141
  }
140
142
  if ('convertFields' in argv && argv.convertFields !== undefined) {
141
- (0, usageTracking_1.trackConvertFieldsUsage)((0, commonOpts_1.getCommandName)(argv));
143
+ (async () => {
144
+ await (0, usageTracking_1.trackConvertFieldsUsage)((0, commonOpts_1.getCommandName)(argv));
145
+ })();
142
146
  }
@@ -108,9 +108,6 @@ describe('commands/get-started', () => {
108
108
  promptUtils_1.promptUser
109
109
  .mockResolvedValueOnce({
110
110
  default: constants_1.GET_STARTED_OPTIONS.APP,
111
- })
112
- .mockResolvedValueOnce({
113
- shouldInstallDependencies: true,
114
111
  })
115
112
  .mockResolvedValueOnce({
116
113
  shouldUpload: true,
@@ -149,13 +146,6 @@ describe('commands/get-started', () => {
149
146
  }),
150
147
  ]);
151
148
  });
152
- it('should skip upload when user declines', async () => {
153
- promptUtils_1.promptUser
154
- .mockResolvedValueOnce({ shouldInstallDependencies: false })
155
- .mockResolvedValueOnce({ shouldUpload: false });
156
- await getStarted_1.default.handler(mockArgs);
157
- expect(process.exit).toHaveBeenCalledWith(exitCodes_1.EXIT_CODES.SUCCESS);
158
- });
159
149
  });
160
150
  describe('tracking', () => {
161
151
  it('should track command usage', async () => {
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,52 @@
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ const yargs_1 = __importDefault(require("yargs"));
7
+ const commonOpts_1 = require("../../../lib/commonOpts");
8
+ const install_1 = __importDefault(require("../install"));
9
+ vi.mock('../../../lib/commonOpts');
10
+ describe('commands/app/install', () => {
11
+ const yargsMock = yargs_1.default;
12
+ describe('command', () => {
13
+ it('should have the correct command structure', () => {
14
+ expect(install_1.default.command).toEqual('install <test-account-id>');
15
+ });
16
+ });
17
+ describe('describe', () => {
18
+ it('should provide a description', () => {
19
+ expect(install_1.default.describe).not.toBeDefined();
20
+ });
21
+ });
22
+ describe('builder', () => {
23
+ it('should support the correct options', () => {
24
+ install_1.default.builder(yargsMock);
25
+ expect(commonOpts_1.addGlobalOptions).toHaveBeenCalledTimes(1);
26
+ expect(commonOpts_1.addGlobalOptions).toHaveBeenCalledWith(yargsMock);
27
+ expect(commonOpts_1.addAccountOptions).toHaveBeenCalledTimes(1);
28
+ expect(commonOpts_1.addAccountOptions).toHaveBeenCalledWith(yargsMock);
29
+ expect(commonOpts_1.addConfigOptions).toHaveBeenCalledTimes(1);
30
+ expect(commonOpts_1.addConfigOptions).toHaveBeenCalledWith(yargsMock);
31
+ expect(commonOpts_1.addUseEnvironmentOptions).toHaveBeenCalledTimes(1);
32
+ expect(commonOpts_1.addUseEnvironmentOptions).toHaveBeenCalledWith(yargsMock);
33
+ expect(commonOpts_1.addJSONOutputOptions).toHaveBeenCalledTimes(1);
34
+ expect(commonOpts_1.addJSONOutputOptions).toHaveBeenCalledWith(yargsMock);
35
+ expect(yargsMock.positional).toHaveBeenCalledTimes(1);
36
+ expect(yargsMock.positional).toHaveBeenCalledWith('test-account-id', expect.objectContaining({
37
+ type: 'number',
38
+ required: true,
39
+ describe: expect.any(String),
40
+ }));
41
+ expect(yargsMock.option).toHaveBeenCalledTimes(2);
42
+ expect(yargsMock.option).toHaveBeenCalledWith('app-uid', expect.objectContaining({
43
+ type: 'string',
44
+ describe: expect.any(String),
45
+ }));
46
+ expect(yargsMock.option).toHaveBeenCalledWith('project-name', expect.objectContaining({
47
+ type: 'string',
48
+ describe: expect.any(String),
49
+ }));
50
+ });
51
+ });
52
+ });
@@ -0,0 +1,8 @@
1
+ import { CommonArgs, ConfigArgs, AccountArgs, EnvironmentArgs, YargsCommandModule, JSONOutputArgs } from '../../types/Yargs';
2
+ type InstallAppArgs = CommonArgs & ConfigArgs & AccountArgs & EnvironmentArgs & JSONOutputArgs & {
3
+ appUid?: string;
4
+ projectName?: string;
5
+ testAccountId: number;
6
+ };
7
+ declare const installAppCommand: YargsCommandModule<unknown, InstallAppArgs>;
8
+ export default installAppCommand;
@@ -0,0 +1,127 @@
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ const developerTestAccounts_1 = require("@hubspot/local-dev-lib/api/developerTestAccounts");
7
+ const usageTracking_1 = require("../../lib/usageTracking");
8
+ const en_1 = require("../../lang/en");
9
+ const exitCodes_1 = require("../../lib/enums/exitCodes");
10
+ const yargsUtils_1 = require("../../lib/yargsUtils");
11
+ const constants_1 = require("../../lib/constants");
12
+ const logger_1 = require("../../lib/ui/logger");
13
+ const SpinniesManager_1 = __importDefault(require("../../lib/ui/SpinniesManager"));
14
+ const errorHandlers_1 = require("../../lib/errorHandlers");
15
+ const polling_1 = require("../../lib/polling");
16
+ const config_1 = require("../../lib/projects/config");
17
+ const upload_1 = require("../../lib/projects/upload");
18
+ const structure_1 = require("../../lib/projects/structure");
19
+ const command = 'install <test-account-id>';
20
+ const describe = undefined; // commands.app.subcommands.install.describe;
21
+ async function handler(args) {
22
+ const { derivedAccountId, appUid, projectName, testAccountId, formatOutputAsJson, } = args;
23
+ (0, usageTracking_1.trackCommandUsage)('app-install', {}, derivedAccountId);
24
+ const jsonOutput = {};
25
+ let targetProjectName = projectName;
26
+ let targetAppUid = appUid;
27
+ const { projectConfig, projectDir } = await (0, config_1.getProjectConfig)();
28
+ if (!targetProjectName) {
29
+ (0, config_1.validateProjectConfig)(projectConfig, projectDir);
30
+ targetProjectName = projectConfig?.name;
31
+ }
32
+ if (!targetProjectName) {
33
+ logger_1.uiLogger.error(en_1.commands.app.subcommands.install.errors.mustSpecifyProjectName);
34
+ process.exit(exitCodes_1.EXIT_CODES.ERROR);
35
+ }
36
+ let isAppOauth = true;
37
+ if (!targetAppUid) {
38
+ const intermediateRepresentation = await (0, upload_1.handleTranslate)(projectDir, projectConfig, derivedAccountId, true, undefined);
39
+ if (intermediateRepresentation) {
40
+ Object.values(intermediateRepresentation.intermediateNodesIndexedByUid).forEach(node => {
41
+ if ((0, structure_1.isAppIRNode)(node)) {
42
+ targetAppUid = node.uid;
43
+ isAppOauth = node.config.auth.type === constants_1.APP_AUTH_TYPES.OAUTH;
44
+ }
45
+ });
46
+ }
47
+ }
48
+ if (!targetAppUid) {
49
+ logger_1.uiLogger.error(en_1.commands.app.subcommands.install.errors.noAppUidFound);
50
+ process.exit(exitCodes_1.EXIT_CODES.ERROR);
51
+ }
52
+ if (!isAppOauth) {
53
+ logger_1.uiLogger.error(en_1.commands.app.subcommands.install.errors.appMustBeOauth);
54
+ process.exit(exitCodes_1.EXIT_CODES.ERROR);
55
+ }
56
+ try {
57
+ const { data } = await (0, developerTestAccounts_1.installOauthAppIntoDeveloperTestAccount)(derivedAccountId, testAccountId, targetProjectName, targetAppUid);
58
+ if (data?.authCodes.length > 0) {
59
+ jsonOutput.authCode = data.authCodes[0].authCode;
60
+ }
61
+ }
62
+ catch (err) {
63
+ (0, errorHandlers_1.logError)(err);
64
+ process.exit(exitCodes_1.EXIT_CODES.ERROR);
65
+ }
66
+ SpinniesManager_1.default.init({
67
+ succeedColor: 'white',
68
+ });
69
+ SpinniesManager_1.default.add('installApp', {
70
+ text: en_1.commands.app.subcommands.install.polling.start,
71
+ });
72
+ let appInstallSucceeded = false;
73
+ try {
74
+ await (0, polling_1.poll)(() => (0, developerTestAccounts_1.fetchDeveloperTestAccountOauthAppInstallStatus)(derivedAccountId, targetProjectName, targetAppUid), {
75
+ successStates: ['SUCCESS'],
76
+ errorStates: [],
77
+ });
78
+ appInstallSucceeded = true;
79
+ }
80
+ catch (err) {
81
+ SpinniesManager_1.default.fail('installApp');
82
+ (0, errorHandlers_1.logError)(err);
83
+ process.exit(exitCodes_1.EXIT_CODES.ERROR);
84
+ }
85
+ if (!appInstallSucceeded) {
86
+ SpinniesManager_1.default.fail('installApp');
87
+ process.exit(exitCodes_1.EXIT_CODES.ERROR);
88
+ }
89
+ SpinniesManager_1.default.succeed('installApp', {
90
+ text: en_1.commands.app.subcommands.install.polling.success,
91
+ });
92
+ if (formatOutputAsJson) {
93
+ logger_1.uiLogger.json(jsonOutput);
94
+ }
95
+ process.exit(exitCodes_1.EXIT_CODES.SUCCESS);
96
+ }
97
+ function installAppBuilder(yargs) {
98
+ yargs.positional('test-account-id', {
99
+ describe: en_1.commands.app.subcommands.install.positionals.testAccountId,
100
+ required: true,
101
+ type: 'number',
102
+ });
103
+ yargs.option('app-uid', {
104
+ describe: en_1.commands.app.subcommands.install.options.appUid,
105
+ type: 'string',
106
+ });
107
+ yargs.option('project-name', {
108
+ describe: en_1.commands.app.subcommands.install.options.projectName,
109
+ type: 'string',
110
+ });
111
+ yargs.example('install 1234567890 --app-uid=my-app-uid --project-name=my-project', en_1.commands.app.subcommands.install.example);
112
+ return yargs;
113
+ }
114
+ const builder = (0, yargsUtils_1.makeYargsBuilder)(installAppBuilder, command, en_1.commands.app.subcommands.install.describe, {
115
+ useGlobalOptions: true,
116
+ useAccountOptions: true,
117
+ useConfigOptions: true,
118
+ useEnvironmentOptions: true,
119
+ useJSONOutputOptions: true,
120
+ });
121
+ const installAppCommand = {
122
+ command,
123
+ describe,
124
+ handler,
125
+ builder,
126
+ };
127
+ exports.default = installAppCommand;
package/commands/app.js CHANGED
@@ -5,12 +5,17 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
5
5
  Object.defineProperty(exports, "__esModule", { value: true });
6
6
  const migrate_1 = __importDefault(require("./app/migrate"));
7
7
  const secret_1 = __importDefault(require("./app/secret"));
8
+ const install_1 = __importDefault(require("./app/install"));
8
9
  const yargsUtils_1 = require("../lib/yargsUtils");
9
10
  const command = ['app', 'apps'];
10
11
  // Keep the command hidden for now
11
12
  const describe = undefined;
12
13
  function appBuilder(yargs) {
13
- yargs.command(migrate_1.default).command(secret_1.default).demandCommand(1, '');
14
+ yargs
15
+ .command(migrate_1.default)
16
+ .command(secret_1.default)
17
+ .command(install_1.default)
18
+ .demandCommand(1, '');
14
19
  return yargs;
15
20
  }
16
21
  const builder = (0, yargsUtils_1.makeYargsBuilder)(appBuilder, command, describe);
@@ -127,35 +127,20 @@ async function handler(args) {
127
127
  logger_1.uiLogger.log(' ');
128
128
  logger_1.uiLogger.log(en_1.commands.getStarted.prompts.projectCreated.description);
129
129
  logger_1.uiLogger.log(' ');
130
- // 5. Install dependencies - Ask user if they want to install dependencies
130
+ // 5. Install dependencies
131
131
  const installLocations = await (0, dependencyManagement_1.getProjectPackageJsonLocations)(projectDest);
132
- const { shouldInstallDependencies } = await (0, promptUtils_1.promptUser)([
133
- {
134
- type: 'confirm',
135
- name: 'shouldInstallDependencies',
136
- message: en_1.commands.getStarted.prompts.installDependencies,
137
- default: true,
138
- },
139
- ]);
140
- if (shouldInstallDependencies) {
141
- try {
142
- await (0, dependencyManagement_1.installPackages)({
143
- installLocations: installLocations,
144
- });
145
- logger_1.uiLogger.log(' ');
146
- logger_1.uiLogger.success(en_1.commands.getStarted.logs.dependenciesInstalled);
147
- logger_1.uiLogger.log(' ');
148
- }
149
- catch (err) {
150
- logger_1.uiLogger.log(' ');
151
- logger_1.uiLogger.error(en_1.commands.getStarted.errors.installDepsFailed);
152
- (0, errorHandlers_1.logError)(err);
153
- logger_1.uiLogger.log(' ');
154
- }
132
+ try {
133
+ await (0, dependencyManagement_1.installPackages)({
134
+ installLocations: installLocations,
135
+ });
136
+ logger_1.uiLogger.log(' ');
137
+ logger_1.uiLogger.success(en_1.commands.getStarted.logs.dependenciesInstalled);
138
+ logger_1.uiLogger.log(' ');
155
139
  }
156
- else {
140
+ catch (err) {
157
141
  logger_1.uiLogger.log(' ');
158
- logger_1.uiLogger.log(en_1.commands.getStarted.logs.dependenciesNotInstalled);
142
+ logger_1.uiLogger.error(en_1.commands.getStarted.errors.installDepsFailed);
143
+ (0, errorHandlers_1.logError)(err);
159
144
  logger_1.uiLogger.log(' ');
160
145
  }
161
146
  // 6. Ask user if they want to upload the project
@@ -217,7 +202,7 @@ async function handler(args) {
217
202
  targetAccountId: derivedAccountId,
218
203
  env: env,
219
204
  appId: lastCreatedApp.id,
220
- }), { url: true });
205
+ }) + '&tourId=get-started', { url: true });
221
206
  logger_1.uiLogger.log(' ');
222
207
  logger_1.uiLogger.success(en_1.commands.getStarted.openedDeveloperOverview);
223
208
  }
@@ -37,6 +37,7 @@ function setupBuilder(yargs) {
37
37
  })
38
38
  .option('add-docs-search', {
39
39
  type: 'boolean',
40
+ hidden: true,
40
41
  });
41
42
  return yargs;
42
43
  }
@@ -1,3 +1,6 @@
1
1
  import { CommonArgs, YargsCommandModule } from '../../types/Yargs';
2
- declare const mcpStartCommand: YargsCommandModule<unknown, CommonArgs>;
2
+ interface McpStartArgs extends CommonArgs {
3
+ aiAgent: string;
4
+ }
5
+ declare const mcpStartCommand: YargsCommandModule<unknown, McpStartArgs>;
3
6
  export default mcpStartCommand;
@@ -24,9 +24,9 @@ async function handler(args) {
24
24
  process.exit(exitCodes_1.EXIT_CODES.ERROR);
25
25
  }
26
26
  (0, usageTracking_1.trackCommandUsage)('mcp-start', {}, args.derivedAccountId);
27
- await startMcpServer();
27
+ await startMcpServer(args.aiAgent);
28
28
  }
29
- async function startMcpServer() {
29
+ async function startMcpServer(aiAgent) {
30
30
  try {
31
31
  const serverPath = path_1.default.join(__dirname, '..', '..', 'mcp-server', 'server.js');
32
32
  // Check if server file exists
@@ -36,11 +36,13 @@ async function startMcpServer() {
36
36
  }
37
37
  logger_1.uiLogger.info(en_1.commands.mcp.start.startingServer);
38
38
  logger_1.uiLogger.info(en_1.commands.mcp.start.stopInstructions);
39
+ const args = [serverPath];
39
40
  // Start the server using ts-node
40
- const child = (0, child_process_1.spawn)('node', [serverPath], {
41
+ const child = (0, child_process_1.spawn)(`node`, args, {
41
42
  stdio: 'inherit',
42
43
  env: {
43
44
  ...process.env,
45
+ HUBSPOT_MCP_AI_AGENT: aiAgent || 'unknown',
44
46
  },
45
47
  });
46
48
  // Handle server process events
@@ -63,6 +65,9 @@ async function startMcpServer() {
63
65
  }
64
66
  }
65
67
  function startBuilder(yargs) {
68
+ yargs.option('ai-agent', {
69
+ type: 'string',
70
+ });
66
71
  return yargs;
67
72
  }
68
73
  const builder = (0, yargsUtils_1.makeYargsBuilder)(startBuilder, command, describe, {
@@ -39,8 +39,8 @@ Object.defineProperty(exports, "__esModule", { value: true });
39
39
  const axios_1 = require("axios");
40
40
  const yargs_1 = __importDefault(require("yargs"));
41
41
  const chalk_1 = __importDefault(require("chalk"));
42
- const configUtils = __importStar(require("@hubspot/local-dev-lib/config"));
43
42
  const logger_1 = require("@hubspot/local-dev-lib/logger");
43
+ const configUtils = __importStar(require("@hubspot/local-dev-lib/config"));
44
44
  const projectApiUtils = __importStar(require("@hubspot/local-dev-lib/api/projects"));
45
45
  const ui = __importStar(require("../../../lib/ui"));
46
46
  const commonOpts_1 = require("../../../lib/commonOpts");
@@ -54,7 +54,7 @@ const exitCodes_1 = require("../../../lib/enums/exitCodes");
54
54
  const exampleProject_json_1 = __importDefault(require("./fixtures/exampleProject.json"));
55
55
  const testUtils_1 = require("../../../lib/testUtils");
56
56
  const deploy_1 = __importDefault(require("../deploy"));
57
- vi.mock('@hubspot/local-dev-lib/logger');
57
+ const logger_2 = require("../../../lib/ui/logger");
58
58
  vi.mock('@hubspot/local-dev-lib/api/projects');
59
59
  vi.mock('@hubspot/local-dev-lib/config');
60
60
  vi.mock('../../../lib/commonOpts');
@@ -65,8 +65,9 @@ vi.mock('../../../lib/projects/buildAndDeploy');
65
65
  vi.mock('../../../lib/prompts/projectNamePrompt');
66
66
  vi.mock('../../../lib/prompts/promptUtils');
67
67
  vi.mock('../../../lib/usageTracking');
68
+ vi.mock('../../../lib/ui/logger');
69
+ vi.mock('@hubspot/local-dev-lib/logger');
68
70
  vi.spyOn(ui, 'uiLine');
69
- const uiLinkSpy = vi.spyOn(ui, 'uiLink').mockImplementation(text => text);
70
71
  const uiCommandReferenceSpy = vi.spyOn(ui, 'uiCommandReference');
71
72
  const uiAccountDescriptionSpy = vi.spyOn(ui, 'uiAccountDescription');
72
73
  const getProjectConfigSpy = vi.spyOn(projectUtils, 'getProjectConfig');
@@ -89,6 +90,7 @@ const conflictsSpy = vi
89
90
  describe('commands/project/deploy', () => {
90
91
  const projectFlag = 'project';
91
92
  const buildFlag = 'build';
93
+ const deployLatestBuildFlag = 'deployLatestBuild';
92
94
  const profileFlag = 'profile';
93
95
  const forceFlag = 'force';
94
96
  describe('command', () => {
@@ -117,6 +119,11 @@ describe('commands/project/deploy', () => {
117
119
  alias: ['build-id'],
118
120
  type: 'number',
119
121
  }),
122
+ [deployLatestBuildFlag]: expect.objectContaining({
123
+ type: 'boolean',
124
+ alias: ['deploy-latest-build'],
125
+ default: false,
126
+ }),
120
127
  [profileFlag]: expect.objectContaining({
121
128
  type: 'string',
122
129
  alias: ['p'],
@@ -133,6 +140,8 @@ describe('commands/project/deploy', () => {
133
140
  expect(commonOpts_1.addAccountOptions).toHaveBeenCalledWith(yargs_1.default);
134
141
  expect(commonOpts_1.addUseEnvironmentOptions).toHaveBeenCalledTimes(1);
135
142
  expect(commonOpts_1.addUseEnvironmentOptions).toHaveBeenCalledWith(yargs_1.default);
143
+ expect(commonOpts_1.addJSONOutputOptions).toHaveBeenCalledTimes(1);
144
+ expect(commonOpts_1.addJSONOutputOptions).toHaveBeenCalledWith(yargs_1.default);
136
145
  });
137
146
  it('should provide examples', () => {
138
147
  deploy_1.default.builder(yargs_1.default);
@@ -146,9 +155,9 @@ describe('commands/project/deploy', () => {
146
155
  const projectNameFromPrompt = 'project name from prompt';
147
156
  const deployDetails = {
148
157
  id: 123,
158
+ buildResultType: 'DEPLOY_QUEUED',
149
159
  };
150
160
  const projectDetailUrl = 'http://project-details-page-url.com';
151
- const viewProjectsInHubSpot = 'View project builds in HubSpot';
152
161
  beforeEach(() => {
153
162
  args = {
154
163
  project: 'project name from options',
@@ -168,9 +177,6 @@ describe('commands/project/deploy', () => {
168
177
  projectName: projectNameFromPrompt,
169
178
  });
170
179
  getProjectDetailUrlSpy.mockReturnValue(projectDetailUrl);
171
- uiLinkSpy.mockImplementation(text => {
172
- return text;
173
- });
174
180
  getAccountConfigSpy.mockReturnValue({ accountType, env: 'qa' });
175
181
  fetchProjectSpy.mockReturnValue((0, testUtils_1.mockHubSpotHttpResponse)(exampleProject_json_1.default));
176
182
  deployProjectSpy.mockReturnValue((0, testUtils_1.mockHubSpotHttpResponse)(deployDetails));
@@ -233,28 +239,24 @@ describe('commands/project/deploy', () => {
233
239
  it('should log an error and exit when latest build is not defined', async () => {
234
240
  fetchProjectSpy.mockReturnValue((0, testUtils_1.mockHubSpotHttpResponse)({}));
235
241
  await deploy_1.default.handler(args);
236
- expect(logger_1.logger.error).toHaveBeenCalledTimes(1);
237
- expect(logger_1.logger.error).toHaveBeenCalledWith('Deploy error: no builds for this project were found.');
242
+ expect(logger_2.uiLogger.error).toHaveBeenCalledTimes(1);
243
+ expect(logger_2.uiLogger.error).toHaveBeenCalledWith('Deploy error: no builds for this project were found.');
238
244
  expect(processExitSpy).toHaveBeenCalledTimes(1);
239
245
  expect(processExitSpy).toHaveBeenCalledWith(exitCodes_1.EXIT_CODES.ERROR);
240
246
  });
241
247
  it('should log an error and exit when buildId option is not a valid build', async () => {
242
248
  args.buildId = exampleProject_json_1.default.latestBuild.buildId + 1;
243
249
  await deploy_1.default.handler(args);
244
- expect(uiLinkSpy).toHaveBeenCalledTimes(1);
245
- expect(uiLinkSpy).toHaveBeenCalledWith(viewProjectsInHubSpot, projectDetailUrl);
246
- expect(logger_1.logger.error).toHaveBeenCalledTimes(1);
247
- expect(logger_1.logger.error).toHaveBeenCalledWith(`Build ${args.buildId} does not exist for project ${projectNameFromPrompt}. ${viewProjectsInHubSpot}`);
250
+ expect(logger_2.uiLogger.error).toHaveBeenCalledTimes(1);
251
+ expect(logger_2.uiLogger.error).toHaveBeenCalledWith(expect.stringMatching(`Build ${args.buildId} does not exist for project`));
248
252
  expect(processExitSpy).toHaveBeenCalledTimes(1);
249
253
  expect(processExitSpy).toHaveBeenCalledWith(exitCodes_1.EXIT_CODES.ERROR);
250
254
  });
251
255
  it('should log an error and exit when buildId option is already deployed', async () => {
252
256
  args.buildId = exampleProject_json_1.default.deployedBuildId;
253
257
  await deploy_1.default.handler(args);
254
- expect(uiLinkSpy).toHaveBeenCalledTimes(1);
255
- expect(uiLinkSpy).toHaveBeenCalledWith(viewProjectsInHubSpot, projectDetailUrl);
256
- expect(logger_1.logger.error).toHaveBeenCalledTimes(1);
257
- expect(logger_1.logger.error).toHaveBeenCalledWith(`Build ${args.buildId} is already deployed. ${viewProjectsInHubSpot}`);
258
+ expect(logger_2.uiLogger.error).toHaveBeenCalledTimes(1);
259
+ expect(logger_2.uiLogger.error).toHaveBeenCalledWith(expect.stringMatching(`Build ${args.buildId} is already deployed. View project builds in HubSpot.`));
258
260
  expect(processExitSpy).toHaveBeenCalledTimes(1);
259
261
  expect(processExitSpy).toHaveBeenCalledWith(exitCodes_1.EXIT_CODES.ERROR);
260
262
  });
@@ -271,8 +273,8 @@ describe('commands/project/deploy', () => {
271
273
  promptUserSpy.mockResolvedValue({});
272
274
  await deploy_1.default.handler(args);
273
275
  expect(promptUserSpy).toHaveBeenCalledTimes(1);
274
- expect(logger_1.logger.error).toHaveBeenCalledTimes(1);
275
- expect(logger_1.logger.error).toHaveBeenCalledWith('You must specify a build to deploy');
276
+ expect(logger_2.uiLogger.error).toHaveBeenCalledTimes(1);
277
+ expect(logger_2.uiLogger.error).toHaveBeenCalledWith('You must specify a build to deploy');
276
278
  expect(processExitSpy).toHaveBeenCalledTimes(1);
277
279
  expect(processExitSpy).toHaveBeenCalledWith(exitCodes_1.EXIT_CODES.ERROR);
278
280
  });
@@ -285,8 +287,8 @@ describe('commands/project/deploy', () => {
285
287
  // @ts-expect-error Testing an edge case where the response is empty
286
288
  deployProjectSpy.mockResolvedValue({});
287
289
  await deploy_1.default.handler(args);
288
- expect(logger_1.logger.error).toHaveBeenCalledTimes(1);
289
- expect(logger_1.logger.error).toHaveBeenCalledWith(`Deploy error: an unknown error occurred.`);
290
+ expect(logger_2.uiLogger.error).toHaveBeenCalledTimes(1);
291
+ expect(logger_2.uiLogger.error).toHaveBeenCalledWith(`Deploy error: an unknown error occurred.`);
290
292
  expect(processExitSpy).toHaveBeenCalledTimes(1);
291
293
  expect(processExitSpy).toHaveBeenCalledWith(exitCodes_1.EXIT_CODES.ERROR);
292
294
  });
@@ -307,8 +309,8 @@ describe('commands/project/deploy', () => {
307
309
  });
308
310
  });
309
311
  await deploy_1.default.handler(args);
310
- expect(logger_1.logger.error).toHaveBeenCalledTimes(1);
311
- expect(logger_1.logger.error).toHaveBeenCalledWith(`The project ${chalk_1.default.bold(projectNameFromPrompt)} does not exist in account ${accountDescription}. Run ${commandReference} to upload your project files to HubSpot.`);
312
+ expect(logger_2.uiLogger.error).toHaveBeenCalledTimes(1);
313
+ expect(logger_2.uiLogger.error).toHaveBeenCalledWith(`The project ${chalk_1.default.bold(projectNameFromPrompt)} does not exist in account ${accountDescription}. Run ${commandReference} to upload your project files to HubSpot.`);
312
314
  expect(processExitSpy).toHaveBeenCalledTimes(1);
313
315
  expect(processExitSpy).toHaveBeenCalledWith(exitCodes_1.EXIT_CODES.ERROR);
314
316
  });
@@ -321,8 +323,8 @@ describe('commands/project/deploy', () => {
321
323
  });
322
324
  });
323
325
  await deploy_1.default.handler(args);
324
- expect(logger_1.logger.error).toHaveBeenCalledTimes(1);
325
- expect(logger_1.logger.error).toHaveBeenCalledWith('The request was bad.');
326
+ expect(logger_2.uiLogger.error).toHaveBeenCalledTimes(1);
327
+ expect(logger_2.uiLogger.error).toHaveBeenCalledWith('The request was bad.');
326
328
  expect(processExitSpy).toHaveBeenCalledTimes(1);
327
329
  expect(processExitSpy).toHaveBeenCalledWith(exitCodes_1.EXIT_CODES.ERROR);
328
330
  });
@@ -6,6 +6,7 @@ Object.defineProperty(exports, "__esModule", { value: true });
6
6
  const path_1 = __importDefault(require("path"));
7
7
  const config_1 = require("@hubspot/local-dev-lib/constants/config");
8
8
  const project_parsing_lib_1 = require("@hubspot/project-parsing-lib");
9
+ // import { HsProfileFile } from '@hubspot/project-parsing-lib/src/lib/types';
9
10
  const config_2 = require("@hubspot/local-dev-lib/config");
10
11
  const environment_1 = require("@hubspot/local-dev-lib/environment");
11
12
  const errorHandlers_1 = require("../../../lib/errorHandlers");
@@ -69,9 +70,9 @@ describe('unifiedProjectDevFlow', () => {
69
70
  const mockProjectDir = '/test/project';
70
71
  const mockTargetProjectAccountId = 123;
71
72
  const mockProvidedTargetTestingAccountId = 456;
72
- const mockProfileConfig = {
73
- accountId: 789,
74
- };
73
+ // const mockProfileConfig: HsProfileFile = {
74
+ // accountId: 789,
75
+ // };
75
76
  const mockAccountConfig = {
76
77
  accountId: 123,
77
78
  name: 'test-account',
@@ -157,18 +158,21 @@ describe('unifiedProjectDevFlow', () => {
157
158
  expect(mockLocalDevWatcher.start).toHaveBeenCalled();
158
159
  expect(mockWebsocketServer.start).toHaveBeenCalled();
159
160
  });
160
- it('should complete successfully with profile config', async () => {
161
- await (0, unifiedFlow_1.unifiedProjectDevFlow)({
162
- args: mockArgs,
163
- targetProjectAccountId: mockTargetProjectAccountId,
164
- projectConfig: mockProjectConfig,
165
- projectDir: mockProjectDir,
166
- profileConfig: mockProfileConfig,
167
- });
168
- expect(LocalDevProcess_1.default).toHaveBeenCalledWith(expect.objectContaining({
169
- targetTestingAccountId: mockProfileConfig.accountId,
170
- }));
171
- });
161
+ // TODO: Restore test once we've switched back to using profile account for testing
162
+ // it('should complete successfully with profile config', async () => {
163
+ // await unifiedProjectDevFlow({
164
+ // args: mockArgs,
165
+ // targetProjectAccountId: mockTargetProjectAccountId,
166
+ // projectConfig: mockProjectConfig,
167
+ // projectDir: mockProjectDir,
168
+ // profileConfig: mockProfileConfig,
169
+ // });
170
+ // expect(LocalDevProcess).toHaveBeenCalledWith(
171
+ // expect.objectContaining({
172
+ // targetTestingAccountId: mockProfileConfig.accountId,
173
+ // })
174
+ // );
175
+ // });
172
176
  it('should use target project account as testing account when it is a test account', async () => {
173
177
  accountTypes_1.isTestAccountOrSandbox.mockReturnValue(true);
174
178
  await (0, unifiedFlow_1.unifiedProjectDevFlow)({
@@ -258,7 +262,7 @@ describe('unifiedProjectDevFlow', () => {
258
262
  projectConfig: mockProjectConfig,
259
263
  projectDir: mockProjectDir,
260
264
  });
261
- expect(helpers_1.createDeveloperTestAccountForLocalDev).toHaveBeenCalledWith(mockTargetProjectAccountId, mockAccountConfig, environments_1.ENVIRONMENTS.PROD);
265
+ expect(helpers_1.createDeveloperTestAccountForLocalDev).toHaveBeenCalledWith(mockTargetProjectAccountId, mockAccountConfig, environments_1.ENVIRONMENTS.PROD, true);
262
266
  expect(LocalDevProcess_1.default).toHaveBeenCalledWith(expect.objectContaining({
263
267
  targetTestingAccountId: 999,
264
268
  }));
@@ -68,7 +68,7 @@ async function handler(args) {
68
68
  await (0, github_1.cloneGithubRepo)(repo, projectDest, {
69
69
  sourceDir: selectProjectTemplatePromptResponse.projectTemplate?.path || components,
70
70
  hideLogs: true,
71
- branch: 'main',
71
+ branch: constants_1.DEFAULT_PROJECT_TEMPLATE_BRANCH,
72
72
  });
73
73
  }
74
74
  catch (err) {
@@ -1,9 +1,10 @@
1
- import { CommonArgs, ConfigArgs, AccountArgs, EnvironmentArgs, YargsCommandModule } from '../../types/Yargs';
2
- export type ProjectDeployArgs = CommonArgs & ConfigArgs & AccountArgs & EnvironmentArgs & {
1
+ import { CommonArgs, ConfigArgs, AccountArgs, EnvironmentArgs, JSONOutputArgs, YargsCommandModule } from '../../types/Yargs';
2
+ export type ProjectDeployArgs = CommonArgs & ConfigArgs & AccountArgs & EnvironmentArgs & JSONOutputArgs & {
3
3
  project?: string;
4
4
  build?: number;
5
5
  buildId?: number;
6
6
  profile?: string;
7
+ deployLatestBuild: boolean;
7
8
  force: boolean;
8
9
  };
9
10
  declare const projectDeployCommand: YargsCommandModule<unknown, ProjectDeployArgs>;