Commit 3e44d79
feat: PLAN35-runtime 起動ラッパーとコンテナへの受け渡し (#93)
* feat: 機密を平文ファイルを介さずコンテナへ渡す経路へ移行する
起動ラッパーが共通の機密ファイルを source するのをやめ、機密は Python 本体が
必要になった時点で復号してメモリ上で合成する。コンテナへは変数名だけを列挙
した構成で渡すため、暗号文も平文ファイルも Docker Compose には渡らない。
- 起動ラッパーは非機密設定 ($DEVBASE_ROOT/env) だけを読む。シェルから読める
場所に機密を置かないことが暗号化の前提であり、ここで読むと意味が無くなる
- ホスト側で機密を必要とする処理は実測で 2 系統だけだった。Docker Compose の
変数展開 (ローカル S3 互換サービスの資格情報) と、それを含むビルド呼び出し。
ビルドは devbase env exec 経由にして Python 側から環境変数を渡す
- 生成する構成へは変数名のみを書き、値は devbase 自身の環境変数から解決させる。
台数拡張時に生成する構成そのものへ書き込むため、別ファイルの上書きを重ねる
必要がなく適用順序の問題が起きない
- 重ね順は従来の env_file の並びを維持する。共通機密とプロジェクト機密のキーを
列挙しつつ、両方に同じキーがある場合は値として非機密設定側を採用する。
environment は env_file より優先されるため、こうしないと「プロジェクト設定が
共通設定を上書きする」関係が反転する
- 実在しない env_file 参照は生成時に落とす。暗号化で平文が無くなった参照が
残っていると Compose が起動時に落ちるため
移行コマンド (encrypt / decrypt) を追加した。暗号化は「読み戻せることを確認して
から平文を退避する」順序で行う。鍵の指定を誤ったまま平文を失うと、誰にも復号
できないファイルだけが残るため。退避した平文は自動では消さず、場所を案内する。
構成ファイルの書き換えは行のコメントアウトで行い、元の行をそのまま残す。YAML と
して読み書きし直すと利用者のコメントや整形が失われること、および平文へ戻す操作で
元の行を機械的に復元できることの 2 点による。
コンテナ起動前の設定チェックは、ファイルの有無ではなく秘密ストアに設定があるかで
判定する。移行済みの環境で毎回 env init が走るのを避けるため。
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_016Z922jZ4R3488KR3GETgS1
* fix: env encrypt/decrypt が途中で失敗しても中間状態を残さないようにする
encrypt / decrypt はどちらも対象ごとにループ内で破壊的な操作を実行していたため、
後続の対象で失敗すると先行対象だけ移行済みになり、しかも compose.yml の書き換えは
実行されず「構成ファイルが存在しないファイルを参照する」壊れた状態で終わっていた。
また _apply_compose_changes は書き込み失敗をログに出して次のファイルへ進むため、
機密の移動・削除が済んだあとでもコマンドが成功扱いになっていた。
実行した操作ごとに取り消し手続きを積み、どこで失敗しても逆順に巻き戻す _Rollback を
追加し、encrypt / decrypt の両方で共有する。
- encrypt: 全対象の暗号化と読み戻し検証 → 平文の退避 → compose.yml の書き換え
- decrypt: 全対象の復号確認 (生バイト列も控える) → 平文の書き出し →
compose.yml の復元 → 最後に暗号文を削除。破壊的な削除を最後に置くことで、
途中で失敗したときに失うものを最小にする
- _apply_compose_changes は書き込み失敗を MigrationError で呼び出し元へ伝え、
書けたぶんは巻き戻す。巻き戻し自体が失敗したら何が残っているかを列挙する
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_016Z922jZ4R3488KR3GETgS1
* fix: 部分復号・空行・注入スキップ・インライン env_file の取りこぼしを直す
PR #93 のレビュー指摘 4 件に対応する。
- compose_migrate.enable() に disable() と同じ targets 引数を持たせた。
`env decrypt --project` で一部だけ復号したとき、これまでは compose 内の
全マーカーを戻していたため、まだ暗号化されたままの共通設定
(${DEVBASE_ROOT}/.env) の参照まで有効になり Compose の起動が失敗していた。
無効化と復元で同じ判定 (_compose_targets) を使うようにして揃えた。
キー行 (`env_file:`) は有効なエントリが 1 つ以上戻ったときだけ復元する。
- disable() が env_file リスト内の空行で break していたのをスキップに変えた。
空行以降のエントリを無効化し損ねるうえ、「有効なエントリ 0 件」と誤判定して
`env_file:` キー自体をコメントアウトし、起動失敗を招いていた。ブロックの
終端判定はインデントが受け持つため、空行で止める必要はない。
- 機密注入のスキップ判定を (コマンド, サブコマンド) の組に変えた。args.command
にはトップレベルしか入らないため、鍵がまだ無い / 復号できない状態で実行される
`env keygen` / `env encrypt` / `env decrypt` でも注入が走っていた。
- 行単位では扱えない env_file 記法 (インライン配列・単一文字列) を検出して
ファイルと行番号つきで警告するようにした。移行の対象から漏れることを黙って
いると、利用者は壊れた構成のまま起動して初めて気付く。対応範囲はモジュールの
docstring にも明記した。
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_016Z922jZ4R3488KR3GETgS1
* fix: scale 生成で非機密 environment を残し、機密以外の env_file 欠落を隠さない
生成する構成ファイル (.docker-compose.scale.yml) の作り方を 2 点直した。
- environment を丸ごと落としていたため、元の compose.yml が持つ非機密の固定値や
機能フラグまで消え、スケールした途端に生成コンテナの挙動が変わっていた。
secret_env_names に挙がったキーだけを値なし参照へ置き換え、それ以外は値ごと
残すようにした。元の記法は尊重し、map なら値 None の map、list なら裸のキー名
として出力する。機密キーの値が生成ファイルに残らないことは従来どおり保証する。
- 実在しない env_file 参照を無条件に落としていたため、利用者のタイプミスや未配置
の必須設定まで黙って成功扱いになり、Compose が知らせてくれる構成不備を隠して
いた。落とす対象を「暗号化移行で消える既知の機密参照」に限定し、判定は
compose_migrate.is_secret_entry (新規の公開関数) に集約した。
既存テストのうち、environment を落とす前提だったものは新仕様に合わせて更新した。
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_016Z922jZ4R3488KR3GETgS1
* fix: 移行の中断条件を厳しくし、compose.yml の書き込みと機密の渡し先を直す
暗号化移行が「機密だけ退避されて構成は壊れたまま成功する」経路を塞ぎ、
生成する構成で非 dev サービスに機密が渡らない問題を直す。
- compose.yml を読めない場合は警告してスキップせず MigrationError で移行
全体を中止する。飛ばして続けると平文だけが退避され、存在しないファイルを
指す参照が残ったままコマンドが成功してしまうため
- 機密ファイルを指すインライン記法 (`env_file: [.env]` / `env_file: .env`)
を検出したら移行を失敗させ、手で直してからの再実行を案内する。機密と
無関係なインライン記法は移行に影響しないので従来どおり警告のみ。判定は
compose_migrate.secret_inline_env_file_lines() に切り出した
- compose.yml の書き込みを write_secure_bytes_atomic へ差し替え、途中で
失敗しても部分的なファイルが残らないようにする。compose.yml は機密では
ないため、既存ファイルの権限を読み取って mode に渡し 0600 へ落とさない
- 元々機密ファイルを env_file で参照していた非 dev サービス (db など) にも
機密の変数名を列挙する。移行後は参照がコメントアウトされ YAML から消える
ため、compose.yml の生テキストを見る
compose_migrate.services_with_secret_env_file() で渡し先を決める。参照を
持たないサービスには従来どおり注入しない
- 末尾スペース / 行末コメント付きのエントリ (`- ${DEVBASE_ROOT}/.env # 共通設定`)
が正しく無効化・復元されることを示すテストを追加 (現行実装で処理済み)
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_016Z922jZ4R3488KR3GETgS1
* fix: 機密は「そのサービスが元々参照していた由来」のキーだけに絞って渡す
これまでは「機密参照を持つか」の真偽だけで渡し先を決めていたため、共通の
.env だけを参照していた db のようなサービスにも、プロジェクト専用のトークン
まで全件が environment へ列挙されていた。元々受け取っていなかった機密が渡る
のは機密範囲の拡大にあたるため、由来 (共通 / プロジェクト) 単位で絞り込む。
- compose_migrate.services_with_secret_env_file() の戻り値を
「サービス名 → 参照種別の集合 (TARGET_GLOBAL / TARGET_PROJECT)」へ変更。
コメントアウト済みの参照も従来どおり種別つきで数える
- runtime.SecretEnv に global_names / project_names を持たせ、names は
両者を畳んだ全体を返すプロパティへ (呼び出し側の互換は維持)
- generate_scaled_compose は非 dev サービスへ、そのサービスが参照していた
由来のキーだけを列挙する。dev は従来どおり全件
- 生テキストを読めない場合の「dev のみ・全件」フォールバックは維持
既知の限界として、同じキーが共通機密とプロジェクト機密の両方にある場合は
Compose が実行プロセスの環境変数から 1 つの値しか解決できないため、共通側
だけを参照していたサービスにも合成後 (プロジェクト優先) の値が渡る。値を
サービスごとに変えるには生成ファイルへ機密の値を書く必要があり、本 PR の
前提と矛盾するため受け入れる。コードコメントと plan35.md §7 に明記した。
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_016Z922jZ4R3488KR3GETgS1
* fix: env_file ブロック内のコメント行で走査を止めない
`env_file:` 配下に利用者が書いた単独のコメント行があると
`_LIST_ITEM_RE` に一致せず走査が打ち切られ、コメント行より後ろの機密参照が
無効化されないまま残っていた。その状態で平文を退避すると、Compose が存在
しないファイルを参照して起動できなくなる。
空行と同じく単独のコメント行も読み飛ばすようにし、ブロックの終端は
インデントだけが決めるようにした。無効化済みの行 (DISABLED_MARK 付き) は
見た目がコメント行でも中身はエントリなので、コメント判定より先に除いて
いる (順序を誤ると enable が何も復元できなくなる)。
disable / enable で重複していたブロック走査は `_scan_env_file_block` へ
括り出した。片方だけ直すと無効化と復元がずれるため。
`services_with_secret_env_file` も同じ理由で参照種別を取りこぼしていたので、
共通の `_is_skippable` で読み飛ばすようにした。
テストは、既存の test_user_comments_are_preserved が「何も書き換えられて
いない」ために往復の一致だけで通っていた点を補強し
(修正前にこのアサートが失敗することを確認済み)、コメント行の後ろの参照が
無効化されること・往復で元に戻ること・コメントと空行が混在する場合・
services_with_secret_env_file が種別を拾えることを追加した。
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_016Z922jZ4R3488KR3GETgS1
* fix: env_file の long syntax・クォート付きサービス名・CRLF を取りこぼさない
行ベースの走査に残っていた 3 つの穴を塞ぎ、あわせて「何を扱い、何を扱わないか」
をモジュールの契約として docstring に書き出した。
- long syntax (`- path: .env`) を参照として認識する。1 行で閉じているものは
従来どおり無効化・復元し、`required: false` などの続きの行を持つ形・フロー
記法・シーケンスでない値は書き換えず、機密を指していれば移行を中止する。
続きの行で走査を打ち切らないので、その後ろに並ぶ機密参照も取りこぼさない。
- サービス名を YAML と同じ姿へ正規化する。`"db":` を引用符込みで記録すると
パース済みの `db` と一致せず、そのサービスへ機密が渡らなかった。
- 各行の元の行末を保って書き換える。`rstrip('\n') + '\n'` で CRLF が LF に
変わり、encrypt → decrypt の往復で元の compose.yml に戻らなかった。移行
コマンド側も read_bytes で読み、改行コードを勝手に揃えないようにした。
インライン記法だけを対象にしていた中止判定は扱えない記法全体に広げ、名前を
secret_unsupported_env_file_lines へ変更した。扱えない記法は黙って通さず、
必ず中止か警告のどちらかに落ちる不変条件を docstring に明記している。
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_016Z922jZ4R3488KR3GETgS1
* fix: 単一文字列の env_file を中止せず移行対象に含める
`env_file: .env` のように値が単一文字列で書かれた定義は、これまで「行単位では
扱えない記法」として扱い、機密を指す場合は移行ごと中止していた。利用者は手で
`- ...` の並びへ書き換えないと暗号化できず、実質的に使えない状態だった。
この形はエントリが 1 つしかなく 1 行で完結するため、`env_file:` の行そのものを
コメントアウトすれば安全に無効化でき、`enable` でも元のバイト列へ戻せる。
`_inline_scalar_ref` で「1 行で完結する単一文字列」だけを切り出し、disable /
enable / 中止判定 (`secret_unsupported_env_file_lines`) の 3 箇所で同じ判定を
使うようにした。
フロー記法 (`env_file: [ ... ]` / `{ path: ... }`)、ブロックスカラー、閉じていない
クォートなど 1 行で安全に判断できない記法は、従来どおり警告・中止のままにする。
機密を指さない単一文字列 (`env_file: config/app.env`) は触らない。モジュール
冒頭の「扱う記法 / 中止する記法 / 触らない記法」の契約も実装に合わせて更新した。
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_016Z922jZ4R3488KR3GETgS1
* fix: 書き換え後の compose.yml を YAML として検証し機密参照の残りを検出する
`env_file: >-` のようなブロックスカラーは先頭行に参照先が無く、行ベースの
走査では機密を指しているか判別できない。その結果 `env_file: >-` +次行 `.env`
の構成は無効化も中止もされず、暗号化後も存在しない平文への参照が残ったまま
コマンドが成功していた。モジュールが掲げる「扱えない記法は必ず中止か警告に
落ちる」という不変条件が破れている。
記法ごとに穴を塞ぐ対応では同種の見落としが出続けるため、記法の判別に依らない
事後検証を最後の砦として追加する:
- `compose_migrate.remaining_secret_env_file_refs()` を追加。書き換え後の
テキストを `yaml.safe_load` でパースし、各サービスの `env_file` に残った
機密参照を返す。文字列 / 文字列のリスト / long syntax の dict のいずれも
平坦化して拾う。無効化した行は YAML のコメントなのでパーサからは見えず、
残っていれば走査が取りこぼしたことを意味する
- パースできない `compose.yml` は `ComposeParseError` を投げる。検証できない
以上「参照が無い」とは言い切れないため、読み取り失敗と同じ扱いで中止する
- `env_migrate` の暗号化側で全 `compose.yml` に検証を掛け、残っていれば
どのファイルのどのサービスにどの参照が残るかを示して `MigrationError` で
中止する。差分ゼロのファイルも対象にする (走査が何も見つけられなかった
ファイルこそ取りこぼしの疑いが濃い)
- 復号側では行わない。平文が戻る以上その参照は有効で正しく、ここで止めると
壊れた状態からの復帰手段を塞いでしまう
行ベースの走査は「うまく書き換えられれば書き換える、取りこぼしたら事後検証が
止める」という二段構えになる。既存の
`secret_unsupported_env_file_lines()` はより早い段階で分かりやすいエラーを
出すための仕組みとして残す。設計意図はモジュール docstring とコード内
コメントに明記した。
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_016Z922jZ4R3488KR3GETgS1
* fix: 退避先を排他的に作り、機密は原文のバイト列のまま往復させる
- バックアップ先の衝突で過去の平文を失わないようにする
秒単位の日時ディレクトリは既存のバックアップと衝突しうる。従来は
`exist_ok=True` で掘っていたため、衝突すると `shutil.move` が同名の
`global.env` / プロジェクトの env を上書きし、「削除しないはずの過去の
平文」を失っていた。`_create_backup_dir` を追加して `exist_ok=False` で
排他的に作成し、既にあれば `-2` `-3` … と一意な名前へ寄せる。上限
(100 回) まで空きが無ければ平文に触れないまま中止する。退避先には平文の
機密が置かれるため 0700 で作る (親は他機能と共有するので既定のまま)。
- 往復でコメント・空行・`export` 表記が失われないようにする
暗号化時に平文を辞書へ畳んでいたため、`decrypt` してもコメント・空行・
`export KEY=...` 表記・値のクォートが戻らず、案内している「暗号化前の
状態へそのまま復帰」を満たしていなかった。`SecretStore` /
`PlaintextBackend` / `AgeBackend` に生バイト列を扱う `save_bytes` /
`load_bytes` を追加し、移行は原文のバイト列のまま暗号化・復元する。
読み戻し検証も「暗号化 → 復号 → 元のバイト列と一致」で維持する。
辞書経由の `save` / `load` はそのまま残し、`env set` などで値を書き換えた
ときに正規化されるのは平文だけを使っていた頃と同じ挙動として変えない
(「値を書き換えるまでは原文が保たれ、書き換えると正規化される」)。
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_016Z922jZ4R3488KR3GETgS1
* fix: プロジェクト切替で切替元の機密が環境変数に残らないようにする
cli._load_secret_env は dispatch の前に「現在地のプロジェクト」の機密を
os.environ へ載せるが、TUI や `project up <other>` の直接起動ではその後に
対象プロジェクトへ切り替わる。切替先に同名キーが無い機密 (切替元固有の
トークン等) は載せ直しでは上書きされず残り、Compose や子プロセスへ
引き継がれてしまう。
- runtime.inject が「載せた変数名とその注入前の値」を記録し、
runtime.clear_injected で注入前の状態へ戻せるようにした。自分が載せた
キーだけを対象にし、利用者がシェルで設定していた同名の変数は元の値へ
戻すので消えない
- container._inject_secrets は対象プロジェクトへ chdir した後に呼ばれる
ため、載せ直しの前に clear_injected を通して切替元の機密を落とす
- _resolve_project_name は os.chdir と併せて PWD も切り替える。機密の
解決 (runtime.current_project_name) は wrapper の cd を前提に PWD を
先に見るため、PWD が切替前のままだと切替先ではなく呼び出し元の機密を
読んでしまい、載せ直しが機能しないため (TUI の _run_in_project と同様)
非機密設定 (env) 側の _CALLER_ENV_KEYS / _resolve_project_name と同じ性質を
機密にも与えることになる。
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_016Z922jZ4R3488KR3GETgS1
* fix: 注入履歴を対象の環境マッピングごとに持たせる
注入履歴がモジュールレベルに 1 つしか無かったため、inject(environ=A) の後に
clear_injected(environ=B) を呼ぶと、A に対して記録した内容で B を誤って
「復元」し、かつ A には機密が載ったまま残っていた。
履歴を「どの環境マッピングへ注入したか」と結び付け、clear_injected は同じ
対象に記録された履歴だけを解除して、その対象の履歴を破棄するようにした。
dict は hashable でないため id() をキーにするが、対象そのものへの参照も
一緒に保持し、id の再利用による誤爆を防ぐ。os.environ を既定対象とする
従来の使い勝手は変えていない。
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_016Z922jZ4R3488KR3GETgS1
---------
Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>1 parent 26dae89 commit 3e44d79
22 files changed
Lines changed: 5514 additions & 96 deletions
File tree
- bin
- docs/user/cli-reference
- etc
- issues
- lib/devbase
- commands
- env
- volume
- tests
- cli
- commands
- env
- volume
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
36 | 36 | | |
37 | 37 | | |
38 | 38 | | |
39 | | - | |
| 39 | + | |
| 40 | + | |
| 41 | + | |
| 42 | + | |
| 43 | + | |
| 44 | + | |
| 45 | + | |
40 | 46 | | |
41 | 47 | | |
42 | 48 | | |
43 | 49 | | |
44 | | - | |
| 50 | + | |
45 | 51 | | |
46 | 52 | | |
47 | 53 | | |
| |||
61 | 67 | | |
62 | 68 | | |
63 | 69 | | |
| 70 | + | |
| 71 | + | |
| 72 | + | |
| 73 | + | |
| 74 | + | |
| 75 | + | |
| 76 | + | |
| 77 | + | |
| 78 | + | |
64 | 79 | | |
65 | 80 | | |
66 | 81 | | |
| |||
154 | 169 | | |
155 | 170 | | |
156 | 171 | | |
157 | | - | |
| 172 | + | |
158 | 173 | | |
159 | 174 | | |
160 | 175 | | |
| |||
176 | 191 | | |
177 | 192 | | |
178 | 193 | | |
179 | | - | |
| 194 | + | |
180 | 195 | | |
181 | 196 | | |
182 | 197 | | |
| |||
209 | 224 | | |
210 | 225 | | |
211 | 226 | | |
212 | | - | |
| 227 | + | |
213 | 228 | | |
214 | 229 | | |
215 | 230 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
152 | 152 | | |
153 | 153 | | |
154 | 154 | | |
| 155 | + | |
| 156 | + | |
| 157 | + | |
| 158 | + | |
| 159 | + | |
| 160 | + | |
| 161 | + | |
| 162 | + | |
| 163 | + | |
| 164 | + | |
| 165 | + | |
| 166 | + | |
| 167 | + | |
| 168 | + | |
| 169 | + | |
| 170 | + | |
| 171 | + | |
| 172 | + | |
| 173 | + | |
| 174 | + | |
| 175 | + | |
| 176 | + | |
| 177 | + | |
| 178 | + | |
| 179 | + | |
| 180 | + | |
| 181 | + | |
| 182 | + | |
| 183 | + | |
| 184 | + | |
| 185 | + | |
| 186 | + | |
| 187 | + | |
| 188 | + | |
| 189 | + | |
| 190 | + | |
| 191 | + | |
| 192 | + | |
| 193 | + | |
| 194 | + | |
| 195 | + | |
| 196 | + | |
| 197 | + | |
| 198 | + | |
| 199 | + | |
| 200 | + | |
| 201 | + | |
| 202 | + | |
| 203 | + | |
| 204 | + | |
| 205 | + | |
| 206 | + | |
| 207 | + | |
| 208 | + | |
| 209 | + | |
| 210 | + | |
| 211 | + | |
| 212 | + | |
| 213 | + | |
| 214 | + | |
| 215 | + | |
| 216 | + | |
| 217 | + | |
| 218 | + | |
| 219 | + | |
| 220 | + | |
| 221 | + | |
| 222 | + | |
| 223 | + | |
| 224 | + | |
| 225 | + | |
| 226 | + | |
155 | 227 | | |
156 | 228 | | |
157 | 229 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
6 | 6 | | |
7 | 7 | | |
8 | 8 | | |
9 | | - | |
| 9 | + | |
10 | 10 | | |
11 | 11 | | |
12 | 12 | | |
| |||
26 | 26 | | |
27 | 27 | | |
28 | 28 | | |
29 | | - | |
| 29 | + | |
| 30 | + | |
| 31 | + | |
30 | 32 | | |
31 | 33 | | |
32 | 34 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
106 | 106 | | |
107 | 107 | | |
108 | 108 | | |
| 109 | + | |
| 110 | + | |
| 111 | + | |
109 | 112 | | |
110 | 113 | | |
111 | 114 | | |
| |||
288 | 291 | | |
289 | 292 | | |
290 | 293 | | |
| 294 | + | |
| 295 | + | |
| 296 | + | |
| 297 | + | |
| 298 | + | |
| 299 | + | |
| 300 | + | |
| 301 | + | |
| 302 | + | |
291 | 303 | | |
292 | 304 | | |
293 | 305 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
35 | 35 | | |
36 | 36 | | |
37 | 37 | | |
38 | | - | |
| 38 | + | |
39 | 39 | | |
40 | 40 | | |
41 | 41 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
230 | 230 | | |
231 | 231 | | |
232 | 232 | | |
| 233 | + | |
233 | 234 | | |
234 | 235 | | |
235 | 236 | | |
| |||
255 | 256 | | |
256 | 257 | | |
257 | 258 | | |
258 | | - | |
259 | | - | |
260 | | - | |
| 259 | + | |
| 260 | + | |
| 261 | + | |
| 262 | + | |
| 263 | + | |
| 264 | + | |
| 265 | + | |
| 266 | + | |
| 267 | + | |
| 268 | + | |
| 269 | + | |
| 270 | + | |
261 | 271 | | |
262 | 272 | | |
263 | 273 | | |
| |||
0 commit comments