Skip to content

Commit 3d48a5c

Browse files
authored
Merge pull request #234 from loopandlearn/dev_updates_to_main_certs_fastlane
Match main updates in dev, Automate certificates, Update to Fastlane 2.225.0
2 parents ec86060 + e7e54e5 commit 3d48a5c

File tree

12 files changed

+440
-245
lines changed

12 files changed

+440
-245
lines changed

.github/workflows/add_identifiers.yml

Lines changed: 10 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ jobs:
1212
identifiers:
1313
name: Add Identifiers
1414
needs: validate
15-
runs-on: macos-14
15+
runs-on: macos-15
1616
steps:
1717
# Uncomment to manually select latest Xcode if needed
1818
#- name: Select Latest Xcode
@@ -24,8 +24,15 @@ jobs:
2424

2525
# Patch Fastlane Match to not print tables
2626
- name: Patch Match Tables
27-
run: find /usr/local/lib/ruby/gems -name table_printer.rb | xargs sed -i "" "/puts(Terminal::Table.new(params))/d"
28-
27+
run: |
28+
TABLE_PRINTER_PATH=$(ruby -e 'puts Gem::Specification.find_by_name("fastlane").gem_dir')/match/lib/match/table_printer.rb
29+
if [ -f "$TABLE_PRINTER_PATH" ]; then
30+
sed -i "" "/puts(Terminal::Table.new(params))/d" "$TABLE_PRINTER_PATH"
31+
else
32+
echo "table_printer.rb not found"
33+
exit 1
34+
fi
35+
2936
# Install project dependencies
3037
- name: Install Project Dependencies
3138
run: bundle install

.github/workflows/build_loop.yml

Lines changed: 194 additions & 152 deletions
Large diffs are not rendered by default.

.github/workflows/create_certs.yml

Lines changed: 94 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -1,31 +1,47 @@
11
name: 3. Create Certificates
22
run-name: Create Certificates (${{ github.ref_name }})
3-
on:
4-
workflow_dispatch:
3+
4+
on: [workflow_call, workflow_dispatch]
5+
6+
env:
7+
TEAMID: ${{ secrets.TEAMID }}
8+
GH_PAT: ${{ secrets.GH_PAT }}
9+
GH_TOKEN: ${{ secrets.GH_PAT }}
10+
MATCH_PASSWORD: ${{ secrets.MATCH_PASSWORD }}
11+
FASTLANE_KEY_ID: ${{ secrets.FASTLANE_KEY_ID }}
12+
FASTLANE_ISSUER_ID: ${{ secrets.FASTLANE_ISSUER_ID }}
13+
FASTLANE_KEY: ${{ secrets.FASTLANE_KEY }}
514

615
jobs:
716
validate:
817
name: Validate
918
uses: ./.github/workflows/validate_secrets.yml
1019
secrets: inherit
11-
12-
certificates:
13-
name: Create Certificates
20+
21+
22+
create_certs:
23+
name: Certificates
1424
needs: validate
15-
runs-on: macos-14
25+
runs-on: macos-15
26+
outputs:
27+
new_certificate_needed: ${{ steps.set_output.outputs.new_certificate_needed }}
28+
1629
steps:
17-
# Uncomment to manually select latest Xcode if needed
18-
#- name: Select Latest Xcode
19-
# run: "sudo xcode-select --switch /Applications/Xcode_13.0.app/Contents/Developer"
20-
2130
# Checks-out the repo
2231
- name: Checkout Repo
2332
uses: actions/checkout@v4
2433

2534
# Patch Fastlane Match to not print tables
2635
- name: Patch Match Tables
27-
run: find /usr/local/lib/ruby/gems -name table_printer.rb | xargs sed -i "" "/puts(Terminal::Table.new(params))/d"
28-
36+
run: |
37+
TABLE_PRINTER_PATH=$(ruby -e 'puts Gem::Specification.find_by_name("fastlane").gem_dir')/match/lib/match/table_printer.rb
38+
if [ -f "$TABLE_PRINTER_PATH" ]; then
39+
sed -i "" "/puts(Terminal::Table.new(params))/d" "$TABLE_PRINTER_PATH"
40+
else
41+
echo "table_printer.rb not found"
42+
exit 1
43+
fi
44+
2945
# Install project dependencies
3046
- name: Install Project Dependencies
3147
run: bundle install
@@ -34,13 +50,69 @@ jobs:
3450
- name: Sync clock
3551
run: sudo sntp -sS time.windows.com
3652

37-
# Create or update certificates for app
38-
- name: Create Certificates
39-
run: bundle exec fastlane certs
40-
env:
41-
TEAMID: ${{ secrets.TEAMID }}
42-
GH_PAT: ${{ secrets.GH_PAT }}
43-
MATCH_PASSWORD: ${{ secrets.MATCH_PASSWORD }}
44-
FASTLANE_KEY_ID: ${{ secrets.FASTLANE_KEY_ID }}
45-
FASTLANE_ISSUER_ID: ${{ secrets.FASTLANE_ISSUER_ID }}
46-
FASTLANE_KEY: ${{ secrets.FASTLANE_KEY }}
53+
# Create or update Distribution certificate and provisioning profiles
54+
- name: Check and create or update Distribution certificate and profiles if needed
55+
run: |
56+
echo "Running Fastlane certs lane..."
57+
bundle exec fastlane certs || true # ignore and continue on errors without annotating an exit code
58+
- name: Check Distribution certificate and launch Nuke certificates if needed
59+
run: bundle exec fastlane check_and_renew_certificates
60+
id: check_certs
61+
62+
- name: Set output and annotations based on Fastlane result
63+
id: set_output
64+
run: |
65+
CERT_STATUS_FILE="${{ github.workspace }}/fastlane/new_certificate_needed.txt"
66+
ENABLE_NUKE_CERTS=${{ vars.ENABLE_NUKE_CERTS }}
67+
68+
if [ -f "$CERT_STATUS_FILE" ]; then
69+
CERT_STATUS=$(cat "$CERT_STATUS_FILE" | tr -d '\n' | tr -d '\r') # Read file content and strip newlines
70+
echo "new_certificate_needed: $CERT_STATUS"
71+
echo "new_certificate_needed=$CERT_STATUS" >> $GITHUB_OUTPUT
72+
else
73+
echo "Certificate status file not found. Defaulting to false."
74+
echo "new_certificate_needed=false" >> $GITHUB_OUTPUT
75+
fi
76+
# Check if ENABLE_NUKE_CERTS is not set to true when certs are valid
77+
if [ "$CERT_STATUS" != "true" ] && [ "$ENABLE_NUKE_CERTS" != "true" ]; then
78+
echo "::notice::🔔 Automated renewal of certificates is disabled because the repository variable ENABLE_NUKE_CERTS is not set to 'true'."
79+
fi
80+
# Check if ENABLE_NUKE_CERTS is not set to true when certs are not valid
81+
if [ "$CERT_STATUS" = "true" ] && [ "$ENABLE_NUKE_CERTS" != "true" ]; then
82+
echo "::error::❌ No valid distribution certificate found. Automated renewal of certificates was skipped because the repository variable ENABLE_NUKE_CERTS is not set to 'true'."
83+
exit 1
84+
fi
85+
# Check if vars.FORCE_NUKE_CERTS is not set to true
86+
if [ vars.FORCE_NUKE_CERTS = "true" ]; then
87+
echo "::warning::‼️ Nuking of certificates was forced because the repository variable FORCE_NUKE_CERTS is set to 'true'."
88+
fi
89+
# Nuke Certs if needed, and if the repository variable ENABLE_NUKE_CERTS is set to 'true', or if FORCE_NUKE_CERTS is set to 'true', which will always force certs to be nuked
90+
nuke_certs:
91+
name: Nuke certificates
92+
needs: [validate, create_certs]
93+
runs-on: macos-15
94+
if: ${{ (needs.create_certs.outputs.new_certificate_needed == 'true' && vars.ENABLE_NUKE_CERTS == 'true') || vars.FORCE_NUKE_CERTS == 'true' }}
95+
steps:
96+
- name: Output from step id 'check_certs'
97+
run: echo "new_certificate_needed=${{ needs.create_certs.outputs.new_certificate_needed }}"
98+
99+
- name: Checkout repository
100+
uses: actions/checkout@v4
101+
102+
- name: Install dependencies
103+
run: bundle install
104+
105+
- name: Run Fastlane nuke_certs
106+
run: |
107+
set -e # Set error immediately after this step if error occurs
108+
bundle exec fastlane nuke_certs
109+
- name: Recreate Distribution certificate after nuking
110+
run: |
111+
set -e # Set error immediately after this step if error occurs
112+
bundle exec fastlane certs
113+
- name: Add success annotations for nuke and certificate recreation
114+
if: ${{ success() }}
115+
run: |
116+
echo "::warning::⚠️ All Distribution certificates and TestFlight profiles have been revoked and recreated."
117+
echo "::warning::❗️ If you have other apps being distributed by GitHub Actions / Fastlane / TestFlight that does not renew certificates automatically, please run the '3. Create Certificates' workflow for each of these apps to allow these apps to be built."
118+
echo "::warning::✅ But don't worry about your existing TestFlight builds, they will keep working!"

.github/workflows/validate_secrets.yml

Lines changed: 27 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ on: [workflow_call, workflow_dispatch]
55
jobs:
66
validate-access-token:
77
name: Access
8-
runs-on: macos-14
8+
runs-on: macos-15
99
env:
1010
GH_PAT: ${{ secrets.GH_PAT }}
1111
GH_TOKEN: ${{ secrets.GH_PAT }}
@@ -16,14 +16,14 @@ jobs:
1616
id: access-token
1717
run: |
1818
# Validate Access Token
19-
19+
2020
# Ensure that gh exit codes are handled when output is piped.
2121
set -o pipefail
22-
22+
2323
# Define patterns to validate the access token (GH_PAT) and distinguish between classic and fine-grained tokens.
2424
GH_PAT_CLASSIC_PATTERN='^ghp_[a-zA-Z0-9]{36}$'
2525
GH_PAT_FINE_GRAINED_PATTERN='^github_pat_[a-zA-Z0-9]{22}_[a-zA-Z0-9]{59}$'
26-
26+
2727
# Validate Access Token (GH_PAT)
2828
if [ -z "$GH_PAT" ]; then
2929
failed=true
@@ -65,26 +65,26 @@ jobs:
6565
echo "has_workflow_permission=true" >> $GITHUB_OUTPUT
6666
fi
6767
fi
68-
68+
6969
# Exit unsuccessfully if secret validation failed.
7070
if [ $failed ]; then
7171
exit 2
7272
fi
73-
73+
7474
validate-match-secrets:
7575
name: Match-Secrets
7676
needs: validate-access-token
77-
runs-on: macos-14
77+
runs-on: macos-15
7878
env:
7979
GH_TOKEN: ${{ secrets.GH_PAT }}
8080
steps:
8181
- name: Validate Match-Secrets
8282
run: |
8383
# Validate Match-Secrets
84-
84+
8585
# Ensure that gh exit codes are handled when output is piped.
8686
set -o pipefail
87-
87+
8888
# If a Match-Secrets repository does not exist, attempt to create one.
8989
if ! visibility=$(gh repo view ${{ github.repository_owner }}/Match-Secrets --json visibility | jq --raw-output '.visibility | ascii_downcase'); then
9090
echo "A '${{ github.repository_owner }}/Match-Secrets' repository could not be found using the GH_PAT secret. Attempting to create one..."
@@ -103,16 +103,16 @@ jobs:
103103
else
104104
echo "Found a private '${{ github.repository_owner }}/Match-Secrets' repository to use."
105105
fi
106-
106+
107107
# Exit unsuccessfully if secret validation failed.
108108
if [ $failed ]; then
109109
exit 2
110110
fi
111-
111+
112112
validate-fastlane-secrets:
113113
name: Fastlane
114114
needs: [validate-access-token, validate-match-secrets]
115-
runs-on: macos-14
115+
runs-on: macos-15
116116
env:
117117
GH_PAT: ${{ secrets.GH_PAT }}
118118
GH_TOKEN: ${{ secrets.GH_PAT }}
@@ -124,18 +124,18 @@ jobs:
124124
steps:
125125
- name: Checkout Repo
126126
uses: actions/checkout@v4
127-
127+
128128
- name: Install Project Dependencies
129129
run: bundle install
130130

131131
# Sync the GitHub runner clock with the Windows time server (workaround as suggested in https://github.com/actions/runner/issues/2996)
132132
- name: Sync clock
133133
run: sudo sntp -sS time.windows.com
134-
134+
135135
- name: Validate Fastlane Secrets
136136
run: |
137137
# Validate Fastlane Secrets
138-
138+
139139
# Validate TEAMID
140140
if [ -z "$TEAMID" ]; then
141141
failed=true
@@ -147,50 +147,49 @@ jobs:
147147
failed=true
148148
echo "::error::The TEAMID secret is set but invalid. Verify that it is set correctly (only uppercase letters and numbers) and try again."
149149
fi
150-
150+
151151
# Validate MATCH_PASSWORD
152152
if [ -z "$MATCH_PASSWORD" ]; then
153153
failed=true
154154
echo "::error::The MATCH_PASSWORD secret is unset or empty. Set it and try again."
155155
fi
156-
156+
157157
# Ensure that fastlane exit codes are handled when output is piped.
158158
set -o pipefail
159-
159+
160160
# Validate FASTLANE_ISSUER_ID, FASTLANE_KEY_ID, and FASTLANE_KEY
161161
FASTLANE_KEY_ID_PATTERN='^[A-Z0-9]+$'
162162
FASTLANE_ISSUER_ID_PATTERN='^\{?[A-F0-9a-f]{8}-[A-F0-9a-f]{4}-[A-F0-9a-f]{4}-[A-F0-9a-f]{4}-[A-F0-9a-f]{12}\}?$'
163-
163+
164164
if [ -z "$FASTLANE_ISSUER_ID" ] || [ -z "$FASTLANE_KEY_ID" ] || [ -z "$FASTLANE_KEY" ]; then
165165
failed=true
166166
[ -z "$FASTLANE_ISSUER_ID" ] && echo "::error::The FASTLANE_ISSUER_ID secret is unset or empty. Set it and try again."
167167
[ -z "$FASTLANE_KEY_ID" ] && echo "::error::The FASTLANE_KEY_ID secret is unset or empty. Set it and try again."
168168
[ -z "$FASTLANE_KEY" ] && echo "::error::The FASTLANE_KEY secret is unset or empty. Set it and try again."
169169
elif [ ${#FASTLANE_KEY_ID} -ne 10 ]; then
170170
failed=true
171-
echo "::error::The FASTLANE_KEY_ID secret is set but has wrong length. Verify that you copied it correctly from the 'Keys' tab at https://appstoreconnect.apple.com/access/api and try again."
171+
echo "::error::The FASTLANE_KEY_ID secret is set but has wrong length. Verify that you copied it correctly from the 'Keys' tab at https://appstoreconnect.apple.com/access/integrations/api and try again."
172172
elif ! [[ $FASTLANE_KEY_ID =~ $FASTLANE_KEY_ID_PATTERN ]]; then
173173
failed=true
174-
echo "::error::The FASTLANE_KEY_ID secret is set but invalid. Verify that you copied it correctly from the 'Keys' tab at https://appstoreconnect.apple.com/access/api and try again."
174+
echo "::error::The FASTLANE_KEY_ID secret is set but invalid. Verify that you copied it correctly from the 'Keys' tab at https://appstoreconnect.apple.com/access/integrations/api and try again."
175175
elif ! [[ $FASTLANE_ISSUER_ID =~ $FASTLANE_ISSUER_ID_PATTERN ]]; then
176176
failed=true
177-
echo "::error::The FASTLANE_ISSUER_ID secret is set but invalid. Verify that you copied it correctly from the 'Keys' tab at https://appstoreconnect.apple.com/access/api and try again."
177+
echo "::error::The FASTLANE_ISSUER_ID secret is set but invalid. Verify that you copied it correctly from the 'Keys' tab at https://appstoreconnect.apple.com/access/integrations/api and try again."
178178
elif ! echo "$FASTLANE_KEY" | openssl pkcs8 -nocrypt >/dev/null; then
179179
failed=true
180180
echo "::error::The FASTLANE_KEY secret is set but invalid. Verify that you copied it correctly from the API Key file (*.p8) you downloaded and try again."
181-
elif ! bundle exec fastlane validate_secrets 2>&1 | tee fastlane.log; then
181+
elif ! (bundle exec fastlane validate_secrets 2>&1 || true) | tee fastlane.log; then # ignore "fastlane validate_secrets" errors and continue on errors without annotating an exit code
182182
if grep -q "bad decrypt" fastlane.log; then
183183
failed=true
184184
echo "::error::Unable to decrypt the Match-Secrets repository using the MATCH_PASSWORD secret. Verify that it is set correctly and try again."
185185
elif grep -q -e "required agreement" -e "license agreement" fastlane.log; then
186186
failed=true
187-
echo "::error::Unable to create a valid authorization token for the App Store Connect API. Verify that the latest developer program license agreement has been accepted at https://developer.apple.com/account (review and accept any updated agreement), then wait a few minutes for changes to propagate and try again."
188-
elif ! grep -q -e "No code signing identity found" -e "Could not install WWDR certificate" fastlane.log; then
189-
failed=true
190-
echo "::error::Unable to create a valid authorization token for the App Store Connect API. Verify that the FASTLANE_ISSUER_ID, FASTLANE_KEY_ID, and FASTLANE_KEY secrets are set correctly and try again."
187+
echo "::error::❗️ Verify that the latest developer program license agreement has been accepted at https://developer.apple.com/account (review and accept any updated agreement), then wait a few minutes for changes to take effect and try again."
188+
elif grep -q "Your certificate .* is not valid" fastlane.log; then
189+
echo "::notice::Your Distribution certificate is invalid or expired. Automated renewal of the certificate will be attempted."
191190
fi
192191
fi
193-
192+
194193
# Exit unsuccessfully if secret validation failed.
195194
if [ $failed ]; then
196195
exit 2

0 commit comments

Comments
 (0)