Build iOS #242
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
name: Build iOS | |
on: | |
workflow_dispatch: | |
inputs: | |
use-published-plugins: | |
description: 'Boolean indicating whether to use a published plugin for the SDK. Default = false.' | |
required: false | |
type: boolean | |
default: false | |
liquid-sdk-plugin-version: | |
description: 'Version for the published Liquid SDK plugin "v(MAJOR.MINOR.BUILD)". Defaults to latest published version on "breez/breez-sdk-liquid-flutter"' | |
required: false | |
type: string | |
default: '' | |
liquid-sdk-ref: | |
description: 'Liquid SDK commit/tag/branch reference when not using a published plugin. Default = "main"' | |
required: false | |
type: string | |
default: 'main' | |
jobs: | |
pre-setup: | |
name: Pre-setup | |
runs-on: ubuntu-latest | |
outputs: | |
# These outputs mimic the inputs for the workflow. | |
# Their only purpose is to be able to test this workflow if you make | |
# changes that you won't want to commit to main yet. | |
# You can set these values manually, to test how the CI behaves with | |
# certain inputs. | |
use-published-plugins: ${{ inputs.use-published-plugins }} | |
liquid-sdk-plugin-version: ${{ inputs.liquid-sdk-plugin-version }} | |
liquid-sdk-ref: ${{ inputs.liquid-sdk-ref }} | |
steps: | |
- name: Checkout repository | |
if: ${{ needs.pre-setup.outputs.use-published-plugins == 'true' && needs.pre-setup.outputs.liquid-sdk-plugin-version == ''}} | |
uses: actions/checkout@v4 | |
with: | |
repository: 'breez/breez-sdk-liquid-flutter' | |
- name: Get the latest tag and set 'liquid-sdk-plugin-version' | |
if: ${{ needs.pre-setup.outputs.use-published-plugins == 'true' && needs.pre-setup.outputs.liquid-sdk-plugin-version == ''}} | |
run: | | |
latest_tag=$(git describe --tags `git rev-list --tags --max-count=1`) | |
echo "::set-output name=liquid-sdk-plugin-version::$latest_tag" | |
- run: echo "set pre-setup output variables" | |
- name: Disk Cleanup | |
run: | | |
echo "::group::Free space before cleanup" | |
df -h --total | |
echo "::endgroup::" | |
echo "::group::Cleaned Files" | |
sudo rm -rf /usr/local/.ghcup | |
sudo rm -rf /opt/hostedtoolcache/CodeQL | |
sudo rm -rf /usr/local/lib/android/sdk/ndk | |
sudo rm -rf /usr/share/dotnet | |
sudo rm -rf /opt/ghc | |
sudo rm -rf /usr/local/share/boost | |
sudo apt-get clean | |
echo "::endgroup::" | |
echo "::group::Free space after cleanup" | |
df -h --total | |
echo "::endgroup::" | |
setup: | |
name: Setup | |
needs: pre-setup | |
runs-on: ubuntu-latest | |
outputs: | |
# Careful, a boolean input is not a boolean output. A boolean input is | |
# actually a boolean, but these outputs are strings. All the boolean | |
# checks in this file have the format `boolean == 'true'`. So feel free | |
# to set these variables here to `true` or `false` | |
# (e.g. bindings-windows: true) if you want to test something. | |
use-published-plugins: ${{ needs.pre-setup.outputs.use-published-plugins }} | |
liquid-sdk-plugin-version: ${{ needs.pre-setup.outputs.liquid-sdk-plugin-version }} | |
liquid-sdk-ref: ${{ needs.pre-setup.outputs.liquid-sdk-ref }} | |
steps: | |
- run: echo "set setup output variables" | |
build-ios: | |
needs: setup | |
name: Build iOS | |
runs-on: macos-14 | |
env: | |
SCHEME: Runner | |
BUILD_CONFIGURATION: Release | |
TESTFLIGHT_USERNAME: ${{ secrets.TESTFLIGHT_USERNAME }} | |
TESTFLIGHT_PASSWORD: ${{ secrets.TESTFLIGHT_PASSWORD }} | |
IOS_VERSION_STRING: 0.1.0 | |
DISTRIBUTION_CERT: ${{ secrets.DISTRIBUTION_CERT }} | |
P12_BASE64: ${{ secrets.P12_BASE64 }} | |
P12_PASSWORD: ${{ secrets.P12_PASSWORD }} | |
GOOGLE_SERVICES_IOS: ${{ secrets.GOOGLE_SERVICES_IOS }} | |
FIREBASE_PROJECT: breez-technology | |
FIREBASE_ANDROID_PACKAGE_NAME: com.breez.liquid.l_breez | |
FIREBASE_IOS_BUNDLE_ID: com.breez.liquid.lBreez | |
# A bug causes 'flutterfire configure' to require app id's for non-selected platforms on CI workflows https://github.com/invertase/flutterfire_cli/issues/233 | |
# If the app id does not exists on project, 'flutterfire configure' automatically create an app with that app id, that is why an existing app id is used here. | |
FIREBASE_PLACEHOLDER_APP_ID: com.breez.liquid.lBreez | |
GOOGLE_APPLICATION_CREDENTIALS_BASE64: ${{ secrets.GOOGLE_APPLICATION_CREDENTIALS_BASE64 }} | |
steps: | |
- name: Disk Cleanup | |
run: | | |
echo "::group::Free space before cleanup" | |
df -hI | |
echo "::endgroup::" | |
echo "::group::Cleaned Files" | |
sudo rm -rf /Users/runner/Library/Android/sdk | |
sudo rm -rf /Applications/Xcode_14.3.1.app | |
sudo rm -rf /Applications/Xcode_15.0.1.app | |
sudo rm -rf /Applications/Xcode_15.1.app | |
sudo rm -rf /Applications/Xcode_15.2.app | |
sudo rm -rf /Applications/Xcode_15.3.app | |
echo "::endgroup::" | |
echo "::group::Free space after cleanup" | |
df -hI | |
echo "::endgroup::" | |
- name: 🏗️ Check-out l-breez repository | |
uses: actions/checkout@v4 | |
with: | |
path: 'lbreez' | |
- name: Set Liquid SDK plugin version | |
if: ${{ needs.setup.outputs.use-published-plugins == 'true' }} | |
working-directory: lbreez | |
run: | | |
mv pubspec_overrides.yaml.workflow pubspec_overrides.yaml | |
sed -i.bak -e 's/ref:.*/ref: ${{ needs.setup.outputs.liquid-sdk-plugin-version }}/' pubspec_overrides.yaml | |
rm pubspec_overrides.yaml.bak | |
- name: Install rust | |
if: ${{ needs.setup.outputs.use-published-plugins == 'false' }} | |
run: | | |
rustup set auto-self-update disable | |
rustup toolchain install stable --profile minimal | |
- name: Set IPHONEOS_DEPLOYMENT_TARGET | |
run: echo "IPHONEOS_DEPLOYMENT_TARGET=13.0" >> $GITHUB_ENV | |
- name: Install Protoc | |
if: ${{ needs.setup.outputs.use-published-plugins == 'false' }} | |
uses: arduino/setup-protoc@v3 | |
with: | |
version: "27.2" | |
repo-token: ${{ secrets.GITHUB_TOKEN }} | |
- name: 🏗️ Setup Java | |
uses: actions/setup-java@v4 | |
with: | |
distribution: 'zulu' | |
java-version: '17' | |
- name: 🏗️ Setup Flutter | |
uses: subosito/flutter-action@v2 | |
with: | |
channel: 'stable' | |
flutter-version: 3.22.3 # Pinned until resource linking issues on Android is resolved with 3.24 | |
cache: true | |
- name: Set up just | |
if: ${{ needs.setup.outputs.use-published-plugins == 'false' }} | |
uses: extractions/setup-just@v2 | |
- name: Set up Melos | |
if: ${{ needs.setup.outputs.use-published-plugins == 'false' }} | |
uses: bluefireteam/melos-action@v3 | |
with: | |
run-bootstrap: false | |
- name: Set up Firebase CLI | |
run: sudo npm i -g firebase-tools | |
- name: Decode Google Application Credentials | |
working-directory: lbreez | |
run: base64 --decode <<< $GOOGLE_APPLICATION_CREDENTIALS_BASE64 > google-application-credentials.json | |
- name: Set up FlutterFire | |
working-directory: lbreez | |
run: dart pub global activate flutterfire_cli | |
- name: Configure Firebase | |
working-directory: lbreez | |
run: flutterfire configure -p $FIREBASE_PROJECT -o lib/firebase/firebase_options.dart --platforms="android,ios" -a $FIREBASE_ANDROID_PACKAGE_NAME -i $FIREBASE_IOS_BUNDLE_ID -m $FIREBASE_PLACEHOLDER_APP_ID -w $FIREBASE_PLACEHOLDER_APP_ID -x $FIREBASE_PLACEHOLDER_APP_ID --service-account=google-application-credentials.json -y | |
- name: 🔐 Install Keychain keys | |
run: | | |
KEYCHAIN_PATH=$RUNNER_TEMP/ios-build.keychain | |
security create-keychain -p ci $KEYCHAIN_PATH | |
security default-keychain -s $KEYCHAIN_PATH | |
security unlock-keychain -p ci $KEYCHAIN_PATH | |
security set-keychain-settings -t 6400 -l $KEYCHAIN_PATH | |
CERT_PATH=$RUNNER_TEMP/apple_distribution.cer | |
echo -n "$DISTRIBUTION_CERT" | base64 --decode -o $CERT_PATH | |
security import $CERT_PATH -k $KEYCHAIN_PATH -A | |
P12_KEY_PATH=$RUNNER_TEMP/key.p12 | |
echo -n "$P12_BASE64" | base64 --decode -o $P12_KEY_PATH | |
security import $P12_KEY_PATH -k $KEYCHAIN_PATH -P "$P12_PASSWORD" -A | |
security set-key-partition-list -S apple-tool:,apple: -s -k ci $KEYCHAIN_PATH > /dev/null | |
- name: 🏗️ Copy Firebase configuration file | |
working-directory: lbreez | |
run: echo "$GOOGLE_SERVICES_IOS" > ios/Runner/GoogleService-Info.plist | |
- name: 🏗️ Setup breez-sdk-liquid repository | |
if: ${{ needs.setup.outputs.use-published-plugins == 'false' }} | |
uses: actions/checkout@v4 | |
with: | |
repository: 'breez/breez-sdk-liquid' | |
ssh-key: ${{ secrets.REPO_SSH_KEY }} | |
path: 'breez-sdk-liquid' | |
ref: ${{ needs.setup.outputs.liquid-sdk-ref }} | |
- name: 🏗️ Rust cache | |
if: ${{ needs.setup.outputs.use-published-plugins == 'false' }} | |
uses: Swatinem/rust-cache@v2 | |
with: | |
workspaces: breez-sdk-liquid/lib/ | |
cache-all-crates: true | |
- name: 📦 Install Breez Liquid SDK dependencies | |
if: ${{ needs.setup.outputs.use-published-plugins == 'false' }} | |
working-directory: breez-sdk-liquid/lib/bindings/langs/flutter/ | |
run: | | |
just clean | |
just init | |
- name: Install flutter_rust_bridge_codegen dependencies | |
if: ${{ needs.setup.outputs.use-published-plugins == 'false' }} | |
working-directory: breez-sdk-liquid/lib/bindings/langs/flutter/ | |
run: just frb | |
- name: 🔒 Install SSH Key | |
env: | |
SSH_PRIVATE_KEY: ${{ secrets.REPO_SSH_KEY }} | |
run: | | |
mkdir -p ~/.ssh | |
echo "$SSH_PRIVATE_KEY" > ~/.ssh/id_rsa | |
sudo chmod 600 ~/.ssh/id_rsa | |
ssh-add ~/.ssh/id_rsa | |
- name: 🔨 Build Breez Liquid SDK Swift bindings | |
if: ${{ needs.setup.outputs.use-published-plugins == 'false' }} | |
working-directory: breez-sdk-liquid/lib/bindings/langs/flutter/ | |
run: just build-uniffi-swift | |
- name: Generate Dart/Flutter bindings & Softlink C Headers | |
if: ${{ needs.setup.outputs.use-published-plugins == 'false' }} | |
working-directory: breez-sdk-liquid/lib/bindings/langs/flutter/ | |
continue-on-error: true | |
run: just gen | |
- name: 🗂️ Populate Flutter tool's cache of binary artifacts. | |
working-directory: lbreez | |
run: flutter precache | |
- name: 📦 Install Flutter dependencies | |
working-directory: lbreez | |
run: flutter pub get | |
- name: 🔍 Perform static analysis | |
working-directory: lbreez | |
run: dart analyze --fatal-infos | |
- name: ⚙️ Setup compile-time variables | |
env: | |
CONFIG_FILE: ${{ secrets.CONFIG_FILE }} | |
run: echo "$CONFIG_FILE" > ./lbreez/config.json | |
- name: 📝 Install the Provisioning Profile | |
run: | | |
set -euo pipefail | |
# Define paths | |
PP_PATH=$RUNNER_TEMP/build_pp.mobileprovision | |
NOTIFICATIONS_PP_PATH=$RUNNER_TEMP/build_notifications_pp.mobileprovision | |
PROFILES_DIR=~/Library/MobileDevice/Provisioning\ Profiles | |
# Decode and save provisioning profiles | |
echo "Decoding main provisioning profile..." | |
echo -n "${{ secrets.PROVISIONING_PROFILE_BASE64 }}" | base64 --decode > $PP_PATH | |
echo "Decoding notification provisioning profile..." | |
echo -n "${{ secrets.NOTIFICATION_PROVISIONING_PROFILE_BASE64 }}" | base64 --decode > $NOTIFICATIONS_PP_PATH | |
# Verify provisioning profile validity | |
echo "Verifying provisioning profile validity..." | |
for profile in "$PP_PATH" "$NOTIFICATIONS_PP_PATH"; do | |
if ! security cms -D -i "$profile" > /dev/null 2>&1; then | |
echo "Error: Invalid provisioning profile: $(basename $profile)" >&2 | |
exit 1 | |
fi | |
# Check expiration date | |
expiration=$(security cms -D -i "$profile" | plutil -extract ExpirationDate raw -) | |
expiration_epoch=$(date -j -f "%Y-%m-%dT%H:%M:%SZ" "$expiration" "+%s" 2>/dev/null) | |
current_epoch=$(date "+%s") | |
if [ -z "$expiration_epoch" ] || [ "$expiration_epoch" -lt "$current_epoch" ]; then | |
echo "Error: Expired or invalid date in provisioning profile: $(basename $profile)" >&2 | |
echo "Expiration date: $expiration" >&2 | |
exit 1 | |
fi | |
echo "Provisioning profile $(basename $profile) is valid and not expired." | |
done | |
# Apply provisioning profiles | |
echo "Creating profiles directory..." | |
mkdir -p "$PROFILES_DIR" | |
echo "Copying provisioning profiles..." | |
cp "$PP_PATH" "$PROFILES_DIR" | |
cp "$NOTIFICATIONS_PP_PATH" "$PROFILES_DIR" | |
# Verify copying was successful | |
echo "Verifying provisioning profiles are installed..." | |
if [ ! -f "$PROFILES_DIR/$(basename $PP_PATH)" ]; then | |
echo "Error: Main provisioning profile copy failed!" >&2 | |
exit 1 | |
fi | |
if [ ! -f "$PROFILES_DIR/$(basename $NOTIFICATIONS_PP_PATH)" ]; then | |
echo "Error: Notification provisioning profile copy failed!" >&2 | |
exit 1 | |
fi | |
echo "Listing installed provisioning profiles:" | |
ls -l "$PROFILES_DIR" | |
echo "Provisioning profile details:" | |
for profile in "$PROFILES_DIR"/*; do | |
echo "Profile: $(basename "$profile")" | |
security cms -D -i "$profile" | grep -A1 -E "Name|UUID|TeamIdentifier" | sed 's/^[[:space:]]*//' | sed 'N;s/\n/ /' | |
echo "Entitlements:" | |
security cms -D -i "$profile" | sed -n '/Entitlements/,/<\/dict>/p' | grep -v "Entitlements" | sed 's/^[[:space:]]*//' | sed 'N;s/\n/ /' | |
done | |
# Cleanup temporary files | |
echo "Cleaning up temporary files..." | |
rm -f "$PP_PATH" "$NOTIFICATIONS_PP_PATH" | |
echo "Provisioning profiles installed successfully." | |
- name: 🚀 Build app | |
working-directory: lbreez | |
run: flutter build ios --target=lib/main/main.dart --release --split-debug-info=./obsfucated/debug --obfuscate --no-config-only --no-pub --no-codesign --dart-define-from-file=config.json | |
- name: 📦 Resolve Swift package dependencies | |
working-directory: lbreez | |
run: xcodebuild -resolvePackageDependencies -workspace ios/Runner.xcworkspace -scheme ${{ env.SCHEME }} -configuration ${{ env.BUILD_CONFIGURATION }} | |
- name: 🔨 Build application and generate xcarchive file | |
working-directory: lbreez | |
run: | | |
buildNumber=$(($GITHUB_RUN_NUMBER + 6000)).1 | |
/usr/libexec/PlistBuddy -c "Set :CFBundleVersion $buildNumber" ios/Runner/Info.plist | |
/usr/libexec/PlistBuddy -c "Set :CFBundleShortVersionString ${{ env.IOS_VERSION_STRING }}" ios/Runner/Info.plist | |
xcodebuild -workspace ios/Runner.xcworkspace -scheme ${{ env.SCHEME }} -configuration ${{ env.BUILD_CONFIGURATION }} -sdk 'iphoneos' -destination 'generic/platform=iOS' -archivePath build-output/app.xcarchive clean archive | |
- name: 📤 Export the archive to an ipa file | |
working-directory: lbreez | |
run: xcodebuild -exportArchive -archivePath build-output/app.xcarchive -exportPath build-output/ios -exportOptionsPlist ios/ExportOptions.plist | |
- name: 🗃️ Compress build folder | |
if: github.event_name == 'release' | |
uses: TheDoctor0/zip-release@master | |
with: | |
filename: build.zip | |
directory: lbreez/build/ios/iphoneos | |
type: zip | |
- name: 📤 Upload release | |
if: github.event_name == 'release' | |
uses: svenstaro/upload-release-action@v2 | |
with: | |
asset_name: release-iOS.zip | |
file: lbreez/build/ios/iphoneos/build.zip | |
overwrite: true | |
repo_token: ${{ secrets.GITHUB_TOKEN }} | |
- name: 📤 Upload artifact | |
if: github.event_name != 'release' | |
uses: actions/upload-artifact@v4 | |
with: | |
name: release-iOS | |
path: lbreez/build/ios/iphoneos | |
- name: 📱 Publish to TestFlight | |
run: | | |
altool="$(dirname "$(xcode-select -p)")/Developer/usr/bin/altool" | |
ipa="$PWD/lbreez/build-output/ios/l_breez.ipa" | |
"$altool" --upload-app --type ios --file "$ipa" --username $TESTFLIGHT_USERNAME --password $TESTFLIGHT_PASSWORD | |
- name: Cleanup Google Application Credentials | |
if: success() || failure() | |
run: | | |
if [ -d "lbreez" ] && [ -f "lbreez/google-application-credentials.json" ]; then | |
rm lbreez/google-application-credentials.json | |
fi |