Skip to content

Commit

Permalink
Merge branch 'main' of github.com:HubSpot/hubspot-cli into jy/fix-gh-…
Browse files Browse the repository at this point in the history
…rate-limit
  • Loading branch information
joe-yeager committed Jan 13, 2025
2 parents 479f254 + 196917c commit f55e7d7
Show file tree
Hide file tree
Showing 13 changed files with 238 additions and 341 deletions.
3 changes: 2 additions & 1 deletion acceptance-tests/tests/workflows/secretsFlow.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,8 @@ async function waitForSecretsListToContainSecret(testState: TestState) {
.toContain(SECRET.name);
}

describe('Secrets Flow', () => {
// TODO: Re-enable when the caching issue is resolved on the BE
describe.skip('Secrets Flow', () => {
let testState: TestState;

beforeAll(async () => {
Expand Down
43 changes: 37 additions & 6 deletions bin/cli.js
Original file line number Diff line number Diff line change
Expand Up @@ -8,16 +8,13 @@ const { logger } = require('@hubspot/local-dev-lib/logger');
const { addUserAgentHeader } = require('@hubspot/local-dev-lib/http');
const {
loadConfig,
getAccountId,
configFileExists,
getConfigPath,
validateConfig,
} = require('@hubspot/local-dev-lib/config');
const { logError } = require('../lib/errorHandlers/index');
const {
setLogLevel,
getCommandName,
injectAccountIdMiddleware,
} = require('../lib/commonOpts');
const { setLogLevel, getCommandName } = require('../lib/commonOpts');
const { validateAccount } = require('../lib/validation');
const {
trackHelpUsage,
Expand All @@ -27,7 +24,7 @@ const { getIsInProject } = require('../lib/projects');
const pkg = require('../package.json');
const { i18n } = require('../lib/lang');
const { EXIT_CODES } = require('../lib/enums/exitCodes');
const { UI_COLORS, uiCommandReference } = require('../lib/ui');
const { UI_COLORS, uiCommandReference, uiDeprecatedTag } = require('../lib/ui');
const { checkAndWarnGitInclusion } = require('../lib/ui/git');

const removeCommand = require('../commands/remove');
Expand Down Expand Up @@ -176,6 +173,39 @@ const SKIP_CONFIG_VALIDATION = {
auth: { target: true },
};

const handleDeprecatedEnvVariables = options => {
// HUBSPOT_PORTAL_ID is deprecated, but we'll still support it for now
// The HubSpot GH Deploy Action still uses HUBSPOT_PORTAL_ID
if (
options.useEnv &&
process.env.HUBSPOT_PORTAL_ID &&
!process.env.HUBSPOT_ACCOUNT_ID
) {
uiDeprecatedTag(
i18n(`${i18nKey}.handleDeprecatedEnvVariables.portalEnvVarDeprecated`, {
configPath: getConfigPath(),
})
);
process.env.HUBSPOT_ACCOUNT_ID = process.env.HUBSPOT_PORTAL_ID;
}
};

/**
* Auto-injects the derivedAccountId flag into all commands
*/
const injectAccountIdMiddleware = async options => {
const { account } = options;

// Preserves the original --account flag for certain commands.
options.providedAccountId = account;

if (options.useEnv && process.env.HUBSPOT_ACCOUNT_ID) {
options.derivedAccountId = parseInt(process.env.HUBSPOT_ACCOUNT_ID, 10);
} else {
options.derivedAccountId = getAccountId(account);
}
};

const loadConfigMiddleware = async options => {
// Skip this when no command is provided
if (!options._.length) {
Expand Down Expand Up @@ -261,6 +291,7 @@ const argv = yargs
.middleware([
setLogLevel,
setRequestHeaders,
handleDeprecatedEnvVariables,
loadConfigMiddleware,
injectAccountIdMiddleware,
checkAndWarnGitInclusionMiddleware,
Expand Down
17 changes: 6 additions & 11 deletions commands/sandbox/create.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,9 +10,9 @@ const { EXIT_CODES } = require('../../lib/enums/exitCodes');
const { getAccountConfig, getEnv } = require('@hubspot/local-dev-lib/config');
const { uiFeatureHighlight, uiBetaTag } = require('../../lib/ui');
const {
sandboxTypeMap,
SANDBOX_TYPE_MAP,
getAvailableSyncTypes,
syncTypes,
SYNC_TYPES,
validateSandboxUsageLimits,
} = require('../../lib/sandboxes');
const { getValidEnv } = require('@hubspot/local-dev-lib/environment');
Expand Down Expand Up @@ -65,7 +65,7 @@ exports.handler = async options => {
let typePrompt;
let namePrompt;

if ((type && !sandboxTypeMap[type.toLowerCase()]) || !type) {
if ((type && !SANDBOX_TYPE_MAP[type.toLowerCase()]) || !type) {
if (!force) {
typePrompt = await sandboxTypePrompt();
} else {
Expand All @@ -74,7 +74,7 @@ exports.handler = async options => {
}
}
const sandboxType = type
? sandboxTypeMap[type.toLowerCase()]
? SANDBOX_TYPE_MAP[type.toLowerCase()]
: typePrompt.type;

// Check usage limits and exit if parent portal has no available sandboxes for the selected type
Expand Down Expand Up @@ -141,12 +141,7 @@ exports.handler = async options => {
const sandboxAccountConfig = getAccountConfig(result.sandbox.sandboxHubId);
// For v1 sandboxes, keep sync here. Once we migrate to v2, this will be handled by BE automatically
const handleSyncSandbox = async syncTasks => {
await syncSandbox({
accountConfig: sandboxAccountConfig,
parentAccountConfig: accountConfig,
env,
syncTasks,
});
await syncSandbox(sandboxAccountConfig, accountConfig, env, syncTasks);
};
try {
let availableSyncTasks = await getAvailableSyncTypes(
Expand All @@ -156,7 +151,7 @@ exports.handler = async options => {

if (!contactRecordsSyncPromptResult) {
availableSyncTasks = availableSyncTasks.filter(
t => t.type !== syncTypes.OBJECT_RECORDS
t => t.type !== SYNC_TYPES.OBJECT_RECORDS
);
}
await handleSyncSandbox(availableSyncTasks);
Expand Down
4 changes: 4 additions & 0 deletions lang/en.lyaml
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,8 @@ en:
cliUpdateNotification: "HubSpot CLI version {{#cyan}}{{#bold}}{currentVersion}{{/bold}}{{/cyan}} is outdated.\nRun {{ updateCommand }} to upgrade to version {{#cyan}}{{#bold}}{latestVersion}{{/bold}}{{/cyan}}"
srcIsProject: "\"{{ src }}\" is in a project folder. Did you mean \"hs project {{command}}\"?"
setDefaultAccountMoved: "This command has moved. Try `hs accounts use` instead"
handleDeprecatedEnvVariables:
portalEnvVarDeprecated: "The HUBSPOT_PORTAL_ID environment variable is deprecated. Please use HUBSPOT_ACCOUNT_ID instead."
loadConfigMiddleware:
configFileExists: "A configuration file already exists at {{ configPath }}. To specify a new configuration file, delete the existing one and try again."
completion:
Expand Down Expand Up @@ -1418,6 +1420,7 @@ en:
failure:
invalidUser: "Couldn't create {{#bold}}{{ accountName }}{{/bold}} because your account has been removed from {{#bold}}{{ parentAccountName }}{{/bold}} or your permission set doesn't allow you to create the sandbox. To update your permissions, contact a super admin in {{#bold}}{{ parentAccountName }}{{/bold}}."
403Gating: "Couldn't create {{#bold}}{{ accountName }}{{/bold}} because {{#bold}}{{ parentAccountName }}{{/bold}} does not have access to development sandboxes. To opt in to the CRM Development Beta and use development sandboxes, visit https://app.hubspot.com/l/product-updates/in-beta?update=13899236."
usageLimitsFetch: "Unable to fetch sandbox usage limits. Please try again."
limit:
developer:
one: "{{#bold}}{{ accountName }}{{/bold}} reached the limit of {{ limit }} development sandbox.
Expand Down Expand Up @@ -1477,6 +1480,7 @@ en:
syncInProgress: "Couldn't run the sync because there's another sync in progress. Wait for the current sync to finish and then try again. To check the sync status, visit the sync activity log: {{ url }}."
notSuperAdmin: "Couldn't run the sync because you are not a super admin in {{ account }}. Ask the account owner for super admin access to the sandbox."
objectNotFound: "Couldn't sync the sandbox because {{#bold}}{{ account }}{{/bold}} may have been deleted through the UI. Run {{#bold}}hs sandbox delete{{/bold}} to remove this account from the config. "
syncTypeFetch: "Unable to fetch available sandbox sync types. Please try again."
errorHandlers:
index:
errorOccurred: "Error: {{ error }}"
Expand Down
9 changes: 6 additions & 3 deletions lib/buildAccount.ts
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,10 @@ const {
HUBSPOT_ACCOUNT_TYPES,
} = require('@hubspot/local-dev-lib/constants/config');
const { createSandbox } = require('@hubspot/local-dev-lib/api/sandboxHubs');
const { sandboxApiTypeMap, handleSandboxCreateError } = require('./sandboxes');
const {
SANDBOX_API_TYPE_MAP,
handleSandboxCreateError,
} = require('./sandboxes');
const {
handleDeveloperTestAccountCreateError,
} = require('./developerTestAccounts');
Expand Down Expand Up @@ -132,7 +135,7 @@ async function buildNewAccount({
let resultAccountId;
try {
if (isSandbox) {
const sandboxApiType = sandboxApiTypeMap[accountType]; // API expects sandbox type as 1 or 2.
const sandboxApiType = SANDBOX_API_TYPE_MAP[accountType]; // API expects sandbox type as 1 or 2.

const { data } = await createSandbox(accountId, name, sandboxApiType);
result = { name, ...data };
Expand All @@ -159,7 +162,7 @@ async function buildNewAccount({
});

if (isSandbox) {
handleSandboxCreateError({ err, env, accountConfig, name, accountId });
handleSandboxCreateError(err, env, name, accountId);
}
if (isDeveloperTestAccount) {
handleDeveloperTestAccountCreateError(err, env, accountId, portalLimit);
Expand Down
22 changes: 0 additions & 22 deletions lib/commonOpts.ts
Original file line number Diff line number Diff line change
Expand Up @@ -117,28 +117,6 @@ export function getAccountId(
return getAccountIdFromConfig(account);
}

/**
* Auto-injects the derivedAccountId flag into all commands
*/
export async function injectAccountIdMiddleware(
options: Arguments<{
derivedAccountId?: number | null;
account?: number | string;
}>
): Promise<void> {
const { account } = options;

// Preserves the original --account flag for certain commands.
options.providedAccountId = account;

if (options.useEnv && process.env.HUBSPOT_ACCOUNT_ID) {
options.derivedAccountId = parseInt(process.env.HUBSPOT_ACCOUNT_ID, 10);
return;
}

options.derivedAccountId = getAccountIdFromConfig(account);
}

export function getCmsPublishMode(
options: Arguments<{ cmsPublishMode?: CmsPublishMode }>
): CmsPublishMode {
Expand Down
64 changes: 33 additions & 31 deletions lib/dependencyManagement.ts
Original file line number Diff line number Diff line change
@@ -1,21 +1,20 @@
// @ts-nocheck
const { logger } = require('@hubspot/local-dev-lib/logger');
const { getProjectConfig } = require('./projects');
const { exec: execAsync } = require('child_process');
const { walk } = require('@hubspot/local-dev-lib/fs');
const path = require('path');
const { uiLink } = require('./ui');
const util = require('util');
const { i18n } = require('./lang');
const SpinniesManager = require('./ui/SpinniesManager');
const fs = require('fs');
const pkg = require('../package.json');
const DEFAULT_PACKAGE_MANAGER = 'npm';
import { logger } from '@hubspot/local-dev-lib/logger';
import { getProjectConfig } from './projects';
import { exec as execAsync } from 'child_process';
import { walk } from '@hubspot/local-dev-lib/fs';
import path from 'path';
import { uiLink } from './ui';
import util from 'util';
import { i18n } from './lang';
import SpinniesManager from './ui/SpinniesManager';
import fs from 'fs';
import pkg from '../package.json';

const DEFAULT_PACKAGE_MANAGER = 'npm';
const i18nKey = `commands.project.subcommands.installDeps`;

class NoPackageJsonFilesError extends Error {
constructor(projectName) {
constructor(projectName: string) {
super(
i18n(`${i18nKey}.noPackageJsonInProject`, {
projectName,
Expand All @@ -28,7 +27,7 @@ class NoPackageJsonFilesError extends Error {
}
}

export async function isGloballyInstalled(command) {
export async function isGloballyInstalled(command: string): Promise<boolean> {
const exec = util.promisify(execAsync);
try {
await exec(`${command} --version`);
Expand All @@ -38,24 +37,36 @@ export async function isGloballyInstalled(command) {
}
}

export async function getLatestCliVersion(): { latest: string; next: string } {
export async function getLatestCliVersion(): Promise<{
latest: string;
next: string;
}> {
const exec = util.promisify(execAsync);
const { stdout } = await exec(`npm info ${pkg.name} dist-tags --json`);
const { latest, next } = JSON.parse(stdout);
return { latest, next };
}

async function installPackages({ packages, installLocations }) {
export async function installPackages({
packages,
installLocations,
}: {
packages?: string[];
installLocations?: string[];
}): Promise<void> {
const installDirs =
installLocations || (await getProjectPackageJsonLocations());
await Promise.all(
installDirs.map(async dir => {
await installPackagesInDirectory(packages, dir);
await installPackagesInDirectory(dir, packages);
})
);
}

async function installPackagesInDirectory(packages, directory) {
async function installPackagesInDirectory(
directory: string,
packages?: string[]
): Promise<void> {
const spinner = `installingDependencies-${directory}`;
const relativeDir = path.relative(process.cwd(), directory);
SpinniesManager.init();
Expand Down Expand Up @@ -102,7 +113,7 @@ async function installPackagesInDirectory(packages, directory) {
}
}

async function getProjectPackageJsonLocations() {
export async function getProjectPackageJsonLocations(): Promise<string[]> {
const projectConfig = await getProjectConfig();

if (
Expand Down Expand Up @@ -148,7 +159,7 @@ async function getProjectPackageJsonLocations() {
throw new NoPackageJsonFilesError(name);
}

const packageParentDirs = [];
const packageParentDirs: string[] = [];
packageJsonFiles.forEach(packageJsonFile => {
const parentDir = path.dirname(packageJsonFile);
packageParentDirs.push(parentDir);
Expand All @@ -157,19 +168,10 @@ async function getProjectPackageJsonLocations() {
return packageParentDirs;
}

export async function hasMissingPackages(directory) {
export async function hasMissingPackages(directory: string): Promise<boolean> {
const exec = util.promisify(execAsync);
const { stdout } = await exec(`npm install --ignore-scripts --dry-run`, {
cwd: directory,
});
return !stdout?.includes('up to date in');
}

module.exports = {
isGloballyInstalled,
installPackages,
DEFAULT_PACKAGE_MANAGER,
getProjectPackageJsonLocations,
getLatestCliVersion,
hasMissingPackages,
};
10 changes: 5 additions & 5 deletions lib/localDev.ts
Original file line number Diff line number Diff line change
Expand Up @@ -223,13 +223,13 @@ const createSandboxForLocalDev = async (accountId, accountConfig, env) => {
sandboxAccountConfig
);
// For v1 sandboxes, keep sync here. Once we migrate to v2, this will be handled by BE automatically
await syncSandbox({
accountConfig: sandboxAccountConfig,
parentAccountConfig: accountConfig,
await syncSandbox(
sandboxAccountConfig,
accountConfig,
env,
syncTasks,
slimInfoMessage: true,
});
true
);
return targetAccountId;
} catch (err) {
logError(err);
Expand Down
Loading

0 comments on commit f55e7d7

Please sign in to comment.