Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
95 commits
Select commit Hold shift + click to select a range
3dbb647
feat(cli-internal): add assess, refactor, and generate-new commands
iliapolo Mar 17, 2026
6873cfc
chore: fix test
iliapolo Mar 17, 2026
16846fa
test(cli-internal): replace null-as-any logger with noOpLogger helper
iliapolo Mar 17, 2026
317737f
feat(cli-internal): print validation report on failure
iliapolo Mar 17, 2026
c0a38cb
refactor(cli-internal): classify auth stacks by resource type
iliapolo Mar 17, 2026
ff63d5d
Merge branch 'gen2-migration' into epolon/refactor-after-rollback-auth
iliapolo Mar 19, 2026
d309cc8
refactor(cli-internal): split auth refactorers and improve resource m…
iliapolo Mar 20, 2026
2d45f9e
revert(cli-internal): remove trailing-newline normalization from sani…
iliapolo Mar 20, 2026
e136a67
Merge branch 'gen2-migration' into epolon/refactor-after-rollback-auth
iliapolo Mar 20, 2026
6a4bed3
chore: remove merge markers
iliapolo Mar 20, 2026
5cbbfa7
chore: cleanup
iliapolo Mar 20, 2026
db785ac
feat(cli-internal): group plan operations by resource
iliapolo Mar 21, 2026
8827079
fix(cli-internal): tweak plan resource group display
iliapolo Mar 21, 2026
5562032
feat(cli-internal): refine plan display and support UserPool Groups r…
iliapolo Mar 21, 2026
5811ef7
docs: add commit OOM prevention and scratch file cleanup
iliapolo Mar 21, 2026
28f8dd4
feat(cli-internal): add changeset preview and move table to refactor …
iliapolo Mar 21, 2026
9d2e090
fix(cli-internal): improve changeset report formatting
iliapolo Mar 21, 2026
9554e90
refactor(cli-internal): use cli-table3 for move table and fix changes…
iliapolo Mar 21, 2026
bee6cae
feat(cli-internal): validate stack updates via changeset during refac…
iliapolo Mar 21, 2026
e569cd2
test(cli-internal): fix gen2-migration refactor tests
iliapolo Mar 21, 2026
960e6e5
refactor(cli-internal): improve refactor workflow resilience
iliapolo Mar 22, 2026
c511745
refactor(cli-internal): add physicalResourceId to MoveMapping
iliapolo Mar 22, 2026
09d2be8
refactor(cli-internal): hoist computation out of callbacks and simpli…
iliapolo Mar 22, 2026
195f982
refactor(cli-internal): consolidate CFN operations into Cfn class
iliapolo Mar 22, 2026
e1a83f6
refactor(cli-internal): add SpinningLogger to Cfn class
iliapolo Mar 22, 2026
7d4374e
refactor(cli-internal): clean up refactorer operations
iliapolo Mar 22, 2026
5fd4d21
refactor(cli-internal): harden refactor workflow for multi-stack moves
iliapolo Mar 22, 2026
8e9b42b
refactor(cli-internal): improve refactor logging, remove caching, add…
iliapolo Mar 22, 2026
45878ff
fix(cli-internal): defer template resolution to execution time in ref…
iliapolo Mar 22, 2026
b2d1b05
refactor(cli-internal): move template manipulation into Cfn.refactor …
iliapolo Mar 22, 2026
2698a8f
refactor(cli-internal): use SDK ResourceMapping and strip all DependsOn
iliapolo Mar 22, 2026
aaaa376
fix(cli-internal): handle absent holding stacks and defer template fe…
iliapolo Mar 22, 2026
a41cf12
refactor(cli-internal): share Cfn instance, enable rollback resolutio…
iliapolo Mar 23, 2026
32091b1
test(cli-internal): update refactor tests for new interfaces
iliapolo Mar 23, 2026
3ba4939
fix(cli-internal): prevent test hangs with missing mocks and async fixes
iliapolo Mar 23, 2026
a66f7cc
test(cli-internal): fix all refactor tests — 17 suites, 92 tests pass
iliapolo Mar 23, 2026
ee86aa1
docs(cli-internal): update JSDoc for refactor workflow changes
iliapolo Mar 23, 2026
5545ea4
docs(cli-internal): fix remaining JSDoc inaccuracies
iliapolo Mar 23, 2026
cec05b7
test(cli-internal): make CloudFormation mock stateful with template map
iliapolo Mar 23, 2026
50ef166
test(cli-internal): reorganize refactor tests to mirror source structure
iliapolo Mar 23, 2026
8b3d080
chore: revert snapshots
iliapolo Mar 23, 2026
e5c5b13
fix(cli-internal): clone empty holding template to prevent cross-refa…
iliapolo Mar 23, 2026
1e72d2a
docs: add coding guideline for module-level mutable constants
iliapolo Mar 23, 2026
98a01d3
chore(cli-internal): remove duplicate ResourceMapping, add dictionary…
iliapolo Mar 23, 2026
676324f
style(cli-internal): consistent property order in ResourceMapping obj…
iliapolo Mar 23, 2026
20a1610
chore: regen fitness tracker snapshots
iliapolo Mar 24, 2026
8c3aac1
chore: update snapshots
iliapolo Mar 24, 2026
8822035
refactor(cli-internal): simplify beforeMove/afterMove to take stack ID
iliapolo Mar 24, 2026
72fdab7
test(cli-internal): fix tests for beforeMove/afterMove signature change
iliapolo Mar 24, 2026
d398c77
chore: recapture snapshot
iliapolo Mar 24, 2026
6ffd821
chore: update snapshots
iliapolo Mar 24, 2026
7c2aedb
docs(cli-internal): update refactor.md for beforeMove/afterMove changes
iliapolo Mar 24, 2026
398db04
test(cli-internal): add buildResourceMappings edge case tests
iliapolo Mar 24, 2026
576a347
test(cli-internal): add targetLogicalId and match tests for all refac…
iliapolo Mar 24, 2026
b9e4ef7
docs(cli-internal): document resource mapping happy and unhappy paths
iliapolo Mar 24, 2026
e3d532d
fix(cli-internal): skip holding stack resources already present in be…
iliapolo Mar 24, 2026
c1f546f
feat(cli-internal): delete holding stack after rollback if only place…
iliapolo Mar 24, 2026
7813ad6
Merge branch 'gen2-migration' into epolon/refactor-after-rollback-auth
iliapolo Mar 24, 2026
0920d3e
chore: bring back discussions from gen2-migration
iliapolo Mar 24, 2026
84584cf
chore: update snapshots
iliapolo Mar 24, 2026
d448d6b
fix(cli-internal): align tests with gen2-migration merge changes
iliapolo Mar 24, 2026
977bf82
fix(cli-internal): drop removed hasS3Bucket param from DynamoDB tests
iliapolo Mar 25, 2026
5a735d8
refactor(cli-internal): remove hasS3Bucket param from DynamoDBGenerator
iliapolo Mar 25, 2026
c7bec91
feat(cli-internal): add feature-level assessment to assess command
iliapolo Mar 25, 2026
10aa8de
docs: add commit message path and case rules to AGENTS.md
iliapolo Mar 25, 2026
6f19709
refactor(cli-internal): replace Assessment map with arrays
iliapolo Mar 25, 2026
ca51285
docs: add "keep parallel structures symmetric" guideline
iliapolo Mar 25, 2026
5c59b4d
refactor(cli-internal): align assessment API symmetry
iliapolo Mar 25, 2026
706271c
refactor(cli-internal): make assessor reusable by generate and refact…
iliapolo Mar 25, 2026
bc4eda6
feat(cli-internal): add feature assessment validation to generate and…
iliapolo Mar 25, 2026
7054567
refactor(cli-internal): use per-resource assessment validations
iliapolo Mar 25, 2026
1f82c9d
Merge branch 'gen2-migration' into epolon/feature-assessment
iliapolo Mar 28, 2026
8ba6687
chore: merge cleanup
iliapolo Mar 28, 2026
29ff7f3
refactor(cli-internal): consolidate step constructor to accept Gen1App
iliapolo Mar 28, 2026
75c3f30
test(cli-internal): add assessment validation unit tests
iliapolo Mar 28, 2026
2d25c33
refactor(cli-internal): fix remaining test failures and simplify asse…
iliapolo Mar 29, 2026
24ef79f
fix(cli-internal): fix all test failures from Gen1App/AwsClients refa…
iliapolo Mar 29, 2026
46dcaf2
refactor(cli-internal): rename assessment module and assessor method
iliapolo Mar 29, 2026
a04da6b
refactor(cli-internal): replace SupportLevel strings with Support type
iliapolo Mar 29, 2026
1d7b9c7
fix(cli-internal): remove bogus default cases from exhaustive switches
iliapolo Mar 29, 2026
0389994
chore(cli-internal): misc fixes and AGENTS.md PR stage update
iliapolo Mar 29, 2026
e9118fe
docs(cli-internal): update gen2-migration docs and add PR body
iliapolo Mar 29, 2026
ab930b0
docs: refine AGENTS.md PR stage instructions
iliapolo Mar 29, 2026
c8d0bbc
fix(cli-internal): code review fixes for assessment refactor
iliapolo Mar 29, 2026
f808fa4
docs: write scratch files to repo root
iliapolo Mar 29, 2026
726211d
refactor(cli-internal): centralize AWS SDK clients and validations
iliapolo Mar 29, 2026
4221058
refactor(cli-internal): simplify AmplifyGen2MigrationValidations cons…
iliapolo Mar 29, 2026
b184624
refactor(cli-internal): move infra files to _infra/ and fix imports
iliapolo Mar 29, 2026
309d858
fix(cli-internal): skip empty tables in assessment render
iliapolo Mar 29, 2026
452c453
chore: remove unused `AwsClients` import from category refactorer
iliapolo Mar 29, 2026
4a733c4
refactor(cli-internal): type AwsClients config with AmplifyClientConfig
iliapolo Mar 29, 2026
92e24ba
fix(cli-internal): validate team-provider-info.json exists before rea…
iliapolo Mar 29, 2026
cecc18a
fix(cli-internal): validate environment exists in team-provider-info
iliapolo Mar 29, 2026
6dd1cbb
fix(cli-internal): fix stale import paths in test files
iliapolo Mar 30, 2026
dce2089
chore: fix test compilation
iliapolo Mar 30, 2026
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
31 changes: 25 additions & 6 deletions AGENTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -48,14 +48,21 @@ Verify your changes by following these guidelines:
work prefixed with a "Prompt: " after a single line consisting of '---'. Make sure there are no empty lines before or after this line.
Word wrap all paragraphs at 72 columns including the prompt. For the author of the commit, use the configured username in git with
' (AI)' appended and the user email. For example, `git commit --author="John Doe (AI) <john@bigco.com>" -m "docs: update configuration guide"`.
To avoid issues with multi-line commit messages, write the message to `.commit-message.ai-generated.txt` and use `-F`:
To avoid issues with multi-line commit messages, write the message to `.commit-message.ai-generated.txt` **at the repository root** and use `-F` with the path relative to your cwd:

```bash
NODE_OPTIONS="--max-old-space-size=8192" git commit --author="John Doe (AI) <john@bigco.com>" -F .commit-message.ai-generated.txt
# From packages/amplify-cli/:
NODE_OPTIONS="--max-old-space-size=8192" git commit --author="John Doe (AI) <john@bigco.com>" -F ../../.commit-message.ai-generated.txt
```

Always set `NODE_OPTIONS="--max-old-space-size=8192"` when committing to prevent OOM failures in the lint-staged hook.
After a successful commit, delete the scratch file: `rm -f .commit-message.ai-generated.txt`.
After a successful commit, delete the scratch file: `rm -f ../../.commit-message.ai-generated.txt` (adjust the relative path to point to the repo root).

- **CRITICAL: Always write `.commit-message.ai-generated.txt` to the repository root**, not inside a package directory. The `-F` path
in `git commit -F` is resolved relative to the cwd, so adjust the relative path accordingly (e.g., `../../.commit-message.ai-generated.txt`
when committing from `packages/amplify-cli/`). This prevents stale files from accumulating in package directories.
- The commit message subject line must be lowercase (commitlint enforces `subject-case`). Write `feat(scope): add feature` not
`feat(scope): Add feature`.

- Since this repo has a commit hook that takes quite a long time to run, don't immediately commit every
change you were asked to do. Apply your judgment, if the diff is still fairly small just keep going.
Expand All @@ -68,7 +75,12 @@ Verify your changes by following these guidelines:

### 5. PR Stage

This stage prepares the PR description — the user is responsible for creating the actual PR.
Creating the PR means making sure everything meets our high bar and is
ready for peer review and merge. The user is responsible for actually
creating the PR, you are just preparing it.

Ask the user which branch the PR will be targetting and inspect the entire diff.
Then, run the following phases, taking into account all the changes that were made:

#### 5.1 Update Docs

Expand All @@ -77,9 +89,16 @@ Documentation is updated at PR time — not per-commit — because code changes
- Update the .md files in `docs/` that correspond to the code files you touched.
- Update the appropriate skill files when a change impacts the contents of the skill.

#### 5.2 Create Body File
#### 5.2 Code Review

Before creating the PR body, do a final pass over every file you touched:

- Verify all code follows [CODING_GUIDELINES](./CODING_GUIDELINES.md) — read the guidelines file and check every rule against the code you touched.
- Update JSDoc on every public member that was added or changed. Be concise.

#### 5.3 Create Body File

When asked to create a PR, generate a body into `.pr-body.ai-generated.md` and follow these guidelines:
When asked to create a PR, generate a body into `.pr-body.ai-generated.md` **at the repository root** (not inside a package directory) and follow these guidelines:

- Use the PR template in `.github/PULL_REQUEST_TEMPLATE.md` as the structure.
- Focus on **why** the change is being made and **what** it accomplishes, not the implementation details that are obvious from the diff.
Expand Down
65 changes: 65 additions & 0 deletions CODING_GUIDELINES.md
Original file line number Diff line number Diff line change
Expand Up @@ -106,6 +106,71 @@ Scattered client usage also risks inconsistent instantiation. For example, if AP

---

### Keep parallel structures symmetric

When a class, interface, or module has two or more fields, methods, or data paths that serve the same role for different concerns, they should use the same types, the same access patterns, and the same naming conventions. Asymmetry — one field is a `Map` while its sibling is an `Array`, one uses a setter while its sibling uses a constructor parameter, one is optional while its sibling is required — signals that the design has drifted or that one path was added as an afterthought without aligning it with the existing one.

Asymmetry is a code smell even when the code is functionally correct. A reader scanning the class sees two fields that should be peers but are shaped differently, and has to figure out _why_ — is there a semantic reason, or is it accidental? That investigation costs time and often reveals that the difference is accidental.

This applies to naming too. When two sibling interfaces represent the same kind of thing for different domains, their property names should follow the same pattern. If one wraps its identity in a typed object (`resource: DiscoveredResource`), the other should too (`feature: DiscoveredFeature`) — not flatten it into loose fields (`feature: string; path: string`). And within those identity objects, analogous fields should use the same naming convention — if one has `resourceName`, the other should have `name`, not repeat the type (`feature.feature` stutters, `feature.name` doesn't).

This extends to method signatures. Sibling methods that do the same thing for different domains should accept the same number of arguments in the same shape. If `recordFeature` takes a single `FeatureAssessment` object, `recordResource` should take a single `ResourceAssessment` object — not three positional arguments. And the parameter names should follow the same convention: if one is `feature: FeatureAssessment`, the other should be `resource: ResourceAssessment`, not `assessment: ResourceAssessment`.

```typescript
// Bad — two collections serving the same role, different types
class Assessment {
private readonly _resources = new Map<string, ResourceAssessment>();
private readonly _features: FeatureAssessment[] = [];
}

// Good — both are arrays, same access pattern
class Assessment {
private readonly _resources: ResourceAssessment[] = [];
private readonly _features: FeatureAssessment[] = [];
}

// Bad — sibling interfaces with asymmetric identity shapes
interface ResourceAssessment {
readonly resource: DiscoveredResource; // wrapped in typed object
readonly generate: SupportLevel;
readonly refactor: SupportLevel;
}
interface FeatureAssessment {
readonly feature: string; // flat string — asymmetric
readonly path: string; // flat string — asymmetric
readonly generate: SupportLevel;
readonly refactor: SupportLevel;
}

// Good — both wrap identity in a typed object, same pattern
interface ResourceAssessment {
readonly resource: DiscoveredResource;
readonly generate: SupportLevel;
readonly refactor: SupportLevel;
}
interface FeatureAssessment {
readonly feature: DiscoveredFeature;
readonly generate: SupportLevel;
readonly refactor: SupportLevel;
}

// Bad — sibling methods with asymmetric signatures
class Assessment {
recordResource(resource: DiscoveredResource, generate: SupportLevel, refactor: SupportLevel): void { ... }
recordFeature(feature: FeatureAssessment): void { ... }
}

// Good — both take a single typed object, parameter named after the type
class Assessment {
recordResource(resource: ResourceAssessment): void { ... }
recordFeature(feature: FeatureAssessment): void { ... }
}
```

The test: look at sibling fields, sibling methods, sibling interfaces, or sibling parameters. If they serve analogous roles but differ in type, shape, naming pattern, or access pattern, ask whether the difference is justified by a real semantic distinction. If not, align them.

---

## Mutability & State Management

### Minimize mutability
Expand Down
19 changes: 9 additions & 10 deletions docs/packages/amplify-cli/src/commands/gen2-migration.md
Original file line number Diff line number Diff line change
Expand Up @@ -26,22 +26,17 @@ const rollingBack = (context.input.options ?? {})['rollback'] ?? false;

### Common Gen1 Configuration Extraction

Extracts shared Gen1 configuration (`appId`, `appName`, `envName`, `stackName`, `region`) once from state manager and Amplify service,
then passes these values to step constructors. This establishes a single source of truth; subcommands should use the injected values
rather than re-extracting them independently.
Creates a `Gen1App` facade that encapsulates all Gen1 app state — AWS clients, environment config, and the cloud backend snapshot. `Gen1App.create(context)` reads `team-provider-info.json`, fetches the app from the Amplify service, downloads the cloud backend from S3, and reads `amplify-meta.json`. The resulting instance is passed to all step constructors.

```ts
const appId = (Object.values(stateManager.getTeamProviderInfo())[0] as any).awscloudformation.AmplifyAppId;
const envName = localEnvName ?? migratingEnvName;
// ... extract other config values
const implementation: AmplifyMigrationStep = new step.class(logger, envName, appName, appId, stackName, region, context);
const gen1App = await Gen1App.create(context);
const implementation: AmplifyMigrationStep = new step.class(logger, gen1App, context);
```

### Subcommand Dispatching

Maps the subcommand name to its implementation class via the `STEPS` registry, then instantiates the step with extracted configuration.
The `assess` subcommand is intercepted before the `STEPS` lookup — it creates an `AmplifyMigrationAssessor` instead of a step,
calls `assess()` on the generate and refactor steps, and renders the result.
The `assess` subcommand is intercepted before the `STEPS` lookup — it creates an `AmplifyMigrationAssessor` with the `Gen1App` instance, calls `assess()` to collect resource and feature support levels, and prints the report.

### Plan-Based Execution

Expand Down Expand Up @@ -116,7 +111,7 @@ flowchart LR

[`src/commands/gen2-migration/_step.ts`](../../../packages/amplify-cli/src/commands/gen2-migration/_step.ts)

Abstract base class that defines the lifecycle contract for all migration steps. Each step returns a `Plan` from `forward()` and `rollback()`.
Abstract base class that defines the lifecycle contract for all migration steps. Constructor takes `(logger, gen1App, context)` — the `Gen1App` facade provides all app state. Each step returns a `Plan` from `forward()` and `rollback()`.

### `AmplifyMigrationOperation`

Expand Down Expand Up @@ -173,6 +168,10 @@ amplify gen2-migration <step> [options]
- `SpinningLogger` is the only logger class — the deprecated `Logger` subclass has been removed. Import directly from `_spinning-logger.ts`.
- Automatic rollback is enabled by default but can be disabled with `--no-rollback`.
- The `--rollback` flag explicitly executes rollback operations for a step.
- `Gen1App` is the single facade for all Gen1 app state. It is created once in the dispatcher via `Gen1App.create(context)` and passed to all steps. Steps access `gen1App.appId`, `gen1App.region`, `gen1App.envName`, etc. instead of individual constructor params.
- `AwsClients` has a private constructor — use `AwsClients.create(context)` in production. Tests bypass this with `new (AwsClients as any)(...)`.
- Assessment uses a `Support` type with `level` and `note` fields. Each assessor provides its own note for unsupported entries. Use the `supported()`, `unsupported(note)`, `notApplicable()` helpers.
- `KNOWN_RESOURCE_KEYS` (in `gen1-app.ts`) defines all supported category:service pairs. Unknown resources get the `'UNKNOWN'` key.

**Common pitfalls:**

Expand Down
Loading
Loading