Integrazione CI/CD
Affida compilazione e firma alla tua pipeline e passa l'artefatto a foxstore-cli. La CLI collega gli store dal tuo Runner senza avviare Desktop; non offre HTTP API o Runner ospitati. Consulta comandi, parametri, file JSON e codici di uscita in Riferimento CLI / API.
Preparare Runner e input
Gli esempi Apple richiedono foxstore-cli preinstallato per sistema e architettura, nel PATH, oltre a Bash e jq. Esegui prima foxstore-cli version --output json per registrare la versione. Non si presume che l'installer Desktop installi CLI e non serve una Action aggiuntiva di installazione Fox Store.
La compilazione deve prima produrre un build/App.ipa firmato. Il progetto gestisce compilazione, firma e permessi dello store; la CLI non crea certificati né compila app. Pubblica in sequenza la stessa app per evitare scritture simultanee di due pipeline sulla stessa versione.
Configura queste variabili:
| Nome | Tipo | Contenuto |
|---|---|---|
APPLE_PRIVATE_KEY | CI Secret | Chiave privata PEM completa di App Store Connect API Key, mantenendo le interruzioni di riga. |
APPLE_ISSUER_ID, APPLE_KEY_ID | CI variable | Issuer ID e Key ID corrispondenti alla chiave privata. |
APPLE_APP_ID | CI variable | ID numerico dell'app in App Store Connect, passato come string alla CLI. |
--private-key-env RELEASE_KEY riceve il nome della variabile, non il valore; per impostazione predefinita legge APPLE_PRIVATE_KEY. Se la piattaforma fornisce un percorso di file Secret, usa --private-key-file "$APPLE_KEY_FILE"; non mettere il percorso in APPLE_PRIVATE_KEY.
Script di pubblicazione condiviso
Salva lo script seguente come ci/publish-apple.sh nel repository. Tutti gli esempi CI lo riutilizzano senza cambiare gli strumenti di compilazione esistenti.
#!/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"Il codice 0 di Apple preflight indica solo analisi riuscita; può restituire data.status: "blocked", quindi lo script verifica ready. Un publish riuscito restituisce buildUploadId, che conferma la chiamata di caricamento, non l'elaborazione Apple, la disponibilità TestFlight o l'approvazione. status apple restituisce build visibili senza filtro app-id; confronta bundleId, version e buildNumber di preflight.json. Possibili ritardi: una singola istantanea senza il Build non dimostra un errore di caricamento.
Con --output json, i successi vanno su stdout e gli envelope di errore su stderr. Anche --output ndjson emette solo un risultato finale, senza avanzamento continuo. Un processo terminato dal Runner può non produrre un envelope completo; verifica il remoto come per il codice 7.
GitHub Actions
Aggiungi il job release ai jobs del workflow di compilazione esistente. Dipende da build, che deve caricare App.ipa come artifact ipa, con il file nella radice. L'etichetta personalizzata foxstore indica un Runner con CLI, Bash e jq configurati; sostituiscila con la tua. Il repository deve contenere lo script precedente.
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: 14Inietta i Secret tramite env, senza inserire la chiave privata nel codice shell. Consulta configurazione e ambito in Secret di GitHub Actions. L'artifact deve includere solo la cartella indicata, mai chiavi private o l'intero workspace. Limita anche l'accesso ai dati dell'app nei risultati.
GitLab CI
Configura le tre variabili ordinarie e un Secret protetto in CI/CD Variables. L'esempio salva PEM come valore; con il tipo File di GitLab, il valore sarà un percorso temporaneo e dovrai adattare lo script a --private-key-file. Consulta Variabili CI/CD di GitLab.
Integra in .gitlab-ci.yml mantenendo il job build, che deve archiviare build/App.ipa. Gli stages esistenti devono includere deploy; il Runner foxstore deve avere CLI, Bash e 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
Inserisci lo stage seguente dopo la compilazione del Declarative Pipeline esistente, nello stesso workspace. Genera prima build/App.ipa e recupera ci/publish-apple.sh. L'Agent richiede CLI, Bash e jq; configura i tre ID in environment e crea la credenziale Secret text apple-private-key in Jenkins. Lo stage usa credentials binding; consigliamo disableConcurrentBuilds() nelle options della pipeline.
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
}
}
}Consulta Secret binding e post in Jenkins Pipeline. Non interpolare chiavi private nei comandi Groovy né attivare log di debug che stampino Secret.
Cambiare provider e gestire gli errori
Dopo la compilazione, una pipeline può chiamare comandi indipendenti di più provider; il CI esistente organizza dipendenze e concorrenza. Per esempio, per 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 jsonInietta prima GOOGLE_PLAY_CREDENTIALS_JSON. Mantieni la separazione stdout/stderr e la gestione dei codici dello script; non copiare solo le tre righe attivando tentativi incondizionati del job. WeChat richiede Node.js e due pacchetti npm miniprogram CI; doctor/status di Huawei Android richiede di scegliere esplicitamente API Client, vedi Parametri.
I codici 2/3 richiedono di norma correzioni a input o credenziali; 4/5 vanno valutati con error.retryable e la fase di scrittura remota; 6 indica non supportato; con 7 mantieni l'errore e riconcilia prima; per 10 conserva versione ed errore. Non esistono wait, cancel, reconcile o --dry-run unificati. Se devi attendere, configura consultazioni limitate nella pipeline e non ripetere publish prima di confermare lo stato remoto.
