Skip to content

Commit 8212df3

Browse files
authored
Merge pull request #216 from dnzxy/dev
Migrate Validation Improvements from LoopKit/LoopWorkspace
2 parents f3f58d7 + a4dc797 commit 8212df3

File tree

5 files changed

+181
-50
lines changed

5 files changed

+181
-50
lines changed

.github/workflows/add_identifiers.yml

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,16 @@
11
name: 2. Add Identifiers
2-
run-name: Add Identifiers
2+
run-name: Add Identifiers (${{ github.ref_name }})
33
on:
44
workflow_dispatch:
55

66
jobs:
7-
secrets:
7+
validate:
8+
name: Validate
89
uses: ./.github/workflows/validate_secrets.yml
910
secrets: inherit
1011

1112
identifiers:
12-
needs: secrets
13+
needs: validate
1314
runs-on: macos-12
1415
steps:
1516
# Uncomment to manually select latest Xcode if needed

.github/workflows/build_LoopFollow.yml

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
name: 4. Build Loop Follow
2-
run-name: Build Loop Follow
2+
run-name: Build Loop Follow (${{ github.ref_name }})
33
on:
44
workflow_dispatch:
55

@@ -12,12 +12,14 @@ on:
1212

1313

1414
jobs:
15-
secrets:
15+
validate:
16+
name: Validate
1617
uses: ./.github/workflows/validate_secrets.yml
1718
secrets: inherit
1819

1920
build:
20-
needs: secrets
21+
name: Build
22+
needs: validate
2123
runs-on: macos-12
2224
steps:
2325
# Uncomment to manually select latest Xcode if needed

.github/workflows/create_certs.yml

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,17 @@
11
name: 3. Create Certificates
2-
run-name: Create Certificates
2+
run-name: Create Certificates (${{ github.ref_name }})
33
on:
44
workflow_dispatch:
55

66
jobs:
7-
secrets:
7+
validate:
8+
name: Validate
89
uses: ./.github/workflows/validate_secrets.yml
910
secrets: inherit
1011

1112
certificates:
12-
needs: secrets
13+
name: Create Certificates
14+
needs: validate
1315
runs-on: macos-12
1416
steps:
1517
# Uncomment to manually select latest Xcode if needed

.github/workflows/validate_secrets.yml

Lines changed: 160 additions & 40 deletions
Original file line numberDiff line numberDiff line change
@@ -1,70 +1,190 @@
11
name: 1. Validate Secrets
2-
run-name: Validate Secrets
2+
run-name: Validate Secrets (${{ github.ref_name }})
33
on: [workflow_call, workflow_dispatch]
44

55
jobs:
6-
validate:
7-
runs-on: macos-12
6+
validate-access-token:
7+
name: Access
8+
runs-on: macos-13
9+
env:
10+
GH_PAT: ${{ secrets.GH_PAT }}
11+
GH_TOKEN: ${{ secrets.GH_PAT }}
12+
outputs:
13+
HAS_WORKFLOW_PERMISSION: ${{ steps.access-token.outputs.has_workflow_permission }}
14+
steps:
15+
- name: Validate Access Token
16+
id: access-token
17+
run: |
18+
# Validate Access Token
19+
20+
# Ensure that gh exit codes are handled when output is piped.
21+
set -o pipefail
22+
23+
# Define patterns to validate the access token (GH_PAT) and distinguish between classic and fine-grained tokens.
24+
GH_PAT_CLASSIC_PATTERN='^ghp_[a-zA-Z0-9]{36}$'
25+
GH_PAT_FINE_GRAINED_PATTERN='^github_pat_[a-zA-Z0-9]{22}_[a-zA-Z0-9]{59}$'
26+
27+
# Validate Access Token (GH_PAT)
28+
if [ -z "$GH_PAT" ]; then
29+
failed=true
30+
echo "::error::The GH_PAT secret is unset or empty. Set it and try again."
31+
else
32+
if [[ $GH_PAT =~ $GH_PAT_CLASSIC_PATTERN ]]; then
33+
provides_scopes=true
34+
echo "The GH_PAT secret is a structurally valid classic token."
35+
elif [[ $GH_PAT =~ $GH_PAT_FINE_GRAINED_PATTERN ]]; then
36+
echo "The GH_PAT secret is a structurally valid fine-grained token."
37+
else
38+
unknown_format=true
39+
echo "The GH_PAT secret does not have a known token format."
40+
fi
41+
42+
# Attempt to capture the x-oauth-scopes scopes of the token.
43+
if ! scopes=$(curl -sS -f -I -H "Authorization: token $GH_PAT" https://api.github.com | { grep -i '^x-oauth-scopes:' || true; } | cut -d ' ' -f2- | tr -d '\r'); then
44+
failed=true
45+
if [ $unknown_format ]; then
46+
echo "::error::Unable to connect to GitHub using the GH_PAT secret. Verify that it is set correctly (including the 'ghp_' or 'github_pat_' prefix) and try again."
47+
else
48+
echo "::error::Unable to connect to GitHub using the GH_PAT secret. Verify that the token exists and has not expired at https://github.com/settings/tokens. If necessary, regenerate or create a new token (and update the secret), then try again."
49+
fi
50+
elif [[ $scopes =~ workflow ]]; then
51+
echo "The GH_PAT secret has repo and workflow permissions."
52+
echo "has_workflow_permission=true" >> $GITHUB_OUTPUT
53+
elif [[ $scopes =~ repo ]]; then
54+
echo "The GH_PAT secret has repo (but not workflow) permissions."
55+
elif [ $provides_scopes ]; then
56+
failed=true
57+
if [ -z "$scopes" ]; then
58+
echo "The GH_PAT secret is valid and can be used to connect to GitHub, but it does not provide any permission scopes."
59+
else
60+
echo "The GH_PAT secret is valid and can be used to connect to GitHub, but it only provides the following permission scopes: $scopes"
61+
fi
62+
echo "::error::The GH_PAT secret is lacking at least the 'repo' permission scope required to access the Match-Secrets repository. Update the token permissions at https://github.com/settings/tokens (to include the 'repo' and 'workflow' scopes) and try again."
63+
else
64+
echo "The GH_PAT secret is valid and can be used to connect to GitHub, but it does not provide inspectable scopes. Assuming that the 'repo' and 'workflow' permission scopes required to access the Match-Secrets repository and perform automations are present."
65+
echo "has_workflow_permission=true" >> $GITHUB_OUTPUT
66+
fi
67+
fi
68+
69+
# Exit unsuccessfully if secret validation failed.
70+
if [ $failed ]; then
71+
exit 2
72+
fi
73+
74+
validate-match-secrets:
75+
name: Match-Secrets
76+
needs: validate-access-token
77+
runs-on: macos-13
78+
env:
79+
GH_TOKEN: ${{ secrets.GH_PAT }}
80+
steps:
81+
- name: Validate Match-Secrets
82+
run: |
83+
# Validate Match-Secrets
84+
85+
# Ensure that gh exit codes are handled when output is piped.
86+
set -o pipefail
87+
88+
# If a Match-Secrets repository does not exist, attempt to create one.
89+
if ! visibility=$(gh repo view ${{ github.repository_owner }}/Match-Secrets --json visibility | jq --raw-output '.visibility | ascii_downcase'); then
90+
echo "A '${{ github.repository_owner }}/Match-Secrets' repository could not be found using the GH_PAT secret. Attempting to create one..."
91+
92+
# Create a private Match-Secrets repository and verify that it exists and that it is private.
93+
if gh repo create ${{ github.repository_owner }}/Match-Secrets --private >/dev/null && [ "$(gh repo view ${{ github.repository_owner }}/Match-Secrets --json visibility | jq --raw-output '.visibility | ascii_downcase')" == "private" ]; then
94+
echo "Created a private '${{ github.repository_owner }}/Match-Secrets' repository."
95+
else
96+
failed=true
97+
echo "::error::Unable to create a private '${{ github.repository_owner }}/Match-Secrets' repository. Create a private 'Match-Secrets' repository manually and try again. If a private 'Match-Secrets' repository already exists, verify that the token permissions of the GH_PAT are set correctly (or update them) at https://github.com/settings/tokens and try again."
98+
fi
99+
# Otherwise, if a Match-Secrets repository exists, but it is public, cause validation to fail.
100+
elif [[ "$visibility" == "public" ]]; then
101+
failed=true
102+
echo "::error::A '${{ github.repository_owner }}/Match-Secrets' repository was found, but it is public. Change the repository visibility to private (or delete it) and try again. If necessary, a private repository will be created for you."
103+
else
104+
echo "Found a private '${{ github.repository_owner }}/Match-Secrets' repository to use."
105+
fi
106+
107+
# Exit unsuccessfully if secret validation failed.
108+
if [ $failed ]; then
109+
exit 2
110+
fi
111+
112+
validate-fastlane-secrets:
113+
name: Fastlane
114+
needs: [validate-access-token, validate-match-secrets]
115+
runs-on: macos-13
116+
env:
117+
GH_PAT: ${{ secrets.GH_PAT }}
118+
GH_TOKEN: ${{ secrets.GH_PAT }}
119+
FASTLANE_ISSUER_ID: ${{ secrets.FASTLANE_ISSUER_ID }}
120+
FASTLANE_KEY_ID: ${{ secrets.FASTLANE_KEY_ID }}
121+
FASTLANE_KEY: ${{ secrets.FASTLANE_KEY }}
122+
MATCH_PASSWORD: ${{ secrets.MATCH_PASSWORD }}
123+
TEAMID: ${{ secrets.TEAMID }}
8124
steps:
9-
# Checks-out the repo
10125
- name: Checkout Repo
11126
uses: actions/checkout@v3
12-
13-
# Validates the repo secrets
14-
- name: Validate Secrets
127+
128+
- name: Validate Fastlane Secrets
15129
run: |
16-
# Validate Secrets
17-
echo Validating Repository Secrets...
18-
130+
# Validate Fastlane Secrets
131+
19132
# Validate TEAMID
20133
if [ -z "$TEAMID" ]; then
21134
failed=true
22-
echo "::error::TEAMID secret is unset or empty. Set it and try again."
135+
echo "::error::The TEAMID secret is unset or empty. Set it and try again."
23136
elif [ ${#TEAMID} -ne 10 ]; then
24137
failed=true
25-
echo "::error::TEAMID secret is set but has wrong length. Verify that it is set correctly and try again."
26-
fi
27-
28-
# Validate GH_PAT
29-
if [ -z "$GH_PAT" ]; then
138+
echo "::error::The TEAMID secret is set but has wrong length. Verify that it is set correctly and try again."
139+
elif ! [[ $TEAMID =~ ^[A-Z0-9]+$ ]]; then
30140
failed=true
31-
echo "::error::GH_PAT secret is unset or empty. Set it and try again."
32-
elif [ "$(gh api -H "Accept: application/vnd.github+json" /repos/${{ github.repository_owner }}/Match-Secrets | jq --raw-output '.permissions.push')" != "true" ]; then
141+
echo "::error::The TEAMID secret is set but invalid. Verify that it is set correctly (only uppercase letters and numbers) and try again."
142+
fi
143+
144+
# Validate MATCH_PASSWORD
145+
if [ -z "$MATCH_PASSWORD" ]; then
33146
failed=true
34-
echo "::error::GH_PAT secret is set but invalid or lacking appropriate privileges on the ${{ github.repository_owner }}/Match-Secrets repository. Verify that it is set correctly and try again."
147+
echo "::error::The MATCH_PASSWORD secret is unset or empty. Set it and try again."
35148
fi
36-
149+
150+
# Ensure that fastlane exit codes are handled when output is piped.
151+
set -o pipefail
152+
37153
# Validate FASTLANE_ISSUER_ID, FASTLANE_KEY_ID, and FASTLANE_KEY
154+
FASTLANE_KEY_ID_PATTERN='^[A-Z0-9]+$'
155+
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}\}?$'
156+
38157
if [ -z "$FASTLANE_ISSUER_ID" ] || [ -z "$FASTLANE_KEY_ID" ] || [ -z "$FASTLANE_KEY" ]; then
39158
failed=true
40159
[ -z "$FASTLANE_ISSUER_ID" ] && echo "::error::The FASTLANE_ISSUER_ID secret is unset or empty. Set it and try again."
41160
[ -z "$FASTLANE_KEY_ID" ] && echo "::error::The FASTLANE_KEY_ID secret is unset or empty. Set it and try again."
42161
[ -z "$FASTLANE_KEY" ] && echo "::error::The FASTLANE_KEY secret is unset or empty. Set it and try again."
43-
elif ! echo "$FASTLANE_KEY" | openssl pkcs8 -nocrypt >/dev/null; then
162+
elif [ ${#FASTLANE_KEY_ID} -ne 10 ]; then
44163
failed=true
45-
echo "::error::The FASTLANE_KEY secret is set but invalid. Verify that it is set correctly and try again."
46-
elif ! fastlane validate_secrets; then
164+
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."
165+
elif ! [[ $FASTLANE_KEY_ID =~ $FASTLANE_KEY_ID_PATTERN ]]; then
47166
failed=true
48-
echo "::error::Unable to create a valid authorization token for the App Store Connect API.\
49-
Verify that the FASTLANE_ISSUER_ID, FASTLANE_KEY_ID, and FASTLANE_KEY secrets are set correctly and try again."
50-
fi
51-
52-
# Validate MATCH_PASSWORD
53-
if [ -z "$MATCH_PASSWORD" ]; then
167+
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."
168+
elif ! [[ $FASTLANE_ISSUER_ID =~ $FASTLANE_ISSUER_ID_PATTERN ]]; then
54169
failed=true
55-
echo "::error::The MATCH_PASSWORD secret is unset or empty. Set it and try again."
170+
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."
171+
elif ! echo "$FASTLANE_KEY" | openssl pkcs8 -nocrypt >/dev/null; then
172+
failed=true
173+
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."
174+
elif ! fastlane validate_secrets 2>&1 | tee fastlane.log; then
175+
if grep -q "bad decrypt" fastlane.log; then
176+
failed=true
177+
echo "::error::Unable to decrypt the Match-Secrets repository using the MATCH_PASSWORD secret. Verify that it is set correctly and try again."
178+
elif grep -q -e "required agreement" -e "license agreement" fastlane.log; then
179+
failed=true
180+
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."
181+
elif ! grep -q -e "No code signing identity found" -e "Could not install WWDR certificate" fastlane.log; then
182+
failed=true
183+
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."
184+
fi
56185
fi
57-
186+
58187
# Exit unsuccessfully if secret validation failed.
59188
if [ $failed ]; then
60189
exit 2
61190
fi
62-
shell: bash
63-
env:
64-
TEAMID: ${{ secrets.TEAMID }}
65-
GH_PAT: ${{ secrets.GH_PAT }}
66-
FASTLANE_ISSUER_ID: ${{ secrets.FASTLANE_ISSUER_ID }}
67-
FASTLANE_KEY_ID: ${{ secrets.FASTLANE_KEY_ID }}
68-
FASTLANE_KEY: ${{ secrets.FASTLANE_KEY }}
69-
MATCH_PASSWORD: ${{ secrets.MATCH_PASSWORD }}
70-
GH_TOKEN: ${{ secrets.GH_PAT }}

fastlane/Fastfile

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -172,7 +172,13 @@ platform :ios do
172172
bundle_id = Spaceship::ConnectAPI::BundleId.find(identifier)
173173
end
174174

175-
find_bundle_id("com.#{TEAMID}.loopkit.Loop")
175+
find_bundle_id("com.#{TEAMID}.loopkit.LoopFollow")
176+
177+
match(
178+
type: "appstore",
179+
git_basic_authorization: Base64.strict_encode64("#{GITHUB_REPOSITORY_OWNER}:#{GH_PAT}"),
180+
app_identifier: [],
181+
)
176182
end
177183

178184
desc "Nuke Certs"

0 commit comments

Comments
 (0)