From iOS signing setup to distribution uploads.
In v2.2.0, we introduced the CLI flow for preparing App Store Connect keys and shared progress on IPA builds. With v2.3.0, a Dart workflow can generate a signed IPA and upload it to TestFlight or Firebase App Distribution.
This article covers the changes from v2.2.0 on October 9 to v2.3.0 on October 23. Alongside mobile app distribution, we updated GitHub connections in the web dashboard, Cloud plans for teams, and automatic syncing after secret registration in the CLI.
Build a signed IPA and send it to TestFlight.
buildIpa() now handles certificate import, provisioning profile application, ExportOptions.plist generation, and flutter build ipa. It supports Ad Hoc and App Store signing and returns the absolute path of the generated IPA.
Pass that path directly to a distribution API instead of constructing the artifact path in your workflow. For example, sign for the App Store and upload to TestFlight:
final ipaPath = await ci.flutter.buildIpa(
distributionMethod: IosDistributionMethod.appStore,
ascKeys: ascKeys,
certificatePrivateKey: certificatePrivateKey,
);
await ci.flutter.deployIpaToTestFlight(
ipaPath: ipaPath,
ascKeys: ascKeys,
);These are steps using ci created with OpenCI.init(). ascKeys is an AppStoreConnectKeys instance prepared from team secrets, and certificatePrivateKey is the signing certificate's private key. Imports and initialization are omitted. iOS builds require macOS with Xcode and signing tools.
After uploading, deployIpaToTestFlight() waits for Apple to finish processing the build. Assigning tester groups and submitting for external testing review are separate steps afterward.
We also added deployIpaToFirebaseAppDistribution(). It authenticates with a Base64-encoded service account JSON and uploads the IPA through the REST API. Firebase CLI and Node.js are not required. Without specified testers or groups, it only uploads the build.
Android and Shorebird distribution, in Dart too.
Alongside APK and AAB builds, Android workflows now have these upload APIs:
| Destination | Artifact | API |
|---|---|---|
| Firebase App Distribution | APK | deployApkToFirebaseAppDistribution() |
| Firebase App Distribution | AAB | deployAabToFirebaseAppDistribution() |
| Google Play Console | AAB | deployAabToPlayConsole() |
Google Play Console uploads use AABs and default to the internal testing track. AAB uploads to Firebase App Distribution require a Firebase–Google Play connection. For each destination, register the target app and prepare a service account with the required permissions.
buildApk() and buildAab() also gain dartDefines, so environment-specific settings can be passed as a map.
await ci.flutter.buildAab(
flavor: 'production',
dartDefines: {'API_BASE_URL': 'https://api.example.com'},
);For Shorebird, we added ci.shorebird.releaseIos(), releaseApk(), and releaseAab(). They accept a flavor, Dart defines, and a dry-run option, and return the absolute path to the generated IPA, APK, or AAB. Prepare Shorebird CLI and SHOREBIRD_TOKEN in your build environment. This release adds Shorebird release creation.
These distribution APIs and the Go helpers described below are available in the published openci_workflow 0.1.4.
Confirm your GitHub connection in the dashboard.
The web dashboard now includes a flow for connecting to GitHub.com.
- Open Connect GitHub in your team's settings.
- Authenticate with GitHub and return to the dashboard.
- Select an available GitHub App installation and confirm its connection to your team.
The server verifies connection permissions before fetching repositories and branches. If no eligible installation is available, follow the GitHub App installation instructions and fetch the list again.
Teams using the previous GitHub configuration should reconnect through Connect GitHub. Manually entered Installation IDs and GitHub URLs have been replaced by this verified connection flow.
Monthly Cloud plans for teams.
You can now purchase and manage Cloud subscriptions from the web dashboard. Subscribe per team through Stripe Checkout, then use Stripe Customer Portal to change payment methods, view invoices, or cancel at the end of the billing period.
| Plan | Monthly price | CI runtime per billing period |
|---|---|---|
| Light | US$10 | 600 minutes shared by the team |
| Pro | US$100 | 6,000 minutes shared by the team |
Usage is the sum of the time each job actually runs. Time spent in a queue or waiting for dependencies is excluded. For example, ten jobs running for ten minutes in parallel consume 100 minutes.
There is no concurrency cap per plan or team. The number of jobs that can start depends on worker availability.
Once the runtime allowance is used up, new CI runs are blocked. Jobs already running can finish, and there are no overage charges. Running jobs are also allowed to finish when access expires. Logs and settings remain accessible.
Payment is taken when you subscribe, with a seven-day money-back guarantee. Requests must be made within 168 hours of the first successful payment; renewals and resubscriptions are excluded. A refund cancels the subscription immediately. The dashboard's pricing screen lists the request contact and terms.
The pricing screen shows the plan, whether new CI runs can start, and the end of the paid period. Numeric displays of used and remaining runtime, and an initial free trial, are planned for later. Cloud pricing does not apply to self-hosted deployments.
Register a secret and sync generated files.
In the published genuineci_cli 0.6.0, successful genuineci register secret and genuineci register secretFile commands automatically sync workspace paths and secret definitions.
This updates the files under openci/generated/ introduced in the previous release, reducing the need to run genuineci sync separately after adding a secret. If syncing fails, the saved secret is retained. Fix the cause and run genuineci sync to regenerate the files.
Update the CLI with:
genuineci updateGo checks in the same workflow.
We added Go helpers to openci_workflow. If your repository includes Go services, you can format, vet, and test them from a Dart workflow.
await ci.go.format(dir: 'apps/genuineci_server');
await ci.go.vet(dir: 'apps/genuineci_server');
await ci.go.test(dir: 'apps/genuineci_server');This example uses a ci instance whose working directory is the repository root.
format() runs go fmt ./... and may modify files. The helpers do not install Go, so prepare it in your build environment.
GenuineCI itself now includes a Go service for Cloud billing and runtime accounting, with checks included in CI.
Updating the SDK.
The package for ordinary workflows is openci_workflow, currently published as 0.1.4. If your workflow tests used OpenCI.forTesting(), replace it with the public OpenCI() constructor and pass a commandRunner.
For developers using GenuineCI APIs and models directly, we also published openci_shared 2.0.0. It adds APIs for GitHub connections, billing state, and worker execution records.
This version removes githubBaseUrl from Team and BuildJobPlan. Remove the corresponding constructor arguments and property references. GitHub URL configuration is managed by the server.
See GitHub Release v2.3.0 for the change list and the comparison with v2.2.0 for implementation details. The Docs cover the basics of writing workflows.