Rork で組み上げたアプリを手元に持ってきて、いよいよ自分の GitHub リポジトリに置こうとする場面です。
ここで多くの方が、フォルダを開いてすぐ git init と打ちます。順番としては、これが一番もったいない選択になります。
エクスポートされた中身には、公開してはいけないものが最初から混ざっているためです。しかも Git は、一度記録したものを「後から無かったこと」にはしてくれません。
除外設定を先に置いた場合と後から足した場合で何がどう変わるのか、実際に手を動かして測ってみました。以下はその出力をそのまま並べたものです。必要なコマンドは3つだけです。
エクスポート直後のフォルダには、上げてはいけないものが混ざっています
Rork からエクスポートした Expo プロジェクトを展開すると、ざっと次のような顔ぶれが並びます。自分で書いた覚えのないファイルが多いはずです。
| ファイル・フォルダ | 正体 | GitHub に置くか |
|---|---|---|
app/ assets/ app.json |
自分のアプリそのもの | 置く |
package.json package-lock.json |
使っている部品の一覧と、そのバージョン固定 | 置く |
node_modules/ |
部品の実体。npm install でいつでも作り直せる |
置かない |
.env .env.production |
API キーや接続先を書いた設定 | 絶対に置かない |
*.p8 *.jks *.keystore |
App Store と Google Play へ提出するときの署名鍵 | 絶対に置かない |
google-services.json GoogleService-Info.plist |
Firebase の接続情報 | 置かない |
ios/ android/ |
expo prebuild が生成するネイティブ側の一式 |
条件つき(後述) |
.expo/ |
開発サーバーが作る手元専用のキャッシュ | 置かない |
node_modules/ を置かないのは容量の話ですが、.env と署名鍵はまったく別の話です。
公開リポジトリに一度でも載れば、消しても取り返しがつきません。鍵そのものを作り直すしか手がなくなります。
.gitignore を先に置くと、追跡されるのは9ファイルだけになりました
まず「先に置いた」側を測ります。
エクスポート直後の構成を再現したフォルダを用意して、git init の前に .gitignore を作りました。中身はこれです。
node_modules/
.expo/
dist/
web-build/
ios/
android/
*.p8
*.p12
*.jks
*.keystore
*.mobileprovision
google-services.json
GoogleService-Info.plist
.env
.env.*
!.env.example
npm-debug.log*
yarn-error.log*
.DS_Store.gitignore は「この名前に当てはまるものは記録しなくていい」と Git に伝えるだけの、ただのテキストファイルです。プロジェクトの一番上の階層に、この名前ちょうどで置きます。
置いてから git init と git add -A を実行し、記録の対象に入ったものを数えました。
$ git status --porcelain | sed 's/^A //' | sort
.gitignore
app.json
app/(tabs)/index.tsx
assets/images/icon.png
eas.json
expo-env.d.ts
package-lock.json
package.json
tsconfig.json
9ファイルです。.env も鍵も node_modules/ も入っていません。
どの行がどのファイルを止めたのかは、git check-ignore -v で1件ずつ確認できます。手元で流したときの出力がこちらです。
$ git check-ignore -v .env node_modules/expo/package.json AuthKey_ABC123.p8
.gitignore:14:.env .env
.gitignore:1:node_modules/ node_modules/expo/package.json
.gitignore:7:*.p8 AuthKey_ABC123.p8
左が .gitignore の何行目か、右が止められたファイルです。設定を書き足したときに、狙ったものが本当に止まっているかを確かめる用途に向いています。
順番を逆にすると、あとから足しても履歴からは消えません
次に「後から足した」側を測ります。ここが本題です。
.gitignore を用意せずに git init と最初のコミットを済ませたところ、追跡対象は6ファイルになりました。
$ git ls-files
.env
.expo/devices.json
AuthKey_ABC.p8
app.json
node_modules/expo/package.json
package.json
.env と .p8 が、この時点でリポジトリの中身になっています。
ここで気づいて .gitignore を書き、もう一度コミットしました。それでも結果は変わりません。
$ git ls-files | grep -E '\.env|node_modules|\.expo|p8'
.env
.expo/devices.json
AuthKey_ABC.p8
node_modules/expo/package.json
4件そのまま残っています。.gitignore が効くのは「まだ追跡していないファイル」に対してだけで、すでに追跡が始まったものには届かないためです。
追跡を外すには、明示的に外すコマンドが要ります。
git rm -r --cached node_modules .expo .env AuthKey_ABC.p8
git commit -m "Stop tracking local-only files"--cached を付けると、手元のファイルは残したままリポジトリの管理下からだけ外れます。これで追跡対象は3ファイルまで減りました。
ただし、ここからが肝心です。過去のコミットには残り続けます。
$ git cat-file -p HEAD~2:.env
(削除したはずの .env の中身がそのまま出てくる)
つまり、公開リポジトリに一度 push してしまった鍵は、その後どれだけ丁寧に消しても漏れた前提で扱うほかありません。私自身は、この段階まで来たら他の作業より先に鍵の作り直しを済ませます。個人開発では鍵の管理者が自分ひとりなので、気づいた本人が動かないと誰も止めてくれないためです。境界の引き方とローテーションの手順はクライアントに置いた鍵は公開情報 — Rork 生成アプリの秘密境界とローテーション手順に詳しくまとめてあります。
順番を守るだけで、この作業はまるごと発生しません。3分の差です。
除外の解除は、ファイル単位では効いてディレクトリ配下では効きません
.gitignore には、行頭に ! を付けて「これだけは例外的に記録する」と書ける機能があります。
便利なのですが、効く場面と効かない場面がはっきり分かれます。両方を実際に試しました。
まず、うまくいく側です。.env.* でまとめて止めたうえで、雛形として配りたい .env.example だけを戻します。
.env
.env.*
!.env.exampleこの状態で git add -A した結果です。
$ git diff --cached --name-only
.env.example
.gitignore
app.json
.env.example は入り、.env.local と .env.production は入っていません。狙いどおりです。
次に、うまくいかない側です。ios/ をまとめて止めたうえで、ios/Podfile だけを戻そうとしました。
ios/
!ios/Podfile$ git diff --cached --name-only
.gitignore
app.json
ios/Podfile は出てきません。書き方は間違っていないのに、素通りされます。
| 書き方 | 止め方 | 結果 |
|---|---|---|
.env.* + !.env.example |
ファイル名のパターンで止めている | 例外が効く |
ios/ + !ios/Podfile |
ディレクトリごと止めている | 例外が効かない |
理由は、Git がディレクトリを除外した時点でその中を見に行かなくなるからです。中を見ていないので、中のファイルに対する例外も読まれません。
ディレクトリ配下の1ファイルだけを残したいときは、ディレクトリ全体ではなく中身のパターンで止めます。ios/ ではなく ios/build/ ios/Pods/ のように、要らないものを名指しする形です。
git check-ignore -v の出力だけを見て判断すると、ここで取り違えます。! の行が表示されても、それは「この行に当てはまった」という報告であって、記録されるという意味ではありません。確実なのは git add -A のあとに git diff --cached --name-only を見ることです。
ios/ と android/ を除外してよいかは、prebuild を使うかで決まります
上の一覧で「条件つき」と書いた部分です。ここだけは、正解が読者の運用によって変わります。
判断は次の一点で決まります。ネイティブ側の設定を、手で書き換えているかどうかです。
| 自分の状況 | ios/ android/ の扱い |
|---|---|
| Rork と EAS Build に任せていて、Xcode や Android Studio を開いたことがない | 除外してよい(毎回生成し直されるため) |
app.json と config plugin だけで設定している |
除外してよい |
| Info.plist や build.gradle を直接編集した | 除外しない。消えると再現できない |
個人開発で1人1リポジトリという規模なら、迷ったときは除外する側で始めるのが安全です。あとから必要になったときに .gitignore から2行消せば済みます。逆向き、つまり一度履歴に載せたものを取り下げる方は、先ほど見たとおり手間がかかります。
なお、手で入れた変更を残したまま expo prebuild --clean を通すと、その手入れは戻ってきません。この点はExpo SDK 57 に上げる前に、prebuild で消えるネイティブ変更を洗い出すで扱っています。
最初の1回だけ、この順番で進めます
まとめると、やることは3手です。
# 1. エクスポートしたフォルダに入って、まず .gitignore を作る
cd my-rork-app
cat > .gitignore <<'EOF'
node_modules/
.expo/
dist/
web-build/
ios/
android/
*.p8
*.p12
*.jks
*.keystore
*.mobileprovision
google-services.json
GoogleService-Info.plist
.env
.env.*
!.env.example
npm-debug.log*
yarn-error.log*
.DS_Store
EOF
# 2. それから初期化して、記録の対象を目で確かめる
git init
git add -A
git diff --cached --name-only
# 3. 一覧に .env や鍵が無いことを確認してから、コミットする
git commit -m "Initial commit"手順2で一覧を眺める数秒が、この記事のすべてです。ここで見慣れない名前が出てきたら、コミットの前に .gitignore へ1行足せば済みます。
次に GitHub へ push したあとは、EAS Build を GitHub Actions から回す流れが自然な続きになります。その組み方はRork × GitHub Actions で CI/CD を構築する — EAS Build 自動化からテストフライト配布までにまとめました。
まずは手元のプロジェクトで git add -A と git diff --cached --name-only を続けて打ってみてください。想定外のファイルが並んでいないか、それだけ確かめていただければ十分です。