◉RORK LABEN
●GPT-6.1 SOL — Rork のモデルメニューに追加(09-29)。Low/Medium/High を選べ、Pro・Max に含まれます●SONNET 5.5 — UI づくりが得意で、Sonnet 5 より出力が速いモデルが選べます(09-28)●SDK 58 — Expo SDK 58 が 10/15 に正式リリースされました(Beta は 09/15 から)。上げる前に依存の対応状況を確認しましょう●SDK 58 ISSUE — Android ウィジェットが定期更新されない、Stripe が静的リンクになるなど初期報告が出ています●OBSERVE — EAS Observe がネイティブクラッシュも記録します(SDK 57.0.21 以降・10/07)●NEW — 起動広告が、背面から戻った回にしか出ていませんでした。「前面に戻った」合図が、…●GPT-6.1 SOL — Rork のモデルメニューに追加(09-29)。Low/Medium/High を選べ、Pro・Max に含まれます●SONNET 5.5 — UI づくりが得意で、Sonnet 5 より出力が速いモデルが選べます(09-28)●SDK 58 — Expo SDK 58 が 10/15 に正式リリースされました(Beta は 09/15 から)。上げる前に依存の対応状況を確認しましょう●SDK 58 ISSUE — Android ウィジェットが定期更新されない、Stripe が静的リンクになるなど初期報告が出ています●OBSERVE — EAS Observe がネイティブクラッシュも記録します(SDK 57.0.21 以降・10/07)●NEW — 起動広告が、背面から戻った回にしか出ていませんでした。「前面に戻った」合図が、…
記事一覧/開発ツール
⬡ 開発ツール/2026-09-06中級

eas env:list に --json がないと分かってから、環境変数の棚卸しを組み直しました

eas env:list に --json フラグは用意されていません。--format short の出力を安全に JSON へ変換し、三つの環境の抜けを一行で見つけるまでの手順を書き残します。

EAS10Expo223環境変数10CI7

preview ビルドを流す前に、EAS のダッシュボードを開いて環境変数を目で数えていた夜がありました。development・preview・production の三つに、プロジェクト単位とアカウント単位のスコープが重なります。画面を行き来しているうちに、どこまで確認し終えたのか分からなくなりました。

手で数えるのをやめようと思って eas env:list --json と打ちました。通りませんでした。

最初にお伝えしたいのは、これが打ち間違いではないということです。eas env:list に --json は用意されておりません。同じところで手が止まった方に向けて、代わりに何を使うのか、そしてその出力をどう扱えば安全なのかを書き残します。

eas env:list が受け取るフラグは五つです

eas-cli v22.0.0 のソース(packages/eas-cli/src/commands/env/list.ts)で定義されているフラグは、次の五つです。

フラグ既定値役割
--environment(対話で選択)対象の環境。複数回の指定が可能
--formatshort出力形式。選択肢は long と short のみ
--scopeprojectプロジェクト単位かアカウント単位か
--include-sensitivefalsesensitive の値を伏せ字にせず表示
--include-file-contentfalseファイル型変数の中身を表示

引数として環境名をそのまま渡すこともできます。eas env:list production という書き方です。出力形式の選択肢は long と short の二つだけで、JSON はそこに含まれておりません。--json を足しても、定義されていないフラグとして扱われます。

ここで一つ、私が遠回りした点をお伝えします。ダッシュボードの表を見て「JSON で落とせるはずだ」と思い込み、フラグの一覧を確かめる前にオプション名を三つほど試しました。いま思えば、ソースの static override flags を先に読めば一分で済んだ話でした。

--format short の出力を、そのまま .env として使えない理由

--format short は NAME=value を一行ずつ出しますので、一見すると .env そのものに見えます。ただ、そのまま流用すると三か所で崩れます。

  • 先頭に環境名の見出し行が入ります(Environment: production の形)
  • 変数が一つも無い環境では No variables found for this environment. の一文だけが返ります
  • 変数名は太字で出力されますので、端末の条件によっては装飾の制御文字が混ざります

さらに厄介なのは、値そのものが読めない場合があることです。可視性が sensitive の変数は --include-sensitive を付けない限り伏せ字になり、secret の変数はそもそも EAS のサーバーから出てきません。伏せ字を値だと思って書き戻せば、伏せ字が本物の値を上書きします。

可視性の三段階と EXPO_PUBLIC_ 接頭辞の関係は別の軸の話ですので、そちらはEAS の secret は「アプリに入れない」設定ではありませんに譲ります。ここでは「読める値と読めない値が同じ行の形で並んで出てくる」という一点だけを押さえておければ十分です。

三十行ほどで JSON に変換します

見出し行を落とし、制御文字を除き、最初の = だけで分割します。伏せ字だけの値には readable: false を付けて、値ではなく印であることを残します。

#!/usr/bin/env python3
# eas env:list --format short --environment production | python3 eas_env_to_json.py production
import json
import re
import sys
 
ANSI = re.compile(r"\x1b\[[0-9;]*m")
HEADER = re.compile(r"^(Environment|Variables for this project|Account-wide variables)\b")
MASKED = re.compile(r"^\*+(\s*\(.*\))?$")
 
 
def parse(lines):
    out = []
    for raw in lines:
        line = ANSI.sub("", raw).rstrip("\n").strip()
        if not line or HEADER.match(line):
            continue
        if line.startswith("No variables found"):
            continue
        if "=" not in line:
            continue
        name, value = line.split("=", 1)          # 値に = が含まれても壊さない
        name = name.strip()
        if not re.fullmatch(r"[A-Za-z_][A-Za-z0-9_]*", name):
            continue                              # 装飾の残骸や説明行を捨てる
        out.append({
            "name": name,
            "value": value,
            "readable": not MASKED.match(value.strip()),
        })
    return out
 
 
if __name__ == "__main__":
    env = sys.argv[1] if len(sys.argv) > 1 else None
    json.dump(
        {"environment": env, "variables": parse(sys.stdin.readlines())},
        sys.stdout, ensure_ascii=False, indent=2,
    )
    print()

v22.0.0 のソースが出力する short 形式を再現した入力で動かした結果です。

{
  "environment": "production",
  "variables": [
    { "name": "APP_VARIANT", "value": "production", "readable": true },
    { "name": "EXPO_PUBLIC_API_URL", "value": "https://api.example.com", "readable": true },
    { "name": "SENTRY_AUTH_TOKEN", "value": "*****", "readable": false }
  ]
}

変数が一つも無い環境を渡しても、variables が空の配列になるだけで落ちません。Base64 のように値の中に = を含む文字列も、最初の = だけで分割していますので保たれます。

三つの環境を並べると、抜けが一行で見つかります

環境ごとに JSON を作ってから、次のスクリプトで並べます。

#!/usr/bin/env python3
# python3 eas_env_matrix.py env-development.json env-preview.json env-production.json
import json
import sys
 
envs, table = [], {}
for path in sys.argv[1:]:
    with open(path, encoding="utf-8") as f:
        data = json.load(f)
    env = data.get("environment") or path
    envs.append(env)
    for v in data["variables"]:
        table.setdefault(v["name"], {})[env] = "secret" if not v["readable"] else "ok"
 
missing = 0
print("NAME".ljust(28) + "".join(e.ljust(14) for e in envs))
for name in sorted(table):
    row = [table[name].get(e, "-") for e in envs]
    missing += row.count("-")
    print(name.ljust(28) + "".join(c.ljust(14) for c in row))
print(f"\nmissing cells: {missing}")
sys.exit(1 if missing else 0)      # 抜けがあれば CI を落とす

手元で動かすと、次のように出ます。

NAME                        development   preview       production
APP_VARIANT                 ok            ok            ok
EXPO_PUBLIC_API_URL         ok            ok            ok
SENTRY_AUTH_TOKEN           -             secret        secret
 
missing cells: 1

- が抜けです。この形にしてから、ダッシュボードで目を往復させる作業がなくなりました。終了コードを 1 で返していますので、ビルド前のジョブに挟んでおけば、設定を足し忘れたまま preview を流すことも防げます。

私が取り違えていたのは、読めない値と入っていない値の区別でした

最初のうち、私は伏せ字の行を「まだ入れていない変数」として数えていました。足りないと思って同じ名前を作り直し、production 側の値を上書きしかけたことがあります。原因は仕組みではなく、私の読み方でした。

伏せ字は「読めない」の印であって、「無い」の印ではありません。 この区別だけは、急いでいる日でも先に確かめるようにしています。

eas env:pull --environment production を使うときも同じ注意が要ります。このコマンドは .env.local を書き出しますが、secret の変数は値ではなくコメント行として出力されます。行の見た目だけで判断すると、サーバー側には確かに入っている値を、もう一度作り直してしまうのです。

環境変数の棚卸しは、SDK を上げる前にも効いてきます。ネイティブ側の設定がどこで消えるかを追う話はExpo SDK 57 に上げる前に、prebuild で消えるネイティブ変更を洗い出すにまとめてありますので、あわせてご覧ください。

まずは production を一度だけ JSON にして、日付を付けて手元に残してみてください。翌週の同じ出力と差分を取るだけで、いつ誰が何を足したのかが自分の手元に残ります。私はそこから始めました。

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

シェア

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

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

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

もしこの記事がお役に立ちましたら、チップ(¥150)で応援いただけると大変励みになります。広告なしでの運営を続けるため、皆さまのご支援が大きな力になっています。

関連記事

⬡ 開発ツール2026-09-04
EAS の secret は「アプリに入れない」設定ではありません — 接頭辞と可視性を別々に決める
EXPO_PUBLIC_ の接頭辞は成果物に入るかを決め、EAS の可視性は誰が読めるかを決めます。二つを重ねたときに OTA 更新でだけ値が消える筋道と、出荷前に自分の手で確かめる手順をまとめました。
⬡ 開発ツール2026-10-15
EXPO_PUBLIC_ の値が、開発では読めて本番だけ undefined になる書き方を、変換結果で見分けます
API の URL が開発中は読めるのに、ビルドした途端に undefined になる。原因は process.env の書き方でした。babel-preset-expo の変換結果を開発・本番で並べ、置き換わる書き方と残ってしまう書き方を表にしました。
⬡ 開発ツール2026-10-13
リリース前に、書き出した Expo バンドルの中から自分の鍵を探してみる
ソースに鍵がなくても、出来上がったアプリの中に鍵が残ることがあります。Expo SDK 57 で実際に expo export を実行し、バンドルの中身を grep と 60 行ほどの Node スクリプトで調べて、見つかる鍵と見つからない鍵の違いを確かめました。