@hubspot/cli 7.7.30-experimental.0 → 7.7.31-experimental.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/commands/app/__tests__/install.test.d.ts +1 -0
- package/commands/app/__tests__/install.test.js +47 -0
- package/commands/app/install.d.ts +8 -0
- package/commands/app/install.js +122 -0
- package/commands/app.js +6 -1
- package/commands/project/upload.d.ts +2 -2
- package/commands/project/upload.js +1 -1
- package/commands/testAccount/create.js +3 -0
- package/lang/en.d.ts +24 -0
- package/lang/en.js +25 -1
- package/lib/mcp/setup.js +1 -1
- package/lib/projectProfiles.d.ts +1 -1
- package/lib/projectProfiles.js +10 -2
- package/lib/projects/__tests__/AppDevModeInterface.test.js +10 -0
- package/lib/projects/localDev/AppDevModeInterface.d.ts +5 -1
- package/lib/projects/localDev/AppDevModeInterface.js +33 -10
- package/lib/projects/structure.d.ts +2 -2
- package/lib/projects/upload.d.ts +2 -1
- package/lib/projects/upload.js +1 -0
- package/package.json +1 -1
- package/types/Yargs.d.ts +1 -1
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
import yargs from 'yargs';
|
|
2
|
+
import { addAccountOptions, addConfigOptions, addUseEnvironmentOptions, addGlobalOptions, addJSONOutputOptions, } from '../../../lib/commonOpts.js';
|
|
3
|
+
import installCommand from '../install.js';
|
|
4
|
+
vi.mock('../../../lib/commonOpts');
|
|
5
|
+
describe('commands/app/install', () => {
|
|
6
|
+
const yargsMock = yargs;
|
|
7
|
+
describe('command', () => {
|
|
8
|
+
it('should have the correct command structure', () => {
|
|
9
|
+
expect(installCommand.command).toEqual('install <test-account-id>');
|
|
10
|
+
});
|
|
11
|
+
});
|
|
12
|
+
describe('describe', () => {
|
|
13
|
+
it('should provide a description', () => {
|
|
14
|
+
expect(installCommand.describe).not.toBeDefined();
|
|
15
|
+
});
|
|
16
|
+
});
|
|
17
|
+
describe('builder', () => {
|
|
18
|
+
it('should support the correct options', () => {
|
|
19
|
+
installCommand.builder(yargsMock);
|
|
20
|
+
expect(addGlobalOptions).toHaveBeenCalledTimes(1);
|
|
21
|
+
expect(addGlobalOptions).toHaveBeenCalledWith(yargsMock);
|
|
22
|
+
expect(addAccountOptions).toHaveBeenCalledTimes(1);
|
|
23
|
+
expect(addAccountOptions).toHaveBeenCalledWith(yargsMock);
|
|
24
|
+
expect(addConfigOptions).toHaveBeenCalledTimes(1);
|
|
25
|
+
expect(addConfigOptions).toHaveBeenCalledWith(yargsMock);
|
|
26
|
+
expect(addUseEnvironmentOptions).toHaveBeenCalledTimes(1);
|
|
27
|
+
expect(addUseEnvironmentOptions).toHaveBeenCalledWith(yargsMock);
|
|
28
|
+
expect(addJSONOutputOptions).toHaveBeenCalledTimes(1);
|
|
29
|
+
expect(addJSONOutputOptions).toHaveBeenCalledWith(yargsMock);
|
|
30
|
+
expect(yargsMock.positional).toHaveBeenCalledTimes(1);
|
|
31
|
+
expect(yargsMock.positional).toHaveBeenCalledWith('test-account-id', expect.objectContaining({
|
|
32
|
+
type: 'number',
|
|
33
|
+
required: true,
|
|
34
|
+
describe: expect.any(String),
|
|
35
|
+
}));
|
|
36
|
+
expect(yargsMock.option).toHaveBeenCalledTimes(2);
|
|
37
|
+
expect(yargsMock.option).toHaveBeenCalledWith('app-uid', expect.objectContaining({
|
|
38
|
+
type: 'string',
|
|
39
|
+
describe: expect.any(String),
|
|
40
|
+
}));
|
|
41
|
+
expect(yargsMock.option).toHaveBeenCalledWith('project-name', expect.objectContaining({
|
|
42
|
+
type: 'string',
|
|
43
|
+
describe: expect.any(String),
|
|
44
|
+
}));
|
|
45
|
+
});
|
|
46
|
+
});
|
|
47
|
+
});
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import { CommonArgs, ConfigArgs, AccountArgs, EnvironmentArgs, YargsCommandModule, JSONOutputArgs } from '../../types/Yargs.js';
|
|
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,122 @@
|
|
|
1
|
+
import { fetchDeveloperTestAccountOauthAppInstallStatus, installOauthAppIntoDeveloperTestAccount, } from '@hubspot/local-dev-lib/api/developerTestAccounts';
|
|
2
|
+
import { trackCommandUsage } from '../../lib/usageTracking.js';
|
|
3
|
+
import { commands } from '../../lang/en.js';
|
|
4
|
+
import { EXIT_CODES } from '../../lib/enums/exitCodes.js';
|
|
5
|
+
import { makeYargsBuilder } from '../../lib/yargsUtils.js';
|
|
6
|
+
import { APP_AUTH_TYPES } from '../../lib/constants.js';
|
|
7
|
+
import { uiLogger } from '../../lib/ui/logger.js';
|
|
8
|
+
import SpinniesManager from '../../lib/ui/SpinniesManager.js';
|
|
9
|
+
import { poll } from '../../lib/polling.js';
|
|
10
|
+
import { getProjectConfig, validateProjectConfig, } from '../../lib/projects/config.js';
|
|
11
|
+
import { handleTranslate } from '../../lib/projects/upload.js';
|
|
12
|
+
import { isAppIRNode } from '../../lib/projects/structure.js';
|
|
13
|
+
import { logError } from '../../lib/errorHandlers/index.js';
|
|
14
|
+
const command = 'install <test-account-id>';
|
|
15
|
+
const describe = undefined; // commands.app.subcommands.install.describe;
|
|
16
|
+
async function handler(args) {
|
|
17
|
+
const { derivedAccountId, appUid, projectName, testAccountId, formatOutputAsJson, } = args;
|
|
18
|
+
trackCommandUsage('app-install', {}, derivedAccountId);
|
|
19
|
+
const jsonOutput = {};
|
|
20
|
+
let targetProjectName = projectName;
|
|
21
|
+
let targetAppUid = appUid;
|
|
22
|
+
const { projectConfig, projectDir } = await getProjectConfig();
|
|
23
|
+
if (!targetProjectName) {
|
|
24
|
+
validateProjectConfig(projectConfig, projectDir);
|
|
25
|
+
targetProjectName = projectConfig?.name;
|
|
26
|
+
}
|
|
27
|
+
if (!targetProjectName) {
|
|
28
|
+
uiLogger.error(commands.app.subcommands.install.errors.mustSpecifyProjectName);
|
|
29
|
+
process.exit(EXIT_CODES.ERROR);
|
|
30
|
+
}
|
|
31
|
+
let isAppOauth = true;
|
|
32
|
+
if (!targetAppUid) {
|
|
33
|
+
const intermediateRepresentation = await handleTranslate(projectDir, projectConfig, derivedAccountId, true, undefined);
|
|
34
|
+
if (intermediateRepresentation) {
|
|
35
|
+
Object.values(intermediateRepresentation.intermediateNodesIndexedByUid).forEach(node => {
|
|
36
|
+
if (isAppIRNode(node)) {
|
|
37
|
+
targetAppUid = node.uid;
|
|
38
|
+
isAppOauth = node.config.auth.type === APP_AUTH_TYPES.OAUTH;
|
|
39
|
+
}
|
|
40
|
+
});
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
if (!targetAppUid) {
|
|
44
|
+
uiLogger.error(commands.app.subcommands.install.errors.noAppUidFound);
|
|
45
|
+
process.exit(EXIT_CODES.ERROR);
|
|
46
|
+
}
|
|
47
|
+
if (!isAppOauth) {
|
|
48
|
+
uiLogger.error(commands.app.subcommands.install.errors.appMustBeOauth);
|
|
49
|
+
process.exit(EXIT_CODES.ERROR);
|
|
50
|
+
}
|
|
51
|
+
try {
|
|
52
|
+
const { data } = await installOauthAppIntoDeveloperTestAccount(derivedAccountId, testAccountId, targetProjectName, targetAppUid);
|
|
53
|
+
if (data?.authCodes.length > 0) {
|
|
54
|
+
jsonOutput.authCode = data.authCodes[0].authCode;
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
catch (err) {
|
|
58
|
+
logError(err);
|
|
59
|
+
process.exit(EXIT_CODES.ERROR);
|
|
60
|
+
}
|
|
61
|
+
SpinniesManager.init({
|
|
62
|
+
succeedColor: 'white',
|
|
63
|
+
});
|
|
64
|
+
SpinniesManager.add('installApp', {
|
|
65
|
+
text: commands.app.subcommands.install.polling.start,
|
|
66
|
+
});
|
|
67
|
+
let appInstallSucceeded = false;
|
|
68
|
+
try {
|
|
69
|
+
await poll(() => fetchDeveloperTestAccountOauthAppInstallStatus(derivedAccountId, targetProjectName, targetAppUid), {
|
|
70
|
+
successStates: ['SUCCESS'],
|
|
71
|
+
errorStates: [],
|
|
72
|
+
});
|
|
73
|
+
appInstallSucceeded = true;
|
|
74
|
+
}
|
|
75
|
+
catch (err) {
|
|
76
|
+
SpinniesManager.fail('installApp');
|
|
77
|
+
logError(err);
|
|
78
|
+
process.exit(EXIT_CODES.ERROR);
|
|
79
|
+
}
|
|
80
|
+
if (!appInstallSucceeded) {
|
|
81
|
+
SpinniesManager.fail('installApp');
|
|
82
|
+
process.exit(EXIT_CODES.ERROR);
|
|
83
|
+
}
|
|
84
|
+
SpinniesManager.succeed('installApp', {
|
|
85
|
+
text: commands.app.subcommands.install.polling.success,
|
|
86
|
+
});
|
|
87
|
+
if (formatOutputAsJson) {
|
|
88
|
+
uiLogger.json(jsonOutput);
|
|
89
|
+
}
|
|
90
|
+
process.exit(EXIT_CODES.SUCCESS);
|
|
91
|
+
}
|
|
92
|
+
function installAppBuilder(yargs) {
|
|
93
|
+
yargs.positional('test-account-id', {
|
|
94
|
+
describe: commands.app.subcommands.install.positionals.testAccountId,
|
|
95
|
+
required: true,
|
|
96
|
+
type: 'number',
|
|
97
|
+
});
|
|
98
|
+
yargs.option('app-uid', {
|
|
99
|
+
describe: commands.app.subcommands.install.options.appUid,
|
|
100
|
+
type: 'string',
|
|
101
|
+
});
|
|
102
|
+
yargs.option('project-name', {
|
|
103
|
+
describe: commands.app.subcommands.install.options.projectName,
|
|
104
|
+
type: 'string',
|
|
105
|
+
});
|
|
106
|
+
yargs.example('install 1234567890 --app-uid=my-app-uid --project-name=my-project', commands.app.subcommands.install.example);
|
|
107
|
+
return yargs;
|
|
108
|
+
}
|
|
109
|
+
const builder = makeYargsBuilder(installAppBuilder, command, commands.app.subcommands.install.describe, {
|
|
110
|
+
useGlobalOptions: true,
|
|
111
|
+
useAccountOptions: true,
|
|
112
|
+
useConfigOptions: true,
|
|
113
|
+
useEnvironmentOptions: true,
|
|
114
|
+
useJSONOutputOptions: true,
|
|
115
|
+
});
|
|
116
|
+
const installAppCommand = {
|
|
117
|
+
command,
|
|
118
|
+
describe,
|
|
119
|
+
handler,
|
|
120
|
+
builder,
|
|
121
|
+
};
|
|
122
|
+
export default installAppCommand;
|
package/commands/app.js
CHANGED
|
@@ -1,11 +1,16 @@
|
|
|
1
1
|
import migrateCommand from './app/migrate.js';
|
|
2
2
|
import appSecretCommand from './app/secret.js';
|
|
3
|
+
import installAppCommand from './app/install.js';
|
|
3
4
|
import { makeYargsBuilder } from '../lib/yargsUtils.js';
|
|
4
5
|
const command = ['app', 'apps'];
|
|
5
6
|
// Keep the command hidden for now
|
|
6
7
|
const describe = undefined;
|
|
7
8
|
function appBuilder(yargs) {
|
|
8
|
-
yargs
|
|
9
|
+
yargs
|
|
10
|
+
.command(migrateCommand)
|
|
11
|
+
.command(appSecretCommand)
|
|
12
|
+
.command(installAppCommand)
|
|
13
|
+
.demandCommand(1, '');
|
|
9
14
|
return yargs;
|
|
10
15
|
}
|
|
11
16
|
const builder = makeYargsBuilder(appBuilder, command, describe);
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { CommonArgs, JSONOutputArgs, YargsCommandModule } from '../../types/Yargs.js';
|
|
2
|
-
type ProjectUploadArgs = CommonArgs & JSONOutputArgs & {
|
|
1
|
+
import { CommonArgs, EnvironmentArgs, JSONOutputArgs, YargsCommandModule } from '../../types/Yargs.js';
|
|
2
|
+
type ProjectUploadArgs = CommonArgs & JSONOutputArgs & EnvironmentArgs & {
|
|
3
3
|
forceCreate: boolean;
|
|
4
4
|
message: string;
|
|
5
5
|
m: string;
|
|
@@ -25,7 +25,7 @@ async function handler(args) {
|
|
|
25
25
|
validateProjectConfig(projectConfig, projectDir);
|
|
26
26
|
let targetAccountId;
|
|
27
27
|
if (useV3Api(projectConfig.platformVersion)) {
|
|
28
|
-
targetAccountId = await loadAndValidateProfile(projectConfig, projectDir, profile);
|
|
28
|
+
targetAccountId = await loadAndValidateProfile(projectConfig, projectDir, profile, args.useEnv);
|
|
29
29
|
}
|
|
30
30
|
targetAccountId = targetAccountId || derivedAccountId;
|
|
31
31
|
const accountConfig = getAccountConfig(targetAccountId);
|
|
@@ -75,6 +75,9 @@ async function handler(args) {
|
|
|
75
75
|
resultJson.personalAccessKey = createResult.personalAccessKey;
|
|
76
76
|
}
|
|
77
77
|
catch (err) {
|
|
78
|
+
SpinniesManager.fail('createTestAccount', {
|
|
79
|
+
text: commands.testAccount.create.polling.createFailure,
|
|
80
|
+
});
|
|
78
81
|
logError(err);
|
|
79
82
|
SpinniesManager.fail('createTestAccount', {
|
|
80
83
|
text: commands.testAccount.create.polling.createFailure,
|
package/lang/en.d.ts
CHANGED
|
@@ -1550,6 +1550,28 @@ ${string}`;
|
|
|
1550
1550
|
readonly app: {
|
|
1551
1551
|
readonly describe: "Commands for managing apps.";
|
|
1552
1552
|
readonly subcommands: {
|
|
1553
|
+
readonly install: {
|
|
1554
|
+
readonly describe: "Install an OAuth app into a test account.";
|
|
1555
|
+
readonly options: {
|
|
1556
|
+
readonly appUid: "The uid of the app to install";
|
|
1557
|
+
readonly projectName: "The name of the project that contains the app";
|
|
1558
|
+
};
|
|
1559
|
+
readonly positionals: {
|
|
1560
|
+
readonly testAccountId: "The id of the test account to install the app into";
|
|
1561
|
+
};
|
|
1562
|
+
readonly errors: {
|
|
1563
|
+
readonly mustSpecifyProjectName: `You must specify a project name. Use the ${string} flag to specify the project name or run this command from within a project directory.`;
|
|
1564
|
+
readonly noAppUidFound: `No app uid found. Please specify the app uid with the ${string} flag or run this command from within a project that contains an app.`;
|
|
1565
|
+
readonly appMustBeOauth: "This command only supports installing oauth apps. Please specify an app with oauth auth type.";
|
|
1566
|
+
};
|
|
1567
|
+
readonly polling: {
|
|
1568
|
+
readonly start: "Installing app...";
|
|
1569
|
+
readonly success: "App installed successfully";
|
|
1570
|
+
readonly failure: "App installation failed";
|
|
1571
|
+
readonly error: "Error installing app";
|
|
1572
|
+
};
|
|
1573
|
+
readonly example: "Install the app with uid my-app-uid from the project named \"my-project\" into the target account with id 1234567890";
|
|
1574
|
+
};
|
|
1553
1575
|
readonly secret: {
|
|
1554
1576
|
readonly describe: "Commands for managing secrets.";
|
|
1555
1577
|
readonly subcommands: {
|
|
@@ -2569,6 +2591,8 @@ export declare const lib: {
|
|
|
2569
2591
|
readonly activeInstallations: (appName: string, installCount: number) => string;
|
|
2570
2592
|
readonly error: "An error occurred while checking installations for your app";
|
|
2571
2593
|
};
|
|
2594
|
+
readonly distributionChanged: `Your app's distribution type has been changed from private to marketplace. Once uploaded, this change cannot be reversed. Before uploading your project, confirm that you want to ${string} change your app's distribution type. This will uninstall your app from all accounts.`;
|
|
2595
|
+
readonly authTypeChanged: `Your app's auth type has been changed from static to oauth. Once uploaded, this change cannot be reversed. Before uploading your project, confirm that you want to ${string} change your app's auth type. This will uninstall your app from all accounts.`;
|
|
2572
2596
|
};
|
|
2573
2597
|
readonly LocalDevWebsocketServer: {
|
|
2574
2598
|
readonly errors: {
|
package/lang/en.js
CHANGED
|
@@ -5,7 +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, } from '../lib/projects/urls.js';
|
|
8
|
-
import { PROJECT_CONFIG_FILE, PROJECT_WITH_APP } from '../lib/constants.js';
|
|
8
|
+
import { APP_DISTRIBUTION_TYPES, APP_AUTH_TYPES, PROJECT_CONFIG_FILE, PROJECT_WITH_APP, } from '../lib/constants.js';
|
|
9
9
|
import { getAccountIdentifier } from '@hubspot/local-dev-lib/config/getAccountIdentifier';
|
|
10
10
|
export const commands = {
|
|
11
11
|
generalErrors: {
|
|
@@ -1547,6 +1547,28 @@ export const commands = {
|
|
|
1547
1547
|
app: {
|
|
1548
1548
|
describe: 'Commands for managing apps.',
|
|
1549
1549
|
subcommands: {
|
|
1550
|
+
install: {
|
|
1551
|
+
describe: 'Install an OAuth app into a test account.',
|
|
1552
|
+
options: {
|
|
1553
|
+
appUid: 'The uid of the app to install',
|
|
1554
|
+
projectName: 'The name of the project that contains the app',
|
|
1555
|
+
},
|
|
1556
|
+
positionals: {
|
|
1557
|
+
testAccountId: 'The id of the test account to install the app into',
|
|
1558
|
+
},
|
|
1559
|
+
errors: {
|
|
1560
|
+
mustSpecifyProjectName: `You must specify a project name. Use the ${uiCommandReference('--project-name')} flag to specify the project name or run this command from within a project directory.`,
|
|
1561
|
+
noAppUidFound: `No app uid found. Please specify the app uid with the ${uiCommandReference('--app-uid')} flag or run this command from within a project that contains an app.`,
|
|
1562
|
+
appMustBeOauth: 'This command only supports installing oauth apps. Please specify an app with oauth auth type.',
|
|
1563
|
+
},
|
|
1564
|
+
polling: {
|
|
1565
|
+
start: 'Installing app...',
|
|
1566
|
+
success: 'App installed successfully',
|
|
1567
|
+
failure: 'App installation failed',
|
|
1568
|
+
error: 'Error installing app',
|
|
1569
|
+
},
|
|
1570
|
+
example: 'Install the app with uid my-app-uid from the project named "my-project" into the target account with id 1234567890',
|
|
1571
|
+
},
|
|
1550
1572
|
secret: {
|
|
1551
1573
|
describe: 'Commands for managing secrets.',
|
|
1552
1574
|
subcommands: {
|
|
@@ -2566,6 +2588,8 @@ export const lib = {
|
|
|
2566
2588
|
activeInstallations: (appName, installCount) => `[WARNING] Your app ${chalk.bold(appName)} is installed in ${chalk.bold(`${installCount} ${installCount === 1 ? 'account' : 'accounts'}`)}`,
|
|
2567
2589
|
error: 'An error occurred while checking installations for your app',
|
|
2568
2590
|
},
|
|
2591
|
+
distributionChanged: `Your app's distribution type has been changed from ${APP_DISTRIBUTION_TYPES.PRIVATE} to ${APP_DISTRIBUTION_TYPES.MARKETPLACE}. Once uploaded, this change cannot be reversed. Before uploading your project, confirm that you want to ${chalk.bold('permanantly')} change your app's distribution type. This will uninstall your app from all accounts.`,
|
|
2592
|
+
authTypeChanged: `Your app's auth type has been changed from ${APP_AUTH_TYPES.STATIC} to ${APP_AUTH_TYPES.OAUTH}. Once uploaded, this change cannot be reversed. Before uploading your project, confirm that you want to ${chalk.bold('permanantly')} change your app's auth type. This will uninstall your app from all accounts.`,
|
|
2569
2593
|
},
|
|
2570
2594
|
LocalDevWebsocketServer: {
|
|
2571
2595
|
errors: {
|
package/lib/mcp/setup.js
CHANGED
|
@@ -182,7 +182,7 @@ export async function setupClaudeCode(mcpCommand = defaultMcpCommand) {
|
|
|
182
182
|
});
|
|
183
183
|
await execAsync(`claude mcp remove "${mcpServerName}" --scope user`);
|
|
184
184
|
}
|
|
185
|
-
await execAsync(`claude mcp add-json "${mcpServerName}" ${
|
|
185
|
+
await execAsync(`claude mcp add-json "${mcpServerName}" '${mcpConfig}' --scope user`);
|
|
186
186
|
SpinniesManager.succeed('claudeCode', {
|
|
187
187
|
text: commands.mcp.setup.spinners.configuredClaudeCode,
|
|
188
188
|
});
|
package/lib/projectProfiles.d.ts
CHANGED
|
@@ -4,4 +4,4 @@ export declare function logProfileHeader(profileName: string): void;
|
|
|
4
4
|
export declare function logProfileFooter(profile: HsProfileFile, includeVariables?: boolean): void;
|
|
5
5
|
export declare function loadProfile(projectConfig: ProjectConfig | null, projectDir: string | null, profileName: string): HsProfileFile | undefined;
|
|
6
6
|
export declare function exitIfUsingProfiles(projectConfig: ProjectConfig | null, projectDir: string | null): Promise<void>;
|
|
7
|
-
export declare function loadAndValidateProfile(projectConfig: ProjectConfig | null, projectDir: string | null, argsProfile: string | undefined): Promise<number | undefined>;
|
|
7
|
+
export declare function loadAndValidateProfile(projectConfig: ProjectConfig | null, projectDir: string | null, argsProfile: string | undefined, useEnv?: boolean): Promise<number | undefined>;
|
package/lib/projectProfiles.js
CHANGED
|
@@ -38,7 +38,12 @@ export function loadProfile(projectConfig, projectDir, profileName) {
|
|
|
38
38
|
uiLogger.error(lib.projectProfiles.loadProfile.errors.missingAccountId(profileFilename));
|
|
39
39
|
return;
|
|
40
40
|
}
|
|
41
|
-
return
|
|
41
|
+
return {
|
|
42
|
+
...profile,
|
|
43
|
+
accountId: process.env.HUBSPOT_ACCOUNT_ID
|
|
44
|
+
? Number(process.env.HUBSPOT_ACCOUNT_ID)
|
|
45
|
+
: profile.accountId,
|
|
46
|
+
};
|
|
42
47
|
}
|
|
43
48
|
catch (e) {
|
|
44
49
|
uiLogger.error(lib.projectProfiles.loadProfile.errors.failedToLoadProfile(profileFilename));
|
|
@@ -54,7 +59,7 @@ export async function exitIfUsingProfiles(projectConfig, projectDir) {
|
|
|
54
59
|
}
|
|
55
60
|
}
|
|
56
61
|
}
|
|
57
|
-
export async function loadAndValidateProfile(projectConfig, projectDir, argsProfile) {
|
|
62
|
+
export async function loadAndValidateProfile(projectConfig, projectDir, argsProfile, useEnv = false) {
|
|
58
63
|
if (argsProfile) {
|
|
59
64
|
logProfileHeader(argsProfile);
|
|
60
65
|
const profile = loadProfile(projectConfig, projectDir, argsProfile);
|
|
@@ -63,6 +68,9 @@ export async function loadAndValidateProfile(projectConfig, projectDir, argsProf
|
|
|
63
68
|
process.exit(EXIT_CODES.ERROR);
|
|
64
69
|
}
|
|
65
70
|
logProfileFooter(profile, true);
|
|
71
|
+
if (useEnv) {
|
|
72
|
+
return Number(process.env.HUBSPOT_ACCOUNT_ID);
|
|
73
|
+
}
|
|
66
74
|
return profile.accountId;
|
|
67
75
|
}
|
|
68
76
|
else {
|
|
@@ -100,6 +100,7 @@ describe('AppDevModeInterface', () => {
|
|
|
100
100
|
setAppDataForUid: vi.fn(),
|
|
101
101
|
addListener: vi.fn(),
|
|
102
102
|
addUploadWarning: vi.fn(),
|
|
103
|
+
removeListener: vi.fn(),
|
|
103
104
|
};
|
|
104
105
|
mockLocalDevLogger = {};
|
|
105
106
|
// Mock constructors
|
|
@@ -387,6 +388,15 @@ describe('AppDevModeInterface', () => {
|
|
|
387
388
|
await appDevModeInterface.cleanup();
|
|
388
389
|
expect(UIEDevModeInterface.cleanup).toHaveBeenCalled();
|
|
389
390
|
});
|
|
391
|
+
it('should remove state listeners', async () => {
|
|
392
|
+
await appDevModeInterface.cleanup();
|
|
393
|
+
expect(mockLocalDevState.removeListener).toHaveBeenCalledWith('devServerMessage',
|
|
394
|
+
// @ts-expect-error access private method for testing
|
|
395
|
+
appDevModeInterface.onDevServerMessage);
|
|
396
|
+
expect(mockLocalDevState.removeListener).toHaveBeenCalledWith('projectNodes',
|
|
397
|
+
// @ts-expect-error
|
|
398
|
+
appDevModeInterface.onChangeProjectNodes);
|
|
399
|
+
});
|
|
390
400
|
});
|
|
391
401
|
describe('isAutomaticallyInstallable()', () => {
|
|
392
402
|
it('should return true for static auth app on test account with correct parent', () => {
|
|
@@ -11,6 +11,7 @@ declare class AppDevModeInterface {
|
|
|
11
11
|
_appNode?: AppIRNode | null;
|
|
12
12
|
marketplaceAppInstalls?: number;
|
|
13
13
|
constructor(options: AppDevModeInterfaceConstructorOptions);
|
|
14
|
+
private getAppNodeFromProjectNodes;
|
|
14
15
|
private get appNode();
|
|
15
16
|
private get appData();
|
|
16
17
|
private set appData(value);
|
|
@@ -21,7 +22,10 @@ declare class AppDevModeInterface {
|
|
|
21
22
|
private autoInstallStaticAuthApp;
|
|
22
23
|
private installAppOrOpenInstallUrl;
|
|
23
24
|
private checkTestAccountAppInstallation;
|
|
24
|
-
private
|
|
25
|
+
private onDevServerMessage;
|
|
26
|
+
private onChangeProjectNodes;
|
|
27
|
+
private setUpStateListeners;
|
|
28
|
+
private removeStateListeners;
|
|
25
29
|
setup(args: any): Promise<void>;
|
|
26
30
|
start(): Promise<void>;
|
|
27
31
|
fileChange(filePath: string, event: string): Promise<void>;
|
|
@@ -30,12 +30,13 @@ class AppDevModeInterface {
|
|
|
30
30
|
process.exit(EXIT_CODES.ERROR);
|
|
31
31
|
}
|
|
32
32
|
}
|
|
33
|
+
getAppNodeFromProjectNodes(projectNodes) {
|
|
34
|
+
return Object.values(projectNodes).find(isAppIRNode) || null;
|
|
35
|
+
}
|
|
33
36
|
// Assumes only one app per project
|
|
34
37
|
get appNode() {
|
|
35
38
|
if (this._appNode === undefined) {
|
|
36
|
-
this._appNode =
|
|
37
|
-
Object.values(this.localDevState.projectNodes).find(isAppIRNode) ||
|
|
38
|
-
null;
|
|
39
|
+
this._appNode = this.getAppNodeFromProjectNodes(this.localDevState.projectNodes);
|
|
39
40
|
}
|
|
40
41
|
return this._appNode;
|
|
41
42
|
}
|
|
@@ -175,12 +176,33 @@ class AppDevModeInterface {
|
|
|
175
176
|
}
|
|
176
177
|
return { needsInstall: !isInstalledWithScopeGroups, isReinstall };
|
|
177
178
|
}
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
179
|
+
onDevServerMessage = (message) => {
|
|
180
|
+
if (message === LOCAL_DEV_SERVER_MESSAGE_TYPES.WEBSOCKET_SERVER_CONNECTED) {
|
|
181
|
+
this.checkTestAccountAppInstallation();
|
|
182
|
+
}
|
|
183
|
+
};
|
|
184
|
+
onChangeProjectNodes = (nodes) => {
|
|
185
|
+
const newAppNode = this.getAppNodeFromProjectNodes(nodes);
|
|
186
|
+
const oldDistribution = this.appNode?.config.distribution;
|
|
187
|
+
const newDistribution = newAppNode?.config.distribution;
|
|
188
|
+
const oldAuthType = this.appNode?.config.auth.type;
|
|
189
|
+
const newAuthType = newAppNode?.config.auth.type;
|
|
190
|
+
if (newDistribution === APP_DISTRIBUTION_TYPES.MARKETPLACE &&
|
|
191
|
+
oldDistribution !== APP_DISTRIBUTION_TYPES.MARKETPLACE) {
|
|
192
|
+
this.localDevState.addUploadWarning(lib.AppDevModeInterface.distributionChanged);
|
|
193
|
+
}
|
|
194
|
+
else if (newAuthType === APP_AUTH_TYPES.OAUTH &&
|
|
195
|
+
oldAuthType !== APP_AUTH_TYPES.OAUTH) {
|
|
196
|
+
this.localDevState.addUploadWarning(lib.AppDevModeInterface.authTypeChanged);
|
|
197
|
+
}
|
|
198
|
+
};
|
|
199
|
+
setUpStateListeners() {
|
|
200
|
+
this.localDevState.addListener('devServerMessage', this.onDevServerMessage);
|
|
201
|
+
this.localDevState.addListener('projectNodes', this.onChangeProjectNodes);
|
|
202
|
+
}
|
|
203
|
+
removeStateListeners() {
|
|
204
|
+
this.localDevState.removeListener('devServerMessage', this.onDevServerMessage);
|
|
205
|
+
this.localDevState.removeListener('projectNodes', this.onChangeProjectNodes);
|
|
184
206
|
}
|
|
185
207
|
// @ts-expect-error TODO: reconcile types between CLI and UIE Dev Server
|
|
186
208
|
// In the future, update UIE Dev Server to use LocalDevState
|
|
@@ -215,7 +237,7 @@ class AppDevModeInterface {
|
|
|
215
237
|
catch (e) {
|
|
216
238
|
logError(e);
|
|
217
239
|
}
|
|
218
|
-
this.
|
|
240
|
+
this.setUpStateListeners();
|
|
219
241
|
return UIEDevModeInterface.setup(args);
|
|
220
242
|
}
|
|
221
243
|
async start() {
|
|
@@ -239,6 +261,7 @@ class AppDevModeInterface {
|
|
|
239
261
|
if (!this.appNode) {
|
|
240
262
|
return;
|
|
241
263
|
}
|
|
264
|
+
this.removeStateListeners();
|
|
242
265
|
return UIEDevModeInterface.cleanup();
|
|
243
266
|
}
|
|
244
267
|
}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { ComponentTypes, Component, GenericComponentConfig, PublicAppComponentConfig, PrivateAppComponentConfig, AppCardComponentConfig } from '../../types/Projects.js';
|
|
2
|
-
import { IntermediateRepresentationNodeLocalDev } from '@hubspot/project-parsing-lib/src/lib/types.js';
|
|
2
|
+
import { IntermediateRepresentationNode, IntermediateRepresentationNodeLocalDev } from '@hubspot/project-parsing-lib/src/lib/types.js';
|
|
3
3
|
import { AppIRNode } from '../../types/ProjectComponents.js';
|
|
4
4
|
export declare const CONFIG_FILES: {
|
|
5
5
|
[k in ComponentTypes]: string;
|
|
@@ -15,4 +15,4 @@ export declare function getProjectComponentTypes(components: Array<Component>):
|
|
|
15
15
|
export declare function getComponentUid(component?: Component | null): string | null;
|
|
16
16
|
export declare function componentIsApp(component?: Component | null): component is Component<PublicAppComponentConfig | PrivateAppComponentConfig>;
|
|
17
17
|
export declare function componentIsPublicApp(component?: Component | null): component is Component<PublicAppComponentConfig>;
|
|
18
|
-
export declare function isAppIRNode(component: IntermediateRepresentationNodeLocalDev): component is AppIRNode;
|
|
18
|
+
export declare function isAppIRNode(component: IntermediateRepresentationNodeLocalDev | IntermediateRepresentationNode): component is AppIRNode;
|
package/lib/projects/upload.d.ts
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { FileResult } from 'tmp';
|
|
2
|
+
import { IntermediateRepresentation } from '@hubspot/project-parsing-lib';
|
|
2
3
|
import { ProjectConfig } from '../../types/Projects.js';
|
|
3
4
|
type ProjectUploadCallbackFunction<T> = (accountId: number, projectConfig: ProjectConfig, tempFile: FileResult, buildId: number) => Promise<T>;
|
|
4
5
|
type ProjectUploadResult<T> = {
|
|
@@ -20,5 +21,5 @@ type HandleProjectUploadArg<T> = {
|
|
|
20
21
|
export declare function handleProjectUpload<T>({ accountId, projectConfig, projectDir, callbackFunc, profile, uploadMessage, forceCreate, isUploadCommand, sendIR, skipValidation, }: HandleProjectUploadArg<T>): Promise<ProjectUploadResult<T>>;
|
|
21
22
|
export declare function validateSourceDirectory(srcDir: string, projectConfig: ProjectConfig): void;
|
|
22
23
|
export declare function validateNoHSMetaMismatch(srcDir: string, projectConfig: ProjectConfig): Promise<void>;
|
|
23
|
-
export declare function handleTranslate(projectDir: string, projectConfig: ProjectConfig, accountId: number, skipValidation: boolean, profile: string | undefined): Promise<
|
|
24
|
+
export declare function handleTranslate(projectDir: string, projectConfig: ProjectConfig, accountId: number, skipValidation: boolean, profile: string | undefined): Promise<IntermediateRepresentation | undefined>;
|
|
24
25
|
export {};
|
package/lib/projects/upload.js
CHANGED
package/package.json
CHANGED