Skip to content

Nx Migration ジェネレーター

Nx Plugin に Nx Migration を追加します。Migration を使用すると、破壊的変更(ターゲットの名前変更、提供される設定の変更、またはコード変更を伴う依存関係のバージョンアップなど)を行った際に、nx migrate がプラグインの以前のバージョンで生成されたプロジェクトを自動的に更新できるようになります。

このジェネレーターは、migration のスキャフォールディングを行い、プラグインの migrations.json に登録し、プラグインの package.jsonnx-migrations フィールドを配線します(存在しない場合は両方とも作成します)。

Terminal window
pnpm nx g @aws/nx-plugin:ts#nx-migration
変更されるファイルを確認するためにドライランを実行することもできます
Terminal window
pnpm nx g @aws/nx-plugin:ts#nx-migration --dry-run
パラメータデフォルト説明
project 必須string-マイグレーションを追加するNx Pluginプロジェクト。ts#nx-pluginジェネレーターを使用して作成することを推奨します。
name 必須string-マイグレーション名、kebab-case形式で指定(例:rename-foo-target)
description string-マイグレーションが何を行うかの説明。nx migrateコマンドで表示されます
kind deterministic | agentic | hybriddeterministicマイグレーションの種類:deterministic(コードモッド)、agentic(ユーザー所有コードに対するAI適用プロンプト)、またはhybrid(機械的な部分を処理した後、エージェントに引き継ぐコードモッド)
preferInstallDependencies booleantrueジェネレーター実行後に依存関係のインストールを優先するかどうか。複数のジェネレーターをバッチ処理する際にインストールを延期する場合はfalseに設定します(後続のジェネレーターがNxプロジェクトグラフを計算できるよう、必要に応じてインストールは実行されます)。最後に一度だけインストールします。

Migration には 3 つの形式があり、migrations.json エントリが持つフィールドによって区別されます。--kind を渡して選択します(デフォルトは deterministic):

  • Deterministicimplementation)— 正確な変更前/変更後を持つコードモッドで、最初に使用すべきものです。CI や非対話型ターミナルを含む無人環境で実行され、すべてのユーザーに対して同じように適用されます。変更が現れるすべての場所で確実に一致できる形状を持つ場合に単独で使用し、スキップしたものは nextSteps 経由で報告します。
  • Hybridimplementation + prompt)— 1 つの変更の両方の部分です。変更が破壊的で機械的に完全に適用できない場合に使用します:コードモッドは安全に一致できる限りのことを行い、変更した内容とスキップした内容を記述する agentContext を返します。Nx はそのコンテキストをペアの prompt に渡し、残りの部分をエージェントに指示します。プロンプトは、残りの編集がコードモッドするには形状が多すぎる場合に、ワークスペースが壊れた状態で放置されるのを防ぎます。
  • Agenticprompt)— Nx のエージェント型 nx migrate フローを介してユーザーのローカルコーディングエージェントによって適用される markdown 指示ファイルです。正しい編集がユーザーが構築したものに依存するため、機械的な変更前/変更後がまったく成立しない場合に単独で使用します。エージェントが実行されない場合(CI、エージェントがインストールされていない、同意が拒否された)、Nx はプロンプトを手動の指示として表示します。そのため、プロンプトは自己完結型で人間が実行可能な手順として記述してください。

ジェネレーターは、選択した kind に応じて、プラグインプロジェクトのソースフォルダー配下に以下のファイルを作成します:

  • Directorysrc/migrations/latest/<name>/
    • migration.ts Codemod implementation (deterministic and hybrid)
    • migration.spec.ts Tests for your migration (deterministic and hybrid)
    • prompt.md Agent/human instructions (agentic and hybrid)
  • migrations.json Created or updated to register your migration
  • package.json Created or updated to add an “nx-migrations” entry

新しい migration は latest/ に配置されます。migration をリリースごとにグループ化することで、コレクションが増えるにつれて順序が一目でわかるようになります。バージョンを割り当てる際には、migration を v<x.y.z>/ フォルダーに移動し(migrations.json でそのパスを更新)します。

migration は、その種類のフィールドを持ち、version なしで migrations.json に登録されます。詳細は以下のバージョニングを参照してください。登録されるキーには、migration が存在するフォルダーがプレフィックスとして付けられます(latest-<name>)。これにより、後の変更で名前を再利用しても、すでに出荷された migration が静かに上書きされることはありません。

deterministic(または hybrid)migration は、仮想ファイルシステム(Tree)を変更し、MigrationReturnObject を返す関数です。以下は、両方を返す hybrid の例です:

import { type MigrationReturnObject, type Tree } from '@nx/devkit';
import { formatFilesInSubtree } from '@aws/nx-plugin/sdk/utils/format';
export default async function migration(
tree: Tree,
): Promise<MigrationReturnObject> {
const nextSteps: string[] = [];
const agentContext: string[] = [];
// Read and update the files your generators produce here.
await formatFilesInSubtree(tree);
return { nextSteps, agentContext };
}
  • nextStepsnx migrate の実行後に Nx がユーザーに表示するフォローアップアクションです。各エントリは、手動で行う必要があることである必要があります:スキップしたファイルの調整(以下のガードレールを参照)、またはロックファイルを更新するためのインストールなど、migration の範囲を超えた何かを完了することです。変更内容のログではありません。変換がクリーンに適用される場合は何も追加しないでください。そうしないと、実際にアクションが必要なエントリが埋もれてしまいます。
  • agentContext(hybrid のみ)— コードモッドが変更した内容の要約で、Nx のエージェント型フローで実行される際にペアの prompt に渡され、エージェントがユーザー所有の部分に集中できるようにします。

migration で実行できる操作の例(ファイルの生成、GritQL を使用した既存ファイルの変更、JSON の読み取りと更新)については、Nx Generator ガイドを参照してください。

Migration は制御できないワークスペースに対して実行されるため、以下に従うことをお勧めします(スキャフォールディングされたスケルトンにはこれらが組み込まれています):

  • GritQL でソースコードを変換する。 正規表現や文字列置換ではなく、GritQL ヘルパーapplyGritQLmatchGritQL)を使用してソースを編集します。GritQL は AST に一致するため、ユーザーのコピーが変化したフォーマット、空白、引用符にかかわらず、1 つのパターンが保持されます。テキストマッチングでは、同等のコードを静かに見逃したり、ファイルを破損したりします。JSON には updateJson を使用し、他の構造化された設定には一致するパーサーを使用します。
  • 書き込む前にパターンマッチする。 ターゲットファイルがジェネレーターが生成する形状から逸脱している場合は、ユーザーの変更を上書きするのではなく、スキップして nextSteps 経由で報告するか、hybrid migration を検討してください。matchGritQL はそのチェックを行う便利な方法です。
  • 残っていることだけを報告する。 nextSteps はユーザーが引き継ぐ必要がある作業用であり、行った編集の記録ではありません。それらはすでに git diff にあります。
  • 冪等性。 migration を再実行しても何も起こらないようにする必要があります。コードを挿入する GritQL の書き換えを where { ... <: not contains ... } 句でガードし、2 回目の実行で重複が追加されないようにします。
  • 書き込んだものをフォーマットする。 formatFilesInSubtree(tree) で終了し、migration が書き込んだファイルが正しくフォーマットされるようにします。

スキャフォールディングされた migration.spec.ts は、createTreeUsingTsSolutionSetup() を使用して、プリセットが生成する形状に一致する仮想ワークスペースを構築します。migration がワークスペースを「変更前」の状態からターゲット状態に移行し、認識できないコードをスキップ(および報告)し、冪等であることをテストします:

import type { Tree } from '@nx/devkit';
import { createTreeUsingTsSolutionSetup } from '@aws/nx-plugin/sdk/utils/test';
import migration from './migration.js';
describe('my migration', () => {
let tree: Tree;
beforeEach(() => {
tree = createTreeUsingTsSolutionSetup();
});
it('should update the generated shape', async () => {
// Set up a tree with the "before" state
tree.write('project.json', JSON.stringify({ targets: { foo: {} } }));
// Run the migration
await migration(tree);
// Check that the tree matches the target state
const projectJson = JSON.parse(tree.read('project.json', 'utf-8'));
expect(projectJson.targets).toHaveProperty('bar');
expect(projectJson.targets).not.toHaveProperty('foo');
});
});

ユニットテストは、migration が書いたとおりに動作することを証明するだけです。プラグインの公開されたバージョンによって生成されたワークスペース(ユーザーがアップグレードする元の状態)に対してもテストし、結果が今日のジェネレーターが生成するものと一致することを確認してください:差分を検査し、同じプロジェクトを新しいワークスペースに生成して比較し、ワークスペースがまだビルドできることを確認してから、migration を再実行して何も変更されないことを確認します。最初にカスタマイズしたワークスペースで繰り返し、migration が編集を残し、代わりに報告することを確認します。

Nx は、ユーザーがインストールしたプラグインのバージョンが migration の version より小さい場合に migration を実行します。このジェネレーターは version フィールドを書き込みません。migration を出荷するリリースをカットする際に割り当て(またはリリースプロセスからスタンプ)して、以前のバージョンからアップグレードするすべてのユーザーに対して実行されるようにします。

バージョンを割り当てると、各種類の migration を 1 つずつ登録する migrations.json は次のようになります:

{
"$schema": "http://json-schema.org/schema",
"name": "my-plugin",
"generators": {
"v2.0.0-rename-foo-target": {
"version": "2.0.0",
"description": "Rename the foo target to bar",
"implementation": "./src/migrations/v2.0.0/rename-foo-target/migration"
},
"v3.0.0-migrate-custom-handlers": {
"version": "3.0.0",
"description": "Update custom handlers for the new API",
"prompt": "./src/migrations/v3.0.0/migrate-custom-handlers/prompt.md"
},
"v4.0.0-upgrade-framework": {
"version": "4.0.0",
"description": "Upgrade the framework and reconcile call sites",
"implementation": "./src/migrations/v4.0.0/upgrade-framework/migration",
"prompt": "./src/migrations/v4.0.0/upgrade-framework/prompt.md"
}
}
}

ユーザーは、2 ステップの nx migrate フローでプラグインの migration を適用します。保留中の migration を生成し、インストールしてから実行します:

Terminal window
pnpm nx migrate my-plugin@latest
pnpm nx migrate --run-migrations

Agentic および hybrid migration は、利用可能な場合、nx migrate --run-migrations 中にユーザーのローカルコーディングエージェントによって適用されます。

アップグレードするユーザーの視点からの完全なフロー(deterministic および agentic migration の出力例を含む)については、ワークスペースのアップグレードを参照してください。