forked from github-such-software/hash-wallet
LibTorch.xcframework ships with each slice's Info.plist claiming
MinimumOSVersion=12.0, but the Mach-O LC_BUILD_VERSION inside the
binaries actually requires 13.0 (device) and 14.0 (simulator). Apple's
validator catches the lie and rejects TestFlight uploads with ITMS-90208
("LibTorch.framework does not support the minimum OS Version specified
in the Info.plist") regardless of the app's deployment target.
Revert the previous 15.0 deployment-target bump (which was based on the
wrong hypothesis) back to 13.0 — that's actually what LibTorch supports.
Add a workflow step that plutil-rewrites both slice Info.plists to match
the value their binary actually requires. Bump build number 1.0.0+2 ->
1.0.0+3 for the re-upload.
491 lines
21 KiB
YAML
491 lines
21 KiB
YAML
name: Hash Bags iOS TestFlight build
|
|
|
|
# Phase 2 of iOS CI: full signed build that uploads to TestFlight.
|
|
# Phase 1 (build-ios-sim.yml) validates the Mac runner with no signing —
|
|
# run that first if TestFlight blows up unexpectedly.
|
|
#
|
|
# Required Gitea Actions secrets (all account/app credentials):
|
|
# APPLE_TEAM_ID — 10-char (D8PL9F7X33)
|
|
# APPLE_KEY_ID — 10-char (CHK6B69G58)
|
|
# APPLE_ISSUER_ID — UUID
|
|
# APPLE_API_KEY_P8_BASE64 — base64 of AuthKey_<KeyID>.p8
|
|
# APPLE_DIST_CERT_P12_BASE64 — base64 of distribution.p12
|
|
# APPLE_DIST_CERT_P12_PASSWORD — password set during openssl pkcs12 export
|
|
# APPLE_PROVISIONING_PROFILE_BASE64 — base64 of Hash_Wallet.mobileprovision
|
|
# APPLE_KEYCHAIN_PASSWORD — any string, used to create the temp keychain
|
|
# TROCADOR_* — reused from Linux/Android
|
|
|
|
on:
|
|
# Don't auto-trigger on every push — TestFlight uploads are slow and use
|
|
# build numbers. Manual trigger only; later we can add a workflow_dispatch
|
|
# input for build number or auto-bump.
|
|
workflow_dispatch:
|
|
|
|
concurrency:
|
|
group: ios-testflight-${{ github.ref }}
|
|
cancel-in-progress: true
|
|
|
|
defaults:
|
|
run:
|
|
shell: bash
|
|
|
|
jobs:
|
|
build:
|
|
runs-on: macos-latest
|
|
env:
|
|
APP_IOS_TYPE: cakewallet
|
|
BRANCH_NAME: ${{ github.head_ref || github.ref_name }}
|
|
# CocoaPods needs UTF-8; Gitea runner shell defaults to ASCII-8BIT.
|
|
LANG: en_US.UTF-8
|
|
LC_ALL: en_US.UTF-8
|
|
# See note in build-ios-sim.yml — fallback if /etc/hosts trick
|
|
# isn't in place. Safe no-op once it is.
|
|
NODE_TLS_REJECT_UNAUTHORIZED: '0'
|
|
|
|
steps:
|
|
- uses: actions/checkout@v4
|
|
with:
|
|
fetch-depth: 1
|
|
|
|
- name: Check Flutter 3.32.0
|
|
id: flutter_check
|
|
run: |
|
|
if command -v flutter >/dev/null && flutter --version 2>/dev/null | grep -q "3\.32\.0"; then
|
|
echo "installed=true" >> "$GITHUB_OUTPUT"
|
|
flutter --version
|
|
else
|
|
echo "installed=false" >> "$GITHUB_OUTPUT"
|
|
fi
|
|
|
|
- name: Install Flutter 3.32.0 (if missing)
|
|
if: steps.flutter_check.outputs.installed != 'true'
|
|
uses: subosito/flutter-action@v2
|
|
with:
|
|
flutter-version: '3.32.0'
|
|
channel: stable
|
|
cache: true
|
|
|
|
- name: Install CocoaPods (if missing)
|
|
run: |
|
|
if command -v pod >/dev/null; then
|
|
pod --version
|
|
exit 0
|
|
fi
|
|
if command -v brew >/dev/null; then
|
|
brew install cocoapods
|
|
else
|
|
export GEM_HOME="$HOME/.gem"
|
|
export PATH="$GEM_HOME/bin:$PATH"
|
|
echo "GEM_HOME=$HOME/.gem" >> "$GITHUB_ENV"
|
|
echo "$HOME/.gem/bin" >> "$GITHUB_PATH"
|
|
gem install --user-install cocoapods --no-document
|
|
fi
|
|
pod --version
|
|
|
|
- name: Install Rust + iOS targets (if missing)
|
|
run: |
|
|
if ! command -v rustup >/dev/null; then
|
|
curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh -s -- -y --default-toolchain stable --no-modify-path
|
|
echo "$HOME/.cargo/bin" >> "$GITHUB_PATH"
|
|
export PATH="$HOME/.cargo/bin:$PATH"
|
|
fi
|
|
rm -rf "$HOME/.rustup/downloads"/*.partial 2>/dev/null || true
|
|
# Stable + iOS targets (most plugins use these)
|
|
rustup target add aarch64-apple-ios
|
|
rustup target add aarch64-apple-ios-sim
|
|
rustup target add x86_64-apple-ios
|
|
# Nightly + iOS targets — at least one plugin (sp_scanner / payjoin
|
|
# / something in the cargokit chain) builds its rust component
|
|
# with +nightly. The TestFlight archive bombs at "Missing manifest
|
|
# in toolchain 'nightly-aarch64-apple-darwin'" if nightly isn't
|
|
# installed alongside stable.
|
|
rustup toolchain install nightly --profile minimal
|
|
rustup target add aarch64-apple-ios --toolchain nightly
|
|
rustup target add aarch64-apple-ios-sim --toolchain nightly
|
|
rustup target add x86_64-apple-ios --toolchain nightly
|
|
rustup show
|
|
|
|
- name: Install Go + gomobile (if missing)
|
|
run: |
|
|
if ! command -v go >/dev/null; then
|
|
if command -v brew >/dev/null; then
|
|
brew install go
|
|
else
|
|
echo "Go missing and brew not available"; exit 1
|
|
fi
|
|
fi
|
|
go version
|
|
GOPATH=$(go env GOPATH)
|
|
echo "$GOPATH/bin" >> "$GITHUB_PATH"
|
|
export PATH="$PATH:$GOPATH/bin"
|
|
if ! command -v gomobile >/dev/null; then
|
|
go install golang.org/x/mobile/cmd/gomobile@latest
|
|
go install golang.org/x/mobile/cmd/gobind@latest
|
|
fi
|
|
which gomobile && gomobile version || true
|
|
|
|
- name: Show toolchain
|
|
run: |
|
|
set -x
|
|
flutter --version
|
|
xcodebuild -version
|
|
pod --version
|
|
uname -m
|
|
|
|
# ---- External prebuilt deps -----------------------------------------
|
|
# macOS doesn't ship wget; using curl everywhere.
|
|
- name: Fetch prebuilt torch_dart
|
|
run: |
|
|
set -x -e
|
|
pushd scripts
|
|
rm -rf torch_dart torch_dart.tar.gz
|
|
curl -fsSL -o torch_dart.tar.gz https://github.com/MrCyjaneK/torch_dart/releases/download/v1.0.17/torch_dart-v1.0.17.tar.gz
|
|
mkdir torch_dart
|
|
tar -xzf torch_dart.tar.gz -C torch_dart
|
|
rm torch_dart.tar.gz
|
|
popd
|
|
# LibTorch.xcframework ships with a lie: each slice's Info.plist
|
|
# claims MinimumOSVersion=12.0, but the Mach-O LC_BUILD_VERSION
|
|
# in the binaries says 13.0 (device) and 14.0 (simulator).
|
|
# Apple's TestFlight validator catches this mismatch and rejects
|
|
# the upload with ITMS-90208. Patch the Info.plists to match
|
|
# the actual binary build versions.
|
|
DEVICE_PLIST=scripts/torch_dart/ios/LibTorch.xcframework/ios-arm64/LibTorch.framework/Info.plist
|
|
SIM_PLIST=scripts/torch_dart/ios/LibTorch.xcframework/ios-arm64-simulator/LibTorch.framework/Info.plist
|
|
for entry in "$DEVICE_PLIST:13.0" "$SIM_PLIST:14.0"; do
|
|
plist="${entry%:*}"
|
|
min="${entry#*:}"
|
|
if [[ -f "$plist" ]]; then
|
|
plutil -replace MinimumOSVersion -string "$min" "$plist"
|
|
echo "patched $plist -> MinimumOSVersion $min"
|
|
plutil -p "$plist" | grep -E "MinimumOSVersion|CFBundleName" || true
|
|
else
|
|
echo "WARNING: $plist not found"
|
|
fi
|
|
done
|
|
|
|
- name: Fetch prebuilt reown_flutter
|
|
run: |
|
|
set -x -e
|
|
pushd scripts
|
|
rm -rf reown_flutter reown_flutter.tar.gz
|
|
curl -fsSL -o reown_flutter.tar.gz https://github.com/cake-tech/reown_flutter/releases/download/v0.0.4/reown_flutter-v0.0.4.tar.gz
|
|
mkdir reown_flutter
|
|
tar -xzf reown_flutter.tar.gz -C reown_flutter
|
|
rm reown_flutter.tar.gz
|
|
popd
|
|
|
|
- name: Clone BitBox Flutter (iOS — skip Android bindings)
|
|
run: |
|
|
# See note in build-ios-sim.yml — iOS uses bitbox's native plugin,
|
|
# not the .aar that build_bindings.sh generates.
|
|
set -x -e
|
|
pushd scripts
|
|
if [[ ! -d bitbox_flutter ]]; then
|
|
git clone https://github.com/konstantinullrich/bitbox_flutter
|
|
fi
|
|
cd bitbox_flutter
|
|
git fetch -a
|
|
git reset --hard
|
|
git checkout 5a6e6dd388ef64003f86094af80d5453518b601d
|
|
git reset --hard
|
|
popd
|
|
|
|
- name: Fetch prebuilt monero_c bundle
|
|
run: |
|
|
set -x -e
|
|
./scripts/prepare_moneroc.sh
|
|
MONERO_C_TAG=$(cd scripts/monero_c && git describe --tags)
|
|
mkdir -p "scripts/monero_c/release/$MONERO_C_TAG"
|
|
pushd "scripts/monero_c/release/$MONERO_C_TAG"
|
|
curl -fsSL -O https://github.com/MrCyjaneK/monero_c/releases/download/v0.18.4.6-RC1/release-bundle.zip
|
|
unzip -q release-bundle.zip
|
|
rm release-bundle.zip
|
|
popd
|
|
|
|
- name: Build MoneroWallet + WowneroWallet XCFrameworks
|
|
run: |
|
|
set -x -e
|
|
pushd scripts/ios
|
|
bash ./gen_framework.sh
|
|
popd
|
|
ls -la ios/MoneroWallet.xcframework ios/WowneroWallet.xcframework
|
|
|
|
# ---- Configure: pubspec.yaml, Info.plist, etc. ----------------------
|
|
- name: Run iOS configure (cakewallet profile)
|
|
run: |
|
|
set -x -e
|
|
pushd scripts/ios
|
|
source ./app_env.sh cakewallet
|
|
./app_config.sh
|
|
popd
|
|
|
|
# ---- Secrets ---------------------------------------------------------
|
|
- name: Generate per-module secrets.g.dart files (empty defaults)
|
|
run: dart run tool/generate_new_secrets.dart
|
|
|
|
- name: Inject Trocador affiliate secrets
|
|
env:
|
|
TROCADOR_API_KEY: ${{ secrets.TROCADOR_API_KEY }}
|
|
TROCADOR_MONERO_API_KEY: ${{ secrets.TROCADOR_MONERO_API_KEY }}
|
|
TROCADOR_EXCHANGE_MARKUP: ${{ secrets.TROCADOR_EXCHANGE_MARKUP }}
|
|
run: |
|
|
sed -i '' \
|
|
-e "s|const trocadorApiKey = '';|const trocadorApiKey = '${TROCADOR_API_KEY}';|" \
|
|
-e "s|const trocadorMoneroApiKey = '';|const trocadorMoneroApiKey = '${TROCADOR_MONERO_API_KEY}';|" \
|
|
-e "s|const trocadorExchangeMarkup = '';|const trocadorExchangeMarkup = '${TROCADOR_EXCHANGE_MARKUP:-1}';|" \
|
|
lib/.secrets.g.dart
|
|
|
|
# ---- Apple signing setup --------------------------------------------
|
|
- name: Sanity-check Apple secrets are reaching the runner
|
|
env:
|
|
APPLE_TEAM_ID: ${{ secrets.APPLE_TEAM_ID }}
|
|
APPLE_KEY_ID: ${{ secrets.APPLE_KEY_ID }}
|
|
APPLE_ISSUER_ID: ${{ secrets.APPLE_ISSUER_ID }}
|
|
APPLE_API_KEY_P8_BASE64: ${{ secrets.APPLE_API_KEY_P8_BASE64 }}
|
|
APPLE_DIST_CERT_P12_BASE64: ${{ secrets.APPLE_DIST_CERT_P12_BASE64 }}
|
|
APPLE_DIST_CERT_P12_PASSWORD: ${{ secrets.APPLE_DIST_CERT_P12_PASSWORD }}
|
|
APPLE_PROVISIONING_PROFILE_BASE64: ${{ secrets.APPLE_PROVISIONING_PROFILE_BASE64 }}
|
|
APPLE_KEYCHAIN_PASSWORD: ${{ secrets.APPLE_KEYCHAIN_PASSWORD }}
|
|
run: |
|
|
# Print LENGTHS only — never the secret values. Length 0 means the
|
|
# secret isn't actually reaching the runner (most often: secret is
|
|
# set on a different Gitea repo than this workflow is running in).
|
|
set +e
|
|
echo "APPLE_TEAM_ID length: ${#APPLE_TEAM_ID}"
|
|
echo "APPLE_KEY_ID length: ${#APPLE_KEY_ID}"
|
|
echo "APPLE_ISSUER_ID length: ${#APPLE_ISSUER_ID}"
|
|
echo "APPLE_API_KEY_P8_BASE64 length: ${#APPLE_API_KEY_P8_BASE64}"
|
|
echo "APPLE_DIST_CERT_P12_BASE64 length: ${#APPLE_DIST_CERT_P12_BASE64}"
|
|
echo "APPLE_DIST_CERT_P12_PASSWORD length: ${#APPLE_DIST_CERT_P12_PASSWORD}"
|
|
echo "APPLE_PROVISIONING_PROFILE_BASE64 length: ${#APPLE_PROVISIONING_PROFILE_BASE64}"
|
|
echo "APPLE_KEYCHAIN_PASSWORD length: ${#APPLE_KEYCHAIN_PASSWORD}"
|
|
set -e
|
|
# Fail fast on any zero-length secret so we don't waste time on the
|
|
# subsequent steps.
|
|
for n in TEAM_ID KEY_ID ISSUER_ID API_KEY_P8_BASE64 DIST_CERT_P12_BASE64 DIST_CERT_P12_PASSWORD PROVISIONING_PROFILE_BASE64 KEYCHAIN_PASSWORD; do
|
|
var="APPLE_$n"
|
|
if [[ -z "${!var}" ]]; then
|
|
echo "FATAL: $var is empty — set it as a Gitea repo secret on the repo this workflow runs in (Builds/hash-wallet)."
|
|
exit 1
|
|
fi
|
|
done
|
|
|
|
- name: Create temp keychain + import distribution cert
|
|
env:
|
|
APPLE_DIST_CERT_P12_BASE64: ${{ secrets.APPLE_DIST_CERT_P12_BASE64 }}
|
|
APPLE_DIST_CERT_P12_PASSWORD: ${{ secrets.APPLE_DIST_CERT_P12_PASSWORD }}
|
|
APPLE_KEYCHAIN_PASSWORD: ${{ secrets.APPLE_KEYCHAIN_PASSWORD }}
|
|
run: |
|
|
set -e
|
|
KEYCHAIN_PATH="$RUNNER_TEMP/build.keychain-db"
|
|
# Create + unlock dedicated keychain so we don't touch the user's login keychain
|
|
security create-keychain -p "$APPLE_KEYCHAIN_PASSWORD" "$KEYCHAIN_PATH"
|
|
security set-keychain-settings -lut 21600 "$KEYCHAIN_PATH" # 6 hours
|
|
security unlock-keychain -p "$APPLE_KEYCHAIN_PASSWORD" "$KEYCHAIN_PATH"
|
|
# Add to user search list so codesign can find it
|
|
security list-keychains -d user -s "$KEYCHAIN_PATH" $(security list-keychains -d user | sed 's/"//g')
|
|
# Decode the user-uploaded .p12 directly. macOS Keychain Access
|
|
# exports .p12 with legacy PKCS12 crypto (RC2-40-CBC, PBE-SHA1)
|
|
# which is exactly what `security import` expects — no openssl
|
|
# conversion needed. Earlier attempts to "convert to legacy"
|
|
# via openssl 3.x failed because openssl 3.x has RC2-40-CBC
|
|
# disabled by default; that was a non-problem we made for
|
|
# ourselves. The original "Unknown format" failure was caused
|
|
# by a truncated base64 secret value, since fixed by re-pasting
|
|
# the secret via Gitea's API.
|
|
P12="$RUNNER_TEMP/dist.p12"
|
|
echo "$APPLE_DIST_CERT_P12_BASE64" | openssl base64 -d -A > "$P12"
|
|
echo "=== decoded .p12 ==="
|
|
ls -la "$P12"
|
|
xxd "$P12" | head -1
|
|
file "$P12" || true
|
|
|
|
security import "$P12" -k "$KEYCHAIN_PATH" -P "$APPLE_DIST_CERT_P12_PASSWORD" \
|
|
-T /usr/bin/codesign -T /usr/bin/security
|
|
security set-key-partition-list -S apple-tool:,apple:,codesign: -s -k "$APPLE_KEYCHAIN_PASSWORD" "$KEYCHAIN_PATH"
|
|
# Sanity-check: list identities the keychain offers
|
|
security find-identity -v -p codesigning "$KEYCHAIN_PATH"
|
|
rm -f "$P12"
|
|
|
|
- name: Install provisioning profile + parse name/UUID for ExportOptions
|
|
env:
|
|
APPLE_PROVISIONING_PROFILE_BASE64: ${{ secrets.APPLE_PROVISIONING_PROFILE_BASE64 }}
|
|
run: |
|
|
set -e
|
|
PROFILES_DIR="$HOME/Library/MobileDevice/Provisioning Profiles"
|
|
mkdir -p "$PROFILES_DIR"
|
|
PROFILE="$RUNNER_TEMP/Hash_Wallet.mobileprovision"
|
|
echo "$APPLE_PROVISIONING_PROFILE_BASE64" | openssl base64 -d -A > "$PROFILE"
|
|
|
|
# Parse the .mobileprovision to get its UUID + Name (CMS-decoded plist).
|
|
PROFILE_UUID=$(security cms -D -i "$PROFILE" | plutil -extract UUID raw -o - -)
|
|
PROFILE_NAME=$(security cms -D -i "$PROFILE" | plutil -extract Name raw -o - -)
|
|
echo "Profile UUID: $PROFILE_UUID"
|
|
echo "Profile name: $PROFILE_NAME"
|
|
echo "PROFILE_UUID=$PROFILE_UUID" >> "$GITHUB_ENV"
|
|
echo "PROFILE_NAME=$PROFILE_NAME" >> "$GITHUB_ENV"
|
|
|
|
# Xcode looks profiles up by UUID filename.
|
|
cp "$PROFILE" "$PROFILES_DIR/$PROFILE_UUID.mobileprovision"
|
|
ls -la "$PROFILES_DIR" | head -10
|
|
|
|
- name: Install App Store Connect API key for altool
|
|
env:
|
|
APPLE_API_KEY_P8_BASE64: ${{ secrets.APPLE_API_KEY_P8_BASE64 }}
|
|
APPLE_KEY_ID: ${{ secrets.APPLE_KEY_ID }}
|
|
run: |
|
|
set -e
|
|
mkdir -p "$HOME/.appstoreconnect/private_keys"
|
|
P8="$HOME/.appstoreconnect/private_keys/AuthKey_${APPLE_KEY_ID}.p8"
|
|
echo "$APPLE_API_KEY_P8_BASE64" | openssl base64 -d -A > "$P8"
|
|
ls -la "$HOME/.appstoreconnect/private_keys/"
|
|
|
|
# Diagnostics — verify the PEM file is well-formed without
|
|
# printing the private key body. A valid AuthKey .p8 must:
|
|
# - Start with "-----BEGIN PRIVATE KEY-----"
|
|
# - End with "-----END PRIVATE KEY-----"
|
|
# - openssl pkey -in file -noout must succeed (real ASN.1 parse)
|
|
echo "=== .p8 first/last lines (no key body) ==="
|
|
head -1 "$P8"
|
|
tail -1 "$P8"
|
|
echo "=== openssl parse check (silent on success) ==="
|
|
if openssl pkey -in "$P8" -noout 2>&1; then
|
|
echo "OK — .p8 parses as a valid EC private key"
|
|
else
|
|
echo "FATAL: .p8 doesn't parse — secret likely truncated or wrong content"
|
|
echo "Re-paste APPLE_API_KEY_P8_BASE64 via the Gitea API (same flow"
|
|
echo "as the .p12 re-paste); the file decoded to:"
|
|
wc -c "$P8"
|
|
exit 1
|
|
fi
|
|
|
|
# ---- Flutter init + codegen + pod install ---------------------------
|
|
- name: Initialize Flutter SDK (iOS precache)
|
|
run: |
|
|
flutter --version
|
|
flutter precache --ios --no-linux --no-android --no-macos --no-windows --no-fuchsia --no-web || true
|
|
|
|
- name: Build generated code
|
|
run: bash model_generator.sh
|
|
|
|
- name: Generate localization
|
|
run: dart run tool/generate_localization.dart
|
|
|
|
- name: Compile SVG assets (res/pictures/*.svg → assets/new-ui/*.svg.vec)
|
|
run: ./compile_graphics.sh
|
|
|
|
- name: pod install
|
|
run: |
|
|
cd ios
|
|
pod install --repo-update
|
|
|
|
# ---- Override signing in project.pbxproj for the archive step -------
|
|
# The committed Release config uses Automatic signing + "Apple
|
|
# Development" identity (set up for local dev where Xcode is signed
|
|
# into an Apple ID). On CI we have only the cert + provisioning
|
|
# profile we installed in the temp keychain; no Xcode account.
|
|
#
|
|
# First attempt was an xcconfig append, but Xcode build-setting
|
|
# precedence puts target-level pbxproj settings ABOVE xcconfig —
|
|
# so the pbxproj values won and the build did Automatic signing
|
|
# anyway. Patch the Release config in pbxproj directly, only
|
|
# within the Runner target block (id 97C147071CF9000F007C117D);
|
|
# leaves Debug/Profile configs untouched so local builds stay
|
|
# on Automatic signing.
|
|
- name: Override signing settings in pbxproj for archive
|
|
env:
|
|
APPLE_TEAM_ID: ${{ secrets.APPLE_TEAM_ID }}
|
|
run: |
|
|
set -x
|
|
PBX=ios/Runner.xcodeproj/project.pbxproj
|
|
# BSD sed (macOS): -i '' for in-place, regex range scoped to
|
|
# the Runner target's Release config by its stable UUID.
|
|
sed -i '' "/97C147071CF9000F007C117D \/\* Release/,/name = Release;/ {
|
|
s/CODE_SIGN_IDENTITY = \"Apple Development\";/CODE_SIGN_IDENTITY = \"Apple Distribution\";/
|
|
s/CODE_SIGN_STYLE = Automatic;/CODE_SIGN_STYLE = Manual;/
|
|
s/DEVELOPMENT_TEAM = [A-Z0-9]\{1,\};/DEVELOPMENT_TEAM = ${APPLE_TEAM_ID};/
|
|
s|PROVISIONING_PROFILE_SPECIFIER = \"\";|PROVISIONING_PROFILE_SPECIFIER = \"${PROFILE_NAME}\";|
|
|
}" "$PBX"
|
|
echo "=== patched Runner Release config ==="
|
|
sed -n '/97C147071CF9000F007C117D \/\* Release/,/name = Release;/p' "$PBX"
|
|
|
|
# ---- Generate ExportOptions.plist + build IPA -----------------------
|
|
- name: Write ExportOptions.plist
|
|
env:
|
|
APPLE_TEAM_ID: ${{ secrets.APPLE_TEAM_ID }}
|
|
run: |
|
|
cat > ios/ExportOptions.plist <<EOF
|
|
<?xml version="1.0" encoding="UTF-8"?>
|
|
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
|
<plist version="1.0">
|
|
<dict>
|
|
<key>method</key>
|
|
<string>app-store</string>
|
|
<key>teamID</key>
|
|
<string>${APPLE_TEAM_ID}</string>
|
|
<key>signingStyle</key>
|
|
<string>manual</string>
|
|
<key>signingCertificate</key>
|
|
<string>Apple Distribution</string>
|
|
<key>provisioningProfiles</key>
|
|
<dict>
|
|
<key>com.suchsoftware.hashwallet</key>
|
|
<string>${PROFILE_NAME}</string>
|
|
</dict>
|
|
<key>uploadBitcode</key>
|
|
<false/>
|
|
<key>uploadSymbols</key>
|
|
<true/>
|
|
<key>destination</key>
|
|
<string>export</string>
|
|
<key>stripSwiftSymbols</key>
|
|
<true/>
|
|
</dict>
|
|
</plist>
|
|
EOF
|
|
cat ios/ExportOptions.plist
|
|
|
|
- name: Build IPA
|
|
run: |
|
|
set -x -e
|
|
flutter build ipa --release \
|
|
--dart-define-from-file=env.json \
|
|
--export-options-plist=ios/ExportOptions.plist
|
|
ls -la build/ios/ipa/
|
|
|
|
- name: Upload IPA artifact
|
|
uses: actions/upload-artifact@v3
|
|
with:
|
|
name: hash-wallet-ios-${{ github.sha }}
|
|
path: build/ios/ipa/*.ipa
|
|
retention-days: 30
|
|
|
|
# ---- Upload to TestFlight -------------------------------------------
|
|
- name: Upload to TestFlight via altool
|
|
env:
|
|
APPLE_KEY_ID: ${{ secrets.APPLE_KEY_ID }}
|
|
APPLE_ISSUER_ID: ${{ secrets.APPLE_ISSUER_ID }}
|
|
run: |
|
|
set -e
|
|
IPA=$(ls build/ios/ipa/*.ipa | head -1)
|
|
echo "Uploading $IPA to TestFlight..."
|
|
xcrun altool --upload-app \
|
|
--type ios \
|
|
--file "$IPA" \
|
|
--apiKey "$APPLE_KEY_ID" \
|
|
--apiIssuer "$APPLE_ISSUER_ID"
|
|
|
|
# ---- Cleanup keychain so we don't leak it across builds -------------
|
|
- name: Cleanup
|
|
if: always()
|
|
env:
|
|
APPLE_KEYCHAIN_PASSWORD: ${{ secrets.APPLE_KEYCHAIN_PASSWORD }}
|
|
run: |
|
|
set +e
|
|
KEYCHAIN_PATH="$RUNNER_TEMP/build.keychain-db"
|
|
security delete-keychain "$KEYCHAIN_PATH" 2>/dev/null
|
|
rm -rf "$HOME/.appstoreconnect/private_keys"
|
|
rm -f "$HOME/Library/MobileDevice/Provisioning Profiles"/*.mobileprovision
|
|
true
|