CI/CD 連携
既存パイプラインでビルドと署名を行い、成果物を foxstore-cli に渡します。CLI は自前の Runner からストアに直接接続し、Desktop の起動は不要です。HTTP API やホスト型 Runner は提供しません。すべてのコマンド、引数、JSON リクエストファイル、終了コードは CLI / API リファレンスをご覧ください。
Runner と入力の準備
以下の Apple の例では、Runner の OS・アーキテクチャに合う foxstore-cli を事前にインストールして PATH に追加し、Bash と jq も用意します。まず foxstore-cli version --output json でバージョンを記録してください。デスクトップのインストーラーが CLI を導入することは前提とせず、専用のセットアップ Action にも依存しません。
ビルド手順で署名済みの build/App.ipa を先に生成します。ビルド、署名、ストア権限は既存プロジェクトで管理し、Fox Store CLI は証明書生成やアプリのビルドを行いません。同じアプリの公開は直列化し、2つのパイプラインが同じバージョンに同時書き込みしないようにしてください。
次の変数を設定します。
| 名前 | 種類 | 内容 |
|---|---|---|
APPLE_PRIVATE_KEY | CI Secret | App Store Connect API Key の PEM 秘密鍵全体。改行を保持。 |
APPLE_ISSUER_ID、APPLE_KEY_ID | CI variable | 秘密鍵に対応する Issuer ID と Key ID。 |
APPLE_APP_ID | CI variable | App Store Connect の対象アプリの数値 ID。CLI には文字列で渡します。 |
--private-key-env RELEASE_KEY は値ではなく変数名を受け取り、既定では APPLE_PRIVATE_KEY を読みます。Secret のファイルパスが提供される場合は --private-key-file "$APPLE_KEY_FILE" を使い、パスを APPLE_PRIVATE_KEY に入れないでください。
共通の公開スクリプト
このスクリプトをアプリのリポジトリに ci/publish-apple.sh として保存します。各 CI の例で共通利用でき、既存ビルドツールの変更は不要です。
#!/usr/bin/env bash
set -euo pipefail
: "${APPLE_ISSUER_ID:?Set APPLE_ISSUER_ID}"
: "${APPLE_KEY_ID:?Set APPLE_KEY_ID}"
: "${APPLE_APP_ID:?Set APPLE_APP_ID}"
: "${APPLE_PRIVATE_KEY:?Set APPLE_PRIVATE_KEY secret}"
test -s build/App.ipa
mkdir -p release-results
foxstore-cli version --output json > release-results/version.json
foxstore-cli artifact inspect --file build/App.ipa --output json \
> release-results/artifact.json 2> release-results/artifact-error.json
foxstore-cli doctor apple \
--issuer-id "$APPLE_ISSUER_ID" --key-id "$APPLE_KEY_ID" --output json \
> release-results/doctor.json 2> release-results/doctor-error.json
foxstore-cli preflight apple --file build/App.ipa --output json \
> release-results/preflight.json 2> release-results/preflight-error.json
jq -e '.status == "success" and .data.status == "ready"' \
release-results/preflight.json > /dev/null
if foxstore-cli publish apple \
--issuer-id "$APPLE_ISSUER_ID" --key-id "$APPLE_KEY_ID" \
--app-id "$APPLE_APP_ID" --file build/App.ipa --output json \
> release-results/publish.json 2> release-results/publish-error.json; then
publish_exit=0
else
publish_exit=$?
fi
printf '%s\n' "$publish_exit" > release-results/publish-exit-code.txt
# 一次查询留存快照;保持原发布退出码,不自动重发。
if [ "$publish_exit" -eq 0 ] || [ "$publish_exit" -eq 7 ]; then
if foxstore-cli status apple \
--issuer-id "$APPLE_ISSUER_ID" --key-id "$APPLE_KEY_ID" --output json \
> release-results/status.json 2> release-results/status-error.json; then
printf '%s\n' '已保存远端构建快照,请核对目标应用与构建号。'
else
printf '%s\n' '状态查询失败,请在 App Store Connect 核对结果。' >&2
fi
fi
if [ "$publish_exit" -eq 7 ]; then
printf '%s\n' '发布结果未知,请先对账,禁止自动重发。' >&2
fi
exit "$publish_exit"Apple preflight の終了 0 は分析成功のみを示し、data.status: "blocked" の可能性があるため、スクリプトは ready も確認します。publish 成功時の buildUploadId はアップロード呼び出しの完了を示し、Apple の処理完了、TestFlight の利用可能状態、App Store 審査通過を意味しません。status apple は app-id フィルターなしで認証情報から見えるビルドを返すため、preflight.json の bundleId、version、buildNumber と照合します。反映が遅れる場合があり、1回のスナップショットにないだけで失敗と判断できません。
--output json の成功は stdout、エラー envelope は stderr に出ます。--output ndjson も現在は最終結果1件だけで、進捗の連続配信はしません。Runner に強制終了された場合は完全な envelope が残らないこともあり、終了コード 7 と同じく先にリモート状態を照合してください。
GitHub Actions
この release job を既存ビルド workflow の jobs 配下に追加します。依存する既存の build job は、App.ipa をルートに含む ipa という workflow artifact をアップロードする必要があります。Runner ラベル foxstore は CLI、Bash、jq の設定済み環境を表すので、ご自身のラベルに置き換えてください。上記スクリプトをリポジトリに含めておきます。
jobs:
# 保留已有 build job:构建签名后的 App.ipa,并上传名为 ipa 的 artifact。
release:
needs: build
runs-on: [self-hosted, foxstore]
permissions:
contents: read
concurrency:
group: apple-release-${{ github.repository }}
cancel-in-progress: false
env:
APPLE_ISSUER_ID: ${{ vars.APPLE_ISSUER_ID }}
APPLE_KEY_ID: ${{ vars.APPLE_KEY_ID }}
APPLE_APP_ID: ${{ vars.APPLE_APP_ID }}
steps:
- uses: actions/checkout@v4
- uses: actions/download-artifact@v4
with:
name: ipa
path: build
- name: Preflight and upload
shell: bash
env:
APPLE_PRIVATE_KEY: ${{ secrets.APPLE_PRIVATE_KEY }}
run: bash ci/publish-apple.sh
- name: Save release results
if: always()
uses: actions/upload-artifact@v4
with:
name: release-results
path: release-results/
retention-days: 14Secret は env から渡し、秘密鍵を shell ソースに挿入しないでください。設定と利用範囲は GitHub Actions Secrets を参照してください。成果物には指定の結果ディレクトリだけを含め、秘密鍵ファイルやワークスペース全体を含めないでください。結果内のアプリ情報にもアクセス制限を設けます。
GitLab CI
プロジェクトの CI/CD Variables に3つの通常変数と1つの保護された Secret を設定します。この例は変数値に PEM を保存します。GitLab の File 型では値が一時ファイルのパスになるため、--private-key-file を使うよう変更してください。GitLab CI/CD Variables を参照。
既存の .gitlab-ci.yml に統合し、build job を残して build/App.ipa を保存させます。既存 stages に deploy を含め、foxstore タグの Runner には CLI、Bash、jq を事前導入してください。
release_apple:
stage: deploy
tags: [foxstore]
needs:
- job: build
artifacts: true
resource_group: apple-release
script:
- bash ci/publish-apple.sh
artifacts:
when: always
paths:
- release-results/
expire_in: 14 daysJenkins
既存 Declarative Pipeline のビルド stage の後に、同じ workspace で次の stage を置きます。先に build/App.ipa をビルドし ci/publish-apple.sh をチェックアウトしてください。Agent には CLI、Bash、jq、既存 environment には3つの ID が必要です。Jenkins に ID apple-private-key の Secret text credential を作成します。credentials binding を使用し、既存 pipeline の options には disableConcurrentBuilds() の設定を推奨します。
stage('Release Apple') {
environment {
APPLE_PRIVATE_KEY = credentials('apple-private-key')
}
steps {
sh 'bash ci/publish-apple.sh'
}
post {
always {
archiveArtifacts artifacts: 'release-results/*', allowEmptyArchive: true
}
}
}Secret の binding と post は Jenkins Pipeline ドキュメントを参照してください。Groovy で秘密鍵をコマンドへ展開したり、Secret を出力するデバッグログを有効にしたりしないでください。
Provider の切り替えと失敗処理
ビルド後に複数 Provider の独立したコマンドを呼び出し、依存と並行実行は既存 CI で制御できます。Android では例えば次を実行します。
foxstore-cli preflight android --file build/App.aab \
--expected-package-name com.example.app --output json
foxstore-cli publish google-play --package-name com.example.app \
--file build/App.aab --track internal --release-status completed --output json
foxstore-cli status google-play --package-name com.example.app --output jsonこの例は GOOGLE_PLAY_CREDENTIALS_JSON の事前注入が必要です。実際のパイプラインでも上記の stdout/stderr 分離と終了コード処理を維持し、3行だけをコピーして公開 job 全体を無条件に再試行しないでください。WeChat には Node.js と2つの miniprogram CI npm パッケージが追加で必要です。Huawei Android の doctor/status は API Client のソースを明示してください。パラメーターリファレンスを参照。
終了コード 2/3 は通常入力や認証情報の修正が必要で、4/5 は error.retryable とリモート書き込み段階で判断します。6 は未対応、7 はパイプラインを失敗のままにして先に照合、10 はバージョンとエラー結果を保存して調査します。共通の wait、cancel、reconcile、--dry-run はありません。待機が必要ならパイプラインで照会回数を制限し、リモート状態が確認できるまで再 publish しないでください。
