The pain it removes
Shipping an iOS app from CI is deceptively hard. The build is the easy part — the pain is everything around it.
Code signing is a black hole
Certs, provisioning profiles, keychains, the Developer portal, .p12 exports, App Store Connect API keys. Get one wrong and you get a cryptic failure after a 20-minute build.
Secrets copied everywhere
Every repo re-pastes the same base64 cert, team ID, and API key. Rotate a cert and you're editing ten repos by hand.
150 lines of drifting YAML
Xcode select, Node + Meteor/Expo setup, pod install, Fastlane, archive, upload — copy-pasted between repos, then drifting out of sync.
"Works on my machine"
Local builds sign fine; CI fails because the runner has no keychain and no certs. Debugging means pushing commit after commit on a slow macOS runner.
Fastlane / Ruby / CocoaPods tax
Pinning Ruby, bundler caching, gem installs, Cordova pod quirks — incidental work that has nothing to do with your app.
These actions absorb all of it
Signing is centralized, secrets live once at the org level, and the whole pipeline is one line every repo gets fixes for at once.
Components
setup-meteor
Prepare a Meteor/Cordova build environment — Xcode, Node, Meteor, npm install — before building.
setup-expo
Prepare an Expo build environment — Xcode, Node, JS deps — before expo prebuild.
ios
Sign, archive, and optionally upload an iOS app to TestFlight. Use directly for bare React Native or custom pipelines.
ios-meteor.yml
One-call end-to-end pipeline for Meteor/Cordova iOS apps.
ios-expo.yml
One-call end-to-end pipeline for Expo iOS apps.
Quick start
Store signing secrets at the org level
Add APPLE_TEAM_ID, MATCH_GIT_BASIC_AUTHORIZATION, MATCH_PASSWORD, APPLE_API_KEY_ID, APPLE_API_ISSUER_ID, and APPLE_API_KEY_P8_BASE64 once. Every repo inherits them.
Call the reusable workflow
Add a workflow to your repo with secrets: inherit and your app identifier.
name: iOS
on: [push]
jobs:
ios:
uses: mieweb/actions/.github/workflows/ios-meteor.yml@v1
secrets: inherit
with:
app_identifier: org.mieweb.os.dev
meteor_server: https://app.example.com
Push
TestFlight gets a signed build. No keychain wrangling, no copy-pasted YAML.
Full inputs, signing modes, and Expo usage live in the README ↗.