@hubspot/cli 5.3.1 → 5.4.1-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/bin/cli.js +24 -5
- package/commands/__tests__/projects.test.js +105 -0
- package/commands/accounts/clean.js +1 -1
- package/commands/cms/convertFields.js +13 -7
- package/commands/project/__tests__/deploy.test.js +1 -1
- package/commands/project/__tests__/installDeps.test.js +168 -0
- package/commands/project/__tests__/logs.test.js +305 -0
- package/commands/project/add.js +24 -12
- package/commands/project/cloneApp.js +13 -21
- package/commands/project/deploy.js +4 -1
- package/commands/project/dev.js +22 -11
- package/commands/project/download.js +6 -3
- package/commands/project/installDeps.js +78 -0
- package/commands/project/logs.js +80 -242
- package/commands/project/migrateApp.js +8 -9
- package/commands/project/upload.js +5 -3
- package/commands/project/watch.js +3 -9
- package/commands/project.js +2 -0
- package/commands/sandbox/create.js +1 -0
- package/commands/sandbox.js +0 -2
- package/lang/en.lyaml +40 -75
- package/lib/LocalDevManager.js +1 -22
- package/lib/__tests__/dependencyManagement.test.js +245 -0
- package/lib/__tests__/projectLogsManager.test.js +210 -0
- package/lib/dependencyManagement.js +157 -0
- package/lib/errorHandlers/apiErrors.js +1 -3
- package/lib/errorHandlers/overrideErrors.js +57 -36
- package/lib/localDev.js +25 -16
- package/lib/projectLogsManager.js +144 -0
- package/lib/projects.js +17 -7
- package/lib/projectsWatch.js +2 -5
- package/lib/prompts/__tests__/projectsLogsPrompt.test.js +46 -0
- package/lib/prompts/createProjectPrompt.js +4 -0
- package/lib/prompts/projectAddPrompt.js +4 -21
- package/lib/prompts/projectDevTargetAccountPrompt.js +16 -25
- package/lib/prompts/projectsLogsPrompt.js +17 -108
- package/lib/sandboxSync.js +13 -15
- package/package.json +6 -6
- package/commands/sandbox/sync.js +0 -225
package/bin/cli.js
CHANGED
|
@@ -15,6 +15,8 @@ const {
|
|
|
15
15
|
const { getIsInProject } = require('../lib/projects');
|
|
16
16
|
const pkg = require('../package.json');
|
|
17
17
|
const { i18n } = require('../lib/lang');
|
|
18
|
+
const { EXIT_CODES } = require('../lib/enums/exitCodes');
|
|
19
|
+
const { UI_COLORS, uiCommandReference } = require('../lib/ui');
|
|
18
20
|
|
|
19
21
|
const removeCommand = require('../commands/remove');
|
|
20
22
|
const initCommand = require('../commands/init');
|
|
@@ -41,7 +43,6 @@ const accountsCommand = require('../commands/accounts');
|
|
|
41
43
|
const sandboxesCommand = require('../commands/sandbox');
|
|
42
44
|
const cmsCommand = require('../commands/cms');
|
|
43
45
|
const feedbackCommand = require('../commands/feedback');
|
|
44
|
-
const { EXIT_CODES } = require('../lib/enums/exitCodes');
|
|
45
46
|
|
|
46
47
|
const notifier = updateNotifier({
|
|
47
48
|
pkg: { ...pkg, name: '@hubspot/cli' },
|
|
@@ -51,12 +52,30 @@ const notifier = updateNotifier({
|
|
|
51
52
|
|
|
52
53
|
const i18nKey = 'commands.generalErrors';
|
|
53
54
|
|
|
54
|
-
const
|
|
55
|
-
chalk.bold('The CMS CLI is now the HubSpot CLI') +
|
|
56
|
-
'\n\nTo upgrade, run:\n\nnpm uninstall -g @hubspot/cms-cli\nand npm install -g @hubspot/cli';
|
|
55
|
+
const CMS_CLI_PACKAGE_NAME = '@hubspot/cms-cli';
|
|
57
56
|
|
|
58
57
|
notifier.notify({
|
|
59
|
-
message:
|
|
58
|
+
message:
|
|
59
|
+
pkg.name === CMS_CLI_PACKAGE_NAME
|
|
60
|
+
? i18n(`${i18nKey}.updateNotify.cmsUpdateNotification`, {
|
|
61
|
+
packageName: CMS_CLI_PACKAGE_NAME,
|
|
62
|
+
updateCommand: uiCommandReference('{updateCommand}'),
|
|
63
|
+
})
|
|
64
|
+
: i18n(`${i18nKey}.updateNotify.cliUpdateNotification`, {
|
|
65
|
+
updateCommand: uiCommandReference('{updateCommand}'),
|
|
66
|
+
}),
|
|
67
|
+
defer: false,
|
|
68
|
+
boxenOptions: {
|
|
69
|
+
borderColor: UI_COLORS.MARIGOLD_DARK,
|
|
70
|
+
margin: 1,
|
|
71
|
+
padding: 1,
|
|
72
|
+
textAlignment: 'center',
|
|
73
|
+
borderStyle: 'round',
|
|
74
|
+
title:
|
|
75
|
+
pkg.name === CMS_CLI_PACKAGE_NAME
|
|
76
|
+
? null
|
|
77
|
+
: chalk.bold(i18n(`${i18nKey}.updateNotify.notifyTitle`)),
|
|
78
|
+
},
|
|
60
79
|
});
|
|
61
80
|
|
|
62
81
|
const getTerminalWidth = () => {
|
|
@@ -0,0 +1,105 @@
|
|
|
1
|
+
const { command, describe: projectDescribe, builder } = require('../project');
|
|
2
|
+
|
|
3
|
+
jest.mock('../project/deploy');
|
|
4
|
+
jest.mock('../project/create');
|
|
5
|
+
jest.mock('../project/upload');
|
|
6
|
+
jest.mock('../project/listBuilds');
|
|
7
|
+
jest.mock('../project/logs');
|
|
8
|
+
jest.mock('../project/watch');
|
|
9
|
+
jest.mock('../project/download');
|
|
10
|
+
jest.mock('../project/open');
|
|
11
|
+
jest.mock('../project/dev');
|
|
12
|
+
jest.mock('../project/add');
|
|
13
|
+
jest.mock('../project/migrateApp');
|
|
14
|
+
jest.mock('../project/cloneApp');
|
|
15
|
+
jest.mock('../project/installDeps');
|
|
16
|
+
jest.mock('../../lib/commonOpts');
|
|
17
|
+
|
|
18
|
+
const deploy = require('../project/deploy');
|
|
19
|
+
const create = require('../project/create');
|
|
20
|
+
const upload = require('../project/upload');
|
|
21
|
+
const listBuilds = require('../project/listBuilds');
|
|
22
|
+
const logs = require('../project/logs');
|
|
23
|
+
const watch = require('../project/watch');
|
|
24
|
+
const download = require('../project/download');
|
|
25
|
+
const open = require('../project/open');
|
|
26
|
+
const dev = require('../project/dev');
|
|
27
|
+
const add = require('../project/add');
|
|
28
|
+
const migrateApp = require('../project/migrateApp');
|
|
29
|
+
const cloneApp = require('../project/cloneApp');
|
|
30
|
+
const installDeps = require('../project/installDeps');
|
|
31
|
+
const { addConfigOptions, addAccountOptions } = require('../../lib/commonOpts');
|
|
32
|
+
|
|
33
|
+
describe('commands/projects', () => {
|
|
34
|
+
describe('command', () => {
|
|
35
|
+
it('should have the correct command structure', () => {
|
|
36
|
+
expect(command).toEqual('project');
|
|
37
|
+
});
|
|
38
|
+
});
|
|
39
|
+
|
|
40
|
+
describe('describe', () => {
|
|
41
|
+
it('should contain the beta tag', () => {
|
|
42
|
+
expect(projectDescribe).toContain('[BETA]');
|
|
43
|
+
});
|
|
44
|
+
it('should provide an accurate description of what the command is doing', () => {
|
|
45
|
+
expect(projectDescribe).toContain(
|
|
46
|
+
'Commands for working with projects. For more information, visit our documentation: https://developers.hubspot.com/docs/platform/build-and-deploy-using-hubspot-projects'
|
|
47
|
+
);
|
|
48
|
+
});
|
|
49
|
+
});
|
|
50
|
+
|
|
51
|
+
describe('builder', () => {
|
|
52
|
+
let yargs;
|
|
53
|
+
|
|
54
|
+
const subcommands = [
|
|
55
|
+
['create', create],
|
|
56
|
+
['add', add],
|
|
57
|
+
['watch', watch],
|
|
58
|
+
['dev', dev],
|
|
59
|
+
['upload', upload],
|
|
60
|
+
['deploy', deploy],
|
|
61
|
+
['logs', logs],
|
|
62
|
+
['listBuilds', listBuilds],
|
|
63
|
+
['download', download],
|
|
64
|
+
['open', open],
|
|
65
|
+
['migrateApp', migrateApp],
|
|
66
|
+
['cloneApp', cloneApp],
|
|
67
|
+
['installDeps', installDeps],
|
|
68
|
+
];
|
|
69
|
+
|
|
70
|
+
beforeEach(() => {
|
|
71
|
+
yargs = {
|
|
72
|
+
command: jest.fn().mockImplementation(() => yargs),
|
|
73
|
+
demandCommand: jest.fn().mockImplementation(() => yargs),
|
|
74
|
+
};
|
|
75
|
+
});
|
|
76
|
+
|
|
77
|
+
it('should add the config options', () => {
|
|
78
|
+
builder(yargs);
|
|
79
|
+
expect(addConfigOptions).toHaveBeenCalledTimes(1);
|
|
80
|
+
expect(addConfigOptions).toHaveBeenCalledWith(yargs);
|
|
81
|
+
});
|
|
82
|
+
|
|
83
|
+
it('should add the account options', () => {
|
|
84
|
+
builder(yargs);
|
|
85
|
+
expect(addAccountOptions).toHaveBeenCalledTimes(1);
|
|
86
|
+
expect(addAccountOptions).toHaveBeenCalledWith(yargs);
|
|
87
|
+
});
|
|
88
|
+
|
|
89
|
+
it('should add the correct number of sub commands', () => {
|
|
90
|
+
builder(yargs);
|
|
91
|
+
expect(yargs.command).toHaveBeenCalledTimes(subcommands.length);
|
|
92
|
+
});
|
|
93
|
+
|
|
94
|
+
it.each(subcommands)('should attach the %s subcommand', (name, module) => {
|
|
95
|
+
builder(yargs);
|
|
96
|
+
expect(yargs.command).toHaveBeenCalledWith(module);
|
|
97
|
+
});
|
|
98
|
+
|
|
99
|
+
it('should demand the command takes one positional argument', () => {
|
|
100
|
+
builder(yargs);
|
|
101
|
+
expect(yargs.demandCommand).toHaveBeenCalledTimes(1);
|
|
102
|
+
expect(yargs.demandCommand).toHaveBeenCalledWith(1, '');
|
|
103
|
+
});
|
|
104
|
+
});
|
|
105
|
+
});
|
|
@@ -54,7 +54,7 @@ exports.handler = async options => {
|
|
|
54
54
|
|
|
55
55
|
for (const account of filteredTestAccounts) {
|
|
56
56
|
try {
|
|
57
|
-
await accessTokenForPersonalAccessKey(account.portalId);
|
|
57
|
+
await accessTokenForPersonalAccessKey(account.portalId, true);
|
|
58
58
|
} catch (error) {
|
|
59
59
|
if (
|
|
60
60
|
isSpecifiedHubSpotAuthError(error, {
|
|
@@ -13,6 +13,7 @@ const {
|
|
|
13
13
|
|
|
14
14
|
const { trackConvertFieldsUsage } = require('../../lib/usageTracking');
|
|
15
15
|
const { logErrorInstance } = require('../../lib/errorHandlers/standardErrors');
|
|
16
|
+
const { EXIT_CODES } = require('../../lib/enums/exitCodes');
|
|
16
17
|
const i18nKey = 'commands.convertFields';
|
|
17
18
|
|
|
18
19
|
exports.command = 'convert-fields';
|
|
@@ -24,23 +25,27 @@ const invalidPath = src => {
|
|
|
24
25
|
path: src,
|
|
25
26
|
})
|
|
26
27
|
);
|
|
28
|
+
process.exit(EXIT_CODES.ERROR);
|
|
27
29
|
};
|
|
28
30
|
|
|
29
31
|
exports.handler = async options => {
|
|
30
|
-
const src = path.resolve(getCwd(), options.src);
|
|
31
|
-
const themeJSONPath = getThemeJSONPath(src);
|
|
32
|
-
const projectRoot = themeJSONPath
|
|
33
|
-
? path.dirname(themeJSONPath)
|
|
34
|
-
: path.dirname(getCwd());
|
|
35
32
|
let stats;
|
|
33
|
+
let projectRoot;
|
|
34
|
+
let src;
|
|
35
|
+
|
|
36
36
|
try {
|
|
37
|
+
src = path.resolve(getCwd(), options.src);
|
|
38
|
+
const themeJSONPath = getThemeJSONPath(options.src);
|
|
39
|
+
projectRoot = themeJSONPath
|
|
40
|
+
? path.dirname(themeJSONPath)
|
|
41
|
+
: path.dirname(getCwd());
|
|
37
42
|
stats = fs.statSync(src);
|
|
38
43
|
if (!stats.isFile() && !stats.isDirectory()) {
|
|
39
|
-
invalidPath(src);
|
|
44
|
+
invalidPath(options.src);
|
|
40
45
|
return;
|
|
41
46
|
}
|
|
42
47
|
} catch (e) {
|
|
43
|
-
invalidPath(src);
|
|
48
|
+
invalidPath(options.src);
|
|
44
49
|
}
|
|
45
50
|
|
|
46
51
|
trackConvertFieldsUsage('process');
|
|
@@ -88,6 +93,7 @@ exports.builder = yargs => {
|
|
|
88
93
|
yargs.option('src', {
|
|
89
94
|
describe: i18n(`${i18nKey}.positionals.src.describe`),
|
|
90
95
|
type: 'string',
|
|
96
|
+
demandOption: i18n(`${i18nKey}.errors.missingSrc`),
|
|
91
97
|
});
|
|
92
98
|
yargs.option('fieldOptions', {
|
|
93
99
|
describe: i18n(`${i18nKey}.options.options.describe`),
|
|
@@ -423,7 +423,7 @@ describe('commands/project/deploy', () => {
|
|
|
423
423
|
|
|
424
424
|
expect(logger.error).toHaveBeenCalledTimes(1);
|
|
425
425
|
expect(logger.error).toHaveBeenCalledWith(
|
|
426
|
-
`The request in account ${accountId} failed due to a client error.`
|
|
426
|
+
`The request for 'project deploy' in account ${accountId} failed due to a client error.`
|
|
427
427
|
);
|
|
428
428
|
expect(processExitSpy).toHaveBeenCalledTimes(1);
|
|
429
429
|
expect(processExitSpy).toHaveBeenCalledWith(EXIT_CODES.ERROR);
|
|
@@ -0,0 +1,168 @@
|
|
|
1
|
+
jest.mock('../../../lib/projects');
|
|
2
|
+
jest.mock('@hubspot/local-dev-lib/logger');
|
|
3
|
+
jest.mock('../../../lib/dependencyManagement');
|
|
4
|
+
jest.mock('../../../lib/prompts/promptUtils');
|
|
5
|
+
jest.mock('../../../lib/usageTracking');
|
|
6
|
+
jest.mock('../../../lib/commonOpts');
|
|
7
|
+
|
|
8
|
+
const { getProjectConfig } = require('../../../lib/projects');
|
|
9
|
+
const { EXIT_CODES } = require('../../../lib/enums/exitCodes');
|
|
10
|
+
const { logger } = require('@hubspot/local-dev-lib/logger');
|
|
11
|
+
const { trackCommandUsage } = require('../../../lib/usageTracking');
|
|
12
|
+
const { getAccountId } = require('../../../lib/commonOpts');
|
|
13
|
+
const {
|
|
14
|
+
installPackages,
|
|
15
|
+
getProjectPackageJsonLocations,
|
|
16
|
+
} = require('../../../lib/dependencyManagement');
|
|
17
|
+
const { promptUser } = require('../../../lib/prompts/promptUtils');
|
|
18
|
+
const {
|
|
19
|
+
command,
|
|
20
|
+
describe: installDepsDescribe,
|
|
21
|
+
builder,
|
|
22
|
+
handler,
|
|
23
|
+
} = require('../installDeps');
|
|
24
|
+
const path = require('path');
|
|
25
|
+
|
|
26
|
+
describe('commands/project/installDeps', () => {
|
|
27
|
+
describe('command', () => {
|
|
28
|
+
it('should have the correct command string', () => {
|
|
29
|
+
expect(command).toEqual('install-deps [packages..]');
|
|
30
|
+
});
|
|
31
|
+
});
|
|
32
|
+
|
|
33
|
+
describe('describe', () => {
|
|
34
|
+
it('should have the correct description', () => {
|
|
35
|
+
expect(installDepsDescribe).toEqual(null);
|
|
36
|
+
});
|
|
37
|
+
});
|
|
38
|
+
|
|
39
|
+
describe('builder', () => {
|
|
40
|
+
let yargs;
|
|
41
|
+
beforeEach(() => {
|
|
42
|
+
yargs = {
|
|
43
|
+
example: jest.fn().mockImplementation(() => yargs),
|
|
44
|
+
};
|
|
45
|
+
});
|
|
46
|
+
|
|
47
|
+
it('should add correct examples', () => {
|
|
48
|
+
builder(yargs);
|
|
49
|
+
expect(yargs.example).toHaveBeenCalledTimes(1);
|
|
50
|
+
expect(yargs.example).toHaveBeenCalledWith([
|
|
51
|
+
['$0 project install-deps', 'Install the dependencies for the project'],
|
|
52
|
+
[
|
|
53
|
+
'$0 project install-deps dependency1 dependency2',
|
|
54
|
+
'Install the dependencies to one or more project subcomponents',
|
|
55
|
+
],
|
|
56
|
+
]);
|
|
57
|
+
});
|
|
58
|
+
});
|
|
59
|
+
|
|
60
|
+
describe('handler', () => {
|
|
61
|
+
let processExitSpy;
|
|
62
|
+
|
|
63
|
+
beforeEach(() => {
|
|
64
|
+
processExitSpy = jest.spyOn(process, 'exit').mockImplementation(() => {});
|
|
65
|
+
});
|
|
66
|
+
|
|
67
|
+
it('should track the command usage', async () => {
|
|
68
|
+
const accountId = 999999;
|
|
69
|
+
getAccountId.mockReturnValue(accountId);
|
|
70
|
+
await handler({});
|
|
71
|
+
|
|
72
|
+
expect(getAccountId).toHaveBeenCalledTimes(1);
|
|
73
|
+
expect(trackCommandUsage).toHaveBeenCalledTimes(1);
|
|
74
|
+
expect(trackCommandUsage).toHaveBeenCalledWith(
|
|
75
|
+
'project-install-deps',
|
|
76
|
+
null,
|
|
77
|
+
accountId
|
|
78
|
+
);
|
|
79
|
+
});
|
|
80
|
+
|
|
81
|
+
it('should handle exceptions', async () => {
|
|
82
|
+
const error = new Error('Something went super wrong');
|
|
83
|
+
|
|
84
|
+
getProjectConfig.mockImplementationOnce(() => {
|
|
85
|
+
throw error;
|
|
86
|
+
});
|
|
87
|
+
|
|
88
|
+
await handler({});
|
|
89
|
+
|
|
90
|
+
expect(logger.debug).toHaveBeenCalledTimes(1);
|
|
91
|
+
expect(logger.debug).toHaveBeenCalledWith(error);
|
|
92
|
+
|
|
93
|
+
expect(logger.error).toHaveBeenCalledTimes(1);
|
|
94
|
+
expect(logger.error).toHaveBeenCalledWith(error.message);
|
|
95
|
+
|
|
96
|
+
expect(processExitSpy).toHaveBeenCalledTimes(1);
|
|
97
|
+
expect(processExitSpy).toHaveBeenCalledWith(EXIT_CODES.ERROR);
|
|
98
|
+
});
|
|
99
|
+
|
|
100
|
+
it('should log an error and exit when the project config is not defined', async () => {
|
|
101
|
+
getProjectConfig.mockResolvedValueOnce(null);
|
|
102
|
+
await handler({});
|
|
103
|
+
|
|
104
|
+
expect(logger.error).toHaveBeenCalledTimes(1);
|
|
105
|
+
expect(logger.error).toHaveBeenCalledWith(
|
|
106
|
+
'No project detected. Run this command from a project directory.'
|
|
107
|
+
);
|
|
108
|
+
expect(processExitSpy).toHaveBeenCalledTimes(1);
|
|
109
|
+
expect(processExitSpy).toHaveBeenCalledWith(EXIT_CODES.ERROR);
|
|
110
|
+
});
|
|
111
|
+
|
|
112
|
+
it('should log an error and exit when the project config has no projectDir', async () => {
|
|
113
|
+
getProjectConfig.mockResolvedValueOnce({ projectDir: null });
|
|
114
|
+
await handler({});
|
|
115
|
+
|
|
116
|
+
expect(logger.error).toHaveBeenCalledTimes(1);
|
|
117
|
+
expect(logger.error).toHaveBeenCalledWith(
|
|
118
|
+
'No project detected. Run this command from a project directory.'
|
|
119
|
+
);
|
|
120
|
+
expect(processExitSpy).toHaveBeenCalledTimes(1);
|
|
121
|
+
expect(processExitSpy).toHaveBeenCalledWith(EXIT_CODES.ERROR);
|
|
122
|
+
});
|
|
123
|
+
|
|
124
|
+
it('should prompt for input when packages is defined', async () => {
|
|
125
|
+
const projectDir = 'src';
|
|
126
|
+
getProjectConfig.mockResolvedValue({ projectDir });
|
|
127
|
+
const packageJsonLocation = path.join(projectDir, 'directory1');
|
|
128
|
+
promptUser.mockResolvedValueOnce(packageJsonLocation);
|
|
129
|
+
getProjectPackageJsonLocations.mockResolvedValue([packageJsonLocation]);
|
|
130
|
+
await handler({ packages: ['@hubspot/local-dev-lib'] });
|
|
131
|
+
expect(getProjectPackageJsonLocations).toHaveBeenCalledTimes(1);
|
|
132
|
+
expect(promptUser).toHaveBeenCalledTimes(1);
|
|
133
|
+
expect(promptUser).toHaveBeenCalledWith([
|
|
134
|
+
{
|
|
135
|
+
name: 'selectedInstallLocations',
|
|
136
|
+
type: 'checkbox',
|
|
137
|
+
when: expect.any(Function),
|
|
138
|
+
choices: [
|
|
139
|
+
{
|
|
140
|
+
name: 'directory1',
|
|
141
|
+
value: packageJsonLocation,
|
|
142
|
+
},
|
|
143
|
+
],
|
|
144
|
+
message: 'Choose the project components to install the dependencies:',
|
|
145
|
+
validate: expect.any(Function),
|
|
146
|
+
},
|
|
147
|
+
]);
|
|
148
|
+
});
|
|
149
|
+
|
|
150
|
+
it('should call installPackages correctly', async () => {
|
|
151
|
+
const projectDir = 'src';
|
|
152
|
+
const packageJsonLocation = path.join(projectDir, 'directory1');
|
|
153
|
+
const installLocations = [packageJsonLocation];
|
|
154
|
+
const packages = ['@hubspot/local-dev-lib'];
|
|
155
|
+
|
|
156
|
+
getProjectConfig.mockResolvedValue({ projectDir });
|
|
157
|
+
promptUser.mockResolvedValueOnce(packageJsonLocation);
|
|
158
|
+
getProjectPackageJsonLocations.mockResolvedValue(installLocations);
|
|
159
|
+
await handler({ packages });
|
|
160
|
+
|
|
161
|
+
expect(installPackages).toHaveBeenCalledTimes(1);
|
|
162
|
+
expect(installPackages).toHaveBeenCalledWith({
|
|
163
|
+
packages,
|
|
164
|
+
installLocations,
|
|
165
|
+
});
|
|
166
|
+
});
|
|
167
|
+
});
|
|
168
|
+
});
|