Introduction
So, as I stated in previous article, one day I realized I have to build own CI/CD pipeline for Flutter app. With iOS version of the app I had no problems, but with macOS version I faced some difficulties. So, here is my path.
Code Signing Management
I use match to manage code signing certificates and provisioning profiles.
I have two lanes for this: certificates
and generate_new_certificates
. The first one is used to get certificates and
provisioning profiles from git repository. The second one is used to generate new certificates and provisioning profiles
and push them to git repository. Setup is pretty straightforward, just look into docs.
Build
Well, in general it's as easy as flutter build macos --release
. But I never managed to make it work with Code Signing.
Whatever settings I tried, code signing always failed. So, I decided to use xcodebuild
directly. Not literally, but with
build_mac_app
action. It's a wrapper around xcodebuild
and it works like a charm. I use it like this:
build_mac_app(
configuration: "Release",
skip_package_pkg: false,
output_directory: "builds",
xcodebuild_formatter: 'xcbeautify',
silent: true,
export_method: "app-store",
export_options: {
provisioningProfiles: {
"com.app.flutter" => "match AppStore com.app.flutter macos"
}
}
)
But before that flutter needs to generate some files, so I need to run following commands:
Dir.chdir "../.." do
sh("flutter packages get")
sh("flutter clean")
sh("flutter build macos --release")
end
Unfortunately, flutter build macos
action doesn't support --config-only
flag
just yet, so double build is required. First time your app is built
with flutter build macos
and second time with build_mac_app
action. As long as #118649
will be released, I will be able to use --config-only
flag and save build time.
Deployment to TestFlight
Well, it's easy. First, increment build number:
increment_build_number({
build_number: latest_testflight_build_number(
version: '2.5.0',
api_key_path: 'fastlane/XXXXXX.json',
platform: 'osx'
) + 1
})
Then, upload to TestFlight:
upload_to_testflight(
uses_non_exempt_encryption: false,
api_key_path: 'fastlane/XXXXXX.json',
skip_waiting_for_build_processing: true,
app_version: '2.5.0',
)
And all that orchestrated by gitlab CI:
testflight-macos:
stage: deploy
needs: [ ]
script:
- cd $CI_PROJECT_DIR/apps/frontend/macos
- bundle exec fastlane beta
only:
- main
As easy as that.
Complete Fastlane file
default_platform(:mac)
platform :mac do
desc "Get certificates"
lane :certificates do
sync_code_signing(
type: "development",
api_key_path: 'fastlane/XXXXXX.json',
app_identifier: ['com.app.flutter'],
force_for_new_devices: true,
readonly: true
)
end
desc "Push a new beta build to TestFlight"
lane :beta do
ensure_git_status_clean
match(
type: "appstore",
readonly: is_ci,
platform: "macos",
verbose: false,
additional_cert_types: ["mac_installer_distribution"]
)
Dir.chdir "../.." do
sh("flutter packages get")
sh("flutter clean")
sh("flutter build macos --release")
end
increment_build_number({
build_number: latest_testflight_build_number(
version: '2.5.0',
api_key_path: 'fastlane/XXXXXX.json',
platform: 'osx'
) + 1
})
build_mac_app(
configuration: "Release",
skip_package_pkg: false,
output_directory: "builds",
xcodebuild_formatter: 'xcbeautify',
silent: true,
export_method: "app-store",
export_options: {
provisioningProfiles: {
"com.app.flutter" => "match AppStore com.app.flutter macos"
}
}
)
upload_to_testflight(
uses_non_exempt_encryption: false,
api_key_path: 'fastlane/XXXXXX.json',
skip_waiting_for_build_processing: true,
app_version: '2.5.0',
)
end
end