Skip to content

feat: Add migration for existing project #1424

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 75 commits into from
Apr 16, 2025
Merged
Show file tree
Hide file tree
Changes from 73 commits
Commits
Show all changes
75 commits
Select commit Hold shift + click to select a range
193c571
wip
joe-yeager Mar 12, 2025
307a833
wip
joe-yeager Mar 18, 2025
2d96fbb
Fake it
joe-yeager Mar 18, 2025
503b2c8
wip
joe-yeager Mar 19, 2025
99f343e
Refactor
joe-yeager Mar 21, 2025
b9e1611
Add additional reasons
joe-yeager Mar 21, 2025
175e36c
Review copy with Jono
joe-yeager Mar 26, 2025
e7350b4
test
joe-yeager Mar 27, 2025
0d20207
Integration tweaks
joe-yeager Mar 28, 2025
143ad49
v7.2.3-experimental.0
joe-yeager Mar 28, 2025
ac26a4b
Add logs displaying what will be migrated, prompt to proceed
joe-yeager Mar 31, 2025
009fe74
Remove duplicate tracking event, add shell for hs project migrate
joe-yeager Apr 1, 2025
84013dd
Merge branch 'main' of github.com:HubSpot/hubspot-cli into jy/migrati…
joe-yeager Apr 3, 2025
3f556b3
Clean up
joe-yeager Apr 3, 2025
78cb580
Clean up, add tests
joe-yeager Apr 3, 2025
79ee7c8
Implement polling
joe-yeager Apr 3, 2025
e4bee05
Add the bones for hs project migrate
joe-yeager Apr 3, 2025
54ca912
Merge branch 'main' of github.com:HubSpot/hubspot-cli into jy/migrati…
joe-yeager Apr 7, 2025
80e3001
Udpate after LDL release
joe-yeager Apr 7, 2025
87fd9d5
Add defaultAnswer for dest
joe-yeager Apr 7, 2025
39393cf
clean up
joe-yeager Apr 7, 2025
505df3d
Update LDL version
joe-yeager Apr 7, 2025
bbbed83
Fix wierd flag behavior
joe-yeager Apr 7, 2025
4907c78
Remove async and promise wrapper
joe-yeager Apr 7, 2025
8bb55a6
Merge branch 'main' of github.com:HubSpot/hubspot-cli into jy/migrati…
joe-yeager Apr 7, 2025
bfafd18
Feedback
joe-yeager Apr 7, 2025
9ce5273
feedback
joe-yeager Apr 7, 2025
3ac138b
Merge branch 'jy/migration-v2' of github.com:HubSpot/hubspot-cli into…
joe-yeager Apr 7, 2025
8d3af4f
Refactor, add more of the flow
joe-yeager Apr 7, 2025
8321037
wip
joe-yeager Apr 8, 2025
adfcc01
Get it working with new endpoints
joe-yeager Apr 8, 2025
91b557e
Use experimental release
joe-yeager Apr 8, 2025
cd6fe01
Remove console.logs
joe-yeager Apr 8, 2025
1e5b7c0
Merge branch 'main' of github.com:HubSpot/hubspot-cli into jy/migrati…
joe-yeager Apr 8, 2025
2fcbaa9
Fixes
joe-yeager Apr 8, 2025
d1b3243
Undo
joe-yeager Apr 8, 2025
110baf1
Inline i18nkey
joe-yeager Apr 8, 2025
2a11251
Merge branch 'jy/migration-v2' of github.com:HubSpot/hubspot-cli into…
joe-yeager Apr 8, 2025
210cb5f
clean up
joe-yeager Apr 9, 2025
223f554
PR feedback, move stuff around, add API calls to CLI
joe-yeager Apr 9, 2025
6daa0df
Fix bad import
joe-yeager Apr 9, 2025
8ac0831
Remove migration spinner
joe-yeager Apr 9, 2025
6bff187
Merge branch 'main' of github.com:HubSpot/hubspot-cli into jy/migrati…
joe-yeager Apr 9, 2025
9d60f24
Feedback from demo
joe-yeager Apr 9, 2025
04e54f6
fix bug with filtering apps
joe-yeager Apr 9, 2025
7a8f442
Merge branch 'jy/migration-v2' of github.com:HubSpot/hubspot-cli into…
joe-yeager Apr 10, 2025
eb40310
Changes after merge
joe-yeager Apr 10, 2025
e2d634c
Merge branch 'main' of github.com:HubSpot/hubspot-cli into jy/migrati…
joe-yeager Apr 10, 2025
61831b7
Merge branch 'jy/migration-v2' of github.com:HubSpot/hubspot-cli into…
joe-yeager Apr 10, 2025
7266130
fix existing tests
joe-yeager Apr 10, 2025
e645adb
wip
joe-yeager Apr 10, 2025
4c0996b
fix test
joe-yeager Apr 10, 2025
9c02037
Hide zip extraction logs
joe-yeager Apr 10, 2025
d538391
Merge branch 'jy/migration-v2' of github.com:HubSpot/hubspot-cli into…
joe-yeager Apr 10, 2025
25542fc
Merge branch 'main' of github.com:HubSpot/hubspot-cli into jy/migrate…
joe-yeager Apr 10, 2025
3dd7e93
Update copy
joe-yeager Apr 10, 2025
f0c5f3d
Tweaks after integration with BE.
joe-yeager Apr 11, 2025
2138f4c
Merge branch 'main' of github.com:HubSpot/hubspot-cli into jy/migrate…
joe-yeager Apr 11, 2025
309c64f
fix bugs with migration flow
joe-yeager Apr 11, 2025
b0591ee
Use en.js
joe-yeager Apr 11, 2025
e0e8123
Fix lang file errors, fix bug when not ran in project
joe-yeager Apr 14, 2025
0ed6cc3
Merge branch 'main' of github.com:HubSpot/hubspot-cli into jy/migrate…
joe-yeager Apr 14, 2025
7aa2be2
Merge branch 'main' of github.com:HubSpot/hubspot-cli into jy/migrate…
joe-yeager Apr 14, 2025
ed0586f
Merge branch 'main' of github.com:HubSpot/hubspot-cli into jy/migrate…
joe-yeager Apr 15, 2025
bae540f
Move types to command files
joe-yeager Apr 15, 2025
5f9a4fb
Add platform-version flag, default to 2025.2
joe-yeager Apr 15, 2025
0e14601
Add platform version flag that defaults to 2025.2 and fix a bug when …
joe-yeager Apr 15, 2025
170693b
Merge branch 'main' of github.com:HubSpot/hubspot-cli into jy/migrate…
joe-yeager Apr 15, 2025
153ad7e
PR feedback
joe-yeager Apr 15, 2025
037aa18
PR feedback
joe-yeager Apr 15, 2025
4176b02
Send platformVersion in list apps
joe-yeager Apr 15, 2025
6bd9bc3
Add debug logs
joe-yeager Apr 15, 2025
910b81f
Update test
joe-yeager Apr 15, 2025
f5a8e72
convert query param to enum format, update test
joe-yeager Apr 16, 2025
56e2aa7
Merge branch 'main' of github.com:HubSpot/hubspot-cli into jy/migrate…
joe-yeager Apr 16, 2025
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
10 changes: 8 additions & 2 deletions api/__tests__/migrate.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -51,10 +51,16 @@ describe('api/migrate', () => {
// @ts-expect-error Mock
httpMock.get.mockResolvedValue(mockResponse);

const result = await listAppsForMigration(mockAccountId);
const result = await listAppsForMigration(
mockAccountId,
mockPlatformVersion
);

expect(http.get).toHaveBeenCalledWith(mockAccountId, {
url: 'dfs/migrations/v2/list-apps',
params: {
platformVersion: mockPlatformVersion,
},
});
expect(result).toEqual(mockResponse);
});
Expand Down Expand Up @@ -171,7 +177,7 @@ describe('api/migrate', () => {
const mockResponse: MigrationStatus = {
id: mockMigrationId,
status: MIGRATION_STATUS.FAILURE,
projectErrorsDetail: 'Error details',
projectErrorDetail: 'Error details',
componentErrorDetails: {
'component-1': 'Component error',
},
Expand Down
27 changes: 23 additions & 4 deletions api/migrate.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,8 @@ import {
} from '@hubspot/local-dev-lib/constants/projects';
import { http } from '@hubspot/local-dev-lib/http';
import { MIGRATION_STATUS } from '@hubspot/local-dev-lib/types/Migration';
import { logger } from '@hubspot/local-dev-lib/logger';
import util from 'util';

const MIGRATIONS_API_PATH_V2 = 'dfs/migrations/v2';

Expand Down Expand Up @@ -72,7 +74,7 @@ export interface MigrationSuccess extends MigrationBaseStatus {

export interface MigrationFailed extends MigrationBaseStatus {
status: typeof MIGRATION_STATUS.FAILURE;
projectErrorsDetail?: string;
projectErrorDetail: string;
componentErrorDetails: Record<string, string>;
}

Expand All @@ -82,11 +84,24 @@ export type MigrationStatus =
| MigrationSuccess
| MigrationFailed;

export function isMigrationStatus(error: unknown): error is MigrationStatus {
return (
typeof error === 'object' &&
error !== null &&
'id' in error &&
'status' in error
);
}

export async function listAppsForMigration(
accountId: number
accountId: number,
platformVersion: string
): HubSpotPromise<ListAppsResponse> {
return http.get<ListAppsResponse>(accountId, {
url: `${MIGRATIONS_API_PATH_V2}/list-apps`,
params: {
platformVersion,
},
});
}

Expand Down Expand Up @@ -128,11 +143,15 @@ export async function continueMigration(
});
}

export function checkMigrationStatusV2(
export async function checkMigrationStatusV2(
accountId: number,
id: number
): HubSpotPromise<MigrationStatus> {
return http.get<MigrationStatus>(accountId, {
const response = await http.get<MigrationStatus>(accountId, {
url: `${MIGRATIONS_API_PATH_V2}/migrations/${id}/status`,
});

logger.debug(util.inspect(response.data, { depth: null }));

return response;
}
3 changes: 3 additions & 0 deletions commands/__tests__/project.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import open from '../project/open';
import * as dev from '../project/dev';
import add from '../project/add';
import migrateApp from '../project/migrateApp';
import migrate from '../project/migrate';
import cloneApp from '../project/cloneApp';
import installDeps from '../project/installDeps';

Expand All @@ -27,6 +28,7 @@ jest.mock('../project/dev');
jest.mock('../project/add');
jest.mock('../project/migrateApp', () => ({}));
jest.mock('../project/cloneApp', () => ({}));
jest.mock('../project/migrate', () => ({}));
jest.mock('../project/installDeps');
jest.mock('../../lib/commonOpts');

Expand Down Expand Up @@ -68,6 +70,7 @@ describe('commands/project', () => {
['migrateApp', migrateApp],
['cloneApp', cloneApp],
['installDeps', installDeps],
['migrate', migrate],
];

it('should demand the command takes one positional argument', () => {
Expand Down
11 changes: 5 additions & 6 deletions commands/app/__tests__/migrate.test.ts
Original file line number Diff line number Diff line change
@@ -1,11 +1,10 @@
import { ArgumentsCamelCase, Argv } from 'yargs';
import { handler, builder } from '../migrate';
import { getAccountConfig } from '@hubspot/local-dev-lib/config';
import { migrateApp2025_2 } from '../../../lib/app/migrate';
import { migrateApp2025_2, MigrateAppArgs } from '../../../lib/app/migrate';
import { migrateApp2023_2 } from '../../../lib/app/migrate_legacy';
import { logger } from '@hubspot/local-dev-lib/logger';
import { EXIT_CODES } from '../../../lib/enums/exitCodes';
import { MigrateAppOptions } from '../../../types/Yargs';
import { PLATFORM_VERSIONS } from '@hubspot/local-dev-lib/constants/projects';

jest.mock('@hubspot/local-dev-lib/config');
Expand Down Expand Up @@ -38,7 +37,7 @@ describe('commands/app/migrate', () => {

await handler({
derivedAccountId: mockAccountId,
} as ArgumentsCamelCase<MigrateAppOptions>);
} as ArgumentsCamelCase<MigrateAppArgs>);

expect(mockedLogger.error).toHaveBeenCalled();
expect(exitSpy).toHaveBeenCalledWith(EXIT_CODES.ERROR);
Expand All @@ -49,7 +48,7 @@ describe('commands/app/migrate', () => {
await handler({
derivedAccountId: mockAccountId,
platformVersion: PLATFORM_VERSIONS.v2025_2,
} as ArgumentsCamelCase<MigrateAppOptions>);
} as ArgumentsCamelCase<MigrateAppArgs>);

expect(mockedMigrateApp2025_2).toHaveBeenCalledWith(
mockAccountId,
Expand All @@ -62,7 +61,7 @@ describe('commands/app/migrate', () => {
await handler({
derivedAccountId: mockAccountId,
platformVersion: PLATFORM_VERSIONS.v2023_2,
} as ArgumentsCamelCase<MigrateAppOptions>);
} as ArgumentsCamelCase<MigrateAppArgs>);

expect(mockedMigrateApp2023_2).toHaveBeenCalledWith(
mockAccountId,
Expand All @@ -80,7 +79,7 @@ describe('commands/app/migrate', () => {
await handler({
derivedAccountId: mockAccountId,
platformVersion: PLATFORM_VERSIONS.v2023_2,
} as ArgumentsCamelCase<MigrateAppOptions>);
} as ArgumentsCamelCase<MigrateAppArgs>);

expect(mockedLogger.error).toHaveBeenCalled();
expect(exitSpy).toHaveBeenCalledWith(EXIT_CODES.ERROR);
Expand Down
11 changes: 5 additions & 6 deletions commands/app/migrate.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,8 +15,7 @@ import {
import { i18n } from '../../lib/lang';
import { ApiErrorContext, logError } from '../../lib/errorHandlers';
import { EXIT_CODES } from '../../lib/enums/exitCodes';
import { MigrateAppOptions } from '../../types/Yargs';
import { migrateApp2025_2 } from '../../lib/app/migrate';
import { migrateApp2025_2, MigrateAppArgs } from '../../lib/app/migrate';
import { uiBetaTag, uiLink } from '../../lib/ui';
import { migrateApp2023_2 } from '../../lib/app/migrate_legacy';

Expand All @@ -26,7 +25,7 @@ export const validMigrationTargets = [v2023_2, v2025_2, unstable];
const command = 'migrate';
const describe = undefined; // uiBetaTag(i18n(`commands.project.subcommands.migrateApp.header.text.describe`), false);

export async function handler(options: ArgumentsCamelCase<MigrateAppOptions>) {
export async function handler(options: ArgumentsCamelCase<MigrateAppArgs>) {
const { derivedAccountId, platformVersion } = options;
await trackCommandUsage('migrate-app', {}, derivedAccountId);
const accountConfig = getAccountConfig(derivedAccountId);
Expand Down Expand Up @@ -86,7 +85,7 @@ export async function handler(options: ArgumentsCamelCase<MigrateAppOptions>) {
return process.exit(EXIT_CODES.SUCCESS);
}

export function builder(yargs: Argv): Argv<MigrateAppOptions> {
export function builder(yargs: Argv): Argv<MigrateAppArgs> {
addConfigOptions(yargs);
addAccountOptions(yargs);
addUseEnvironmentOptions(yargs);
Expand Down Expand Up @@ -125,10 +124,10 @@ export function builder(yargs: Argv): Argv<MigrateAppOptions> {
],
]);

return yargs as Argv<MigrateAppOptions>;
return yargs as Argv<MigrateAppArgs>;
}

const migrateCommand: CommandModule<unknown, MigrateAppOptions> = {
const migrateCommand: CommandModule<unknown, MigrateAppArgs> = {
command,
describe,
handler,
Expand Down
2 changes: 2 additions & 0 deletions commands/project.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ const open = require('./project/open');
const dev = require('./project/dev');
const add = require('./project/add');
const migrateApp = require('./project/migrateApp');
const migrate = require('./project/migrate');
const cloneApp = require('./project/cloneApp');
const installDeps = require('./project/installDeps');

Expand All @@ -36,6 +37,7 @@ exports.builder = yargs => {
.command(download)
.command(open)
.command(migrateApp)
.command(migrate)
.command(cloneApp)
.command(installDeps)
.demandCommand(1, '');
Expand Down
17 changes: 15 additions & 2 deletions commands/project/cloneApp.ts
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,12 @@ import { extractZipArchive } from '@hubspot/local-dev-lib/archive';
import { getAccountConfig } from '@hubspot/local-dev-lib/config';
import SpinniesManager from '../../lib/ui/SpinniesManager';
import { ArgumentsCamelCase, Argv, CommandModule } from 'yargs';
import { CloneAppArgs } from '../../types/Yargs';
import {
AccountArgs,
CommonArgs,
ConfigArgs,
EnvironmentArgs,
} from '../../types/Yargs';
import { logInvalidAccountError } from '../../lib/app/migrate';

const i18nKey = 'commands.project.subcommands.cloneApp';
Expand All @@ -40,6 +45,14 @@ export const command = 'clone-app';
export const describe = uiDeprecatedTag(i18n(`${i18nKey}.describe`), false);
export const deprecated = true;

export type CloneAppArgs = ConfigArgs &
EnvironmentArgs &
AccountArgs &
CommonArgs & {
dest: string;
appId: number;
};

export const handler = async (options: ArgumentsCamelCase<CloneAppArgs>) => {
const { derivedAccountId } = options;
await trackCommandUsage('clone-app', {}, derivedAccountId);
Expand All @@ -54,7 +67,7 @@ export const handler = async (options: ArgumentsCamelCase<CloneAppArgs>) => {
}

if (!isAppDeveloperAccount(accountConfig)) {
logInvalidAccountError(i18nKey);
logInvalidAccountError();
process.exit(EXIT_CODES.SUCCESS);
}

Expand Down
84 changes: 84 additions & 0 deletions commands/project/migrate.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,84 @@
import { i18n } from '../../lib/lang';

import { ArgumentsCamelCase, Argv, CommandModule } from 'yargs';
import { logger } from '@hubspot/local-dev-lib/logger';
import {
AccountArgs,
CommonArgs,
ConfigArgs,
EnvironmentArgs,
} from '../../types/Yargs';
import {
addAccountOptions,
addConfigOptions,
addGlobalOptions,
} from '../../lib/commonOpts';
import { migrateApp2025_2 } from '../../lib/app/migrate';
import { getProjectConfig } from '../../lib/projects';
import { PLATFORM_VERSIONS } from '@hubspot/local-dev-lib/constants/projects';
import { logError } from '../../lib/errorHandlers';
import { EXIT_CODES } from '../../lib/enums/exitCodes';

export type ProjectMigrateArgs = CommonArgs &
AccountArgs &
EnvironmentArgs &
ConfigArgs & {
platformVersion: string;
};

export const command = 'migrate';

export const describe = undefined; // i18n('commands.project.subcommands.migrate.noProjectConfig')

export async function handler(
options: ArgumentsCamelCase<ProjectMigrateArgs>
): Promise<void> {
const projectConfig = await getProjectConfig();

if (!projectConfig.projectConfig) {
logger.error(
i18n('commands.project.subcommands.migrate.errors.noProjectConfig')
);
return process.exit(EXIT_CODES.ERROR);
}

const { derivedAccountId } = options;
try {
await migrateApp2025_2(
derivedAccountId,
{
...options,
name: projectConfig?.projectConfig?.name,
platformVersion: options.platformVersion,
},
projectConfig
);
} catch (error) {
logError(error);
return process.exit(EXIT_CODES.ERROR);
}
return process.exit(EXIT_CODES.SUCCESS);
}

export function builder(yargs: Argv): Argv<ProjectMigrateArgs> {
addConfigOptions(yargs);
addAccountOptions(yargs);
addGlobalOptions(yargs);

yargs.option('platform-version', {
type: 'string',
choices: Object.values(PLATFORM_VERSIONS),
default: PLATFORM_VERSIONS.v2025_2,
hidden: true,
});

return yargs as Argv<ProjectMigrateArgs>;
}
const migrateAppCommand: CommandModule<unknown, ProjectMigrateArgs> = {
command,
describe,
handler,
builder,
};

export default migrateAppCommand;
10 changes: 5 additions & 5 deletions commands/project/migrateApp.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,12 +7,12 @@ import {

import { ArgumentsCamelCase, Argv, CommandModule } from 'yargs';
import { logger } from '@hubspot/local-dev-lib/logger';
import { MigrateAppOptions } from '../../types/Yargs';
import {
addAccountOptions,
addConfigOptions,
addUseEnvironmentOptions,
} from '../../lib/commonOpts';
import { MigrateAppArgs } from '../../lib/app/migrate';

export const command = 'migrate-app';

Expand All @@ -23,7 +23,7 @@ export const describe = uiDeprecatedTag(
);
export const deprecated = true;

export async function handler(yargs: ArgumentsCamelCase<MigrateAppOptions>) {
export async function handler(yargs: ArgumentsCamelCase<MigrateAppArgs>) {
logger.warn(
i18n(`commands.project.subcommands.migrateApp.deprecationWarning`, {
oldCommand: uiCommandReference('hs project migrate-app'),
Expand All @@ -33,7 +33,7 @@ export async function handler(yargs: ArgumentsCamelCase<MigrateAppOptions>) {
await migrateHandler(yargs);
}

export function builder(yargs: Argv): Argv<MigrateAppOptions> {
export function builder(yargs: Argv): Argv<MigrateAppArgs> {
addConfigOptions(yargs);
addAccountOptions(yargs);
addUseEnvironmentOptions(yargs);
Expand Down Expand Up @@ -72,10 +72,10 @@ export function builder(yargs: Argv): Argv<MigrateAppOptions> {
],
]);

return yargs as Argv<MigrateAppOptions>;
return yargs as Argv<MigrateAppArgs>;
}

const migrateAppCommand: CommandModule<unknown, MigrateAppOptions> = {
const migrateAppCommand: CommandModule<unknown, MigrateAppArgs> = {
command,
describe,
deprecated,
Expand Down
Loading