Skip to content

Commit 5fca8b4

Browse files
ci: add iOS deployment guide and automation
- Introduced Fastlane-based automated iOS deployment for TestFlight and App Store. - Added related npm scripts and GitHub Actions workflow. - Included setup instructions in `README.md` and created `docs/ios-deployment.md`.
1 parent b7357f8 commit 5fca8b4

File tree

12 files changed

+919
-21
lines changed

12 files changed

+919
-21
lines changed

.env.example

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
# App Store Connect API credentials
2+
# Required for local + GitHub Actions
3+
APP_STORE_CONNECT_KEY_ID=
4+
APP_STORE_CONNECT_ISSUER_ID=
5+
# Base64-encoded contents of AuthKey_<KEY_ID>.p8
6+
APP_STORE_CONNECT_API_KEY=
7+
8+
# iOS signing assets (certificate + provisioning profile)
9+
# Required for GitHub Actions
10+
# Base64-encoded .p12 certificate file
11+
IOS_DISTRIBUTION_CERTIFICATE_BASE64=
12+
# Password used when exporting the .p12 certificate
13+
IOS_DISTRIBUTION_CERTIFICATE_PASSWORD=
14+
# Base64-encoded .mobileprovision file
15+
IOS_APPSTORE_PROVISIONING_PROFILE_BASE64=
16+
# Provisioning profile name as shown in Apple Developer portal
17+
IOS_PROVISIONING_PROFILE_NAME=
18+
# Any strong password used to create/unlock a temporary CI keychain
19+
KEYCHAIN_PASSWORD=
20+
21+
# Optional overrides (defaults are configured in fastlane)
22+
APP_IDENTIFIER=ch.michaelschoenbaechler.parlwatch
23+
APPLE_TEAM_ID=65DUBW68Z2

.github/workflows/ios-deploy.yml

Lines changed: 83 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,83 @@
1+
name: iOS Deploy
2+
3+
on:
4+
workflow_dispatch:
5+
inputs:
6+
target:
7+
description: Deployment target
8+
required: true
9+
type: choice
10+
options:
11+
- testflight
12+
- appstore
13+
default: testflight
14+
15+
jobs:
16+
deploy-ios:
17+
runs-on: macos-latest
18+
env:
19+
APP_IDENTIFIER: ch.michaelschoenbaechler.parlwatch
20+
APPLE_TEAM_ID: 65DUBW68Z2
21+
APP_STORE_CONNECT_KEY_ID: ${{ secrets.APP_STORE_CONNECT_KEY_ID }}
22+
APP_STORE_CONNECT_ISSUER_ID: ${{ secrets.APP_STORE_CONNECT_ISSUER_ID }}
23+
APP_STORE_CONNECT_API_KEY: ${{ secrets.APP_STORE_CONNECT_API_KEY }}
24+
IOS_DISTRIBUTION_CERTIFICATE_BASE64: ${{ secrets.IOS_DISTRIBUTION_CERTIFICATE_BASE64 }}
25+
IOS_DISTRIBUTION_CERTIFICATE_PASSWORD: ${{ secrets.IOS_DISTRIBUTION_CERTIFICATE_PASSWORD }}
26+
IOS_APPSTORE_PROVISIONING_PROFILE_BASE64: ${{ secrets.IOS_APPSTORE_PROVISIONING_PROFILE_BASE64 }}
27+
IOS_PROVISIONING_PROFILE_NAME: ${{ secrets.IOS_PROVISIONING_PROFILE_NAME }}
28+
KEYCHAIN_PASSWORD: ${{ secrets.KEYCHAIN_PASSWORD }}
29+
30+
steps:
31+
- name: Check out source code
32+
uses: actions/checkout@v4
33+
34+
- name: Set up Node.js
35+
uses: actions/setup-node@v4
36+
with:
37+
node-version: 22
38+
cache: npm
39+
40+
- name: Install JavaScript dependencies
41+
run: npm ci
42+
43+
- name: Build web assets and sync iOS project
44+
run: npm run ios:prepare
45+
46+
- name: Set up Ruby
47+
uses: ruby/setup-ruby@v1
48+
with:
49+
ruby-version: "3.2"
50+
51+
- name: Install signing assets from secrets
52+
run: |
53+
: "${IOS_DISTRIBUTION_CERTIFICATE_BASE64:?Missing IOS_DISTRIBUTION_CERTIFICATE_BASE64}"
54+
: "${IOS_DISTRIBUTION_CERTIFICATE_PASSWORD:?Missing IOS_DISTRIBUTION_CERTIFICATE_PASSWORD}"
55+
: "${IOS_APPSTORE_PROVISIONING_PROFILE_BASE64:?Missing IOS_APPSTORE_PROVISIONING_PROFILE_BASE64}"
56+
: "${IOS_PROVISIONING_PROFILE_NAME:?Missing IOS_PROVISIONING_PROFILE_NAME}"
57+
: "${KEYCHAIN_PASSWORD:?Missing KEYCHAIN_PASSWORD}"
58+
59+
CERT_PATH="$RUNNER_TEMP/distribution-cert.p12"
60+
PROFILE_PATH="$RUNNER_TEMP/appstore.mobileprovision"
61+
KEYCHAIN_PATH="$RUNNER_TEMP/app-signing.keychain-db"
62+
63+
echo -n "$IOS_DISTRIBUTION_CERTIFICATE_BASE64" | openssl base64 -d -A > "$CERT_PATH"
64+
echo -n "$IOS_APPSTORE_PROVISIONING_PROFILE_BASE64" | openssl base64 -d -A > "$PROFILE_PATH"
65+
66+
security create-keychain -p "$KEYCHAIN_PASSWORD" "$KEYCHAIN_PATH"
67+
security set-keychain-settings -lut 21600 "$KEYCHAIN_PATH"
68+
security unlock-keychain -p "$KEYCHAIN_PASSWORD" "$KEYCHAIN_PATH"
69+
security import "$CERT_PATH" -P "$IOS_DISTRIBUTION_CERTIFICATE_PASSWORD" -A -t cert -f pkcs12 -k "$KEYCHAIN_PATH"
70+
security set-key-partition-list -S apple-tool:,apple: -s -k "$KEYCHAIN_PASSWORD" "$KEYCHAIN_PATH"
71+
security list-keychain -d user -s "$KEYCHAIN_PATH" login.keychain-db
72+
security default-keychain -d user -s "$KEYCHAIN_PATH"
73+
74+
mkdir -p "$HOME/Library/MobileDevice/Provisioning Profiles"
75+
cp "$PROFILE_PATH" "$HOME/Library/MobileDevice/Provisioning Profiles/appstore.mobileprovision"
76+
77+
- name: Install Fastlane dependencies
78+
run: bundle install
79+
working-directory: ios/App
80+
81+
- name: Deploy to App Store Connect
82+
run: bundle exec fastlane ${{ github.event.inputs.target }}
83+
working-directory: ios/App

.gitignore

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,13 @@ npm-debug.log*
2828
/coverage
2929
/dist
3030
/node_modules
31+
.env
32+
.env.local
33+
.env.*.local
3134
/platforms
3235
/plugins
33-
/www
36+
/www
37+
/ios/App/fastlane/report.xml
38+
/ios/App/fastlane/Preview.html
39+
/ios/App/fastlane/test_output
40+
/ios/App/fastlane/screenshots

README.md

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,17 @@ ParlWatch is an open-source project designed to provide easy access to the parli
2525
npm run start
2626
```
2727

28+
## iOS deployment
29+
30+
Automated iOS deployments (TestFlight/App Store) are available via Fastlane and GitHub Actions.
31+
32+
- Setup and usage guide: [docs/ios-deployment.md](docs/ios-deployment.md)
33+
- Local commands:
34+
- `cp .env.example .env` and load env vars before deploy
35+
- `npm run ios:fastlane:install`
36+
- `npm run ios:deploy:testflight`
37+
- `npm run ios:deploy:appstore`
38+
2839
## Contribution
2940

3041
We welcome contributions from the community. Please refer to the CONTRIBUTING.md file for more details on how to contribute to this project.

docs/ios-deployment.md

Lines changed: 126 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,126 @@
1+
# iOS Deployment (TestFlight and App Store)
2+
3+
This project now supports one-command iOS deployment through Fastlane.
4+
5+
## Local deployment
6+
7+
1. Install Ruby dependencies once:
8+
```bash
9+
npm run ios:fastlane:install
10+
```
11+
2. Create local env vars file and load it:
12+
```bash
13+
cp .env.example .env
14+
set -a; source .env; set +a
15+
```
16+
3. From repo root, run one of:
17+
```bash
18+
npm run ios:deploy:testflight
19+
npm run ios:deploy:appstore
20+
```
21+
22+
The commands build web assets, sync the Capacitor iOS project, archive the app, and upload to App Store Connect.
23+
24+
## Required environment variables
25+
26+
Set these for both local runs and GitHub Actions:
27+
28+
- `APP_STORE_CONNECT_KEY_ID`
29+
- `APP_STORE_CONNECT_ISSUER_ID`
30+
- `APP_STORE_CONNECT_API_KEY` (base64-encoded `.p8` contents)
31+
32+
Set these for GitHub Actions:
33+
34+
- `IOS_DISTRIBUTION_CERTIFICATE_BASE64` (base64-encoded `.p12`)
35+
- `IOS_DISTRIBUTION_CERTIFICATE_PASSWORD` (password used when exporting `.p12`)
36+
- `IOS_APPSTORE_PROVISIONING_PROFILE_BASE64` (base64-encoded `.mobileprovision`)
37+
- `IOS_PROVISIONING_PROFILE_NAME` (profile name from Apple Developer)
38+
- `KEYCHAIN_PASSWORD` (any strong password used in CI keychain setup)
39+
40+
Optional:
41+
42+
- `APP_IDENTIFIER` (default: `ch.michaelschoenbaechler.parlwatch`)
43+
- `APPLE_TEAM_ID` (default: `65DUBW68Z2`)
44+
45+
Local note:
46+
47+
- Local deploy expects signing certificate/profile to already be available in your login keychain.
48+
49+
## GitHub Actions deployment
50+
51+
Use the `iOS Deploy` workflow (`.github/workflows/ios-deploy.yml`) and choose:
52+
53+
- `testflight` to upload a build for testers
54+
- `appstore` to upload a build for App Store submission
55+
56+
Add these repository secrets before running the workflow:
57+
58+
- `APP_STORE_CONNECT_KEY_ID`
59+
- `APP_STORE_CONNECT_ISSUER_ID`
60+
- `APP_STORE_CONNECT_API_KEY`
61+
- `IOS_DISTRIBUTION_CERTIFICATE_BASE64`
62+
- `IOS_DISTRIBUTION_CERTIFICATE_PASSWORD`
63+
- `IOS_APPSTORE_PROVISIONING_PROFILE_BASE64`
64+
- `IOS_PROVISIONING_PROFILE_NAME`
65+
- `KEYCHAIN_PASSWORD`
66+
67+
## Prepare values for GitHub secrets
68+
69+
### 1) Create App Store Connect API key
70+
71+
1. Open App Store Connect > Users and Access > Keys > App Store Connect API.
72+
2. Create a key with at least `App Manager` access.
73+
3. Save:
74+
- `Key ID` -> `APP_STORE_CONNECT_KEY_ID`
75+
- `Issuer ID` -> `APP_STORE_CONNECT_ISSUER_ID`
76+
4. Download `AuthKey_<KEYID>.p8` (only downloadable once).
77+
5. Convert to base64:
78+
```bash
79+
base64 -i AuthKey_<KEYID>.p8 | tr -d '\n'
80+
```
81+
6. Use output as `APP_STORE_CONNECT_API_KEY`.
82+
83+
### 2) Export distribution certificate as .p12
84+
85+
1. On a Mac where the iOS Distribution certificate is installed, open `Keychain Access`.
86+
2. In `My Certificates`, find your `Apple Distribution` certificate with private key.
87+
3. Right-click > Export > save as `.p12`.
88+
4. Set an export password.
89+
5. Convert `.p12` to base64:
90+
```bash
91+
base64 -i distribution.p12 | tr -d '\n'
92+
```
93+
6. Use output as `IOS_DISTRIBUTION_CERTIFICATE_BASE64`.
94+
7. Use the export password as `IOS_DISTRIBUTION_CERTIFICATE_PASSWORD`.
95+
96+
### 3) Download App Store provisioning profile
97+
98+
1. Open Apple Developer > Certificates, IDs & Profiles > Profiles.
99+
2. Open your App Store profile for bundle id `ch.michaelschoenbaechler.parlwatch`.
100+
3. Download the `.mobileprovision` file.
101+
4. Copy profile name exactly (for `IOS_PROVISIONING_PROFILE_NAME`).
102+
5. Convert profile to base64:
103+
```bash
104+
base64 -i profile.mobileprovision | tr -d '\n'
105+
```
106+
6. Use output as `IOS_APPSTORE_PROVISIONING_PROFILE_BASE64`.
107+
108+
### 4) Choose keychain password for CI
109+
110+
1. Generate any strong random value.
111+
2. Save it as `KEYCHAIN_PASSWORD`.
112+
113+
## Add secrets in GitHub
114+
115+
1. Open GitHub repository > `Settings` > `Secrets and variables` > `Actions`.
116+
2. Add these repository secret names exactly:
117+
- `APP_STORE_CONNECT_KEY_ID`
118+
- `APP_STORE_CONNECT_ISSUER_ID`
119+
- `APP_STORE_CONNECT_API_KEY`
120+
- `IOS_DISTRIBUTION_CERTIFICATE_BASE64`
121+
- `IOS_DISTRIBUTION_CERTIFICATE_PASSWORD`
122+
- `IOS_APPSTORE_PROVISIONING_PROFILE_BASE64`
123+
- `IOS_PROVISIONING_PROFILE_NAME`
124+
- `KEYCHAIN_PASSWORD`
125+
3. Trigger workflow: `Actions` > `iOS Deploy` > `Run workflow`.
126+
4. Select target: `testflight` or `appstore`.

ios/App/Gemfile

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
source "https://rubygems.org"
2+
3+
gem "fastlane"

0 commit comments

Comments
 (0)