diff --git a/.circleci/local_publish_helpers_codebuild.sh b/.circleci/local_publish_helpers_codebuild.sh index 1c2a4b7d692..738df9c2a2c 100644 --- a/.circleci/local_publish_helpers_codebuild.sh +++ b/.circleci/local_publish_helpers_codebuild.sh @@ -323,7 +323,7 @@ function checkPackageVersionsInLocalNpmRegistry { if [[ $cli_internal_version != $cli_version ]]; then echo "Versions did not match." - echo "Manual fix: add a proper conventional commit that touches the amplify-cli-npm package to correct its version bump. For example https://github.com/aws-amplify/amplify-cli/commit/6f14792d1db424aa428ec4836fed7d6dd5cccfd0" + echo "Manual fix: add a proper conventional commit that touches the amplify-cli-npm package to correct its version bump. For example https://github.com/aws-amplify/amplify-cli/pull/13759/commits/15dcd96feae925ff26ca51abfb4a0477890af745" exit 1 else echo "Versions matched." diff --git a/packages/amplify-cli-npm/index.ts b/packages/amplify-cli-npm/index.ts index 31efac92c57..cffb591badb 100644 --- a/packages/amplify-cli-npm/index.ts +++ b/packages/amplify-cli-npm/index.ts @@ -16,4 +16,4 @@ export const install = async (): Promise => { return binary.install(); }; -// force version bump to 12.12.0 +// force version bump to 12.13.0 diff --git a/packages/amplify-cli/src/__tests__/commands/init.test.ts b/packages/amplify-cli/src/__tests__/commands/init.test.ts index aa1d8bb6c30..f4e29eec1b4 100644 --- a/packages/amplify-cli/src/__tests__/commands/init.test.ts +++ b/packages/amplify-cli/src/__tests__/commands/init.test.ts @@ -11,7 +11,7 @@ import { import { execSync } from 'child_process'; import { ensureDir, existsSync, readFileSync, readJSON, readdirSync } from 'fs-extra'; import { sync } from 'which'; -import { preInitSetup } from '../../init-steps/preInitSetup'; +import { getPreInitSetup } from '../../init-steps/preInitSetup'; import { analyzeProject } from '../../init-steps/s0-analyzeProject'; import { initFrontend } from '../../init-steps/s1-initFrontend'; import { scaffoldProjectHeadless } from '../../init-steps/s8-scaffoldHeadless'; @@ -137,7 +137,9 @@ describe('amplify init:', () => { }, }, }; - await preInitSetup(context as unknown as $TSContext); + const recommendGen2 = true; + const step = getPreInitSetup(!recommendGen2); + await step(context as unknown as $TSContext); expect(execSync).toBeCalledWith(`git ls-remote ${appUrl}`, { stdio: 'ignore' }); expect(execSync).toBeCalledWith(`git clone ${appUrl} .`, { stdio: 'inherit' }); expect(execSync).toBeCalledWith('yarn install', { stdio: 'inherit' }); diff --git a/packages/amplify-cli/src/__tests__/init-steps/preInitSetup.test.ts b/packages/amplify-cli/src/__tests__/init-steps/preInitSetup.test.ts new file mode 100644 index 00000000000..f2fd428f332 --- /dev/null +++ b/packages/amplify-cli/src/__tests__/init-steps/preInitSetup.test.ts @@ -0,0 +1,95 @@ +import { $TSContext } from '@aws-amplify/amplify-cli-core'; +import { printer, prompter } from '@aws-amplify/amplify-prompts'; +import { getPreInitSetup, preInitSetup, gen2Recommendation } from '../../init-steps/preInitSetup'; +import { isNewProject } from '../../init-steps/s0-analyzeProject'; + +// Mock dependencies +jest.mock('@aws-amplify/amplify-cli-core', () => ({ + ...(jest.requireActual('@aws-amplify/amplify-cli-core') as {}), + FeatureFlags: { + getBoolean: jest.fn(), + getNumber: jest.fn(), + isInitialized: jest.fn().mockReturnValue(true), + ensureDefaultFeatureFlags: jest.fn(), + }, + getPackageManager: jest.fn(), +})); + +jest.mock('@aws-amplify/amplify-prompts', () => ({ + printer: { + warn: jest.fn(), + }, + prompter: { + confirmContinue: jest.fn(), + pick: jest.fn(), + }, +})); + +jest.mock('../../init-steps/s0-analyzeProject', () => ({ + isNewProject: jest.fn(), +})); + +describe('preInitSetup', () => { + it('should return preInitSetupBasic when isHeadless is true', () => { + const result = getPreInitSetup(false); + expect(result).toBe(preInitSetup); + }); + + it('should return a function when isHeadless is false', () => { + const result = getPreInitSetup(false); + expect(typeof result).toBe('function'); + }); +}); + +describe('gen2Recommendation', () => { + let context; + + beforeEach(() => { + context = { exeInfo: {} } as $TSContext; + }); + + afterEach(() => { + jest.clearAllMocks(); + }); + + it('should recommend using Gen 2 for new projects', async () => { + const isNewProjectMock = jest.mocked(isNewProject); + isNewProjectMock.mockReturnValue(true); + + const confirmContinueMock = jest.mocked(prompter.confirmContinue); + confirmContinueMock.mockResolvedValue(true); + + const pickMock = jest.mocked(prompter.pick); + pickMock.mockResolvedValue('I am a current Gen 1 user'); + + await gen2Recommendation(context); + + expect(require('@aws-amplify/amplify-prompts').printer.warn).toHaveBeenCalledWith( + 'For new projects, we recommend starting with AWS Amplify Gen 2, our new code-first developer experience. Get started at https://docs.amplify.aws/react/start/quickstart/', + ); + expect(confirmContinueMock).toHaveBeenCalledWith('Do you want to continue with Amplify Gen 1?'); + expect(pickMock).toHaveBeenCalledWith( + 'Why would you like to use Amplify Gen 1?', + [ + 'I am a current Gen 1 user', + 'Gen 2 is missing features I need from Gen 1', + 'I find the Gen 1 CLI easier to use', + 'Prefer not to answer', + ], + { initial: 3 }, + ); + expect(context.exeInfo.projectConfig).toEqual({ whyContinueWithGen1: 'I am a current Gen 1 user' }); + }); + + it('should return the context for existing projects', async () => { + const isNewProjectMock = jest.mocked(isNewProject); + isNewProjectMock.mockReturnValue(false); + + const result = await gen2Recommendation(context); + + expect(result).toEqual(context); + expect(printer.warn).not.toHaveBeenCalled(); + expect(prompter.confirmContinue).not.toHaveBeenCalled(); + expect(prompter.pick).not.toHaveBeenCalled(); + }); +}); diff --git a/packages/amplify-cli/src/commands/init.ts b/packages/amplify-cli/src/commands/init.ts index 2d2545a1d4d..9f994cda38f 100644 --- a/packages/amplify-cli/src/commands/init.ts +++ b/packages/amplify-cli/src/commands/init.ts @@ -3,7 +3,7 @@ import { constructInputParams } from '../amplify-service-helper'; import { Context } from '../domain/context'; import { raisePostEnvAddEvent } from '../execution-manager'; import { postInitSetup } from '../init-steps/postInitSetup'; -import { preInitSetup } from '../init-steps/preInitSetup'; +import { getPreInitSetup } from '../init-steps/preInitSetup'; import { analyzeProject, analyzeProjectHeadless } from '../init-steps/s0-analyzeProject'; import { initFrontend } from '../init-steps/s1-initFrontend'; import { initProviders } from '../init-steps/s2-initProviders'; @@ -18,11 +18,12 @@ const constructExeInfo = (context: $TSContext): void => { }; }; +const recommendGen2 = true; // eslint-disable-next-line @typescript-eslint/explicit-function-return-type const runStrategy = (quickstart: boolean) => quickstart - ? [preInitSetup, analyzeProjectHeadless, scaffoldProjectHeadless, onHeadlessSuccess] - : [preInitSetup, analyzeProject, initFrontend, initProviders, onSuccess, postInitSetup]; + ? [getPreInitSetup(!recommendGen2), analyzeProjectHeadless, scaffoldProjectHeadless, onHeadlessSuccess] + : [getPreInitSetup(recommendGen2), analyzeProject, initFrontend, initProviders, onSuccess, postInitSetup]; /** * entry point for the init command diff --git a/packages/amplify-cli/src/index.ts b/packages/amplify-cli/src/index.ts index 8283cecf304..7616be8a079 100644 --- a/packages/amplify-cli/src/index.ts +++ b/packages/amplify-cli/src/index.ts @@ -257,5 +257,4 @@ export const executeAmplifyCommand = async (context: Context): Promise => } }; -// bump version to 12.12.0 -// +// bump version to 12.13.0 diff --git a/packages/amplify-cli/src/init-steps/preInitSetup.ts b/packages/amplify-cli/src/init-steps/preInitSetup.ts index 1f5eea6654c..80b89aabbe7 100644 --- a/packages/amplify-cli/src/init-steps/preInitSetup.ts +++ b/packages/amplify-cli/src/init-steps/preInitSetup.ts @@ -3,6 +3,19 @@ import { execSync } from 'child_process'; import * as fs from 'fs-extra'; import * as url from 'url'; import { generateLocalEnvInfoFile } from './s9-onSuccess'; +import { printer, prompter } from '@aws-amplify/amplify-prompts'; +import { isNewProject } from './s0-analyzeProject'; + +export const getPreInitSetup = (recommendGen2: boolean) => { + if (recommendGen2) { + return async (context) => { + await gen2Recommendation(context); + await preInitSetup(context); + }; + } else { + return preInitSetup; + } +}; /** * Executes before init @@ -22,6 +35,42 @@ export const preInitSetup = async (context: $TSContext): Promise<$TSContext> => return context; }; +/** + * recommend using Gen 2 or continue with Gen 1. + * ask for why they are using Gen 1 and store the answer in project-config + */ +export const gen2Recommendation = async (context: $TSContext): Promise<$TSContext> => { + if (!isNewProject(context)) { + return context; + } + printer.warn( + 'For new projects, we recommend starting with AWS Amplify Gen 2, our new code-first developer experience. Get started at https://docs.amplify.aws/react/start/quickstart/', + ); + + const continueWithGen1 = await prompter.confirmContinue('Do you want to continue with Amplify Gen 1?'); + + if (!continueWithGen1) { + process.exit(0); + } + + const whyContinueWithGen1 = await prompter.pick( + 'Why would you like to use Amplify Gen 1?', + [ + 'I am a current Gen 1 user', + 'Gen 2 is missing features I need from Gen 1', + 'I find the Gen 1 CLI easier to use', + 'Prefer not to answer', + ], + { initial: 3 }, + ); + + context.exeInfo.projectConfig = { + whyContinueWithGen1, + }; + + return context; +}; + /** * Checks whether a url is a valid remote github repository * diff --git a/packages/amplify-cli/src/init-steps/s0-analyzeProject.ts b/packages/amplify-cli/src/init-steps/s0-analyzeProject.ts index af0ff0015c4..261845048a7 100644 --- a/packages/amplify-cli/src/init-steps/s0-analyzeProject.ts +++ b/packages/amplify-cli/src/init-steps/s0-analyzeProject.ts @@ -152,6 +152,7 @@ export const analyzeProject = async (context: $TSContext): Promise<$TSContext> = const setProjectConfig = (context: $TSContext, projectName: string): void => { context.exeInfo.isNewProject = isNewProject(context); context.exeInfo.projectConfig = { + ...context.exeInfo.projectConfig, projectName, version: amplifyCLIConstants.CURRENT_PROJECT_CONFIG_VERSION, }; @@ -325,7 +326,7 @@ const isNewEnv = (envName: string): boolean => { return !allEnvs.includes(envName); }; -const isNewProject = (context: $TSContext): boolean => { +export const isNewProject = (context: $TSContext): boolean => { let newProject = true; const projectPath = process.cwd(); const projectConfigFilePath = context.amplify.pathManager.getProjectConfigFilePath(projectPath); diff --git a/packages/amplify-e2e-core/package.json b/packages/amplify-e2e-core/package.json index ceb3a549d99..3cc4b457192 100644 --- a/packages/amplify-e2e-core/package.json +++ b/packages/amplify-e2e-core/package.json @@ -39,7 +39,7 @@ "jest-environment-node": "^26.6.2", "lodash": "^4.17.21", "node-fetch": "^2.6.7", - "node-pty": "beta", + "node-pty": "^1.0.0", "retimer": "2.0.0", "rimraf": "^3.0.0", "semver": "^7.5.4", diff --git a/packages/amplify-e2e-core/src/init/initProjectHelper.ts b/packages/amplify-e2e-core/src/init/initProjectHelper.ts index 976fc07086e..88c32280bf4 100644 --- a/packages/amplify-e2e-core/src/init/initProjectHelper.ts +++ b/packages/amplify-e2e-core/src/init/initProjectHelper.ts @@ -27,6 +27,7 @@ const defaultSettings = { providerConfig: undefined, permissionsBoundaryArn: undefined, includeUsageDataPrompt: true, + includeGen2RecommendationPrompt: true, testingWithLatestCodebase: false, }; @@ -59,7 +60,17 @@ export function initJSProjectWithProfile(cwd: string, settings?: Partial { cwd, stripColors: true, }) + .wait('Do you want to continue with Amplify Gen 1?') + .sendYes() + .wait('Why would you like to use Amplify Gen 1?') + .sendCarriageReturn() .wait('Enter a name for the project') .sendCarriageReturn() .wait('Initialize the project with the above configuration?') @@ -237,6 +260,10 @@ export function initFlutterProjectWithProfile(cwd: string, settings: Record { const chain = spawn(getCLIPath(), ['init'], { cwd, stripColors: true }) + .wait('Do you want to continue with Amplify Gen 1?') + .sendYes() + .wait('Why would you like to use Amplify Gen 1?') + .sendCarriageReturn() .wait('Enter a name for the project') .sendLine(s.name) .wait('Initialize the project with the above configuration?') @@ -286,6 +313,10 @@ export function initProjectWithAccessKey( CLI_DEV_INTERNAL_DISABLE_AMPLIFY_APP_CREATION: '1', }, }) + .wait('Do you want to continue with Amplify Gen 1?') + .sendYes() + .wait('Why would you like to use Amplify Gen 1?') + .sendCarriageReturn() .wait('Enter a name for the project') .sendLine(s.name) .wait('Initialize the project with the above configuration?') diff --git a/packages/amplify-e2e-core/src/utils/pinpoint.ts b/packages/amplify-e2e-core/src/utils/pinpoint.ts index 33b0b44d654..eb40a64d1e9 100644 --- a/packages/amplify-e2e-core/src/utils/pinpoint.ts +++ b/packages/amplify-e2e-core/src/utils/pinpoint.ts @@ -66,6 +66,10 @@ export function initProjectForPinpoint(cwd: string): Promise { CLI_DEV_INTERNAL_DISABLE_AMPLIFY_APP_CREATION: '1', }, }) + .wait('Do you want to continue with Amplify Gen 1?') + .sendYes() + .wait('Why would you like to use Amplify Gen 1?') + .sendCarriageReturn() .wait('Enter a name for the project') .sendLine(settings.name) .wait('Initialize the project with the above configuration?') diff --git a/packages/amplify-e2e-tests/package.json b/packages/amplify-e2e-tests/package.json index 08e4038f34b..87aba51c2d9 100644 --- a/packages/amplify-e2e-tests/package.json +++ b/packages/amplify-e2e-tests/package.json @@ -57,7 +57,7 @@ "lodash": "^4.17.21", "moment": "^2.24.0", "node-fetch": "^2.6.7", - "node-pty": "beta", + "node-pty": "^1.0.0", "rimraf": "^3.0.0", "title-case": "^3.0.3", "upper-case": "^2.0.2", diff --git a/packages/amplify-e2e-tests/src/init-special-cases/index.ts b/packages/amplify-e2e-tests/src/init-special-cases/index.ts index 556d6345a1b..a9534d1e7f5 100644 --- a/packages/amplify-e2e-tests/src/init-special-cases/index.ts +++ b/packages/amplify-e2e-tests/src/init-special-cases/index.ts @@ -51,6 +51,10 @@ async function initWorkflow(cwd: string, settings: { accessKeyId: string; secret CLI_DEV_INTERNAL_DISABLE_AMPLIFY_APP_CREATION: '1', }, }) + .wait('Do you want to continue with Amplify Gen 1?') + .sendYes() + .wait('Why would you like to use Amplify Gen 1?') + .sendCarriageReturn() .wait('Enter a name for the project') .sendCarriageReturn() .wait('Initialize the project with the above configuration?') diff --git a/packages/amplify-migration-tests/src/__tests__/migration_tests/transformer_migration/api.key.migration-2.test.ts b/packages/amplify-migration-tests/src/__tests__/migration_tests/transformer_migration/api.key.migration-2.test.ts index 6b6b818fdc2..d5e4bafd268 100644 --- a/packages/amplify-migration-tests/src/__tests__/migration_tests/transformer_migration/api.key.migration-2.test.ts +++ b/packages/amplify-migration-tests/src/__tests__/migration_tests/transformer_migration/api.key.migration-2.test.ts @@ -28,6 +28,7 @@ describe('amplify key force push', () => { await initJSProjectWithProfile(projRoot, { name: 'gqlkeytwomigration', includeUsageDataPrompt: false, + includeGen2RecommendationPrompt: false, }); }); diff --git a/packages/amplify-migration-tests/src/__tests__/migration_tests/transformer_migration/api.key.migration.test.ts b/packages/amplify-migration-tests/src/__tests__/migration_tests/transformer_migration/api.key.migration.test.ts index 8692660d33c..e2f99d6d920 100644 --- a/packages/amplify-migration-tests/src/__tests__/migration_tests/transformer_migration/api.key.migration.test.ts +++ b/packages/amplify-migration-tests/src/__tests__/migration_tests/transformer_migration/api.key.migration.test.ts @@ -28,6 +28,7 @@ describe('amplify key force push', () => { await initJSProjectWithProfile(projRoot, { name: 'gqlkeymigration', includeUsageDataPrompt: false, + includeGen2RecommendationPrompt: false, }); }); diff --git a/packages/amplify-migration-tests/src/__tests__/migration_tests_v12/auth.migration.test.ts b/packages/amplify-migration-tests/src/__tests__/migration_tests_v12/auth.migration.test.ts index b3fee8619a8..e57fa981f28 100644 --- a/packages/amplify-migration-tests/src/__tests__/migration_tests_v12/auth.migration.test.ts +++ b/packages/amplify-migration-tests/src/__tests__/migration_tests_v12/auth.migration.test.ts @@ -37,7 +37,11 @@ describe('v12: amplify migration test auth', () => { describe('...uses user groups and role mappings', () => { it('...maintains correct role mapping when updated with latest version', async () => { - await initJSProjectWithProfile(projRoot1, { name: 'authTest', disableAmplifyAppCreation: false }); + await initJSProjectWithProfile(projRoot1, { + name: 'authTest', + disableAmplifyAppCreation: false, + includeGen2RecommendationPrompt: false, + }); await addAuthWithGroups(projRoot1); await amplifyPushAuth(projRoot1); diff --git a/packages/amplify-migration-tests/src/migration-helpers-v12/init.ts b/packages/amplify-migration-tests/src/migration-helpers-v12/init.ts index 8b87b639f18..abf45abd853 100644 --- a/packages/amplify-migration-tests/src/migration-helpers-v12/init.ts +++ b/packages/amplify-migration-tests/src/migration-helpers-v12/init.ts @@ -102,6 +102,10 @@ export function initIosProjectWithProfileV12(cwd: string, settings: Record