Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Browser Build "Validate Secrets" Improvements #77

Merged
merged 14 commits into from
Aug 23, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 6 additions & 4 deletions .github/workflows/add_identifiers.yml
Original file line number Diff line number Diff line change
@@ -1,15 +1,17 @@
name: 2. Add Identifiers
run-name: Add Identifiers
run-name: Add Identifiers (${{ github.ref_name }})
on:
workflow_dispatch:

jobs:
secrets:
validate:
name: Validate
uses: ./.github/workflows/validate_secrets.yml
secrets: inherit

identifiers:
needs: secrets
name: Add Identifiers
needs: validate
runs-on: macos-12
steps:
# Uncomment to manually select latest Xcode if needed
Expand Down
36 changes: 18 additions & 18 deletions .github/workflows/build_loop.yml
Original file line number Diff line number Diff line change
Expand Up @@ -5,10 +5,10 @@ on:

## Remove the "#" sign from the beginning of the line below to get automated builds on push (code changes in your repository)
#push:

schedule:
- cron: '0 8 * * 3' # Checks for updates at 08:00 am UTC every Wednesday
- cron: '0 8 1 * 6' # Builds the app on the 1st Saturday every month at 08:00 am UTC
- cron: '0 8 * * 3' # Checks for updates at 08:00 UTC every Wednesday
- cron: '0 8 1 * 6' # Builds the app on the 1st Saturday every month at 08:00 UTC

env:
UPSTREAM_REPO: LoopKit/LoopWorkspace
Expand All @@ -18,21 +18,22 @@ env:
WORKFLOW_PERMISSIONS: false

jobs:
secrets:
validate:
name: Validate
uses: ./.github/workflows/validate_secrets.yml
secrets: inherit

# Checks if GH_PAT holds workflow permissions
# Checks for existence of alive branch; if non-existent creates it
check_alive_and_permissions:
needs: secrets
needs: validate
runs-on: ubuntu-latest
name: Check alive branch and permissions
permissions:
contents: write
outputs:
WORKFLOW_PERMISSION: ${{ steps.workflow-permission.outputs.has_permission }}

steps:
- name: Check for workflow permissions
id: workflow-permission
Expand All @@ -49,7 +50,7 @@ jobs:
echo "Automated build features will be skipped!"
echo "has_permission=false" >> $GITHUB_OUTPUT # Set WORKFLOW_PERMISSION to false.
fi

- name: Check for alive branch
if: steps.workflow-permission.outputs.has_permission == 'true'
env:
Expand All @@ -62,7 +63,7 @@ jobs:
echo "Branch 'alive' does not exist."
echo "ALIVE_BRANCH_EXISTS=false" >> $GITHUB_ENV # Set ALIVE_BRANCH_EXISTS to false
fi

- name: Create alive branch
if: env.ALIVE_BRANCH_EXISTS != 'true'
env:
Expand All @@ -82,7 +83,7 @@ jobs:
/repos/${{ github.repository_owner }}/LoopWorkspace/git/refs \
-f ref='refs/heads/alive' \
-f sha=$SHA

# Checks for changes in upstream repository; if changes exist prompts sync for build
# Performs keepalive to avoid stale fork
check_latest_from_upstream:
Expand All @@ -91,7 +92,7 @@ jobs:
name: Check upstream and keep alive
outputs:
NEW_COMMITS: ${{ steps.sync.outputs.has_new_commits }}

steps:
- name: Checkout target repo
if: |
Expand All @@ -101,7 +102,7 @@ jobs:
with:
token: ${{ secrets.GH_PAT }}
ref: alive

- name: Sync upstream changes
if: | # do not run the upstream sync action on the upstream repository
needs.check_alive_and_permissions.outputs.WORKFLOW_PERMISSION == 'true' &&
Expand All @@ -114,7 +115,7 @@ jobs:
target_repo_token: ${{ secrets.GH_PAT }}
upstream_sync_branch: ${{ env.UPSTREAM_BRANCH }}
upstream_sync_repo: ${{ env.UPSTREAM_REPO }}

# Display a sample message based on the sync output var 'has_new_commits'
- name: New commits found
if: |
Expand All @@ -127,7 +128,7 @@ jobs:
needs.check_alive_and_permissions.outputs.WORKFLOW_PERMISSION == 'true' &&
vars.SCHEDULED_SYNC != 'false' && steps.sync.outputs.has_new_commits == 'false'
run: echo "There were no new commits."

- name: Show value of 'has_new_commits'
if: needs.check_alive_and_permissions.outputs.WORKFLOW_PERMISSION == 'true' && vars.SCHEDULED_SYNC != 'false'
run: |
Expand All @@ -142,7 +143,7 @@ jobs:
uses: gautamkrishnar/keepalive-workflow@v1 # using the workflow with default settings
with:
time_elapsed: 20 # Time elapsed from the previous commit to trigger a new automated commit (in days)

- name: Show scheduled build configuration message
if: needs.check_alive_and_permissions.outputs.WORKFLOW_PERMISSION != 'true'
run: |
Expand All @@ -152,7 +153,6 @@ jobs:
echo "If you want to enable automatic builds and updates for your Loop, please follow the instructions \
under the following path <code>LoopWorkspace/fastlane/testflight.md</code>." >> $GITHUB_STEP_SUMMARY


# Builds Loop
build:
name: Build
Expand All @@ -169,7 +169,7 @@ jobs:
steps:
- name: Select Xcode version
run: "sudo xcode-select --switch /Applications/Xcode_14.3.1.app/Contents/Developer"

- name: Checkout Repo for syncing
if: |
needs.check_alive_and_permissions.outputs.WORKFLOW_PERMISSION == 'true' &&
Expand All @@ -178,7 +178,7 @@ jobs:
with:
token: ${{ secrets.GH_PAT }}
ref: ${{ env.TARGET_BRANCH }}

- name: Sync upstream changes
if: | # do not run the upstream sync action on the upstream repository
needs.check_alive_and_permissions.outputs.WORKFLOW_PERMISSION == 'true' &&
Expand Down
10 changes: 6 additions & 4 deletions .github/workflows/create_certs.yml
Original file line number Diff line number Diff line change
@@ -1,15 +1,17 @@
name: 3. Create Certificates
run-name: Create Certificates
run-name: Create Certificates (${{ github.ref_name }})
on:
workflow_dispatch:

jobs:
secrets:
validate:
name: Validate
uses: ./.github/workflows/validate_secrets.yml
secrets: inherit

certificates:
needs: secrets
name: Create Certificates
needs: validate
runs-on: macos-12
steps:
# Uncomment to manually select latest Xcode if needed
Expand Down
140 changes: 103 additions & 37 deletions .github/workflows/validate_secrets.yml
Original file line number Diff line number Diff line change
@@ -1,70 +1,136 @@
name: 1. Validate Secrets
run-name: Validate Secrets
run-name: Validate Secrets (${{ github.ref_name }})
on: [workflow_call, workflow_dispatch]

jobs:
validate:
runs-on: macos-12
validate-access-token:
name: Access
runs-on: macos-13
env:
GH_PAT: ${{ secrets.GH_PAT }}
GH_TOKEN: ${{ secrets.GH_PAT }}
steps:
- name: Validate Access Token
run: |
# Validate Fastlane Access Token (GH_PAT)
if [ -z "$GH_PAT" ]; then
failed=true
echo "::error::The GH_PAT secret is unset or empty. Set it and try again."
elif [ "$(gh api -H "Accept: application/vnd.github+json" /repos/${{ github.repository_owner }}/LoopWorkspace | jq --raw-output '.permissions.push')" != "true" ]; then
failed=true
echo "::error::The GH_PAT secret is set but invalid or lacking at least 'repo' permission scope ('repo, workflow' is okay too).\
Verify that token permissions are set correctly (or update them) at https://github.com/settings/tokens and try again."
fi

# Exit unsuccessfully if secret validation failed.
if [ $failed ]; then
exit 2
fi

validate-match-secrets:
name: Match-Secrets
needs: validate-access-token
runs-on: macos-13
env:
GH_TOKEN: ${{ secrets.GH_PAT }}
steps:
- name: Validate Match-Secrets
run: |
# Validate Match-Secrets
if [ "$(gh repo list --json name | jq --raw-output 'any(.name=="Match-Secrets")')" != "true" ]; then
echo "A 'Match-Secrets' repository could not be found. Attempting to create one...";

if gh repo create Match-Secrets --private >/dev/null && [ "$(gh repo list --json name,visibility | jq --raw-output '.[] | select(.name=="Match-Secrets") | .visibility == "PRIVATE"')" == "true" ]; then
echo "Created a private 'Match-Secrets' repository."
else
failed=true
echo "::error::Cannot access or create a private 'Match-Secrets' repository. The GH_PAT secret is lacking at least the 'repo' permission scope required to access or create the repository.\
Verify that token permissions are set correctly (or update them) at https://github.com/settings/tokens and try again."
fi
elif [ "$(gh repo list --json name,visibility | jq --raw-output '.[] | select(.name=="Match-Secrets") | .visibility == "PUBLIC"')" == "true" ]; then
failed=true
echo "::error::A 'Match-Secrets' repository was found, but it is is public. Delete it and try again (a private repository will be created for you)."
fi

# Exit unsuccessfully if secret validation failed.
if [ $failed ]; then
exit 2
fi

validate-fastlane-secrets:
name: Fastlane
needs: validate-match-secrets
runs-on: macos-13
env:
GH_PAT: ${{ secrets.GH_PAT }}
GH_TOKEN: ${{ secrets.GH_PAT }}
FASTLANE_ISSUER_ID: ${{ secrets.FASTLANE_ISSUER_ID }}
FASTLANE_KEY_ID: ${{ secrets.FASTLANE_KEY_ID }}
FASTLANE_KEY: ${{ secrets.FASTLANE_KEY }}
MATCH_PASSWORD: ${{ secrets.MATCH_PASSWORD }}
TEAMID: ${{ secrets.TEAMID }}
steps:
# Checks-out the repo
- name: Checkout Repo
uses: actions/checkout@v3

# Validates the repo secrets
- name: Validate Secrets

- name: Validate Fastlane Secrets
run: |
# Validate Secrets
echo Validating Repository Secrets...
# Validate Fastlane Secrets

# Validate TEAMID
if [ -z "$TEAMID" ]; then
failed=true
echo "::error::TEAMID secret is unset or empty. Set it and try again."
echo "::error::The TEAMID secret is unset or empty. Set it and try again."
elif [ ${#TEAMID} -ne 10 ]; then
failed=true
echo "::error::TEAMID secret is set but has wrong length. Verify that it is set correctly and try again."
echo "::error::The TEAMID secret is set but has wrong length. Verify that it is set correctly and try again."
elif ! [[ $TEAMID =~ ^[A-Z0-9]+$ ]]; then
failed=true
echo "::error::The TEAMID secret is set but invalid. Verify that it is set correctly (only uppercase letters and numbers) and try again."
fi

# Validate GH_PAT
if [ -z "$GH_PAT" ]; then
failed=true
echo "::error::GH_PAT secret is unset or empty. Set it and try again."
elif [ "$(gh api -H "Accept: application/vnd.github+json" /repos/${{ github.repository_owner }}/Match-Secrets | jq --raw-output '.permissions.push')" != "true" ]; then
# Validate MATCH_PASSWORD
if [ -z "$MATCH_PASSWORD" ]; then
failed=true
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."
echo "::error::The MATCH_PASSWORD secret is unset or empty. Set it and try again."
fi

# Ensure that fastlane exit codes are handled when output is piped.
set -o pipefail

# Validate FASTLANE_ISSUER_ID, FASTLANE_KEY_ID, and FASTLANE_KEY
FASTLANE_KEY_ID_PATTERN='^[A-Z0-9]+$'
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}\}?$'

if [ -z "$FASTLANE_ISSUER_ID" ] || [ -z "$FASTLANE_KEY_ID" ] || [ -z "$FASTLANE_KEY" ]; then
failed=true
[ -z "$FASTLANE_ISSUER_ID" ] && echo "::error::The FASTLANE_ISSUER_ID secret is unset or empty. Set it and try again."
[ -z "$FASTLANE_KEY_ID" ] && echo "::error::The FASTLANE_KEY_ID secret is unset or empty. Set it and try again."
[ -z "$FASTLANE_KEY" ] && echo "::error::The FASTLANE_KEY secret is unset or empty. Set it and try again."
elif ! echo "$FASTLANE_KEY" | openssl pkcs8 -nocrypt >/dev/null; then
elif [ ${#FASTLANE_KEY_ID} -ne 10 ]; then
failed=true
echo "::error::The FASTLANE_KEY secret is set but invalid. Verify that it is set correctly and try again."
elif ! fastlane validate_secrets; then
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."
elif ! [[ $FASTLANE_KEY_ID =~ $FASTLANE_KEY_ID_PATTERN ]]; then
failed=true
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."
fi

# Validate MATCH_PASSWORD
if [ -z "$MATCH_PASSWORD" ]; then
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."
elif ! [[ $FASTLANE_ISSUER_ID =~ $FASTLANE_ISSUER_ID_PATTERN ]]; then
failed=true
echo "::error::The MATCH_PASSWORD secret is unset or empty. Set it and try again."
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."
elif ! echo "$FASTLANE_KEY" | openssl pkcs8 -nocrypt >/dev/null; then
failed=true
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."
elif ! fastlane validate_secrets 2>&1 | tee fastlane.log; then
if grep -q "bad decrypt" fastlane.log; then
failed=true
echo "::error::Unable to decrypt the Match-Secrets repository using the MATCH_PASSWORD secret. Verify that it is set correctly and try again."
elif ! grep -q "No code signing identity found" fastlane.log; then
failed=true
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."
fi
fi

# Exit unsuccessfully if secret validation failed.
if [ $failed ]; then
exit 2
fi
shell: bash
env:
TEAMID: ${{ secrets.TEAMID }}
GH_PAT: ${{ secrets.GH_PAT }}
FASTLANE_ISSUER_ID: ${{ secrets.FASTLANE_ISSUER_ID }}
FASTLANE_KEY_ID: ${{ secrets.FASTLANE_KEY_ID }}
FASTLANE_KEY: ${{ secrets.FASTLANE_KEY }}
MATCH_PASSWORD: ${{ secrets.MATCH_PASSWORD }}
GH_TOKEN: ${{ secrets.GH_PAT }}
7 changes: 7 additions & 0 deletions fastlane/Fastfile
Original file line number Diff line number Diff line change
Expand Up @@ -249,6 +249,13 @@ platform :ios do
end

find_bundle_id("com.#{TEAMID}.loopkit.Loop")

match(
type: "appstore",
git_basic_authorization: Base64.strict_encode64("#{GITHUB_REPOSITORY_OWNER}:#{GH_PAT}"),
app_identifier: [],
)

end

desc "Nuke Certs"
Expand Down