Skip to content

Commit 32f23ff

Browse files
authored
Fastlane TestFlight (#1179)
* init * get last values from secrets
1 parent 7f023b3 commit 32f23ff

11 files changed

+170
-18
lines changed

.github/workflows/testflight.yml

+70
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,70 @@
1+
name: "TestFlight ⚙️"
2+
3+
on:
4+
repository_dispatch:
5+
types: [testflight]
6+
7+
jobs:
8+
test_build:
9+
runs-on: macos-14
10+
11+
steps:
12+
- name: Checkout
13+
uses: actions/checkout@v4
14+
with:
15+
token: ${{ secrets.GITHUB_TOKEN }}
16+
fetch-depth: 0
17+
18+
- name: git Checkout
19+
if: ${{ github.event.client_payload.hasCheckoutCommit }}
20+
run: |
21+
git checkout ${{ github.event.client_payload.commitSHA }}
22+
23+
- run: 'echo "$SSH_KEY" > key'
24+
shell: bash
25+
env:
26+
SSH_KEY: ${{secrets.P12_PASSWORD}}
27+
28+
- name: Install the Apple certificate and provisioning profile
29+
env:
30+
BUILD_CERTIFICATE_BASE64: ${{ secrets.BUILD_CERTIFICATE_BASE64 }}
31+
BUILD_PROVISION_PROFILE_BASE64: ${{ secrets.BUILD_PROVISION_PROFILE_BASE64 }}
32+
KEYCHAIN_PASSWORD: "CI_PASSWORD"
33+
P12_PASSWORD: ${{ secrets.P12_PASSWORD }}
34+
run: |
35+
# create variables
36+
CERTIFICATE_PATH=$RUNNER_TEMP/build_certificate.p12
37+
PP_PATH=$RUNNER_TEMP/build_pp.mobileprovision
38+
KEYCHAIN_PATH=$RUNNER_TEMP/app-signing.keychain-db
39+
40+
# import certificate and provisioning profile from secrets
41+
echo -n "$BUILD_CERTIFICATE_BASE64" | base64 --decode -o $CERTIFICATE_PATH
42+
echo -n "$BUILD_PROVISION_PROFILE_BASE64" | base64 --decode -o $PP_PATH
43+
44+
# create temporary keychain
45+
security create-keychain -p "$KEYCHAIN_PASSWORD" $KEYCHAIN_PATH
46+
security set-keychain-settings -lut 21600 $KEYCHAIN_PATH
47+
security unlock-keychain -p "$KEYCHAIN_PASSWORD" $KEYCHAIN_PATH
48+
49+
# import certificate to keychain
50+
security import $CERTIFICATE_PATH -P "$P12_PASSWORD" -A -t cert -f pkcs12 -k $KEYCHAIN_PATH
51+
security set-key-partition-list -S apple-tool:,apple: -k "$KEYCHAIN_PASSWORD" $KEYCHAIN_PATH
52+
security list-keychain -d user -s $KEYCHAIN_PATH
53+
54+
# apply provisioning profile
55+
mkdir -p ~/Library/MobileDevice/Provisioning\ Profiles
56+
sudo mkdir -p /Library/MobileDevice/Provisioning\ Profiles
57+
cp $PP_PATH ~/Library/MobileDevice/Provisioning\ Profiles
58+
sudo cp $PP_PATH /Library/MobileDevice/Provisioning\ Profiles
59+
60+
- name: Build and Upload to TestFlight
61+
run: sudo fastlane testFlightLane \
62+
keyID:${{ secrets.APP_STORE_KEY_ID }} \
63+
issuerID:${{ secrets.APP_STORE_ISSUER_ID }} \
64+
keyContents:${{ secrets.APP_STORE_KEY_CONTENTS }} \
65+
scheme:${{ github.event.client_payload.scheme }} \
66+
codeSign64:${{ secrets.CODE_SIGN_64 }} \
67+
profileName64:${{ secrets.PROFILE_NAME_64 }} \
68+
xcodeVersion:${{ github.event.client_payload.xcodeVersion }} \
69+
build:${{ github.event.client_payload.build }} \
70+
version:${{ github.event.client_payload.version }}

fastlane/Fastfile.swift

+76
Original file line numberDiff line numberDiff line change
@@ -63,6 +63,82 @@ class Fastfile: LaneFile {
6363
)
6464
}
6565

66+
// MARK: TestFlight
67+
68+
// TODO: verify tvOS
69+
func testFlightLane(withOptions options: [String: String]?) {
70+
71+
guard let options,
72+
let keyID = options["keyID"],
73+
let issuerID = options["issuerID"],
74+
let keyContents = options["keyContents"],
75+
let scheme = options["scheme"],
76+
let codeSign64 = options["codeSign64"],
77+
let profileName64 = options["profileName64"]
78+
else {
79+
puts(message: "ERROR: missing or incorrect options")
80+
exit(1)
81+
}
82+
83+
guard let decodedCodeSignIdentity = decodeBase64(encoded: codeSign64) else {
84+
puts(message: "ERROR: code sign identity not valid base 64")
85+
exit(1)
86+
}
87+
88+
guard let profileName = decodeBase64(encoded: profileName64) else {
89+
puts(message: "ERROR: profile name not valid base 64")
90+
exit(1)
91+
}
92+
93+
if let xcodeVersion = options["xcodeVersion"] {
94+
xcodes(version: xcodeVersion)
95+
}
96+
97+
appStoreConnectApiKey(
98+
keyId: keyID,
99+
issuerId: .userDefined(issuerID),
100+
keyContent: .userDefined(keyContents),
101+
isKeyContentBase64: true,
102+
duration: 1200,
103+
inHouse: false
104+
)
105+
106+
updateCodeSigningSettings(
107+
path: "Swiftfin.xcodeproj",
108+
useAutomaticSigning: false,
109+
codeSignIdentity: .userDefined(decodedCodeSignIdentity),
110+
profileName: .userDefined(profileName)
111+
)
112+
113+
if let version = options["version"] {
114+
incrementVersionNumber(
115+
versionNumber: .userDefined(version),
116+
xcodeproj: "Swiftfin.xcodeproj"
117+
)
118+
}
119+
120+
if let build = options["build"] {
121+
incrementBuildNumber(
122+
buildNumber: .userDefined(build),
123+
xcodeproj: "Swiftfin.xcodeproj"
124+
)
125+
} else {
126+
incrementBuildNumber(
127+
xcodeproj: "Swiftfin.xcodeproj"
128+
)
129+
}
130+
131+
buildApp(
132+
scheme: .userDefined(scheme),
133+
skipArchive: .userDefined(false),
134+
skipProfileDetection: false
135+
)
136+
137+
uploadToTestflight(
138+
ipa: "Swiftfin.ipa"
139+
)
140+
}
141+
66142
// MARK: Utilities
67143

68144
private func decodeBase64(encoded: String) -> String? {

fastlane/swift/DeliverfileProtocol.swift

+2-2
Original file line numberDiff line numberDiff line change
@@ -95,7 +95,7 @@ public protocol DeliverfileProtocol: AnyObject {
9595
/// Path to the app rating's config
9696
var appRatingConfigPath: String? { get }
9797

98-
/// Extra information for the submission (e.g. compliance specifications, IDFA settings)
98+
/// Extra information for the submission (e.g. compliance specifications)
9999
var submissionInformation: [String: Any]? { get }
100100

101101
/// The ID of your App Store Connect team if you're in multiple teams
@@ -272,4 +272,4 @@ public extension DeliverfileProtocol {
272272

273273
// Please don't remove the lines below
274274
// They are used to detect outdated files
275-
// FastlaneRunnerAPIVersion [0.9.124]
275+
// FastlaneRunnerAPIVersion [0.9.127]

fastlane/swift/Fastlane.swift

+14-10
Original file line numberDiff line numberDiff line change
@@ -190,7 +190,7 @@ public func appStoreBuildNumber(apiKeyPath: OptionalConfigValue<String?> = .fast
190190

191191
- parameters:
192192
- keyId: The key ID
193-
- issuerId: The issuer ID
193+
- issuerId: The issuer ID. It can be nil if the key is individual API key
194194
- keyFilepath: The path to the key p8 file
195195
- keyContent: The content of the key p8 file
196196
- isKeyContentBase64: Whether :key_content is Base64 encoded or not
@@ -201,7 +201,7 @@ public func appStoreBuildNumber(apiKeyPath: OptionalConfigValue<String?> = .fast
201201
Load the App Store Connect API token to use in other fastlane tools and actions
202202
*/
203203
public func appStoreConnectApiKey(keyId: String,
204-
issuerId: String,
204+
issuerId: OptionalConfigValue<String?> = .fastlaneDefault(nil),
205205
keyFilepath: OptionalConfigValue<String?> = .fastlaneDefault(nil),
206206
keyContent: OptionalConfigValue<String?> = .fastlaneDefault(nil),
207207
isKeyContentBase64: OptionalConfigValue<Bool> = .fastlaneDefault(false),
@@ -210,7 +210,7 @@ public func appStoreConnectApiKey(keyId: String,
210210
setSpaceshipToken: OptionalConfigValue<Bool> = .fastlaneDefault(true))
211211
{
212212
let keyIdArg = RubyCommand.Argument(name: "key_id", value: keyId, type: nil)
213-
let issuerIdArg = RubyCommand.Argument(name: "issuer_id", value: issuerId, type: nil)
213+
let issuerIdArg = issuerId.asRubyArgument(name: "issuer_id", type: nil)
214214
let keyFilepathArg = keyFilepath.asRubyArgument(name: "key_filepath", type: nil)
215215
let keyContentArg = keyContent.asRubyArgument(name: "key_content", type: nil)
216216
let isKeyContentBase64Arg = isKeyContentBase64.asRubyArgument(name: "is_key_content_base64", type: nil)
@@ -674,7 +674,7 @@ public func appledoc(input: [String],
674674
- resetRatings: Reset the summary rating when you release a new version of the application
675675
- priceTier: The price tier of this application
676676
- appRatingConfigPath: Path to the app rating's config
677-
- submissionInformation: Extra information for the submission (e.g. compliance specifications, IDFA settings)
677+
- submissionInformation: Extra information for the submission (e.g. compliance specifications)
678678
- teamId: The ID of your App Store Connect team if you're in multiple teams
679679
- teamName: The name of your App Store Connect team if you're in multiple teams
680680
- devPortalTeamId: The short ID of your Developer Portal team, if you're in multiple teams. Different from your iTC team ID!
@@ -3714,7 +3714,7 @@ public func deleteKeychain(name: OptionalConfigValue<String?> = .fastlaneDefault
37143714
- resetRatings: Reset the summary rating when you release a new version of the application
37153715
- priceTier: The price tier of this application
37163716
- appRatingConfigPath: Path to the app rating's config
3717-
- submissionInformation: Extra information for the submission (e.g. compliance specifications, IDFA settings)
3717+
- submissionInformation: Extra information for the submission (e.g. compliance specifications)
37183718
- teamId: The ID of your App Store Connect team if you're in multiple teams
37193719
- teamName: The name of your App Store Connect team if you're in multiple teams
37203720
- devPortalTeamId: The short ID of your Developer Portal team, if you're in multiple teams. Different from your iTC team ID!
@@ -4096,7 +4096,7 @@ public func downloadAppPrivacyDetailsFromAppStore(username: String,
40964096
- appIdentifier: The bundle identifier of your app
40974097
- teamId: The ID of your App Store Connect team if you're in multiple teams
40984098
- teamName: The name of your App Store Connect team if you're in multiple teams
4099-
- platform: The app platform for dSYMs you wish to download (ios, appletvos)
4099+
- platform: The app platform for dSYMs you wish to download (ios, xros, appletvos)
41004100
- version: The app version for dSYMs you wish to download, pass in 'latest' to download only the latest build's dSYMs or 'live' to download only the live version dSYMs
41014101
- buildNumber: The app build_number for dSYMs you wish to download
41024102
- minVersion: The minimum app version for dSYMs you wish to download
@@ -9707,7 +9707,7 @@ public func setBuildNumberRepository(useHgRevisionNumber: OptionalConfigValue<Bo
97079707
- changelog: Changelog text that should be uploaded to App Store Connect
97089708
- teamId: The ID of your App Store Connect team if you're in multiple teams
97099709
- teamName: The name of your App Store Connect team if you're in multiple teams
9710-
- platform: The platform of the app (ios, appletvos, mac)
9710+
- platform: The platform of the app (ios, appletvos, xros, mac)
97119711

97129712
This is useful if you have only one changelog for all languages.
97139713
You can store the changelog in `./changelog.txt` and it will automatically get loaded from there. This integration is useful if you support e.g. 10 languages and want to use the same "What's new"-text for all languages.
@@ -10938,6 +10938,7 @@ public func splunkmint(dsym: OptionalConfigValue<String?> = .fastlaneDefault(nil
1093810938
- xcprettyOutput: Specifies the output type for xcpretty. eg. 'test', or 'simple'
1093910939
- xcprettyArgs: Pass in xcpretty additional command line arguments (e.g. '--test --no-color' or '--tap --no-utf'), requires xcpretty_output to be specified also
1094010940
- verbose: Increase verbosity of informational output
10941+
- veryVerbose: Increase verbosity to include debug output
1094110942
- simulator: Specifies the simulator to pass for Swift Compiler (one of: iphonesimulator, macosx)
1094210943
- simulatorArch: Specifies the architecture of the simulator to pass for Swift Compiler (one of: x86_64, arm64). Requires the simulator option to be specified also, otherwise, it's ignored
1094310944
*/
@@ -10953,6 +10954,7 @@ public func spm(command: String = "build",
1095310954
xcprettyOutput: OptionalConfigValue<String?> = .fastlaneDefault(nil),
1095410955
xcprettyArgs: OptionalConfigValue<String?> = .fastlaneDefault(nil),
1095510956
verbose: OptionalConfigValue<Bool> = .fastlaneDefault(false),
10957+
veryVerbose: OptionalConfigValue<Bool> = .fastlaneDefault(false),
1095610958
simulator: OptionalConfigValue<String?> = .fastlaneDefault(nil),
1095710959
simulatorArch: String = "arm64")
1095810960
{
@@ -10968,6 +10970,7 @@ public func spm(command: String = "build",
1096810970
let xcprettyOutputArg = xcprettyOutput.asRubyArgument(name: "xcpretty_output", type: nil)
1096910971
let xcprettyArgsArg = xcprettyArgs.asRubyArgument(name: "xcpretty_args", type: nil)
1097010972
let verboseArg = verbose.asRubyArgument(name: "verbose", type: nil)
10973+
let veryVerboseArg = veryVerbose.asRubyArgument(name: "very_verbose", type: nil)
1097110974
let simulatorArg = simulator.asRubyArgument(name: "simulator", type: nil)
1097210975
let simulatorArchArg = RubyCommand.Argument(name: "simulator_arch", value: simulatorArch, type: nil)
1097310976
let array: [RubyCommand.Argument?] = [commandArg,
@@ -10982,6 +10985,7 @@ public func spm(command: String = "build",
1098210985
xcprettyOutputArg,
1098310986
xcprettyArgsArg,
1098410987
verboseArg,
10988+
veryVerboseArg,
1098510989
simulatorArg,
1098610990
simulatorArchArg]
1098710991
let args: [RubyCommand.Argument] = array
@@ -12536,7 +12540,7 @@ public func uploadSymbolsToSentry(apiHost: String = "https://app.getsentry.com/a
1253612540
- resetRatings: Reset the summary rating when you release a new version of the application
1253712541
- priceTier: The price tier of this application
1253812542
- appRatingConfigPath: Path to the app rating's config
12539-
- submissionInformation: Extra information for the submission (e.g. compliance specifications, IDFA settings)
12543+
- submissionInformation: Extra information for the submission (e.g. compliance specifications)
1254012544
- teamId: The ID of your App Store Connect team if you're in multiple teams
1254112545
- teamName: The name of your App Store Connect team if you're in multiple teams
1254212546
- devPortalTeamId: The short ID of your Developer Portal team, if you're in multiple teams. Different from your iTC team ID!
@@ -13594,7 +13598,7 @@ public func xcov(workspace: OptionalConfigValue<String?> = .fastlaneDefault(nil)
1359413598
coverallsServiceJobId: OptionalConfigValue<String?> = .fastlaneDefault(nil),
1359513599
coverallsRepoToken: OptionalConfigValue<String?> = .fastlaneDefault(nil),
1359613600
xcconfig: OptionalConfigValue<String?> = .fastlaneDefault(nil),
13597-
ideFoundationPath: String = "/Applications/Xcode-15.3.0.app/Contents/Developer/../Frameworks/IDEFoundation.framework/Versions/A/IDEFoundation",
13601+
ideFoundationPath: String = "/Applications/Xcode_15.4.app/Contents/Developer/../Frameworks/IDEFoundation.framework/Versions/A/IDEFoundation",
1359813602
legacySupport: OptionalConfigValue<Bool> = .fastlaneDefault(false))
1359913603
{
1360013604
let workspaceArg = workspace.asRubyArgument(name: "workspace", type: nil)
@@ -13797,4 +13801,4 @@ public let snapshotfile: Snapshotfile = .init()
1379713801

1379813802
// Please don't remove the lines below
1379913803
// They are used to detect outdated files
13800-
// FastlaneRunnerAPIVersion [0.9.177]
13804+
// FastlaneRunnerAPIVersion [0.9.180]

fastlane/swift/FastlaneSwiftRunner/FastlaneSwiftRunner.xcodeproj/project.pbxproj

+2
Original file line numberDiff line numberDiff line change
@@ -404,6 +404,7 @@
404404
CLANG_ENABLE_MODULES = YES;
405405
CODE_SIGN_IDENTITY = "-";
406406
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/../Frameworks @loader_path/../Frameworks";
407+
MACOSX_DEPLOYMENT_TARGET = 10.12;
407408
PRODUCT_NAME = "$(TARGET_NAME)";
408409
SWIFT_OPTIMIZATION_LEVEL = "-Onone";
409410
SWIFT_VERSION = 4.0;
@@ -416,6 +417,7 @@
416417
CLANG_ENABLE_MODULES = YES;
417418
CODE_SIGN_IDENTITY = "-";
418419
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/../Frameworks @loader_path/../Frameworks";
420+
MACOSX_DEPLOYMENT_TARGET = 10.12;
419421
PRODUCT_NAME = "$(TARGET_NAME)";
420422
SWIFT_VERSION = 4.0;
421423
};

fastlane/swift/GymfileProtocol.swift

+1-1
Original file line numberDiff line numberDiff line change
@@ -208,4 +208,4 @@ public extension GymfileProtocol {
208208

209209
// Please don't remove the lines below
210210
// They are used to detect outdated files
211-
// FastlaneRunnerAPIVersion [0.9.127]
211+
// FastlaneRunnerAPIVersion [0.9.130]

fastlane/swift/MatchfileProtocol.swift

+1-1
Original file line numberDiff line numberDiff line change
@@ -228,4 +228,4 @@ public extension MatchfileProtocol {
228228

229229
// Please don't remove the lines below
230230
// They are used to detect outdated files
231-
// FastlaneRunnerAPIVersion [0.9.121]
231+
// FastlaneRunnerAPIVersion [0.9.124]

fastlane/swift/PrecheckfileProtocol.swift

+1-1
Original file line numberDiff line numberDiff line change
@@ -52,4 +52,4 @@ public extension PrecheckfileProtocol {
5252

5353
// Please don't remove the lines below
5454
// They are used to detect outdated files
55-
// FastlaneRunnerAPIVersion [0.9.120]
55+
// FastlaneRunnerAPIVersion [0.9.123]

fastlane/swift/ScanfileProtocol.swift

+1-1
Original file line numberDiff line numberDiff line change
@@ -320,4 +320,4 @@ public extension ScanfileProtocol {
320320

321321
// Please don't remove the lines below
322322
// They are used to detect outdated files
323-
// FastlaneRunnerAPIVersion [0.9.132]
323+
// FastlaneRunnerAPIVersion [0.9.135]

fastlane/swift/ScreengrabfileProtocol.swift

+1-1
Original file line numberDiff line numberDiff line change
@@ -96,4 +96,4 @@ public extension ScreengrabfileProtocol {
9696

9797
// Please don't remove the lines below
9898
// They are used to detect outdated files
99-
// FastlaneRunnerAPIVersion [0.9.122]
99+
// FastlaneRunnerAPIVersion [0.9.125]

fastlane/swift/SnapshotfileProtocol.swift

+1-1
Original file line numberDiff line numberDiff line change
@@ -204,4 +204,4 @@ public extension SnapshotfileProtocol {
204204

205205
// Please don't remove the lines below
206206
// They are used to detect outdated files
207-
// FastlaneRunnerAPIVersion [0.9.116]
207+
// FastlaneRunnerAPIVersion [0.9.119]

0 commit comments

Comments
 (0)