mcpField
M
migrating-motoko

migrating-motoko

github.com/dfinity開発3977 回インストール今日更新
リポジトリ

migrating-motoko について

migrating-motokoは、Motoko Actorのアップグレード時に状態を移行するスキルです。永続フィールドやデータ構造を変更する開発者向けに、互換変更による暗黙の移行と、Actorに付ける式による明示的な変換を説明します。

主な機能

  1. 01暗黙・明示の移行要件を区別
  2. 02移行関数でActorの状態を変換
  3. 03フィールド名や型の変更例を提示
  4. 04移行関数の入出力規則を説明
  5. 05ビルドとチェック手順を提示

活用シーン

  1. 01アップグレード時の永続フィールド名変更
  2. 02フィールド型や状態構造の変更
  3. 03コレクション内データの変換
  4. 04追加フィールドへの初期値設定

インストール

コマンドをコピーしてターミナルで実行します。

$npx skills add https://github.com/dfinity/icskills --skill migrating-motoko

インストール後、Claude Code で/migrating-motokoを入力して読み込まれたか確認します。

詳細

インラインアクター移行

アクターに付加した移行式を使って、キャニスターのアップグレード時にアクターの状態を移行します。各アップグレードで使用できる移行関数は最大1つです。

migrations/ ディレクトリを使った複数の移行には、代わりに migrating-motoko-enhanced を読み込んでください。

使用する場面

暗黙的な移行(コード不要)

新しいプログラムが古いプログラムと互換性がある場合、ランタイムはアップグレードを許可します。

  • アクターのフィールドを追加する
  • アクターのフィールドを削除する
  • 可変性を変更する(var ↔ let)
  • バリアントのコンストラクターを追加する
  • 型を拡張する(Nat → Int)
明示的な移行が必要
  • フィールド名を変更する
  • フィールドの型を変更する(例:Bool → バリアント、Int → Float)
  • 状態の構造を変更する(フィールドの分割/統合)
  • コレクションの値を変換する

構文

アクターの直前に置く、括弧付きの式:

import Migration "migration";

(with migration = Migration.run)
actor {
  var newState : Float = 0.0;
};

または、インラインで記述します。

import Int "mo:core/Int";

(with migration = func(old : { var state : Int }) : { var newState : Float } {
  { var newState = old.state.toFloat() }
})
actor {
  var newState : Float = 0.0;
};

インポートしたモジュールが migration フィールドをエクスポートする場合は、短縮形も使えます。

import { migration } "migration";

(with migration)
actor { ... };

移行関数のルール

  • 型:func (old : { ... }) : { ... } — ローカルかつ非ジェネリックで、両方のレコードに永続化可能な型を使う必要があります(関数や可変配列は不可)
  • 定義域:古いアクターのフィールド(以前のバージョンの名前と型)
  • 値域:新しいアクターのフィールド(新しいアクターに存在し、互換性のある型である必要があります)
  • アップグレード時のみ実行されます — 新規インストール時は通常どおりイニシャライザーが実行されます
  • 移行関数がトラップすると、アップグレードは中止され、キャニスターは古いバージョンのままになります
フィールドの意味
フィールドの出現箇所効果
入力と出力フィールドが変換されます
出力のみ移行によって新しいフィールドが生成されます
入力のみフィールドが消費されます(コンパイラーはデータ損失の可能性について警告します)
どちらにもない引き継がれるか、宣言によって初期化されます

移行モジュールのパターン

移行は別のモジュールにまとめます。古い型はインラインで定義し、古いコードパスからインポートしないでください。

// migration.mo
import Types "types";
import Map "mo:core/Map";

module {
  type OldTask = { id : Nat; title : Text; completed : Bool };

  type OldActor = {
    var tasks : Map.Map<Nat, OldTask>;
    var nextId : Nat;
  };

  type NewActor = {
    var tasks : Map.Map<Nat, Types.Task>;
    var nextId : Nat;
  };

  public func run(old : OldActor) : NewActor {
    let tasks = old.tasks.map<Nat, OldTask, Types.Task>(
      func(_, task) {
        {
          id = task.id;
          title = task.title;
          due = 0;
          var status = if (task.completed) #completed else #pending;
        }
      }
    );
    { var tasks; var nextId = old.nextId };
  };
};
// main.mo
import Map "mo:core/Map";
import Types "types";
import Migration "migration";

(with migration = Migration.run)
actor {
  var tasks = Map.empty<Nat, Types.Task>();
  var nextId : Nat = 0;
};

フィールドにはイニシャライザーが必要です — 移行関数はアップグレード時のみ実行されます。新規インストール時はイニシャライザーが使われます。

よく使われるパターン

デフォルト値を指定してフィールドを追加する
old.users.map<Nat, OldUser, NewUser>(
  func(_, u) { { u with zipCode = "" } }
)
オプショナルフィールドを追加する
{ task with var assignee = null : ?Principal }
Bool からバリアントへの変換
var status = if (task.completed) #completed else #pending;
フィールド名を変更する

古い名前を消費し、新しい名前を生成します。

func(old : { var state : Int }) : { var value : Int } {
  { var value = old.state }
}
フィールドを削除する

入力でフィールドを消費し、出力から省略します。コンパイラーが警告するため、損失が意図したものか確認してください。

チェックリスト

  • 暗黙的(互換性のある変更)か明示的(移行関数)かを決める
  • 明示的な移行の場合:migration.mo に古い型をインラインで定義する
  • 移行の型:永続化可能な型を使った func (old : RecordIn) : RecordOut
  • アクターの前に (with migration = Migration.run) を付ける
  • データ移行に preupgrade/postupgrade を使わない
  • mops check --fix と mops build で検証する

参考資料

  • Motoko 言語全般のリファレンスと mo:core API については、motoko を読み込みます
  • --enhanced-migration を使った複数の移行については、migrating-motoko-enhanced を読み込みます
  • mops check、mops build、ツールチェーンのセットアップについては、mops-cli を読み込みます

コメント