Skip to content

Commit 002835e

Browse files
Copilotevan361425
andcommitted
Add iOS App Store deployment workflow and Fastlane configuration
Co-authored-by: evan361425 <14554683+evan361425@users.noreply.github.com>
1 parent 1de26c8 commit 002835e

File tree

4 files changed

+264
-14
lines changed

4 files changed

+264
-14
lines changed
Lines changed: 219 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,219 @@
1+
name: Deploy to App Store
2+
3+
on:
4+
workflow_dispatch:
5+
# Enable manual run
6+
inputs:
7+
lane:
8+
description: "Fastlane lane to use (internal, beta, promote_to_production)"
9+
required: true
10+
default: "internal"
11+
tag:
12+
description: "GitHub tag"
13+
required: true
14+
default: "latest"
15+
ref:
16+
description: "GitHub ref, if not provided, will use the tag"
17+
required: false
18+
default: ""
19+
workflow_run:
20+
workflows:
21+
- Add Artifacts for Release
22+
- Add Artifacts for Release Candidate
23+
types: [completed]
24+
release:
25+
# The prereleased type will not trigger for pre-releases published from
26+
# draft releases, but the published type will trigger. If you want a
27+
# workflow to run when stable and pre-releases publish, subscribe to
28+
# published instead of released and prereleased.
29+
types: [published]
30+
31+
jobs:
32+
# Extract some useful variable
33+
# 1. lane - Same as 'workflow_dispatch' inputs, auto generate from tag name
34+
# 2. dev_build_number - extract number of RC
35+
# 3. flavor - 'dev'(internal) or 'prod'(beta)
36+
# 4. build_code - pubspec.yaml build code.
37+
var:
38+
name: Extracting variables
39+
runs-on: ubuntu-latest
40+
if: ${{ github.event_name != 'workflow_run' || github.event.workflow_run.conclusion == 'success' }}
41+
outputs:
42+
ref: ${{ github.event.inputs.ref || steps.tag.outputs.result }}
43+
lane: ${{ steps.lane.outputs.result }}
44+
dev_build_number: ${{ steps.dev_build_number.outputs.result }}
45+
flavor: ${{ steps.flavor.outputs.value }}
46+
build_code: ${{ steps.build_code.outputs.value }}
47+
steps:
48+
- name: Get latest tag
49+
id: tag
50+
uses: actions/github-script@v7
51+
with:
52+
result-encoding: string
53+
script: |
54+
if (context.eventName === 'release') {
55+
return context.payload.release.tag_name;
56+
}
57+
58+
if (context.eventName === 'workflow_dispatch') {
59+
if (context.payload.inputs.tag !== 'latest') {
60+
return context.payload.inputs.tag;
61+
}
62+
}
63+
64+
const res = await github.rest.repos.listTags({
65+
owner: 'evan361425',
66+
repo: 'flutter-pos-system',
67+
per_page: 1,
68+
});
69+
70+
return res.data[0].name;
71+
72+
- name: Extract lane
73+
id: lane
74+
uses: actions/github-script@v7
75+
with:
76+
result-encoding: string
77+
script: |
78+
return context.eventName === 'workflow_dispatch'
79+
? '${{ github.event.inputs.lane }}'
80+
: context.eventName === 'release'
81+
? 'promote_to_production'
82+
: '${{ steps.tag.outputs.result }}'.includes('-rc')
83+
? 'internal'
84+
: 'beta';
85+
86+
- name: Extract Flavor
87+
id: flavor
88+
uses: haya14busa/action-cond@v1
89+
with:
90+
cond: ${{ steps.lane.outputs.result == 'internal' }}
91+
if_true: dev
92+
if_false: prod
93+
94+
- name: Checkout code
95+
uses: actions/checkout@v4
96+
with:
97+
ref: "refs/tags/${{ steps.tag.outputs.result }}"
98+
99+
- name: Extract build code
100+
id: build_code
101+
run: |
102+
ver=$(grep -m 1 '^version: ' pubspec.yaml | cut -d' ' -f2)
103+
echo "value=$(echo "$ver" | cut -f2- -d"+")" >> $GITHUB_OUTPUT
104+
105+
- name: Extract build number
106+
id: dev_build_number
107+
uses: actions/github-script@v7
108+
with:
109+
result-encoding: string
110+
script: |
111+
const ref = '${{ steps.tag.outputs.result }}';
112+
return ref.includes('-rc')
113+
? ref.substr(ref.indexOf('-rc') + 3)
114+
: ''.concat(${{ steps.build_code.outputs.value }} % 100);
115+
116+
fastlane-deploy:
117+
runs-on: macos-latest
118+
needs: var
119+
steps:
120+
- name: Set up Flutter
121+
uses: subosito/flutter-action@v2
122+
with:
123+
flutter-version: "3.35.x"
124+
cache: true
125+
channel: "stable"
126+
127+
- name: Checkout code
128+
uses: actions/checkout@v4
129+
with:
130+
ref: ${{ needs.var.outputs.ref }}
131+
132+
# Setup Ruby, Bundler, and Gemfile dependencies
133+
- name: Setup Fastlane
134+
uses: ruby/setup-ruby@v1
135+
with:
136+
ruby-version: "3.2"
137+
bundler-cache: true
138+
working-directory: ios
139+
140+
- run: bundle exec fastlane --version
141+
working-directory: ios
142+
143+
# Get flutter dependencies.
144+
- name: Build dependencies
145+
env:
146+
GH_READ_PAT: ${{ secrets.GH_READ_PAT }}
147+
run: |
148+
flutter clean
149+
echo "https://oauth:$GH_READ_PAT@github.com" > ~/.git-credentials
150+
git config --global credential.helper store
151+
flutter pub get
152+
153+
# Configure App Store Connect API Key
154+
- name: Configure App Store Connect API Key
155+
run: echo "$APP_STORE_CONNECT_API_KEY" > fastlane-api-key.json
156+
env:
157+
APP_STORE_CONNECT_API_KEY: ${{ secrets.APP_STORE_CONNECT_API_KEY }}
158+
working-directory: ios/fastlane
159+
160+
# Configure certificates and provisioning profiles
161+
- name: Configure code signing
162+
run: |
163+
# Import certificates
164+
echo "$IOS_CERTIFICATE_P12" | base64 --decode > certificate.p12
165+
166+
# Create temporary keychain
167+
security create-keychain -p "$KEYCHAIN_PASSWORD" build.keychain
168+
security default-keychain -s build.keychain
169+
security unlock-keychain -p "$KEYCHAIN_PASSWORD" build.keychain
170+
security set-keychain-settings -t 3600 -u build.keychain
171+
172+
# Import certificate to keychain
173+
security import certificate.p12 -k build.keychain -P "$IOS_CERTIFICATE_PASSWORD" -T /usr/bin/codesign
174+
security set-key-partition-list -S apple-tool:,apple: -s -k "$KEYCHAIN_PASSWORD" build.keychain
175+
176+
# Install provisioning profile
177+
mkdir -p ~/Library/MobileDevice/Provisioning\ Profiles
178+
echo "$IOS_PROVISION_PROFILE" | base64 --decode > ~/Library/MobileDevice/Provisioning\ Profiles/profile.mobileprovision
179+
env:
180+
IOS_CERTIFICATE_P12: ${{ secrets.IOS_CERTIFICATE_P12 }}
181+
IOS_CERTIFICATE_PASSWORD: ${{ secrets.IOS_CERTIFICATE_PASSWORD }}
182+
IOS_PROVISION_PROFILE: ${{ secrets.IOS_PROVISION_PROFILE }}
183+
KEYCHAIN_PASSWORD: ${{ secrets.KEYCHAIN_PASSWORD }}
184+
185+
# Create ExportOptions.plist
186+
- name: Create ExportOptions.plist
187+
run: |
188+
cat > ExportOptions.plist << EOF
189+
<?xml version="1.0" encoding="UTF-8"?>
190+
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
191+
<plist version="1.0">
192+
<dict>
193+
<key>method</key>
194+
<string>app-store</string>
195+
<key>teamID</key>
196+
<string>$TEAM_ID</string>
197+
<key>uploadBitcode</key>
198+
<false/>
199+
<key>uploadSymbols</key>
200+
<true/>
201+
</dict>
202+
</plist>
203+
EOF
204+
env:
205+
TEAM_ID: ${{ secrets.TEAM_ID }}
206+
working-directory: ios
207+
208+
# Build and deploy with Fastlane (by default, to internal track) 🚀.
209+
# Naturally, promote_to_production only deploys.
210+
- name: Fastlane building
211+
run: |
212+
bundle exec fastlane ${{ needs.var.outputs.lane }}
213+
env:
214+
APPLE_ID: ${{ secrets.APPLE_ID }}
215+
ITC_TEAM_ID: ${{ secrets.ITC_TEAM_ID }}
216+
TEAM_ID: ${{ secrets.TEAM_ID }}
217+
BUILD_NUMBER: ${{ needs.var.outputs.dev_build_number }}
218+
VERSION_CODE: ${{ needs.var.outputs.build_code }}
219+
working-directory: ios

ios/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", ">= 2.220.0"

ios/fastlane/Appfile

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
app_identifier("com.evanlu.possystem") # The bundle identifier of your app
2+
apple_id(ENV["APPLE_ID"]) # Your Apple email address
3+
4+
itc_team_id(ENV["ITC_TEAM_ID"]) # App Store Connect Team ID
5+
team_id(ENV["TEAM_ID"]) # Developer Portal Team ID
6+
7+
# For more information about the Appfile, see:
8+
# https://docs.fastlane.tools/advanced/#appfile

ios/fastlane/Fastfile

Lines changed: 34 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -31,23 +31,43 @@ platform :ios do
3131
sh "flutter build ios --no-codesign --dart-define=appFlavor=prod --dart-define=logLevel=info"
3232
end
3333

34-
desc "Submit to TestFlight"
35-
lane :beta do
36-
# Build the app
37-
sh "flutter build ios --dart-define=appFlavor=prod --dart-define=logLevel=info"
34+
desc "Submit a new Internal Build to TestFlight (internal testing)"
35+
lane :internal do
36+
# Build the app for development
37+
sh "flutter build ipa --export-options-plist=ExportOptions.plist --dart-define=appFlavor=dev --dart-define=logLevel=info"
3838

39-
# Code signing and upload would go here when certificates are available
40-
# build_app(workspace: "Runner.xcworkspace", scheme: "Runner")
41-
# upload_to_testflight
39+
# Upload to TestFlight for internal testing
40+
upload_to_testflight(
41+
skip_waiting_for_build_processing: true,
42+
ipa: "../build/ios/ipa/*.ipa",
43+
api_key_path: "fastlane-api-key.json",
44+
)
4245
end
4346

44-
desc "Submit to App Store"
45-
lane :release do
46-
# Build the app
47-
sh "flutter build ios --dart-define=appFlavor=prod --dart-define=logLevel=info"
47+
desc "Submit a new Beta Build to TestFlight (external testing)"
48+
lane :beta do
49+
# Build the app for production
50+
sh "flutter build ipa --export-options-plist=ExportOptions.plist --dart-define=appFlavor=prod --dart-define=logLevel=info"
4851

49-
# Code signing and upload would go here when certificates are available
50-
# build_app(workspace: "Runner.xcworkspace", scheme: "Runner")
51-
# upload_to_app_store
52+
# Upload to TestFlight for external testing
53+
upload_to_testflight(
54+
skip_waiting_for_build_processing: false,
55+
ipa: "../build/ios/ipa/*.ipa",
56+
api_key_path: "fastlane-api-key.json",
57+
)
58+
end
59+
60+
desc "Promote beta to production (App Store)"
61+
lane :promote_to_production do
62+
# Deliver the latest build to App Store
63+
deliver(
64+
skip_metadata: false,
65+
skip_screenshots: false,
66+
skip_binary_upload: true,
67+
submit_for_review: false,
68+
automatic_release: false,
69+
api_key_path: "fastlane-api-key.json",
70+
build_number: ENV['VERSION_CODE'],
71+
)
5272
end
5373
end

0 commit comments

Comments
 (0)