CI との連携
RenMori は、標準的な Ren'Py の配布用ビルドに対して実行するビルド時の単一コマンドであり、ゲームのソースを変更する必要はありません。入力には game/ サブディレクトリを含む任意のディレクトリを指定でき、これは Ren'Py ランチャーの Build Distributions が生成するものに相当します。
このページでは CI での利用方法のみを扱います。コマンドラインオプションの一覧は renmori protect、renmori.toml の設定項目は設定ファイル、出力形式、ビルドマニフェスト、終了コードは出力・ビルドマニフェスト・終了コードを参照してください。
パイプラインの基本的な流れ
protect は game/ 以下のすべてを game/renmori.rmc に格納し、コンパイル済みの Ren'Py スクリプトを変換し、平文の元ファイルを削除したうえで、ローダー拡張 game/renmori.rpe を書き込み、renmori-manifest.json を出力します。verify はこのビルドマニフェストに基づいて保護済みビルドを再検証するため(コンテナのハッシュ、必須ファイル、平文の残存がないこと)、CI のゲートとして適しています。
-o を指定した場合、元のビルドは一切変更されず(保護されるのはコピー)、バックアップも作成されません。-o を指定しないインプレース保護では、まず元のビルドを同じ階層の <dir>_bak ディレクトリにコピーし、このディレクトリがすでに存在する場合は失敗します。--no-backup を指定するとこのコピーを省略します。
stdout の最終行は常に JSON オブジェクト({"status": "ok", …} または {"status": "error", …})であり、進捗は stderr に出力されます。JSON のキーと値はどのロケールでも英語のままであるため、パイプラインでの解析は出力の装飾や言語の影響を受けません。--quiet を指定すると JSON のみが出力されます。パイプラインでは終了コードによって成否を判定してください。各終了コードは出力・ビルドマニフェスト・終了コードに記載されています。
非対話時の動作
RenMori が確認プロンプトを表示するのは対話型端末(TTY)上のみで、対象はインプレース保護前の [y/N] 確認と、クローズドプラグインのダウンロード前の確認です。以下のいずれかに該当する場合はすべてのプロンプトがスキップされるため、パイプラインが入力待ちで停止することはありません。
-y/--yesを指定した場合--quietを指定した場合- stdin が TTY でない場合(CI では通常この状態)
TTY でない環境では色付き出力が自動で無効になり、NO_COLOR も尊重されます。人が読むための出力の言語はシステムのロケールに従い、--lang en|zh または環境変数 RENMORI_LANG で固定できます。
保護強度とクローズドプラグイン
strength を設定しない場合、RenMori はデフォルトでフル保護、すなわちクローズドプラグインが報告する最も強い強度を使用します。利用可能なプラグインがない場合は、ベースライン形式へ暗黙に格下げすることなく実行が失敗します。CI では必要に応じて次のいずれかを選択します。
プラグインが必要な場合、RenMori は --plugin または環境変数 RENMORI_PLUGIN で指定されたパス、ローカルキャッシュの順に探し、最後に公開リリースからプラグインをダウンロードして SHA256SUMS で検証し、キャッシュに保存します。非対話環境では確認なしでダウンロードが行われます。詳細は保護強度とクローズドプラグインを参照してください。
CI でのプラグインのキャッシュ
プラグインのキャッシュディレクトリは、プラグインのリリースタグごとに分かれています。
このディレクトリを CI の実行間で保持すれば、ビルドのたびにプラグインをダウンロードせずに済みます。GitHub Actions では、Protect ステップの前にキャッシュステップを追加し、キャッシュキーを RenMori が使用するプラグインのリリースタグと一致させます。
以下の環境変数により、プラグインの取得方法をさらに制御できます。
鍵の管理
[keys] セクションはベースライン形式にのみ適用されます。クローズドプラグインを使用する場合、プラグインは自ら鍵を導出し、これらの設定を読み込みません。
デフォルトでは、protect はビルドごとに新しいランダムなマスター鍵と、それとは独立したスクリプト鍵を生成し、ローダー拡張に埋め込みます(これはベースラインのプレースホルダーであり、クローズドのランタイムでは代わりに実行時に鍵を導出します)。鍵を自分で管理する場合は、鍵を保持する環境変数の名前を renmori.toml で指定します。各鍵は 16 進数 64 文字(32 バイト)です。
これらの変数には CI のシークレットストアから値を設定し、鍵を決してリポジトリにコミットしないでください。指定した変数が設定されていない場合、RenMori はランダムな鍵にフォールバックし、その旨を報告します。
RenMori が読み込むのは [keys] で名前を指定した環境変数のみです。renmori.toml に [keys] セクションがない場合、CI で RENMORI_MASTER_KEY などの変数を設定しても効果はなく、すべてのビルドで引き続きランダムな鍵が使用されます。
鍵を固定しても、ビルド間で出力がバイト単位で再現されることはありません(各ファイルがランダムなソルトを使用するため)。これは意図された設計です。パイプラインでは、ビルド間でコンテナのハッシュを比較するのではなく、verify の結果とビルドマニフェスト内のファイル一覧に対してアサーションを行ってください。
GitHub Actions の例
リポジトリ内の examples/github-actions-protect.yml は、ゲームのビルド、renmori protect と renmori verify の実行、そして保護済みの配布用ビルドのアーティファクトとしてのアップロードという一連の流れを示しています。
使用手順は次のとおりです。
- このファイルをプロジェクトの
.github/workflows/protect.ymlにコピー - Build the game ステップを実際の Ren'Py のビルドコマンドに置き換える。このステップは
game/サブディレクトリを含むディレクトリを生成する必要がある - 必要に応じてトリガーを調整。この例は手動実行時と
v*タグのプッシュ時に実行される - 必要に応じて上記のプラグインキャッシュのステップを追加し、実行のたびにプラグインがダウンロードされないようにする
RenMori が動作するのは CI のランナー(ビルド環境)上のみです。保護済みのランタイムはゲームに同梱されるため、プレイヤーが Python を別途インストールする必要はありません。この例はデフォルトの強度で実行されるため、ランナー上でクローズドプラグインを取得します。Protect ステップの鍵の環境変数が効果を持つのは、ベースライン形式を使用し、かつ renmori.toml の [keys] セクションでそれらの名前を指定した場合に限られます。詳しくは上記の「鍵の管理」を参照してください。
実際のエンジンによるエンドツーエンドの検証(任意)
tools/verify_in_renpy.py はサンプルゲームを保護し、実際の Ren'Py SDK の中で、すべてのアセットがバイト単位で正確に復号されること、Ren'Py スクリプトが変換されたうえで引き続き解析できること、そして注入されたスクリプトが拒否されることを確認します。SDK とゲームを指定して実行し、SDK が利用できない環境ではこの検証はスキップされます。