@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.
Files changed (39) hide show
  1. package/bin/cli.js +24 -5
  2. package/commands/__tests__/projects.test.js +105 -0
  3. package/commands/accounts/clean.js +1 -1
  4. package/commands/cms/convertFields.js +13 -7
  5. package/commands/project/__tests__/deploy.test.js +1 -1
  6. package/commands/project/__tests__/installDeps.test.js +168 -0
  7. package/commands/project/__tests__/logs.test.js +305 -0
  8. package/commands/project/add.js +24 -12
  9. package/commands/project/cloneApp.js +13 -21
  10. package/commands/project/deploy.js +4 -1
  11. package/commands/project/dev.js +22 -11
  12. package/commands/project/download.js +6 -3
  13. package/commands/project/installDeps.js +78 -0
  14. package/commands/project/logs.js +80 -242
  15. package/commands/project/migrateApp.js +8 -9
  16. package/commands/project/upload.js +5 -3
  17. package/commands/project/watch.js +3 -9
  18. package/commands/project.js +2 -0
  19. package/commands/sandbox/create.js +1 -0
  20. package/commands/sandbox.js +0 -2
  21. package/lang/en.lyaml +40 -75
  22. package/lib/LocalDevManager.js +1 -22
  23. package/lib/__tests__/dependencyManagement.test.js +245 -0
  24. package/lib/__tests__/projectLogsManager.test.js +210 -0
  25. package/lib/dependencyManagement.js +157 -0
  26. package/lib/errorHandlers/apiErrors.js +1 -3
  27. package/lib/errorHandlers/overrideErrors.js +57 -36
  28. package/lib/localDev.js +25 -16
  29. package/lib/projectLogsManager.js +144 -0
  30. package/lib/projects.js +17 -7
  31. package/lib/projectsWatch.js +2 -5
  32. package/lib/prompts/__tests__/projectsLogsPrompt.test.js +46 -0
  33. package/lib/prompts/createProjectPrompt.js +4 -0
  34. package/lib/prompts/projectAddPrompt.js +4 -21
  35. package/lib/prompts/projectDevTargetAccountPrompt.js +16 -25
  36. package/lib/prompts/projectsLogsPrompt.js +17 -108
  37. package/lib/sandboxSync.js +13 -15
  38. package/package.json +6 -6
  39. package/commands/sandbox/sync.js +0 -225
@@ -0,0 +1,210 @@
1
+ jest.mock('../projects');
2
+ jest.mock('@hubspot/local-dev-lib/api/projects');
3
+
4
+ const ProjectLogsManager = require('../projectLogsManager');
5
+ const { getProjectConfig, ensureProjectExists } = require('../projects');
6
+ const {
7
+ fetchProjectComponentsMetadata,
8
+ } = require('@hubspot/local-dev-lib/api/projects');
9
+
10
+ describe('cli/lib/projectLogsManager', () => {
11
+ const accountId = 12345678;
12
+ const appId = 999999;
13
+ const projectName = 'super cool test project';
14
+ const projectConfig = { projectConfig: { name: projectName } };
15
+ const projectId = 987654321;
16
+ const projectDetails = {
17
+ project: {
18
+ id: projectId,
19
+ deployedBuild: {
20
+ subbuildStatuses: {},
21
+ },
22
+ },
23
+ };
24
+
25
+ const function1 = {
26
+ componentName: 'function1',
27
+ type: {
28
+ name: 'APP_FUNCTION',
29
+ },
30
+ deployOutput: {
31
+ appId,
32
+ },
33
+ };
34
+ const functions = [
35
+ function1,
36
+ {
37
+ componentName: 'function2',
38
+ type: {
39
+ name: 'APP_FUNCTION',
40
+ },
41
+ deployOutput: {
42
+ appId,
43
+ },
44
+ },
45
+ ];
46
+
47
+ beforeEach(() => {
48
+ ProjectLogsManager.reset();
49
+
50
+ getProjectConfig.mockResolvedValue(projectConfig);
51
+ ensureProjectExists.mockResolvedValue(projectDetails);
52
+ fetchProjectComponentsMetadata.mockResolvedValue({
53
+ topLevelComponentMetadata: [
54
+ {
55
+ type: {
56
+ name: 'PRIVATE_APP',
57
+ },
58
+ deployOutput: {
59
+ appId,
60
+ },
61
+ featureComponents: [
62
+ ...functions,
63
+ {
64
+ type: {
65
+ name: 'NOT_AN_APP_FUNCTION',
66
+ },
67
+ },
68
+ ],
69
+ },
70
+ ],
71
+ });
72
+ });
73
+
74
+ describe('init', () => {
75
+ it('should load the project config', async () => {
76
+ await ProjectLogsManager.init(accountId);
77
+ expect(getProjectConfig).toHaveBeenCalledTimes(1);
78
+ });
79
+
80
+ it('should throw an error if there is a problem with the config', async () => {
81
+ getProjectConfig.mockResolvedValue({});
82
+ await expect(async () =>
83
+ ProjectLogsManager.init(accountId)
84
+ ).rejects.toThrow(
85
+ 'No project detected. Run this command again from a project directory.'
86
+ );
87
+ expect(getProjectConfig).toHaveBeenCalledTimes(1);
88
+ });
89
+
90
+ it('should ensure the project exists', async () => {
91
+ await ProjectLogsManager.init(accountId);
92
+ expect(ensureProjectExists).toHaveBeenCalledTimes(1);
93
+ expect(ensureProjectExists).toHaveBeenCalledWith(accountId, projectName, {
94
+ allowCreate: false,
95
+ });
96
+ });
97
+
98
+ it('should throw an error if there is data missing from the project details', async () => {
99
+ ensureProjectExists.mockResolvedValue({});
100
+ await expect(async () =>
101
+ ProjectLogsManager.init(accountId)
102
+ ).rejects.toThrow(/There was an error fetching project details/);
103
+ });
104
+
105
+ it('should set all of the expected fields correctly', async () => {
106
+ await ProjectLogsManager.init(accountId);
107
+ expect(ProjectLogsManager.projectId).toEqual(projectId);
108
+ expect(ProjectLogsManager.projectName).toEqual(projectName);
109
+ expect(ProjectLogsManager.accountId).toEqual(accountId);
110
+ expect(ProjectLogsManager.functions).toEqual(functions);
111
+ });
112
+ });
113
+
114
+ describe('fetchFunctionDetails', () => {
115
+ it('should throw an error if the projectId is null when the method is called', async () => {
116
+ await expect(async () =>
117
+ ProjectLogsManager.fetchFunctionDetails()
118
+ ).rejects.toThrow(
119
+ 'No project detected. Run this command again from a project directory.'
120
+ );
121
+ });
122
+
123
+ it('should fetch the component metadata', async () => {
124
+ ProjectLogsManager.projectId = projectId;
125
+ ProjectLogsManager.accountId = accountId;
126
+ await ProjectLogsManager.fetchFunctionDetails();
127
+ expect(fetchProjectComponentsMetadata).toHaveBeenCalledTimes(1);
128
+ expect(fetchProjectComponentsMetadata).toHaveBeenCalledWith(
129
+ accountId,
130
+ projectId
131
+ );
132
+ });
133
+
134
+ it('should set the functions correctly', async () => {
135
+ ProjectLogsManager.projectId = projectId;
136
+ ProjectLogsManager.accountId = accountId;
137
+ await ProjectLogsManager.fetchFunctionDetails();
138
+ expect(ProjectLogsManager.functions).toEqual(functions);
139
+ });
140
+ });
141
+
142
+ describe('getFunctionNames', () => {
143
+ it('should return an empty array if functions is nullable', async () => {
144
+ ProjectLogsManager.functions = undefined;
145
+ expect(ProjectLogsManager.getFunctionNames()).toEqual([]);
146
+ });
147
+
148
+ it('should return an array of the componentNames', async () => {
149
+ ProjectLogsManager.functions = functions;
150
+ expect(ProjectLogsManager.getFunctionNames()).toEqual([
151
+ 'function1',
152
+ 'function2',
153
+ ]);
154
+ });
155
+ });
156
+
157
+ describe('setFunction', () => {
158
+ it('should throw an error when functions is nullable', async () => {
159
+ ProjectLogsManager.functions = undefined;
160
+ expect(() => ProjectLogsManager.setFunction('foo')).toThrow(
161
+ `There aren't any functions in this project`
162
+ );
163
+ });
164
+
165
+ it('should throw an error when the provided function is invalid', async () => {
166
+ ProjectLogsManager.functions = functions;
167
+ const badName = 'foo';
168
+ expect(() => ProjectLogsManager.setFunction(badName)).toThrow(
169
+ `No function with name "${badName}"`
170
+ );
171
+ });
172
+
173
+ it('should set the data correctly for public functions', async () => {
174
+ const functionToChoose = {
175
+ componentName: 'function1',
176
+ type: {
177
+ name: 'APP_FUNCTION',
178
+ },
179
+ deployOutput: {
180
+ endpoint: { path: 'yooooooo', method: ['GET'] },
181
+ },
182
+ };
183
+ ProjectLogsManager.functions = [functionToChoose];
184
+ ProjectLogsManager.setFunction('function1');
185
+ expect(ProjectLogsManager.functionName).toEqual('function1');
186
+ expect(ProjectLogsManager.endpointName).toEqual('yooooooo');
187
+ expect(ProjectLogsManager.selectedFunction).toEqual(functionToChoose);
188
+ expect(ProjectLogsManager.method).toEqual(['GET']);
189
+ expect(ProjectLogsManager.isPublicFunction).toEqual(true);
190
+ });
191
+
192
+ it('should set the data correctly for public functions', async () => {
193
+ ProjectLogsManager.functions = functions;
194
+ ProjectLogsManager.setFunction('function1');
195
+ expect(ProjectLogsManager.selectedFunction).toEqual(function1);
196
+ expect(ProjectLogsManager.functionName).toEqual('function1');
197
+ expect(ProjectLogsManager.isPublicFunction).toEqual(false);
198
+ });
199
+ });
200
+
201
+ describe('reset', () => {
202
+ it('should reset all the values', async () => {
203
+ ProjectLogsManager.someRandomField = 'value';
204
+ expect(ProjectLogsManager.someRandomField).toBeDefined();
205
+
206
+ ProjectLogsManager.reset();
207
+ expect(ProjectLogsManager.isPublicFunction).toBeUndefined();
208
+ });
209
+ });
210
+ });
@@ -0,0 +1,157 @@
1
+ const { logger } = require('@hubspot/local-dev-lib/logger');
2
+ const { getProjectConfig } = require('./projects');
3
+ const { exec: execAsync } = require('child_process');
4
+ const { walk } = require('@hubspot/local-dev-lib/fs');
5
+ const path = require('path');
6
+ const { uiLink } = require('./ui');
7
+ const util = require('util');
8
+ const { i18n } = require('./lang');
9
+ const SpinniesManager = require('./ui/SpinniesManager');
10
+ const fs = require('fs');
11
+
12
+ const DEFAULT_PACKAGE_MANAGER = 'npm';
13
+
14
+ const i18nKey = `commands.project.subcommands.installDeps`;
15
+
16
+ class NoPackageJsonFilesError extends Error {
17
+ constructor(projectName) {
18
+ super(
19
+ i18n(`${i18nKey}.noPackageJsonInProject`, {
20
+ projectName,
21
+ link: uiLink(
22
+ 'Learn how to create a project from scratch.',
23
+ 'https://developers.hubspot.com/beta-docs/guides/crm/intro/create-a-project'
24
+ ),
25
+ })
26
+ );
27
+ }
28
+ }
29
+
30
+ async function isGloballyInstalled(command) {
31
+ const exec = util.promisify(execAsync);
32
+ try {
33
+ await exec(`${command} --version`);
34
+ return true;
35
+ } catch (e) {
36
+ return false;
37
+ }
38
+ }
39
+
40
+ async function installPackages({ packages, installLocations }) {
41
+ const installDirs =
42
+ installLocations || (await getProjectPackageJsonLocations());
43
+ await Promise.all(
44
+ installDirs.map(async dir => {
45
+ await installPackagesInDirectory(packages, dir);
46
+ })
47
+ );
48
+ }
49
+
50
+ async function installPackagesInDirectory(packages, directory) {
51
+ const spinner = `installingDependencies-${directory}`;
52
+ const relativeDir = path.relative(process.cwd(), directory);
53
+ SpinniesManager.init();
54
+ SpinniesManager.add(spinner, {
55
+ text:
56
+ packages && packages.length
57
+ ? i18n(`${i18nKey}.addingDependenciesToLocation`, {
58
+ dependencies: `[${packages.join(', ')}]`,
59
+ directory: relativeDir,
60
+ })
61
+ : i18n(`${i18nKey}.installingDependencies`, {
62
+ directory: relativeDir,
63
+ }),
64
+ });
65
+ let installCommand = `${DEFAULT_PACKAGE_MANAGER} --prefix=${directory} install`;
66
+
67
+ if (packages) {
68
+ installCommand = `${installCommand} ${packages.join(' ')}`;
69
+ }
70
+
71
+ logger.debug(`Running ${installCommand}`);
72
+ try {
73
+ const exec = util.promisify(execAsync);
74
+ await exec(installCommand);
75
+ SpinniesManager.succeed(spinner, {
76
+ text: i18n(`${i18nKey}.installationSuccessful`, {
77
+ directory: relativeDir,
78
+ }),
79
+ });
80
+ } catch (e) {
81
+ SpinniesManager.fail(spinner, {
82
+ text: i18n(`${i18nKey}.installingDependenciesFailed`, {
83
+ directory: relativeDir,
84
+ }),
85
+ });
86
+ throw new Error(
87
+ i18n(`${i18nKey}.installingDependenciesFailed`, {
88
+ directory: relativeDir,
89
+ }),
90
+ {
91
+ cause: e,
92
+ }
93
+ );
94
+ }
95
+ }
96
+
97
+ async function getProjectPackageJsonLocations() {
98
+ const projectConfig = await getProjectConfig();
99
+
100
+ if (
101
+ !projectConfig ||
102
+ !projectConfig.projectDir ||
103
+ !projectConfig.projectConfig
104
+ ) {
105
+ throw new Error(i18n(`${i18nKey}.noProjectConfig`));
106
+ }
107
+
108
+ const {
109
+ projectDir,
110
+ projectConfig: { srcDir, name },
111
+ } = projectConfig;
112
+
113
+ if (!(await isGloballyInstalled(DEFAULT_PACKAGE_MANAGER))) {
114
+ throw new Error(
115
+ i18n(`${i18nKey}.packageManagerNotInstalled`, {
116
+ packageManager: DEFAULT_PACKAGE_MANAGER,
117
+ link: uiLink(
118
+ DEFAULT_PACKAGE_MANAGER,
119
+ 'https://docs.npmjs.com/downloading-and-installing-node-js-and-npm'
120
+ ),
121
+ })
122
+ );
123
+ }
124
+
125
+ if (
126
+ !fs.existsSync(projectConfig.projectDir) ||
127
+ !fs.existsSync(path.join(projectDir, srcDir))
128
+ ) {
129
+ throw new NoPackageJsonFilesError(name);
130
+ }
131
+
132
+ const packageJsonFiles = (await walk(path.join(projectDir, srcDir))).filter(
133
+ file =>
134
+ file.includes('package.json') &&
135
+ !file.includes('node_modules') &&
136
+ !file.includes('.vite')
137
+ );
138
+
139
+ if (packageJsonFiles.length === 0) {
140
+ throw new NoPackageJsonFilesError(name);
141
+ }
142
+
143
+ const packageParentDirs = [];
144
+ packageJsonFiles.forEach(packageJsonFile => {
145
+ const parentDir = path.dirname(packageJsonFile);
146
+ packageParentDirs.push(parentDir);
147
+ });
148
+
149
+ return packageParentDirs;
150
+ }
151
+
152
+ module.exports = {
153
+ isGloballyInstalled,
154
+ installPackages,
155
+ DEFAULT_PACKAGE_MANAGER,
156
+ getProjectPackageJsonLocations,
157
+ };
@@ -28,8 +28,6 @@ class ApiErrorContext extends ErrorContext {
28
28
  this.request = props.request || '';
29
29
  /** @type {string} */
30
30
  this.payload = props.payload || '';
31
- /** @type {string} */
32
- this.projectName = props.projectName || '';
33
31
  }
34
32
  }
35
33
 
@@ -54,7 +52,7 @@ function logValidationErrors(error, context) {
54
52
  */
55
53
  function logApiErrorInstance(error, context) {
56
54
  if (error.isAxiosError) {
57
- if (overrideErrors(error)) return;
55
+ if (overrideErrors(error, context)) return;
58
56
  const errorWithContext = getAxiosErrorWithContext(error, context);
59
57
  logger.error(errorWithContext.message);
60
58
  return;
@@ -1,38 +1,43 @@
1
- const { isSpecifiedError } = require('@hubspot/local-dev-lib/errors/apiErrors');
1
+ const {
2
+ isSpecifiedError,
3
+ isMissingScopeError,
4
+ } = require('@hubspot/local-dev-lib/errors/apiErrors');
2
5
  const { logger } = require('@hubspot/local-dev-lib/logger');
3
-
4
6
  const { PLATFORM_VERSION_ERROR_TYPES } = require('../constants');
5
7
  const { i18n } = require('../lang');
6
- const { uiLine, uiLink } = require('../ui');
8
+ const {
9
+ uiAccountDescription,
10
+ uiLine,
11
+ uiLink,
12
+ uiCommandReference,
13
+ } = require('../ui');
7
14
 
8
15
  const i18nKey = 'lib.errorHandlers.overrideErrors';
9
16
 
10
- function createPlatformVersionError(subCategory, errData) {
11
- const docsLink = uiLink(
12
- i18n(`${i18nKey}.platformVersionErrors.docsLink`),
13
- 'https://developers.hubspot.com/docs/platform/platform-versioning'
14
- );
15
-
16
- const platformVersionKey = {
17
- [PLATFORM_VERSION_ERROR_TYPES.PLATFORM_VERSION_NOT_SPECIFIED]:
18
- 'unspecified platformVersion',
19
- [PLATFORM_VERSION_ERROR_TYPES.PLATFORM_VERSION_RETIRED]:
20
- errData.context.RETIRED_PLATFORM_VERSION,
21
- [PLATFORM_VERSION_ERROR_TYPES.PLATFORM_VERSION_SPECIFIED_DOES_NOT_EXIST]:
22
- errData.context.PLATFORM_VERSION,
23
- };
17
+ function createPlatformVersionError(err, subCategory) {
18
+ let translationKey = 'unspecifiedPlatformVersion';
19
+ let platformVersion = 'unspecified platformVersion';
20
+ const errorContext =
21
+ err.response && err.response.data && err.response.data.context;
24
22
 
25
- const errorTypeToTranslationKey = {
26
- [PLATFORM_VERSION_ERROR_TYPES.PLATFORM_VERSION_NOT_SPECIFIED]:
27
- 'unspecifiedPlatformVersion',
28
- [PLATFORM_VERSION_ERROR_TYPES.PLATFORM_VERSION_RETIRED]:
29
- 'platformVersionRetired',
30
- [PLATFORM_VERSION_ERROR_TYPES.PLATFORM_VERSION_SPECIFIED_DOES_NOT_EXIST]:
31
- 'nonExistentPlatformVersion',
32
- };
33
-
34
- const platformVersion = platformVersionKey[subCategory] || '';
35
- const translationKey = errorTypeToTranslationKey[subCategory];
23
+ switch (subCategory) {
24
+ case [PLATFORM_VERSION_ERROR_TYPES.PLATFORM_VERSION_RETIRED]:
25
+ translationKey = 'platformVersionRetired';
26
+ if (errorContext && errorContext[subCategory]) {
27
+ platformVersion = errorContext[subCategory];
28
+ }
29
+ break;
30
+ case [
31
+ PLATFORM_VERSION_ERROR_TYPES.PLATFORM_VERSION_SPECIFIED_DOES_NOT_EXIST,
32
+ ]:
33
+ translationKey = 'nonExistentPlatformVersion';
34
+ if (errorContext && errorContext[subCategory]) {
35
+ platformVersion = errorContext[subCategory];
36
+ }
37
+ break;
38
+ default:
39
+ break;
40
+ }
36
41
 
37
42
  uiLine();
38
43
  logger.error(i18n(`${i18nKey}.platformVersionErrors.header`));
@@ -44,21 +49,37 @@ function createPlatformVersionError(subCategory, errData) {
44
49
  logger.log(i18n(`${i18nKey}.platformVersionErrors.updateProject`));
45
50
  logger.log(
46
51
  i18n(`${i18nKey}.platformVersionErrors.betaLink`, {
47
- docsLink,
52
+ docsLink: uiLink(
53
+ i18n(`${i18nKey}.platformVersionErrors.docsLink`),
54
+ 'https://developers.hubspot.com/docs/platform/platform-versioning'
55
+ ),
48
56
  })
49
57
  );
50
58
  uiLine();
51
59
  }
52
60
 
53
- function overrideErrors(err) {
61
+ function overrideErrors(err, context) {
62
+ if (isMissingScopeError(err)) {
63
+ logger.error(
64
+ i18n(`${i18nKey}.missingScopeError`, {
65
+ accountName: context.accountId
66
+ ? uiAccountDescription(context.accountId)
67
+ : '',
68
+ request: context.request || 'request',
69
+ authCommand: uiCommandReference('hs auth'),
70
+ })
71
+ );
72
+ return true;
73
+ }
74
+
54
75
  if (
55
76
  isSpecifiedError(err, {
56
77
  subCategory: PLATFORM_VERSION_ERROR_TYPES.PLATFORM_VERSION_NOT_SPECIFIED,
57
78
  })
58
79
  ) {
59
80
  createPlatformVersionError(
60
- PLATFORM_VERSION_ERROR_TYPES.PLATFORM_VERSION_NOT_SPECIFIED,
61
- err.response.data
81
+ err,
82
+ PLATFORM_VERSION_ERROR_TYPES.PLATFORM_VERSION_NOT_SPECIFIED
62
83
  );
63
84
  return true;
64
85
  }
@@ -69,8 +90,8 @@ function overrideErrors(err) {
69
90
  })
70
91
  ) {
71
92
  createPlatformVersionError(
72
- PLATFORM_VERSION_ERROR_TYPES.PLATFORM_VERSION_RETIRED,
73
- err.response.data
93
+ err,
94
+ PLATFORM_VERSION_ERROR_TYPES.PLATFORM_VERSION_RETIRED
74
95
  );
75
96
  return true;
76
97
  }
@@ -82,8 +103,8 @@ function overrideErrors(err) {
82
103
  })
83
104
  ) {
84
105
  createPlatformVersionError(
85
- PLATFORM_VERSION_ERROR_TYPES.PLATFORM_VERSION_SPECIFIED_DOES_NOT_EXIST,
86
- err.response.data
106
+ err,
107
+ PLATFORM_VERSION_ERROR_TYPES.PLATFORM_VERSION_SPECIFIED_DOES_NOT_EXIST
87
108
  );
88
109
  return true;
89
110
  }
package/lib/localDev.js CHANGED
@@ -85,15 +85,32 @@ const confirmDefaultAccountIsTarget = async accountConfig => {
85
85
  // Confirm the default account is a developer account if developing public apps
86
86
  const checkIfAppDeveloperAccount = accountConfig => {
87
87
  if (!isAppDeveloperAccount(accountConfig)) {
88
- logger.error(i18n(`${i18nKey}.checkIfAppDevloperAccount`));
88
+ logger.error(
89
+ i18n(`${i18nKey}.checkIfAppDevloperAccount`, {
90
+ useCommand: uiCommandReference('hs accounts use'),
91
+ authCommand: uiCommandReference('hs auth'),
92
+ })
93
+ );
89
94
  process.exit(EXIT_CODES.SUCCESS);
90
95
  }
91
96
  };
92
97
 
93
98
  // Confirm the default account is a developer account if developing public apps
94
- const checkIfDeveloperTestAccount = accountConfig => {
95
- if (!isDeveloperTestAccount(accountConfig)) {
96
- logger.error(i18n(`${i18nKey}.checkIfDeveloperTestAccount`));
99
+ const validateAccountOption = (accountConfig, hasPublicApps) => {
100
+ if (hasPublicApps && !isDeveloperTestAccount(accountConfig)) {
101
+ logger.error(
102
+ i18n(`${i18nKey}.validateAccountOption.invalidPublicAppAccount`, {
103
+ useCommand: uiCommandReference('hs accounts use'),
104
+ devCommand: uiCommandReference('hs project dev'),
105
+ })
106
+ );
107
+ process.exit(EXIT_CODES.SUCCESS);
108
+ } else if (isAppDeveloperAccount(accountConfig)) {
109
+ logger.error(
110
+ i18n(`${i18nKey}.validateAccountOption.invalidPrivateAppAccount`, {
111
+ useCommand: uiCommandReference('hs accounts use'),
112
+ })
113
+ );
97
114
  process.exit(EXIT_CODES.SUCCESS);
98
115
  }
99
116
  };
@@ -109,20 +126,11 @@ const suggestRecommendedNestedAccount = async (
109
126
  if (hasPublicApps) {
110
127
  logger.log(
111
128
  i18n(
112
- `${i18nKey}.suggestRecommendedNestedAccount.publicAppNonDeveloperTestAccountWarning`
129
+ `${i18nKey}.validateAccountOption.publicAppNonDeveloperTestAccountWarning`
113
130
  )
114
131
  );
115
- } else if (isAppDeveloperAccount(accountConfig)) {
116
- logger.error(
117
- i18n(
118
- `${i18nKey}.suggestRecommendedNestedAccount.privateAppInAppDeveloperAccountError`
119
- )
120
- );
121
- process.exit(EXIT_CODES.ERROR);
122
132
  } else {
123
- logger.log(
124
- i18n(`${i18nKey}.suggestRecommendedNestedAccount.nonSandboxWarning`)
125
- );
133
+ logger.log(i18n(`${i18nKey}.validateAccountOption.nonSandboxWarning`));
126
134
  }
127
135
  uiLine();
128
136
  logger.log();
@@ -187,6 +195,7 @@ const createSandboxForLocalDev = async (accountId, accountConfig, env) => {
187
195
  accountConfig,
188
196
  sandboxAccountConfig
189
197
  );
198
+ // For v1 sandboxes, keep sync here. Once we migrate to v2, this will be handled by BE automatically
190
199
  await syncSandbox({
191
200
  accountConfig: sandboxAccountConfig,
192
201
  parentAccountConfig: accountConfig,
@@ -439,7 +448,7 @@ const getAccountHomeUrl = accountId => {
439
448
  module.exports = {
440
449
  confirmDefaultAccountIsTarget,
441
450
  checkIfAppDeveloperAccount,
442
- checkIfDeveloperTestAccount,
451
+ validateAccountOption,
443
452
  suggestRecommendedNestedAccount,
444
453
  createSandboxForLocalDev,
445
454
  createDeveloperTestAccountForLocalDev,