◉ANTIGRAVITY LABEN
記事一覧/アプリ開発
▣ アプリ開発/2026-07-03上級

CI の検証が緑なのにストア審査でスクリーンショットが差し戻されるとき — 検証ルールの鮮度を計測する運用メモ

CI のストアアセット検証は緑なのに App Store 審査でスクリーンショットが差し戻される——検証ルールの鮮度切れが原因でした。仕様を鮮度つき契約ファイルに分離し、ロケール別のテキストあふれや知覚差分で静かな劣化を捕まえる運用を紹介します。

CI/CD17GitHub Actions5ストアアセットApp Store7Google Play6Antigravity377品質検証スクリーンショット

✦ プレミアム記事

提出前夜、CI のストアアセット検証は全て緑でした。それなのに翌朝、App Store Connect から返ってきたのはスクリーンショット起因のメタデータ差し戻し。ログを何度見返しても、検証スクリプトは「全ファイル合格」と言い続けています。

原因はアセットの生成側ではありませんでした。検証ルールそのものが古くなっていたのです。

スクリーンショットの必須サイズ要件は年に何度か静かに変わります。ところが多くの検証スクリプトは、書いた日のストア仕様を 1170x2532 のような定数としてコードに焼き込んでいます。この瞬間から、CI の緑は「ストアの要件を満たしている」ではなく「書いた当時の要件を満たしている」という意味に変質します。両者のずれは、審査に出すまで誰にも見えません。

私自身、個人開発のアプリを日英含む複数ロケールで長く運用してきて、この差し戻しをリリース締め切りの直前に踏んだことがあります。生成パイプラインは疑ったのに、検証スクリプトを疑うという発想がなかった。以来、ストアアセットの検証は「アセットを検証する層」と「検証ルール自体の鮮度を検証する層」の二段構えで組むようになりました。

ここからは、その二段構えを GitHub Actions に載せる実装を、動くコードと運用上の判断基準ごと整理していきます。

CI の緑が信用できなくなる構造

最初に、なぜこの問題が検出しづらいのかを整理しておきます。

層変化の主体変化の頻度CIで検出できるか
アセット生成(スクリーンショット撮影・加工)自分のコードコミット単位できる(従来の検証)
検証ルール(サイズ・形式・枚数の上限)Apple / Google年数回・不定期そのままではできない
ロケール別キャプション翻訳の追加・修正不定期レンダリングまで見ないとできない

一段目は普通の CI で守れます。問題は二段目と三段目で、どちらも「自分のリポジトリの外」で変化が起きるため、コミットをトリガーとする CI の設計思想と根本的に噛み合いません。変化が起きてもビルドは走らず、走っても古いルールで合格してしまいます。

ここを塞ぐ道具立ては大掛かりなものではなく、次の三つで足ります。

  1. ストア仕様をコードから分離した「契約ファイル」にし、鮮度メタデータを持たせる
  2. 契約ファイルの鮮度を CI が毎回チェックし、賞味期限切れなら警告で落とす
  3. ロケール別のレンダリング結果を知覚差分でゴールデン画像と照合する

ストア仕様を「鮮度つき契約ファイル」に分離する

検証ルールをコードの定数から JSON に追い出し、いつ・どこを見て確認したかを一緒に記録します。

{
  "specVersion": "2026-06-20",
  "verifiedAt": "2026-06-20",
  "verifiedAgainst": "App Store Connect ヘルプのスクリーンショット仕様ページ",
  "staleAfterDays": 45,
  "ios": {
    "screenshots": {
      "requiredSizes": [
        { "label": "6.9inch", "width": 1320, "height": 2868 },
        { "label": "6.5inch", "width": 1284, "height": 2778 }
      ],
      "maxCount": 10,
      "maxBytes": 5242880,
      "formats": ["png", "jpg"]
    }
  },
  "android": {
    "screenshots": {
      "minWidth": 1080,
      "minHeight": 1920,
      "maxCount": 8,
      "maxBytes": 8388608,
      "formats": ["png", "jpg"]
    }
  }
}

ポイントは staleAfterDays です。仕様の中身が正しいかどうかを機械は判定できませんが、「最後に人間が公式ページと突き合わせてから何日経ったか」なら機械で判定できます。検証スクリプトの冒頭で、まず契約ファイル自体を検証します。

// scripts/check-spec-freshness.js
const spec = require('../store-spec.json');
 
const verifiedAt = new Date(spec.verifiedAt);
const ageDays = Math.floor((Date.now() - verifiedAt.getTime()) / 86400000);
 
if (ageDays > spec.staleAfterDays) {
  console.error(
    `❌ store-spec.json は ${ageDays} 日前の確認が最後です(許容 ${spec.staleAfterDays} 日)。` +
    `公式仕様ページと突き合わせて verifiedAt を更新してください。`
  );
  process.exit(1);
}
console.log(`✅ 仕様の鮮度 OK(確認から ${ageDays} 日)`);

なぜ失敗扱いにするのか。警告どまりにすると、通知は最初の一週間だけ読まれて、その後は景色になるからです。私の運用では 45 日を期限にしていますが、これは Apple のサイズ要件変更がおおむね四半期単位で来ることを踏まえ、四半期より一回り短くした値です。更新作業自体は公式ページを開いて突き合わせ、verifiedAt を書き換えるだけなので 10 分かかりません。10 分の定期作業で、締め切り前夜の差し戻しを構造的に消せるなら安い買い物だと考えています。

なお、ここで挙げているサイズの数値はあくまで例です。契約ファイルという仕組みの性質上、記事の数値をコピーするのではなく、必ずご自身で公式仕様と突き合わせて初版を作ってください。それがこの仕組みの出発点になります。

✦

ここまでお読みいただきありがとうございます。

この記事の続きを読む

この先には、実装コードやベンチマーク結果など、実務でお役に立てる内容をご用意しています。このサイトは広告を掲載しておらず、サーバーや開発にかかる費用はメンバーの皆様のご支援で成り立っています。もしお役に立てていましたら、ご支援いただけますと大変ありがたいです。

この記事で得られること
✦ストア仕様を定数で埋め込まず「鮮度つき契約ファイル」として照合するCI設計と、そのまま動く検証コード
✦ロケール別テキストあふれ・ゴールデン画像との知覚差分・仕様の陳腐化を三層で捕まえるGitHub Actionsの組み方
✦アセットセットをビルド番号に束ねて、差し戻し発生時に原因コミットを一発で特定できるようにする運用手順
Stripe による安全な決済 · いつでもキャンセル可能
✦

この記事を購入する

この先の内容をすべてお読みいただけます。一度のご購入で、いつでも何度でもアクセスできます。このサイトは広告を掲載しておらず、皆さまのご支援がサーバー費用などの運営を支えています。

または
メンバーシップなら全記事が読み放題 →
シェア

お読みいただきありがとうございます

Antigravity Lab は広告なしで運営しており、サーバー費用などの運営コストはメンバーシップのご支援で賄っています。実装コード・ベンチマーク・本番設計パターンなど、実務でお役立ていただける記事を毎日更新しています。もし読んでよかったと感じていただけましたら、ぜひご覧ください。

  • ✦コピー&ペーストで使える実装コード付き
  • ✦毎日新しい上級ガイドを追加
  • ✦¥580/月 または ¥2,480 の永久アクセス
メンバーシップを見る →

関連記事

▣ アプリ開発2026-09-29
Cloudflare をパージしても古い壁紙が届き続けた原因は、オリジン側で JPEG を握っていた XPageSpeed
同じ URL に上書きした壁紙画像が、Cloudflare をパージしても古いまま届き続けた運用の記録です。ローカル・オリジン・エッジの三者を突き合わせて層を名指しする検査、プレフィックスパージの使いどころ、Xserver の XPageSpeed を .htaccess で安全に切る書き方まで扱います。
▣ アプリ開発2026-09-19
見積書 PDF の自動化を一度にまとめて頼み、4 つに割り直すまで
受託の見積書を毎回コピーして作っていた手作業を、Antigravity に頼んで小さな PDF 出力ツールへ移した記録です。最初に全部をまとめて頼んで作り直すことになった理由と、データ・計算・見た目・保存の 4 つに割って渡し直した順番を、動くコードと一緒に書き残します。
▣ アプリ開発2026-09-03
musllinux ホイールが無い依存は、Alpine で失敗せずに遅くなります
Antigravity Python SDK の musllinux ホイール対応を機に、Alpine コンテナで C 拡張が純 Python 実装へ落ちる経路を実測しました。protobuf の直列化は 0.043ms から 36.2ms へ変わります。事前の棚卸しと検知の手順まで書き残します。
📚RECOMMENDED BOOKS
大規模言語モデル入門
山田育矢
LLM開発
生成AIプロンプトエンジニアリング入門
我妻幸長
プロンプト
Claude CodeによるAI駆動開発入門
平川知秀
AI駆動開発
※ アフィリエイトリンクを含みます