Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
169 changes: 167 additions & 2 deletions .github/workflows/build.yml
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,8 @@ jobs:
build-android:
name: Build for Android
runs-on: ubuntu-latest
env:

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Will mean release apks get generated for every push by someone with repository secrets? I guess this doesn't mean as much without an automated upload like ios has, but I'm still wondering if we actually want that.

HAS_ANDROID_KEYS: ${{ secrets.ANDROID_KEYSTORE_BASE64 != '' }}
steps:
- name: Clone repository
uses: actions/checkout@v6
Expand Down Expand Up @@ -72,6 +74,23 @@ jobs:
# Include arch as linux aarch64 include isar overriding
# arch can be removed later once isar aarch64 is easily available on pub.dev and overriding is not needed anymore
pub-cache-key: "flutter-pub-:os:-:channel:-:version:-:arch:-:hash:"

- name: Setup Android Keys

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Any particular reason this is integrated into the normal build flow but the ios release is fully separated?

env:
ANDROID_KEYSTORE_BASE64: ${{ secrets.ANDROID_KEYSTORE_BASE64 }}
ANDROID_KEYSTORE_PATH: ${{ github.workspace }}/android/app/keystore.jks
ANDROID_KEYSTORE_PASSWORD: ${{ secrets.ANDROID_KEYSTORE_PASSWORD }}
ANDROID_KEY_ALIAS: ${{ secrets.ANDROID_KEY_ALIAS }}
ANDROID_KEY_PASSWORD: ${{ secrets.ANDROID_KEY_PASSWORD }}
if: env.HAS_ANDROID_KEYS == 'true'
run: |
echo "$ANDROID_KEYSTORE_BASE64" | base64 --decode > "$ANDROID_KEYSTORE_PATH"
cat > android/key.properties << EOF
storePassword=$ANDROID_KEYSTORE_PASSWORD
keyPassword=$ANDROID_KEY_PASSWORD
keyAlias=$ANDROID_KEY_ALIAS
storeFile=$ANDROID_KEYSTORE_PATH
EOF

- name: Gradle cache
uses: actions/cache@v5
Expand All @@ -95,15 +114,40 @@ jobs:
- run: flutter doctor
- run: flutter pub get

- run: flutter build apk --debug --no-pub
- name: Build debug APK
id: build-debug-apk
run: flutter build apk --debug --no-pub

- name: Build release APK
id: build-release-apk
if: env.HAS_ANDROID_KEYS == 'true'
run: flutter build apk --release

- name: Build release bundle
id: build-release-bundle
if: env.HAS_ANDROID_KEYS == 'true'
run: flutter build appbundle --release

- uses: actions/upload-artifact@v6
if: steps.build-deb-apk.outcome == 'success'

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is this meant to be build-debug-apk?

with:
name: finamp-android-debug.apk
path: build/app/outputs/flutter-apk/app-debug.apk
compression-level: 0 # no compression

- run: flutter build appbundle --debug --no-pub
- uses: actions/upload-artifact@v6
if: steps.build-release-apk.outcome == 'success'
with:
name: finamp-android-release.apk
path: build/app/outputs/flutter-apk/app-release.apk
compression-level: 0 # still no compression

- uses: actions/upload-artifact@v6
if: steps.build-release-bundle.outcome == 'success'
with:
name: finamp-android-release.aab
path: build/app/outputs/bundle/release/app-release.aab
compression-level: 0 # STILL no compression

- name: Enable KVM group perms
if: *integration_if
Expand Down Expand Up @@ -203,6 +247,127 @@ jobs:
if: *integration_if
run: flutter test integration_test/test_cases.dart -d 'iPhone 17'

release-ios:

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I believe with these additions, that leaves macos as the only platform we support that doesn't create release builds via CI. How complicated would adding macos test/release builds to the CI be?

name: Release iOS to App Store Connect
if: >-
github.event_name == 'push' &&
startsWith(github.ref, 'refs/tags/') &&
github.repository == 'finamp-app/finamp'
needs: build-ios
runs-on: macos-latest
environment: app-store-connect
concurrency:
group: app-store-connect-${{ github.ref }}
cancel-in-progress: false
steps:
- uses: actions/checkout@v6

- uses: maxim-lobanov/setup-xcode@v1
with:
xcode-version: latest-stable

- name: Set up Flutter
uses: subosito/flutter-action@v2
with:
channel: 'stable'
cache: false
pub-cache: true

- name: Cache CocoaPods
uses: actions/cache@v5
with:
path: |
ios/Pods
~/.cocoapods
key: ${{ runner.os }}-pods-${{ hashFiles('ios/Podfile.lock') }}
restore-keys: ${{ runner.os }}-pods-

- name: Cache Dart build cache
uses: actions/cache@v5
with:
path: |
.dart_tool
build
key: ${{ runner.os }}-dart-build-${{ hashFiles('pubspec.lock') }}
restore-keys: ${{ runner.os }}-dart-build-

- run: flutter doctor
- run: flutter pub get

# https://docs.github.com/en/actions/how-tos/deploy/deploy-to-third-party-platforms/sign-xcode-applications
- name: Install Apple certificate and provisioning profile
env:
BUILD_CERTIFICATE_BASE64: ${{ secrets.BUILD_CERTIFICATE_BASE64 }}
P12_PASSWORD: ${{ secrets.P12_PASSWORD }}
BUILD_PROVISION_PROFILE_BASE64: ${{ secrets.BUILD_PROVISION_PROFILE_BASE64 }}
KEYCHAIN_PASSWORD: ${{ secrets.KEYCHAIN_PASSWORD }}
run: |
# create variables
CERTIFICATE_PATH=$RUNNER_TEMP/build_certificate.p12
PP_PATH=$RUNNER_TEMP/build_pp.mobileprovision
KEYCHAIN_PATH=$RUNNER_TEMP/app-signing.keychain-db

# import certificate and provisioning profile from secrets
echo -n "$BUILD_CERTIFICATE_BASE64" | base64 --decode -o $CERTIFICATE_PATH
echo -n "$BUILD_PROVISION_PROFILE_BASE64" | base64 --decode -o $PP_PATH

# create temporary keychain
security create-keychain -p "$KEYCHAIN_PASSWORD" $KEYCHAIN_PATH
security set-keychain-settings -lut 21600 $KEYCHAIN_PATH
security unlock-keychain -p "$KEYCHAIN_PASSWORD" $KEYCHAIN_PATH

# import certificate to keychain
security import $CERTIFICATE_PATH -P "$P12_PASSWORD" -A -t cert -f pkcs12 -k $KEYCHAIN_PATH
security set-key-partition-list -S apple-tool:,apple: -k "$KEYCHAIN_PASSWORD" $KEYCHAIN_PATH
security list-keychain -d user -s $KEYCHAIN_PATH

# apply provisioning profile
mkdir -p ~/Library/MobileDevice/Provisioning\ Profiles
cp $PP_PATH ~/Library/MobileDevice/Provisioning\ Profiles

- name: Build App Store IPA
# We do this fiddly flutter-build-then-xcodebuild so that we can use the
# CI profiles/certificates while keeping the Xcode project's signing
# settings to auto for normal use
run: |
flutter build ios --release --no-pub --no-codesign --config-only
xcodebuild \
-workspace ios/Runner.xcworkspace \
-scheme Runner \
-configuration Release \
-destination 'generic/platform=iOS' \
-archivePath "$PWD/build/ios/archive/Runner.xcarchive" \
archive \
FINAMP_CODE_SIGN_STYLE=Manual \
FINAMP_CODE_SIGN_IDENTITY='Apple Distribution' \
'FINAMP_PROVISIONING_PROFILE_SPECIFIER=Finamp CI'
mkdir -p build/ios/ipa
xcodebuild \
-exportArchive \
-archivePath "$PWD/build/ios/archive/Runner.xcarchive" \
-exportPath "$PWD/build/ios/ipa" \
-exportOptionsPlist ios/ExportOptions-CI.plist

- name: Upload IPA to App Store Connect
env:
API_KEY_ID: ${{ secrets.APP_STORE_CONNECT_KEY_ID }}
API_ISSUER_ID: ${{ secrets.APP_STORE_CONNECT_ISSUER_ID }}
API_PRIVATE_KEY: ${{ secrets.APP_STORE_CONNECT_PRIVATE_KEY }}
run: |
set -euo pipefail

API_PRIVATE_KEYS_DIR="$RUNNER_TEMP/appstore-keys"
mkdir -p "$API_PRIVATE_KEYS_DIR"
printf '%s' "$API_PRIVATE_KEY" \
> "$API_PRIVATE_KEYS_DIR/AuthKey_${API_KEY_ID}.p8"

API_PRIVATE_KEYS_DIR="$API_PRIVATE_KEYS_DIR" \
xcrun altool --upload-app \
--type ios \
--file build/ios/ipa/*.ipa \
--apiKey "$API_KEY_ID" \
--apiIssuer "$API_ISSUER_ID"

build-linux:
name: Build for Linux
runs-on: ${{ matrix.os }}
Expand Down
17 changes: 17 additions & 0 deletions ios/ExportOptions-CI.plist
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
<?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-connect</string>
<key>provisioningProfiles</key>
<dict>
<key>com.unicornsonlsd.finamp-ios</key>
<string>Finamp CI</string>
</dict>
<key>signingStyle</key>
<string>manual</string>
<key>teamID</key>
<string>PFNS8PTRM7</string>
</dict>
</plist>
4 changes: 4 additions & 0 deletions ios/Flutter/Release.xcconfig
Original file line number Diff line number Diff line change
@@ -1,2 +1,6 @@
#include "Pods/Target Support Files/Pods-Runner/Pods-Runner.release.xcconfig"
#include "Generated.xcconfig"

FINAMP_CODE_SIGN_STYLE = Automatic
FINAMP_CODE_SIGN_IDENTITY = Apple Development
FINAMP_PROVISIONING_PROFILE_SPECIFIER =
6 changes: 3 additions & 3 deletions ios/Runner.xcodeproj/project.pbxproj
Original file line number Diff line number Diff line change
Expand Up @@ -761,8 +761,8 @@
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
CLANG_ENABLE_MODULES = YES;
CODE_SIGN_ENTITLEMENTS = Runner/Runner.entitlements;
CODE_SIGN_IDENTITY = "Apple Development";
CODE_SIGN_STYLE = Automatic;
CODE_SIGN_IDENTITY = "$(FINAMP_CODE_SIGN_IDENTITY)";
CODE_SIGN_STYLE = "$(FINAMP_CODE_SIGN_STYLE)";
CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)";
DEVELOPMENT_TEAM = PFNS8PTRM7;
ENABLE_BITCODE = NO;
Expand All @@ -782,7 +782,7 @@
);
PRODUCT_BUNDLE_IDENTIFIER = "com.unicornsonlsd.finamp-ios";
PRODUCT_NAME = "$(TARGET_NAME)";
PROVISIONING_PROFILE_SPECIFIER = "";
PROVISIONING_PROFILE_SPECIFIER = "$(FINAMP_PROVISIONING_PROFILE_SPECIFIER)";
SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h";
SWIFT_VERSION = 5.0;
VERSIONING_SYSTEM = "apple-generic";
Expand Down
Loading