Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 3 additions & 10 deletions commands/app/migrate.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { logger } from '@hubspot/local-dev-lib/logger';
import { getAccountConfig } from '@hubspot/local-dev-lib/config';
import { getConfigAccountById } from '@hubspot/local-dev-lib/config';
import { PLATFORM_VERSIONS } from '@hubspot/local-dev-lib/constants/projects';
import { ArgumentsCamelCase, Argv, CommandModule } from 'yargs';

Expand Down Expand Up @@ -29,14 +29,7 @@ const describe = undefined; // uiBetaTag(i18n(`commands.project.subcommands.migr
export async function handler(options: ArgumentsCamelCase<MigrateAppOptions>) {
const { derivedAccountId, platformVersion } = options;
await trackCommandUsage('migrate-app', {}, derivedAccountId);
const accountConfig = getAccountConfig(derivedAccountId);

if (!accountConfig) {
logger.error(
i18n(`commands.project.subcommands.migrateApp.errors.noAccountConfig`)
);
return process.exit(EXIT_CODES.ERROR);
}
const account = getConfigAccountById(derivedAccountId);

logger.log('');
logger.log(
Expand All @@ -57,7 +50,7 @@ export async function handler(options: ArgumentsCamelCase<MigrateAppOptions>) {
if (platformVersion === v2025_2 || platformVersion === unstable) {
await migrateApp2025_2(derivedAccountId, options);
} else {
await migrateApp2023_2(derivedAccountId, options, accountConfig);
await migrateApp2023_2(derivedAccountId, options, account);
}
} catch (error) {
if (
Expand Down
94 changes: 37 additions & 57 deletions commands/auth.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,19 +7,20 @@ import {
} from '@hubspot/local-dev-lib/constants/auth';
import { ENVIRONMENTS } from '@hubspot/local-dev-lib/constants/environments';
import { DEFAULT_HUBSPOT_CONFIG_YAML_FILE_NAME } from '@hubspot/local-dev-lib/constants/config';
import { AccessToken, CLIAccount } from '@hubspot/local-dev-lib/types/Accounts';
import {
AccessToken,
HubSpotConfigAccount,
} from '@hubspot/local-dev-lib/types/Accounts';
import { i18n } from '../lib/lang';
import {
getAccessToken,
updateConfigWithAccessToken,
} from '@hubspot/local-dev-lib/personalAccessKey';
import {
updateAccountConfig,
writeConfig,
getConfigPath,
loadConfig,
getConfigDefaultAccount,
getAccountId,
updateConfigAccount,
getConfigFilePath,
getConfigDefaultAccountIfExists,
getConfigAccountByName,
} from '@hubspot/local-dev-lib/config';
import { commaSeparatedValues, toKebabCase } from '@hubspot/local-dev-lib/text';
import { promptUser } from '../lib/prompts/promptUtils';
Expand Down Expand Up @@ -73,89 +74,63 @@ type AuthArgs = CommonArgs &
export async function handler(
args: ArgumentsCamelCase<AuthArgs>
): Promise<void> {
const {
authType: authTypeFlagValue,
config: configFlagValue,
qa,
providedAccountId,
} = args;
const { authType: authTypeFlagValue, qa, providedAccountId } = args;
const authType =
(authTypeFlagValue && authTypeFlagValue.toLowerCase()) ||
PERSONAL_ACCESS_KEY_AUTH_METHOD.value;
setLogLevel(args);

const env = qa ? ENVIRONMENTS.QA : ENVIRONMENTS.PROD;
// Needed to load deprecated config
loadConfig(configFlagValue!);
const configPath = getConfigPath();
if (configPath) {
checkAndWarnGitInclusion(configPath);
}

if (!getConfigPath(configFlagValue)) {
logger.error(i18n(`${i18nKey}.errors.noConfigFileFound`));
process.exit(EXIT_CODES.ERROR);
}
const configPath = getConfigFilePath();
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No longer need to manually error - getConfigFilePath will throw if there's no config

checkAndWarnGitInclusion(configPath);

trackCommandUsage('auth');
trackAuthAction('auth', authType, TRACKING_STATUS.STARTED, providedAccountId);

let configData:
| OauthPromptResponse
| PersonalAccessKeyPromptResponse
| undefined;
let updatedConfig: CLIAccount | null | undefined;
let validName: string | undefined;
let accountPromptData: OauthPromptResponse | PersonalAccessKeyPromptResponse;
let updatedAccount: HubSpotConfigAccount | undefined;
let successAuthMethod: string | undefined;
let token: AccessToken | undefined;
let defaultName: string | undefined;

switch (authType) {
case OAUTH_AUTH_METHOD.value:
configData = await promptUser<OauthPromptResponse>(OAUTH_FLOW);
await authenticateWithOauth({
...configData,
env,
});
accountPromptData = await promptUser<OauthPromptResponse>(OAUTH_FLOW);
await authenticateWithOauth(accountPromptData, env);
successAuthMethod = OAUTH_AUTH_METHOD.name;
break;
case PERSONAL_ACCESS_KEY_AUTH_METHOD.value:
configData = await personalAccessKeyPrompt({
accountPromptData = await personalAccessKeyPrompt({
env,
account: providedAccountId,
});

try {
token = await getAccessToken(configData.personalAccessKey, env);
token = await getAccessToken(accountPromptData.personalAccessKey, env);
defaultName = toKebabCase(token.hubName);

updatedConfig = await updateConfigWithAccessToken(
updatedAccount = await updateConfigWithAccessToken(
token,
configData.personalAccessKey,
accountPromptData.personalAccessKey,
env
);
} catch (e) {
logError(e);
}

if (!updatedConfig) {
if (!updatedAccount) {
break;
}

validName = updatedConfig.name;

if (!validName) {
if (!updatedAccount.name) {
const { name: namePrompt } = await cliAccountNamePrompt(defaultName);
validName = namePrompt;
updatedAccount.name = namePrompt;
}

updateAccountConfig({
...updatedConfig,
env: updatedConfig.env,
tokenInfo: updatedConfig.auth!.tokenInfo,
name: validName,
updateConfigAccount({
...updatedAccount,
});
writeConfig();

successAuthMethod = PERSONAL_ACCESS_KEY_AUTH_METHOD.name;
break;
Expand All @@ -166,7 +141,7 @@ export async function handler(
type: authType,
})
);
break;
process.exit(EXIT_CODES.ERROR);
}

if (!successAuthMethod) {
Expand All @@ -179,11 +154,11 @@ export async function handler(
process.exit(EXIT_CODES.ERROR);
}

const nameFromConfigData =
'name' in configData! ? configData!.name : undefined;
const nameFromPromptData =
'name' in accountPromptData ? accountPromptData.name : undefined;

const accountName =
(updatedConfig && updatedConfig.name) || validName || nameFromConfigData!;
// Name will always exist on either the updatedAccount or prompt response
const accountName = (updatedAccount?.name || nameFromPromptData)!;

const setAsDefault = await setAsDefaultAccountPrompt(accountName);

Expand All @@ -197,7 +172,7 @@ export async function handler(
} else {
logger.info(
i18n(`lib.prompts.setAsDefaultAccountPrompt.keepingCurrentDefault`, {
accountName: getConfigDefaultAccount()!,
accountName: getConfigDefaultAccountIfExists()?.name || '',
})
);
}
Expand All @@ -214,8 +189,13 @@ export async function handler(
'accountsListCommand',
]);

const accountId = getAccountId(accountName) || undefined;
await trackAuthAction('auth', authType, TRACKING_STATUS.COMPLETE, accountId);
const account = getConfigAccountByName(accountName);
await trackAuthAction(
'auth',
authType,
TRACKING_STATUS.COMPLETE,
account.accountId
);

process.exit(EXIT_CODES.SUCCESS);
}
Expand Down
66 changes: 26 additions & 40 deletions commands/init.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,12 +2,10 @@ import path from 'path';
import { ArgumentsCamelCase, Argv } from 'yargs';
import fs from 'fs-extra';
import {
getConfigPath,
getConfigFilePath,
createEmptyConfigFile,
deleteEmptyConfigFile,
updateDefaultAccount,
loadConfig,
configFileExists,
deleteConfigFile,
setConfigAccountAsDefault,
} from '@hubspot/local-dev-lib/config';
import { Environment } from '@hubspot/local-dev-lib/types/Config';
import {
Expand All @@ -23,13 +21,11 @@ import {
import { getCwd } from '@hubspot/local-dev-lib/path';
import { toKebabCase } from '@hubspot/local-dev-lib/text';
import {
CLIAccount,
OAuth2ManagerAccountConfig,
OAuthConfigAccount,
PersonalAccessKeyConfigAccount,
} from '@hubspot/local-dev-lib/types/Accounts';
import { ENVIRONMENTS } from '@hubspot/local-dev-lib/constants/environments';
import { logger } from '@hubspot/local-dev-lib/logger';
import { getAccountIdentifier } from '@hubspot/local-dev-lib/config/getAccountIdentifier';
import { CLIOptions } from '@hubspot/local-dev-lib/types/CLIOptions';
import { setLogLevel } from '../lib/commonOpts';
import { makeYargsBuilder } from '../lib/yargsUtils';
import { handleExit } from '../lib/process';
Expand Down Expand Up @@ -64,9 +60,9 @@ const TRACKING_STATUS = {
async function personalAccessKeyConfigCreationFlow(
env: Environment,
account?: number
): Promise<CLIAccount | null> {
): Promise<PersonalAccessKeyConfigAccount | undefined> {
const { personalAccessKey } = await personalAccessKeyPrompt({ env, account });
let updatedConfig: CLIAccount | null;
let updatedConfig: PersonalAccessKeyConfigAccount | undefined;

try {
const token = await getAccessToken(personalAccessKey, env);
Expand All @@ -84,20 +80,16 @@ async function personalAccessKeyConfigCreationFlow(
logError(e);
}

return updatedConfig!;
return updatedConfig;
}

async function oauthConfigCreationFlow(
env: Environment
): Promise<OAuth2ManagerAccountConfig> {
const configData = await promptUser<OauthPromptResponse>(OAUTH_FLOW);
const accountConfig: OAuth2ManagerAccountConfig = {
...configData,
env,
};
await authenticateWithOauth(accountConfig);
updateDefaultAccount(accountConfig.name!);
return accountConfig;
): Promise<OAuthConfigAccount> {
const promptData = await promptUser<OauthPromptResponse>(OAUTH_FLOW);
const account = await authenticateWithOauth(promptData, env);
setConfigAccountAsDefault(account.name);
return account;
}

const AUTH_TYPE_NAMES = {
Expand Down Expand Up @@ -131,9 +123,14 @@ export async function handler(
(authTypeFlagValue && authTypeFlagValue.toLowerCase()) ||
PERSONAL_ACCESS_KEY_AUTH_METHOD.value;

const configPath =
(configFlagValue && path.join(getCwd(), configFlagValue)) ||
getConfigPath('', useHiddenConfig);
let configPath = configFlagValue && path.join(getCwd(), configFlagValue);

if (!configPath) {
try {
configPath = getConfigFilePath();
} catch (e) {}
}

setLogLevel(args);

if (!disableTracking) {
Expand All @@ -144,7 +141,7 @@ export async function handler(

const env = args.qa ? ENVIRONMENTS.QA : ENVIRONMENTS.PROD;

if (fs.existsSync(configPath!)) {
if (fs.existsSync(configPath || '')) {
logger.error(
i18n(`${i18nKey}.errors.configFileExists`, {
configPath: configPath!,
Expand All @@ -163,21 +160,10 @@ export async function handler(
);
}

const doesOtherConfigFileExist = configFileExists(!useHiddenConfig);
if (doesOtherConfigFileExist) {
const path = getConfigPath('', !useHiddenConfig);
logger.error(
i18n(`${i18nKey}.errors.bothConfigFilesNotAllowed`, { path: path! })
);
process.exit(EXIT_CODES.ERROR);
}

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Figured we could simplify this logic a bit. If any config file exists (which we check for above), exit regardless of whether its local or global. I updated the error message to make it make sense for both cases

trackAuthAction('init', authType, TRACKING_STATUS.STARTED, providedAccountId);
createEmptyConfigFile({ path: configPath! }, useHiddenConfig);
//Needed to load deprecated config
loadConfig(configPath!, args as CLIOptions);
createEmptyConfigFile(useHiddenConfig);

handleExit(deleteEmptyConfigFile);
handleExit(deleteConfigFile);

try {
let accountId: number;
Expand All @@ -188,12 +174,12 @@ export async function handler(
providedAccountId
);
if (personalAccessKeyResult) {
accountId = getAccountIdentifier(personalAccessKeyResult)!;
accountId = personalAccessKeyResult.accountId;
name = personalAccessKeyResult.name;
}
} else {
const oauthResult = await oauthConfigCreationFlow(env);
accountId = oauthResult.accountId!;
accountId = oauthResult.accountId;
name = oauthResult.name;
}

Expand Down
4 changes: 1 addition & 3 deletions lang/en.lyaml
Original file line number Diff line number Diff line change
Expand Up @@ -465,8 +465,7 @@ en:
logs:
updateConfig: "To update an existing config file, use the \"hs auth\" command."
errors:
configFileExists: "The config file {{ configPath }} already exists."
bothConfigFilesNotAllowed: "Unable to create config file, because there is an existing one at \"{{ path }}\". To create a new config file, delete the existing one and try again."
configFileExists: "Unable to create config file. A config file already exists at {{ configPath }}."
lint:
issuesFound: "{{ count }} issues found."
groupName: "Linting {{ path }}"
Expand Down Expand Up @@ -591,7 +590,6 @@ en:
componentsThatWillNotBeMigrated: "[NOTE] These component types are not yet supported for migration but will be available later: {{ components }}"
errors:
noAppsEligible: "No apps in account {{ accountId }} are currently migratable"
noAccountConfig: "There is no account associated with {{ accountId }} in the config file. Please choose another account and try again, or authenticate {{ accountId }} using {{ authCommand }}."
invalidAccountTypeTitle: "{{#bold}}Developer account not targeted{{/bold}}"
invalidAccountTypeDescription: "Only public apps created in a developer account can be converted to a project component. Select a connected developer account with {{useCommand}} or {{authCommand}} and try again."
projectAlreadyExists: "A project with name {{ projectName }} already exists. Please choose another name."
Expand Down
Loading
Loading