セルフホストの Mac mini M4 Runner は速いが、依然として「地域にいるコンピュータ」である。既定タイムゾーン、12/24 時間表記、週の始まり、照合順はノート PC と異なる。CI でこれらを宣言しないと、真夜中、DST 境界、香港から米国へジョブを移した瞬間にテストが反転する。本稿は 2 つのマトリックス、8 つのロールアウト手順、Runbook に貼れる数値ガードレールで、XCTest・スナップショット・サーバー日付計算を Runner と整合させる。
フレーク隔離とリトライ、Xcode 固定とアップグレードドリフト、ディスパッチ可能な Mac mini M4 ノード と併読し、多地域マトリックスを設計する。対話デバッグは VNC。料金は 料金ページ、運用の初期値は ヘルプ を参照。
タイムゾーンとロケールが「ハードウェアフレーク」に見える理由
Apple Silicon はカレンダーの非決定性を消さない。請求ウィンドウの端で Date() を呼ぶテストは、Runner の壁時計が境界を越すまで緑のままだ。ロケールは NumberFormatter の桁区切りを変え、録画時と異なる LANG でスナップショットを壊す。POSIX 順を仮定したファイル一覧ソートも、CI が言語依存照合を継承すると静かに失敗する。
- 真夜中境界:23:55 開始で 00:05 終了の夜間ジョブは、時計を凍結せず「今日」を使うと日付の二重計上やスキップを招く。
- DST ジャンプ:前進日の 23:30 ジョブは実時間が短く、cron や「3600 秒 sleep」前提が崩れる。
- 証明書ウィンドウ:TLS とコード署名は notBefore/notAfter を壁時計と比較する。スキューは間欠障害になる。
- マルチリージョン:同一コミットでもシンガポールとカリフォルニアでは「現地金曜」が食い違う。参照時刻を一本化しないとグローバルリリース列車が衝突する。
マトリックス A:ノード地域と推奨 CI 環境契約
NodeMac は香港・日本・韓国・シンガポール・米国でベアメタル Mac mini M4 を運用する。下表は OS 既定の法的保証ではないが、無人 Runner の出発点として現実的だ。オーケストレータに契約を符号化し、「SG で緑」を「どこでも緑」と誤読しないこと。
| 代表 Runner 地域 | 既定壁時計リスク | 推奨 CI ベースライン | 追加マトリックス |
|---|---|---|---|
| 香港/シンガポール | アジアの暦境界。祝日カレンダーは米国と異なる | コンパイルは TZ=UTC と LC_ALL=C.UTF-8 |
週次で TZ=Asia/Hong_Kong の領収書検証 |
| 日本/韓国 | ロケールパック完備。UI の週番号表記差 | CLI ログは LANG=C.UTF-8 に固定 |
日本語 UI 出荷ならスナップショット段階に ja_JP.UTF-8 |
| 米国 | 12 時間既定。州ごとの DST 差 | テストで 24 時間表記を強制。ロケール無しの「午後3時」解析を禁止 | DST 週に XCTest で合成時刻を 2 本 |
| クロスリージョン | 同一コミットでも「現地の今日」が不一致 | 業務日を UTC に集約。UI 境界でのみ変換 | timezone-matrix ラベルで赤ならリリース停止 |
マトリックス B:症状・プローブ・修正パターン
| 症状 | 高速プローブ | 修正パターン |
|---|---|---|
| UTC 真夜中付近だけ赤 | Runner の TZ、システム時計、開始時刻ヒストグラムを記録 |
依存注入で時計を凍結。境界 XCTest を追加 |
| 数値スナップショットがホスト間で不一致 | ジョブ開始時に有効 LANG/LC_NUMERIC を出力 |
launchd plist または CI ラッパーで LC_ALL=C.UTF-8 |
| TLS や公証がランダムに失敗 | sntp オフセットを参照時計と比較 |
NTP 規律を強制。閾値超えでフェイルファスト |
| ファイル一覧の順序が不安定 | pre-job バナーで locale をダンプ |
Swift で明示 POSIX 比較、または sort で LC_ALL=C |
ポリシー化できる数値ガードレール
- NTP 絶対オフセット:定常状態では 250 ms 未満。Runner が連続 3 回 2 s を超えたらオンコールへ。
- 計画メンテ:顧客向け日付ロジックを走らせる地域では、DST 前後 72 時間は TZ 実験を凍結。
- タイムアウト余白:カレンダー操作スイートは壁時計で 15% の余裕を追加。シミュレーション時間ジャンプは XCTest を非線形に伸ばす。
監査ヒント:junit.xml アップロードごとに有効 TZ、LANG、測定 NTP オフセットを保存する。サイレント OS パッチ後にフレークが消えても説明できる。
8 つのロールアウト手順
- 棚卸し:実時計・ロケール依存フォーマッタ・明示比較器なしのファイル順序テストを列挙。
- preflight バナー:CI ログに TZ、ロケール、OS ビルド、Xcode 版を出力——ドリフト監査 と同じ行。
- 既定を launchd EnvironmentVariables に符号化し、GUI なしセッションと SSH を同一契約に。
- 小さな XCTest ヘルパーで依存注入により時刻凍結。ビジネスロジックでは裸の
Date()を禁止。 - 週次マトリックスで販売タイムゾーン 2 つ以上にトップ 20 の日付テストを再生。
- NTP スパイクと TLS 失敗を相関アラート——多くの場合パケット再読より速い。
- 例外を文書化:POS など意図的ローカル時刻はラベル付き Runner に隔離。
- スケールアウト:キュー深度が「ほぼ真夜中」ウィンドウの共有を強いるなら地域ごとに専用 Mac mini M4 を追加。
FAQ
サーバとシミュレータは同じ TZ 変数を共有すべきか
いいえ。サーバは UTC 既定。UI シミュレータはロケール別実行が必要。1 つの XCTest プロセスで混在させると二重失敗の典型になる。
Apple Silicon は NTP 挙動を変えるか
実質的に変えない。規律デーモンが重要。変わるのはビルド速度で、短いジョブはスキューを隠し、30 分の統合だけが露呈する。
今日レガシーテストを直せない場合
明示ラベルで隔離し、一時的に単一リージョンへピン留めし、フレークプレイブックに沿って書き換えを資金化する。地理的中立のふりはしない。
カレンダー敏感スイートに決定的な金属が要り、ノートのスリープやカフェ Wi‑Fi と戦いたくないなら、NodeMac の Mac mini M4 が ネイティブ macOS、SSH と VNC、ベアメタルで読みやすい時計、香港・日本・韓国・シンガポール・米国の選択肢を提供する。Apple Silicon はワット当たりのスループットが高く、専用機のレンタルは過剰割当 VM の騒音を避ける。マトリックスが別リージョンのホストを要請するなら、YAML を当てずっぽうに書く前に 料金 で地域をマッピングする。