en un clic
create-migration
// Misskey の TypeORM マイグレーションを公式 CLI (migration:generate / migration:create) で正しく生成し、SPDX ヘッダー付与・up/down 整合・check-migrations 確認まで誘導する。エンティティのスキーマ変更を含むあらゆる DB 変更、または手書き SQL によるデータ移行が必要な時に使用する。
// Misskey の TypeORM マイグレーションを公式 CLI (migration:generate / migration:create) で正しく生成し、SPDX ヘッダー付与・up/down 整合・check-migrations 確認まで誘導する。エンティティのスキーマ変更を含むあらゆる DB 変更、または手書き SQL によるデータ移行が必要な時に使用する。
| name | create-migration |
| description | Misskey の TypeORM マイグレーションを公式 CLI (migration:generate / migration:create) で正しく生成し、SPDX ヘッダー付与・up/down 整合・check-migrations 確認まで誘導する。エンティティのスキーマ変更を含むあらゆる DB 変更、または手書き SQL によるデータ移行が必要な時に使用する。 |
packages/backend/migration/ に新規 TypeORM マイグレーションを追加するためのワークフロー。
作り方は AGENTS.md §3 の「
Date.now()で UNIX ms を取得 →{ms}-{PascalName}.jsを手書き」が最低ライン。エンティティ差分から自動生成したい (= TypeORM のmigration:generateを使う) 場合は本 skill の手順に従う。どちらでも構わないが、エンティティ変更を伴う時は CLI 経由のほうが取り漏れが減るので推奨。
| 状況 | 方式 |
|---|---|
エンティティ (packages/backend/src/models/*.ts) を @Column / @Index / @Entity 等で先に変更し、差分から自動生成したい | typeorm migration:generate (本 skill の手順) |
手書き SQL / データ移行 / CREATE INDEX CONCURRENTLY など、エンティティ差分では表現できない変更 | typeorm migration:create で空雛形を作るか、migrate-new command で手書き雛形を作る |
| 列追加 1 本のような小規模変更で、既存ファイルをコピーした方が速い | AGENTS.md §3 の手順 (Date.now() + 手書き) でよい |
迷ったら まずエンティティを変更 → migration:generate が原則。既存 342 ファイルのほぼすべてが queryRunner.query(\SQL...`)` の raw SQL なので、CLI 出力でも手書きでもスタイルは揃う。
ルートディレクトリから以下を実行する。<PascalName> は変更内容を表す PascalCase (例: AddBirthdayIndex, AddCategoryToAvatarDecorations)。
CONTRIBUTING.md §Migration作成方法 に記載の基本形:
# packages/backend ディレクトリで実行する場合 (CONTRIBUTING.md 記載形式)
pnpm dlx typeorm migration:generate -d ormconfig.js -o --esm <PascalName>
リポジトリルートから実行する場合 (AI が使う推奨形式。pnpm --filter backend exec を使うと backend の TypeORM バージョンと一致するため確実):
pnpm --filter backend exec typeorm migration:generate -d ormconfig.js -o --esm migration/<PascalName>
--esmについて:-o/--outputJsは「TS ではなく JS を出力する」オプション、--esmは「ESM 形式 (export class ...) で出力する」オプション。Misskey の既存 migration はすべて ESM JS であるため 両方が必須。--esmを省略すると CommonJS 形式の JS が生成されスタイルが揃わない。
事前準備:
pnpm build-pre を実行して built/meta.json を生成する (loadConfig() が built/meta.json を必須とするため。pnpm build 済みであれば不要)。.config/default.yml が存在すること (なければ .config/example.yml を参考に作成する)。pnpm --filter backend compile-config を実行して built/.config.json を生成する (ormconfig.js が loadConfig() 経由で必須とする。未実行だと "Compiled configuration file not found." エラーになる)。pnpm --filter backend build でエンティティを最新ビルド (CLI は built/ を読む)。docker compose -f compose.local-db.yml up -d)。pnpm --filter backend exec typeorm migration:create -o --esm migration/<PascalName>
ローカル DB の起動とビルドは不要。空の up / down だけが生成される。
-o --esmを 必ず付ける。これが無いと<UnixMs>-<PascalName>.ts(CommonJS / TS 出力) が生成されるが、Misskey のormconfig.jsはmigration/*.jsだけを読み、既存の他 migration も全てexport class ... { async up(queryRunner) {...} }の ESM JS 形式なので、後で手作業で.ts → .jsリネーム +import { MigrationInterface }削除 +class ... implements MigrationInterface削除をしないと走らない。-o --esmを付ければそのまま.jsESM で出るので、後処理は SPDX ヘッダー付与 (ステップ 3) だけで済む。
CLI 出力には SPDX ヘッダーが含まれない。必ず冒頭に追加する (CI の spdx ジョブが失敗するため)。
/*
* SPDX-FileCopyrightText: syuilo and misskey-project
* SPDX-License-Identifier: AGPL-3.0-only
*/
up() の各ステートメントに対し、down() で完全に巻き戻せること。ADD COLUMN) ↔ 列削除 (DROP COLUMN)、テーブル作成 ↔ テーブル削除、
FK 追加 ↔ FK 削除、インデックス作成 ↔ インデックス削除 を必ずペアで書く。down() を空のまま残さない。本番ロールバック時に詰む。大規模テーブルへの CREATE INDEX は本番で長時間ロックする恐れがある。CONCURRENTLY で発行するときは migration 側にも対応が必要: PostgreSQL は CREATE INDEX CONCURRENTLY を transaction 内で実行できないため、migration class に以下を仕込んで TypeORM に「この migration は transaction を張らない」と指示する。
参照実装: packages/backend/migration/1745378064470-composite-note-index.js。
const isConcurrentIndexMigrationEnabled = process.env.MISSKEY_MIGRATION_CREATE_INDEX_CONCURRENTLY === '1';
export class CompositeNoteIndex1745378064470 {
name = 'CompositeNoteIndex1745378064470';
transaction = isConcurrentIndexMigrationEnabled ? false : undefined;
async up(queryRunner) {
const concurrently = isConcurrentIndexMigrationEnabled;
if (concurrently) {
// CREATE INDEX CONCURRENTLY ...
} else {
// CREATE INDEX ...
}
}
async down(queryRunner) {
// 同様に環境変数で分岐
}
}
要点:
transaction = isConcurrentIndexMigrationEnabled ? false : undefined; が必須。これがないと CREATE INDEX CONCURRENTLY が transaction 内で実行されて ERROR: CREATE INDEX CONCURRENTLY cannot run inside a transaction block で失敗する。MISSKEY_MIGRATION_CREATE_INDEX_CONCURRENTLY=1 がデフォルト OFF。OFF のときは普通の CREATE INDEX (transaction 内) で動く必要がある。up/down 双方を環境変数で分岐させる。ormconfig.js の migrationsTransactionMode は 環境変数で切り替わる: MISSKEY_MIGRATION_CREATE_INDEX_CONCURRENTLY=1 のときだけ 'each' (各 migration が個別 transaction)、未設定時は 'all' (全 migration を 1 つの transaction でラップ) (ormconfig.js:19)。普段は 'all' 前提なので、CONCURRENTLY を使う migration を書く時だけこのフラグの存在を意識すれば良い。migration:generate を使った場合、エンティティ側の @Column / @Entity 修正と DB スキーマが食い違うとビルド全体がズレる。生成後に該当エンティティと SQL の対応を目視確認すること。
ルートから実行:
# 未反映の差分が無いか (新規 migration が生成すべき DDL を取り逃していないか)
pnpm --filter backend check-migrations
# ローカル DB に適用
pnpm migrate
# ロールバック (down が壊れていないか)
pnpm revert
# 再適用 (順方向にもう一度通す)
pnpm migrate
check-migrations の実体は scripts/check_migrations_clean.js。TypeORM の dataSource.driver.createSchemaBuilder().log() で pending DDL を取得し、upQueries / downQueries のいずれかが残っていれば非ゼロ終了する。順序検査ではなく「エンティティと migration が同期しているか」の検査。
新規ファイルを書くときは、変更パターンが近い既存ファイルを 必ずひとつ開いて並べて書く。スタイルが激しくズレた PR は差し戻されやすい。
クラス命名規則は PascalCase 名 + 13 桁タイムスタンプ (例: class BirthdayIndex1767169026317)。name プロパティもクラス名と同一文字列にする。
スキーマ変更がユーザーに見える挙動を生む場合のみ、CHANGELOG.md の ## Unreleased → ### Server または ### General に 1 行追加する (AGENTS.md §CHANGELOG 参照)。内部リファクタや純粋なインデックス追加は不要。
Misskey の i18n キーを追加・修正する。locales/ja-JP.yml のみ編集可能で、他言語ファイル (en-US.yml 等 39 言語) は Crowdin の自動配信先のため絶対に触らない。型は packages/i18n が ja-JP.yml から自動再生成する。frontend からは i18n.ts.<key> または i18n.tsx.<key>(...) で参照する。
Misskey の REST API エンドポイント (/api/<category>/<name>) を NestJS DI + meta/paramDef 規約で追加する。バックエンドに新しい API ルートを足す時に必ず使う。endpoint-list.ts への手動登録、e2e テスト、misskey-js 再生成、CHANGELOG までの一連の手順を含む。
Misskey フロントエンドの新規 Vue 3 コンポーネントを追加する。Mk* 命名 / SPDX (HTML コメント) / <script setup lang="ts"> / <style lang="scss" module> / *.stories.impl.ts 併設の規約をまとめて適用する。新しい共有 UI コンポーネントを packages/frontend/src/components/ に作る時に使う。
Claude Code セッションのコンテキスト窓消費を agents/skills/MCP/rules/CLAUDE.md ごとに見える化し、肥大化と冗長コンポーネントを検出して節約候補を提示する。"コンテキスト消費を見せて"、"context budget"、"context audit"、"トークン内訳"、"これ以上 MCP 入る?" 等の発話で起動する。