From 847e7124d72790afdb12404f0a2f674858cf0be5 Mon Sep 17 00:00:00 2001 From: Paul Rastoin <45004772+prastoin@users.noreply.github.com> Date: Fri, 10 Apr 2026 11:43:06 +0200 Subject: [PATCH] Upgrade command internal doc (#19541) Open to discussion not sure on where to store such documentation --- .claude-pr/CLAUDE.md | 21 ++-- .cursor/rules/README.mdc | 8 +- .cursor/rules/changelog-process.mdc | 3 +- .cursor/rules/server-migrations.mdc | 45 +++++-- CLAUDE.md | 21 ++-- .../twenty-server/docs/UPGRADE_COMMANDS.md | 117 ++++++++++++++++++ 6 files changed, 178 insertions(+), 37 deletions(-) create mode 100644 packages/twenty-server/docs/UPGRADE_COMMANDS.md diff --git a/.claude-pr/CLAUDE.md b/.claude-pr/CLAUDE.md index 229477b7aaf..c0200c5c7c2 100644 --- a/.claude-pr/CLAUDE.md +++ b/.claude-pr/CLAUDE.md @@ -71,10 +71,10 @@ npx nx build twenty-server # Database management npx nx database:reset twenty-server # Reset database npx nx run twenty-server:database:init:prod # Initialize database -npx nx run twenty-server:database:migrate:prod # Run migrations +npx nx run twenty-server:database:migrate:prod # Run instance commands (fast only) -# Generate migration -npx nx run twenty-server:database:migrate:generate +# Generate an instance command (fast or slow) +npx nx run twenty-server:database:migrate:generate --name --type ``` ### Database Inspection (Postgres MCP) @@ -158,14 +158,17 @@ packages/ - **Redis** for caching and session management - **BullMQ** for background job processing -### Database & Migrations +### Database & Upgrade Commands - **PostgreSQL** as primary database - **Redis** for caching and sessions - **ClickHouse** for analytics (when enabled) -- Always generate migrations when changing entity files -- Migration names must be kebab-case (e.g. `add-agent-turn-evaluation`) -- Include both `up` and `down` logic in migrations -- Never delete or rewrite committed migrations +- When changing entity files, generate an **instance command** (`database:migrate:generate --name --type `) +- **Fast** instance commands handle schema changes; **slow** ones add a `runDataMigration` step for data backfills +- **Workspace commands** iterate over all active/suspended workspaces for per-workspace upgrades +- Commands use `@RegisteredInstanceCommand` and `@RegisteredWorkspaceCommand` decorators for automatic discovery +- Include both `up` and `down` logic in instance commands +- Never delete or rewrite committed instance command `up`/`down` logic +- See `packages/twenty-server/docs/UPGRADE_COMMANDS.md` for full documentation ### Utility Helpers Use existing helpers from `twenty-shared` instead of manual type guards: @@ -178,7 +181,7 @@ IMPORTANT: Use Context7 for code generation, setup or configuration steps, or li ### Before Making Changes 1. Always run linting (`lint:diff-with-main`) and type checking after code changes 2. Test changes with relevant test suites (prefer single-file test runs) -3. Ensure database migrations are generated for entity changes +3. Ensure instance commands are generated for entity changes (`database:migrate:generate`) 4. Check that GraphQL schema changes are backward compatible 5. Run `graphql:generate` after any GraphQL schema changes diff --git a/.cursor/rules/README.mdc b/.cursor/rules/README.mdc index 88b1b3b495e..c725458435e 100644 --- a/.cursor/rules/README.mdc +++ b/.cursor/rules/README.mdc @@ -12,7 +12,7 @@ This directory contains Twenty's development guidelines and best practices in th ### Core Guidelines - **architecture.mdc** - Project overview, technology stack, and infrastructure setup (Always Applied) - **nx-rules.mdc** - Nx workspace guidelines and best practices (Auto-attached to Nx files) -- **server-migrations.mdc** - Backend migration and TypeORM guidelines for `twenty-server` (Auto-attached to server entities and migration files) +- **server-migrations.mdc** - Upgrade command guidelines (instance commands and workspace commands) for `twenty-server` (Auto-attached to server entities and upgrade command files) - **creating-syncable-entity.mdc** - Comprehensive guide for creating new syncable entities (with universalIdentifier and applicationId) in the workspace migration system (Agent-requested for metadata-modules and workspace-migration files) ### Code Quality @@ -81,10 +81,8 @@ npx nx run twenty-server:typecheck # Type checking npx nx run twenty-server:test # Run unit tests npx nx run twenty-server:test:integration:with-db-reset # Run integration tests -# Migrations -npx nx run twenty-server:database:migrate:generate - -# Workspace +# Upgrade commands (instance + workspace) +npx nx run twenty-server:database:migrate:generate --name --type ``` ## Usage Guidelines diff --git a/.cursor/rules/changelog-process.mdc b/.cursor/rules/changelog-process.mdc index 14776fa66b6..e6a5bbb671a 100644 --- a/.cursor/rules/changelog-process.mdc +++ b/.cursor/rules/changelog-process.mdc @@ -55,7 +55,8 @@ If feature descriptions are not provided or need enhancement, research the codeb - Services: Look for `*.service.ts` files **For Database/ORM Changes:** -- Migrations: `packages/twenty-server/src/database/typeorm/` +- Instance commands (fast/slow): `packages/twenty-server/src/database/commands/upgrade-version-command/` +- Legacy TypeORM migrations: `packages/twenty-server/src/database/typeorm/` - Entities: `packages/twenty-server/src/entities/` ### Research Commands diff --git a/.cursor/rules/server-migrations.mdc b/.cursor/rules/server-migrations.mdc index 57115554dc8..91693bed0ac 100644 --- a/.cursor/rules/server-migrations.mdc +++ b/.cursor/rules/server-migrations.mdc @@ -1,27 +1,46 @@ --- -description: Guidelines for generating and managing TypeORM migrations in twenty-server +description: Guidelines for generating and managing upgrade commands (instance commands and workspace commands) in twenty-server globs: [ "packages/twenty-server/src/**/*.entity.ts", - "packages/twenty-server/src/database/typeorm/**/*.ts" + "packages/twenty-server/src/database/commands/upgrade-version-command/**/*.ts" ] alwaysApply: false --- -## Server Migrations (twenty-server) +## Upgrade Commands (twenty-server) -- **When changing an entity, always generate a migration** - - If you modify a `*.entity.ts` file in `packages/twenty-server/src`, you **must** generate a corresponding TypeORM migration instead of manually editing the database schema. - - Use the Nx command from the project root: +The upgrade system uses two types of commands instead of raw TypeORM migrations: +- **Instance commands** — schema and data migrations that run once at the instance level. +- **Workspace commands** — commands that iterate over all active/suspended workspaces. + +See `packages/twenty-server/docs/UPGRADE_COMMANDS.md` for full documentation. + +### Instance Commands + +- **When changing a `*.entity.ts` file**, generate an instance command: ```bash - npx nx run twenty-server:database:migrate:generate + npx nx run twenty-server:database:migrate:generate --name --type ``` -- **Prefer generated migrations over manual edits** - - Let TypeORM infer schema changes from the updated entities; only adjust the generated migration file manually if absolutely necessary (for example, for data backfills or complex constraints). - - Keep schema changes (DDL) in these generated migrations and avoid mixing in heavy data migrations unless there is a strong reason and clear comments. +- **Fast commands** (`--type fast`, default) are for schema-only changes that must run immediately. They implement `FastInstanceCommand` with `up`/`down` methods and use the `@RegisteredInstanceCommand` decorator. -- **Keep migrations consistent and reversible** - - Ensure the generated migration includes both `up` and `down` logic that correctly applies and reverts the entity change when possible. - - Do not delete or rewrite existing, committed migrations unless you are explicitly working on a pre-release branch where history rewrites are allowed by team conventions. +- **Slow commands** (`--type slow`) add a `runDataMigration` method for potentially long-running data backfills that execute before `up`. They only run when `--include-slow` is passed. Use the decorator with `{ type: 'slow' }`. + +- The generator auto-registers the command in `instance-commands.constant.ts` — do not edit that file manually. + +- **Keep commands consistent and reversible**: include both `up` and `down` logic. Do not delete or rewrite existing, committed commands unless on a pre-release branch. + +### Workspace Commands + +- Use the `@RegisteredWorkspaceCommand` decorator alongside nest-commander's `@Command` decorator. +- Extend `ActiveOrSuspendedWorkspaceCommandRunner` and implement `runOnWorkspace`. +- The base class provides `--dry-run`, `--verbose`, and workspace filter options automatically. + +### Execution Order + +Within a given version, commands run in this order (timestamp-sorted within each group): +1. Instance fast commands +2. Instance slow commands (only with `--include-slow`) +3. Workspace commands diff --git a/CLAUDE.md b/CLAUDE.md index 229477b7aaf..c0200c5c7c2 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -71,10 +71,10 @@ npx nx build twenty-server # Database management npx nx database:reset twenty-server # Reset database npx nx run twenty-server:database:init:prod # Initialize database -npx nx run twenty-server:database:migrate:prod # Run migrations +npx nx run twenty-server:database:migrate:prod # Run instance commands (fast only) -# Generate migration -npx nx run twenty-server:database:migrate:generate +# Generate an instance command (fast or slow) +npx nx run twenty-server:database:migrate:generate --name --type ``` ### Database Inspection (Postgres MCP) @@ -158,14 +158,17 @@ packages/ - **Redis** for caching and session management - **BullMQ** for background job processing -### Database & Migrations +### Database & Upgrade Commands - **PostgreSQL** as primary database - **Redis** for caching and sessions - **ClickHouse** for analytics (when enabled) -- Always generate migrations when changing entity files -- Migration names must be kebab-case (e.g. `add-agent-turn-evaluation`) -- Include both `up` and `down` logic in migrations -- Never delete or rewrite committed migrations +- When changing entity files, generate an **instance command** (`database:migrate:generate --name --type `) +- **Fast** instance commands handle schema changes; **slow** ones add a `runDataMigration` step for data backfills +- **Workspace commands** iterate over all active/suspended workspaces for per-workspace upgrades +- Commands use `@RegisteredInstanceCommand` and `@RegisteredWorkspaceCommand` decorators for automatic discovery +- Include both `up` and `down` logic in instance commands +- Never delete or rewrite committed instance command `up`/`down` logic +- See `packages/twenty-server/docs/UPGRADE_COMMANDS.md` for full documentation ### Utility Helpers Use existing helpers from `twenty-shared` instead of manual type guards: @@ -178,7 +181,7 @@ IMPORTANT: Use Context7 for code generation, setup or configuration steps, or li ### Before Making Changes 1. Always run linting (`lint:diff-with-main`) and type checking after code changes 2. Test changes with relevant test suites (prefer single-file test runs) -3. Ensure database migrations are generated for entity changes +3. Ensure instance commands are generated for entity changes (`database:migrate:generate`) 4. Check that GraphQL schema changes are backward compatible 5. Run `graphql:generate` after any GraphQL schema changes diff --git a/packages/twenty-server/docs/UPGRADE_COMMANDS.md b/packages/twenty-server/docs/UPGRADE_COMMANDS.md new file mode 100644 index 00000000000..024fc3eb0a5 --- /dev/null +++ b/packages/twenty-server/docs/UPGRADE_COMMANDS.md @@ -0,0 +1,117 @@ +# Upgrade Commands + +The upgrade process relies on two types of commands: + +- **Instance commands** — schema and data migrations that run once at the instance level (replacing raw TypeORM migrations). +- **Workspace commands** — commands that iterate over all active or suspended workspaces to apply per-workspace changes. + +Both are registered via decorators and automatically discovered by the upgrade pipeline. + +## Instance Commands + +### Generating an instance command + +```bash +npx nx run twenty-server:database:migrate:generate --name --type +``` + +This generates a timestamped file and auto-registers it in `instance-commands.constant.ts` — do not edit that file manually. + +### Fast instance commands + +Fast commands run immediately during the upgrade. They are used for schema changes that could introduce breaking inconsistencies between the database and the server if delayed. + +A fast command implements `FastInstanceCommand` and provides `up` / `down` methods: + +```ts +@RegisteredInstanceCommand('1.22.0', 1775758621017) +export class AddWorkspaceIdToTotoFastInstanceCommand + implements FastInstanceCommand +{ + public async up(queryRunner: QueryRunner): Promise { + await queryRunner.query( + `ALTER TABLE "core"."toto" ADD "workspaceId" uuid`, + ); + } + + public async down(queryRunner: QueryRunner): Promise { + await queryRunner.query( + `ALTER TABLE "core"."toto" DROP COLUMN "workspaceId"`, + ); + } +} +``` + +### Slow instance commands + +Slow commands are used when a potentially long-running data migration must happen before the schema change. They only run when the `--include-slow` flag is passed. + +A slow command implements `SlowInstanceCommand`, which extends `FastInstanceCommand` with an additional `runDataMigration` method that executes before `up`: + +```ts +@RegisteredInstanceCommand('1.22.0', 1775758621018, { type: 'slow' }) +export class BackfillWorkspaceIdSlowInstanceCommand + implements SlowInstanceCommand +{ + async runDataMigration(dataSource: DataSource): Promise { + // Backfill logic (can be slow — e.g. iterating over workspaces, cache recomputation) + } + + public async up(queryRunner: QueryRunner): Promise { + await queryRunner.query( + `ALTER TABLE "core"."toto" ALTER COLUMN "workspaceId" SET NOT NULL`, + ); + } + + public async down(queryRunner: QueryRunner): Promise { + await queryRunner.query( + `ALTER TABLE "core"."toto" ALTER COLUMN "workspaceId" DROP NOT NULL`, + ); + } +} +``` + +A common pattern is to pair a **fast** command (add a nullable column) with a **slow** command (backfill existing rows, then set `NOT NULL`). + +## Workspace Commands + +Workspace commands run per-workspace logic across all active or suspended workspaces. They are registered with the `@RegisteredWorkspaceCommand` decorator alongside nest-commander's `@Command` decorator: + +```ts +@RegisteredWorkspaceCommand('1.22.0', 1780000002000) +@Command({ + name: 'upgrade:1-22:backfill-standard-skills', + description: + 'Backfill standard skills for existing workspaces', +}) +export class BackfillStandardSkillsCommand + extends ActiveOrSuspendedWorkspaceCommandRunner +{ + constructor( + protected readonly workspaceIteratorService: WorkspaceIteratorService, + // inject any services you need + ) { + super(workspaceIteratorService); + } + + override async runOnWorkspace({ + workspaceId, + options, + }: RunOnWorkspaceArgs): Promise { + // Per-workspace logic goes here + // options.dryRun, options.verbose are available for free + } +} +``` + +The base class `ActiveOrSuspendedWorkspaceCommandRunner` handles workspace iteration and provides `--dry-run`, `--verbose`, and workspace filter options automatically. + +## Execution Order + +Within a given version of Twenty, the upgrade pipeline runs commands in this order, sorted by timestamp within each group: + +1. **Instance fast** commands +2. **Instance slow** commands +3. **Workspace commands** + +Workspace commands are executed sequentially across all active/suspended workspaces.