Bỏ qua để đến nội dung

Nx Migration Generator

Thêm một Nx Migration vào một Nx Plugin. Migrations cho phép nx migrate tự động cập nhật các dự án được tạo bởi phiên bản trước của plugin của bạn khi bạn thực hiện thay đổi breaking — đổi tên targets, thay đổi cấu hình được cung cấp, hoặc nâng cấp dependency yêu cầu các thay đổi code đi kèm.

Generator này tạo khung migration, đăng ký nó trong migrations.json của plugin của bạn, và kết nối trường nx-migrations vào package.json của plugin của bạn (tạo cả hai nếu chúng chưa tồn tại).

Terminal window
pnpm nx g @aws/nx-plugin:ts#nx-migration
Bạn cũng có thể thực hiện chạy thử để xem những tệp nào sẽ bị thay đổi
Terminal window
pnpm nx g @aws/nx-plugin:ts#nx-migration --dry-run
Tham sốKiểuMặc địnhMô tả
project Bắt buộcstring-Dự án Nx Plugin để thêm migration vào. Chúng tôi khuyến nghị sử dụng generator ts#nx-plugin để tạo dự án này.
name Bắt buộcstring-Tên migration, theo định dạng kebab-case (ví dụ: rename-foo-target)
description string-Mô tả về những gì migration thực hiện, được hiển thị bởi nx migrate
kind deterministic | agentic | hybriddeterministicLoại migration: deterministic (một codemod), agentic (một prompt được áp dụng bởi AI cho code do người dùng sở hữu), hoặc hybrid (một codemod thực hiện phần cơ học rồi chuyển giao cho agent)
preferInstallDependencies booleantrueCó nên ưu tiên cài đặt dependencies sau khi generator chạy hay không. Đặt thành false để trì hoãn việc cài đặt khi thực hiện hàng loạt nhiều generator (việc cài đặt vẫn chạy nếu cần thiết để các generator tiếp theo có thể tính toán Nx project graph); cài đặt một lần vào cuối.

Migrations có ba dạng, được phân biệt bởi các trường mà mục nhập migrations.json mang theo. Truyền --kind để chọn (mặc định deterministic):

  • Deterministic (implementation) — một codemod với trạng thái trước/sau chính xác, và là loại nên sử dụng đầu tiên. Nó chạy tự động, bao gồm cả trong CI và các terminal không tương tác, và áp dụng giống hệt nhau cho mọi người dùng. Sử dụng nó một mình khi thay đổi có hình dạng bạn có thể khớp một cách đáng tin cậy ở mọi nơi nó xuất hiện, báo cáo bất kỳ thứ gì nó bỏ qua qua nextSteps.
  • Hybrid (implementation + prompt) — cả hai nửa của một thay đổi. Sử dụng nó khi một thay đổi là breaking và không thể được áp dụng hoàn toàn một cách cơ học: codemod làm nhiều nhất có thể khớp một cách an toàn và trả về agentContext mô tả những gì nó đã thay đổi và những gì nó đã bỏ qua; Nx truyền context đó cho prompt được ghép đôi, điều hướng agent đến phần còn lại. Prompt là thứ ngăn workspace bị bỏ lại trong trạng thái hỏng khi các chỉnh sửa còn lại trải rộng quá nhiều hình dạng để codemod.
  • Agentic (prompt) — một tệp hướng dẫn markdown được áp dụng bởi coding agent cục bộ của người dùng thông qua luồng agentic nx migrate của Nx. Sử dụng nó một mình khi không có trạng thái trước/sau cơ học nào giữ được cả, bởi vì chỉnh sửa đúng phụ thuộc vào những gì người dùng đã xây dựng. Khi không có agent chạy (CI, không cài đặt agent, từ chối đồng ý), Nx hiển thị prompt dưới dạng hướng dẫn thủ công — vì vậy hãy viết prompts dưới dạng các bước độc lập, có thể thực hiện bởi con người.

Generator tạo các tệp sau trong thư mục nguồn của dự án plugin của bạn, tùy thuộc vào kind được chọn:

  • Thư mụcsrc/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

Một migration mới được đặt trong latest/. Nhóm các migrations theo bản phát hành gửi chúng giữ thứ tự của chúng hiển thị ngay lập tức khi bộ sưu tập phát triển — di chuyển một migration vào thư mục v<x.y.z>/ (và cập nhật đường dẫn của nó trong migrations.json) khi bạn gán phiên bản của nó.

Nó đăng ký migration trong migrations.json với các trường cho loại của nó và không có version — xem Versioning bên dưới. Khóa đã đăng ký được tiền tố với thư mục mà migration tồn tại (latest-<name>), vì vậy việc tái sử dụng tên cho một thay đổi sau này không thể ghi đè âm thầm một migration đã được gửi đi.

Một migration deterministic (hoặc hybrid) là một hàm thay đổi hệ thống tệp ảo (Tree), trả về một MigrationReturnObject. Đây là một migration hybrid, trả về cả hai:

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 };
}
  • nextSteps — các hành động tiếp theo mà Nx in ra cho người dùng sau khi nx migrate chạy. Mỗi mục nhập phải là thứ họ cần làm bằng tay: điều hòa một tệp bạn đã bỏ qua (xem các guardrails bên dưới), hoặc hoàn thành thứ gì đó ngoài tầm với của migration, như một lần cài đặt để làm mới tệp lock. Chúng không phải là nhật ký về những gì bạn đã thay đổi — khi một transform áp dụng sạch sẽ, không thêm gì, nếu không các mục nhập thực sự cần hành động sẽ bị chôn vùi.
  • agentContext (chỉ hybrid) — một tóm tắt về những gì codemod đã thay đổi, được truyền cho prompt được ghép đôi khi nó chạy dưới luồng agentic của Nx để agent có thể tập trung vào các phần thuộc sở hữu của người dùng.

Để biết một số ví dụ về các thao tác bạn có thể thực hiện trong migration của mình (tạo tệp, sửa đổi các tệp hiện có bằng GritQL, đọc và cập nhật JSON), hãy tham khảo hướng dẫn Nx Generator.

Migrations chạy trên các workspaces bạn không kiểm soát, vì vậy khuyến nghị tuân thủ những điều sau (khung được tạo ra đã tích hợp những điều này):

  • Chuyển đổi mã nguồn bằng GritQL. Sử dụng các helper GritQL (applyGritQL, matchGritQL) để chỉnh sửa nguồn thay vì regex hoặc thay thế chuỗi. GritQL khớp với AST, vì vậy một pattern giữ được trên toàn bộ định dạng, khoảng trắng và dấu ngoặc kép mà bản sao của người dùng có thể đã trôi vào, trong khi khớp văn bản âm thầm bỏ lỡ code tương đương hoặc làm hỏng tệp. Sử dụng updateJson cho JSON và parser phù hợp cho cấu hình có cấu trúc khác.
  • Khớp pattern trước khi ghi. Nếu một tệp đích đã khác biệt so với hình dạng mà generators của bạn tạo ra, hãy bỏ qua nó và báo cáo nó qua nextSteps, hoặc xem xét một migration hybrid, thay vì ghi đè các thay đổi của người dùng. matchGritQL là một cách thuận tiện để thực hiện kiểm tra đó.
  • Chỉ báo cáo những gì còn lại để làm. nextSteps dành cho công việc người dùng phải tiếp tục, không phải là bản ghi các chỉnh sửa bạn đã thực hiện — những thứ đó đã có trong git diff của họ.
  • Idempotent. Chạy lại migration phải là một no-op. Bảo vệ các rewrites GritQL chèn code bằng một mệnh đề where { ... <: not contains ... } để lần chạy thứ hai không thêm một bản sao trùng lặp.
  • Định dạng những gì bạn viết. Kết thúc bằng formatFilesInSubtree(tree) để các tệp mà migration của bạn viết được định dạng chính xác.

migration.spec.ts được tạo ra sử dụng createTreeUsingTsSolutionSetup() để xây dựng một workspace ảo khớp với hình dạng mà preset của chúng tôi tạo ra. Kiểm tra rằng migration đưa một workspace từ trạng thái “trước” đến trạng thái mục tiêu, bỏ qua (và báo cáo) code mà nó không nhận ra, và là idempotent:

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');
});
});

Unit tests chỉ chứng minh migration làm những gì bạn đã viết nó để làm. Cũng kiểm tra nó trên một workspace được tạo bởi phiên bản đã xuất bản của plugin của bạn — trạng thái mà người dùng của bạn nâng cấp từ đó — và xác nhận kết quả khớp với những gì generators của bạn tạo ra ngày hôm nay: kiểm tra diff, tạo các dự án giống nhau vào một workspace mới và so sánh, kiểm tra workspace vẫn build được, sau đó chạy lại migration để xác nhận nó không thay đổi gì. Lặp lại với một workspace bạn đã tùy chỉnh trước, để kiểm tra migration để các chỉnh sửa của bạn yên và báo cáo chúng thay thế.

Nx chạy một migration khi phiên bản plugin đã cài đặt của người dùng nhỏ hơn version của migration. Generator này viết không có trường version — gán nó (hoặc đóng dấu nó từ quy trình phát hành của bạn) khi bạn cắt bản phát hành gửi migration, để nó chạy cho mọi người nâng cấp từ phiên bản trước đó.

Khi bạn gán các phiên bản, một migrations.json đăng ký một migration của mỗi loại trông như thế này:

{
"$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"
}
}
}

Người dùng áp dụng các migrations của plugin của bạn với luồng hai bước nx migrate — tạo các migrations đang chờ, cài đặt, sau đó chạy chúng:

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

Các migrations agentic và hybrid được áp dụng bởi coding agent cục bộ của người dùng trong quá trình nx migrate --run-migrations khi có sẵn.

Để biết luồng đầy đủ từ góc độ người dùng đang nâng cấp — bao gồm đầu ra ví dụ cho các migrations deterministic và agentic — xem Nâng cấp Workspace của bạn.