diff --git a/.github/workflows/add_identifiers.yml b/.github/workflows/add_identifiers.yml index f31fead2..c7635949 100644 --- a/.github/workflows/add_identifiers.yml +++ b/.github/workflows/add_identifiers.yml @@ -1,9 +1,17 @@ -name: Add Identifiers +name: 2. Add Identifiers +run-name: Add Identifiers (${{ github.ref_name }}) on: workflow_dispatch: jobs: + validate: + name: Validate + uses: ./.github/workflows/validate_secrets.yml + secrets: inherit + identifiers: + name: Add Identifiers + needs: validate runs-on: macos-12 steps: # Uncomment to manually select latest Xcode if needed diff --git a/.github/workflows/build_loopcaregiver.yml b/.github/workflows/build_loopcaregiver.yml index 31d9fd67..83b5be66 100644 --- a/.github/workflows/build_loopcaregiver.yml +++ b/.github/workflows/build_loopcaregiver.yml @@ -1,15 +1,23 @@ -name: Build LoopCaregiver +name: 4. Build LoopCaregiver +run-name: Build LoopCaregiver (${{ github.ref_name }}) on: workflow_dispatch: jobs: + validate: + name: Validate + uses: ./.github/workflows/validate_secrets.yml + secrets: inherit + build: + name: Build + needs: validate runs-on: macos-13 steps: # Uncomment to manually select latest Xcode if needed - name: Select Latest Xcode run: "sudo xcode-select --switch /Applications/Xcode_14.3.1.app/Contents/Developer" - + # Checks-out the repo - name: Checkout Repo uses: actions/checkout@v3 @@ -41,7 +49,7 @@ jobs: FASTLANE_ISSUER_ID: ${{ secrets.FASTLANE_ISSUER_ID }} FASTLANE_KEY: ${{ secrets.FASTLANE_KEY }} MATCH_PASSWORD: ${{ secrets.MATCH_PASSWORD }} - + # Upload IPA and Symbols - name: Upload IPA and Symbol artifacts uses: actions/upload-artifact@v3 diff --git a/.github/workflows/create_certs.yml b/.github/workflows/create_certs.yml index 326ed01b..4c41b2b3 100644 --- a/.github/workflows/create_certs.yml +++ b/.github/workflows/create_certs.yml @@ -1,9 +1,17 @@ -name: Create Certificates +name: 3. Create Certificates +run-name: Create Certificates (${{ github.ref_name }}) on: workflow_dispatch: jobs: + validate: + name: Validate + uses: ./.github/workflows/validate_secrets.yml + secrets: inherit + certificates: + name: Create Certificates + needs: validate runs-on: macos-12 steps: # Uncomment to manually select latest Xcode if needed @@ -13,11 +21,11 @@ jobs: # Checks-out the repo - name: Checkout Repo uses: actions/checkout@v3 - + # Patch Fastlane Match to not print tables - name: Patch Match Tables run: find /usr/local/lib/ruby/gems -name table_printer.rb | xargs sed -i "" "/puts(Terminal::Table.new(params))/d" - + # Create or update certificate for LoopCaregiver - name: Create Caregiver Certificate run: fastlane caregiver_cert diff --git a/.github/workflows/validate_secrets.yml b/.github/workflows/validate_secrets.yml new file mode 100644 index 00000000..835217fb --- /dev/null +++ b/.github/workflows/validate_secrets.yml @@ -0,0 +1,190 @@ +name: 1. Validate Secrets +run-name: Validate Secrets (${{ github.ref_name }}) +on: [workflow_call, workflow_dispatch] + +jobs: + validate-access-token: + name: Access + runs-on: macos-13 + env: + GH_PAT: ${{ secrets.GH_PAT }} + GH_TOKEN: ${{ secrets.GH_PAT }} + outputs: + HAS_WORKFLOW_PERMISSION: ${{ steps.access-token.outputs.has_workflow_permission }} + steps: + - name: Validate Access Token + id: access-token + run: | + # Validate Access Token + + # Ensure that gh exit codes are handled when output is piped. + set -o pipefail + + # Define patterns to validate the access token (GH_PAT) and distinguish between classic and fine-grained tokens. + GH_PAT_CLASSIC_PATTERN='^ghp_[a-zA-Z0-9]{36}$' + GH_PAT_FINE_GRAINED_PATTERN='^github_pat_[a-zA-Z0-9]{22}_[a-zA-Z0-9]{59}$' + + # Validate 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." + else + if [[ $GH_PAT =~ $GH_PAT_CLASSIC_PATTERN ]]; then + provides_scopes=true + echo "The GH_PAT secret is a structurally valid classic token." + elif [[ $GH_PAT =~ $GH_PAT_FINE_GRAINED_PATTERN ]]; then + echo "The GH_PAT secret is a structurally valid fine-grained token." + else + unknown_format=true + echo "The GH_PAT secret does not have a known token format." + fi + + # Attempt to capture the x-oauth-scopes scopes of the token. + 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 + failed=true + if [ $unknown_format ]; then + 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." + else + 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." + fi + elif [[ $scopes =~ workflow ]]; then + echo "The GH_PAT secret has repo and workflow permissions." + echo "has_workflow_permission=true" >> $GITHUB_OUTPUT + elif [[ $scopes =~ repo ]]; then + echo "The GH_PAT secret has repo (but not workflow) permissions." + elif [ $provides_scopes ]; then + failed=true + if [ -z "$scopes" ]; then + echo "The GH_PAT secret is valid and can be used to connect to GitHub, but it does not provide any permission scopes." + else + echo "The GH_PAT secret is valid and can be used to connect to GitHub, but it only provides the following permission scopes: $scopes" + fi + 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." + else + 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." + echo "has_workflow_permission=true" >> $GITHUB_OUTPUT + fi + 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 + + # Ensure that gh exit codes are handled when output is piped. + set -o pipefail + + # If a Match-Secrets repository does not exist, attempt to create one. + if ! visibility=$(gh repo view ${{ github.repository_owner }}/Match-Secrets --json visibility | jq --raw-output '.visibility | ascii_downcase'); then + echo "A '${{ github.repository_owner }}/Match-Secrets' repository could not be found using the GH_PAT secret. Attempting to create one..." + + # Create a private Match-Secrets repository and verify that it exists and that it is private. + 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 + echo "Created a private '${{ github.repository_owner }}/Match-Secrets' repository." + else + failed=true + 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." + fi + # Otherwise, if a Match-Secrets repository exists, but it is public, cause validation to fail. + elif [[ "$visibility" == "public" ]]; then + failed=true + 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." + else + echo "Found a private '${{ github.repository_owner }}/Match-Secrets' repository to use." + fi + + # Exit unsuccessfully if secret validation failed. + if [ $failed ]; then + exit 2 + fi + + validate-fastlane-secrets: + name: Fastlane + needs: [validate-access-token, 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: + - name: Checkout Repo + uses: actions/checkout@v3 + + - name: Validate Fastlane Secrets + run: | + # Validate Fastlane Secrets + + # Validate TEAMID + if [ -z "$TEAMID" ]; then + failed=true + echo "::error::The TEAMID secret is unset or empty. Set it and try again." + elif [ ${#TEAMID} -ne 10 ]; then + failed=true + 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 MATCH_PASSWORD + if [ -z "$MATCH_PASSWORD" ]; then + failed=true + 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 [ ${#FASTLANE_KEY_ID} -ne 10 ]; then + failed=true + 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::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 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 -e "required agreement" -e "license agreement" fastlane.log; then + failed=true + 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." + elif ! grep -q -e "No code signing identity found" -e "Could not install WWDR certificate" 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 diff --git a/fastlane/Fastfile b/fastlane/Fastfile index 96004a6e..00aa4a06 100644 --- a/fastlane/Fastfile +++ b/fastlane/Fastfile @@ -157,6 +157,30 @@ platform :ios do ) end + desc "Validate Secrets" + lane :validate_secrets do + setup_ci if ENV['CI'] + ENV["MATCH_READONLY"] = true.to_s + + app_store_connect_api_key( + key_id: "#{FASTLANE_KEY_ID}", + issuer_id: "#{FASTLANE_ISSUER_ID}", + key_content: "#{FASTLANE_KEY}" + ) + + def find_bundle_id(identifier) + bundle_id = Spaceship::ConnectAPI::BundleId.find(identifier) + end + + find_bundle_id("com.#{TEAMID}.loopkit.LoopCaregiver") + + match( + type: "appstore", + git_basic_authorization: Base64.strict_encode64("#{GITHUB_REPOSITORY_OWNER}:#{GH_PAT}"), + app_identifier: [], + ) + end + desc "Nuke Certs" lane :nuke_certs do setup_ci if ENV['CI'] diff --git a/fastlane/testflight.md b/fastlane/testflight.md index 3e95afa6..32684023 100644 --- a/fastlane/testflight.md +++ b/fastlane/testflight.md @@ -1,101 +1,106 @@ -# Using Github Actions + FastLane to deploy to TestFlight +# Using GitHub Actions + FastLane to deploy to TestFlight -These instructions allow you to build Loop (and optionally, LoopCaregiver) without having access to a Mac. They also allow you to easily install Loop on phones that are not connected to your computer. So you can send builds and updates to those you care for easily, or have an easy to access backup if you run Loop for yourself. You do not need to worry about correct Xcode/Mac versions either. An app built using this method can easily be deployed to newer versions of iOS, as soon as they are available. +These instructions allow you to build LoopCaregiver without having access to a Mac. -The setup steps are somewhat involved, but nearly all are one time steps. Subsequent builds are trivial. Note that TestFlight requires apple id accounts 13 years or older. Your app must be updated once every 90 days, but it's a simple click to make a new build and can be done from anywhere. +* You can install LoopCaregiver on phones via TestFlight that are not connected to your computer +* You can send builds and updates to those you care for +* You can install LoopCaregiver on your phone using only the TestFlight app if a phone was lost or the app is accidentally deleted +* You do not need to worry about specific Xcode/Mac versions for a given iOS + +## Introduction + +The setup steps are somewhat involved, but nearly all are one time steps. Subsequent builds are trivial. Your app must be updated once every 90 days, but it's a simple click to make a new build and can be done from anywhere. The 90-day update is a TestFlight requirement. + +There are more detailed instructions in LoopDocs for using GitHub for Browser Builds of LoopCaregiver and Loop, including troubleshooting and build errors. Please refer to: + +* [LoopDocs: GitHub Other Apps](https://loopkit.github.io/loopdocs/gh-actions/gh-other-apps/) +* [LoopDocs: GitHub Overview](https://loopkit.github.io/loopdocs/gh-actions/gh-overview/) +* [LoopDocs: GitHub Errors](https://loopkit.github.io/loopdocs/gh-actions/gh-errors/) + +Note that installing with TestFlight, (in the US), requires the Apple ID account holder to be 13 years or older. For younger Loopers, an adult must log into Media & Purchase on the child's phone to install LoopCaregiver. More details on this can be found in [LoopDocs](https://loopkit.github.io/loopdocs/gh-actions/gh-deploy/#install-testflight-loop-for-child). ## Prerequisites -* A [github account](https://github.com/signup). The free level comes with plenty of storage and free compute time to build loop, multiple times a day, if you wanted to. -* A paid [Apple Developer account](https://developer.apple.com). You may be able to use the free version, but that has not been tested. +* A [GitHub account](https://github.com/signup). The free level comes with plenty of storage and free compute time to build loop, multiple times a day, if you wanted to. +* A paid [Apple Developer account](https://developer.apple.com). * Some time. Set aside a couple of hours to perform the setup. +## Save 6 Secrets + +You require 6 Secrets (alphanumeric items) to use the GitHub build method, and if you use the GitHub method to build more than LoopCaregiver, e.g., Loop or Loop Follow, you will use the same 6 Secrets for each app you build with this method. Each secret is identified below by `ALL_CAPITAL_LETTER_NAMES`. + +* Four Secrets are from your Apple Account +* Two Secrets are from your GitHub account +* Be sure to save the 6 Secrets in a text file using a text editor + - Do **NOT** use a smart editor, which might auto-correct and change case, because these Secrets are case sensitive ## Generate App Store Connect API Key +This step is common for all GitHub Browser Builds; do this step only once. You will be saving 4 Secrets from your Apple Account in this step. + 1. Sign in to the [Apple developer portal page](https://developer.apple.com/account/resources/certificates/list). -1. Copy the team id from the upper right of the screen. Record this as your `TEAMID`. -1. Go to the [App Store Connect](https://appstoreconnect.apple.com/access/api) interface, click the "Keys" tab, and create a new key with "Admin" access. Give it a name like "FastLane API Key". -1. Record the key id; this will be used for `FASTLANE_KEY_ID`. +1. Copy the Team ID from the upper right of the screen. Record this as your `TEAMID`. +1. Go to the [App Store Connect](https://appstoreconnect.apple.com/access/api) interface, click the "Keys" tab, and create a new key with "Admin" access. Give it the name: "FastLane API Key". 1. Record the issuer id; this will be used for `FASTLANE_ISSUER_ID`. +1. Record the key id; this will be used for `FASTLANE_KEY_ID`. 1. Download the API key itself, and open it in a text editor. The contents of this file will be used for `FASTLANE_KEY`. Copy the full text, including the "-----BEGIN PRIVATE KEY-----" and "-----END PRIVATE KEY-----" lines. -## Setup Github -1. Create a [new empty repository](https://github.com/new) titled `Match-Secrets`. It should be private. -1. Fork https://github.com/LoopKit/LoopWorkspace into your account. +## Create GitHub Personal Access Token + +This step is common for all GitHub Browser Builds; do this step only once. This is the first of two GitHub secrets needed for your build. + 1. Create a [new personal access token](https://github.com/settings/tokens/new): - * Enter a name for your token. Something like "FastLane Access Token". - * 30 days is fine, or you can select longer if you'd like. - * Select the `repo` permission scope. + * Enter a name for your token, use "FastLane Access Token". + * Change the Expiration selection to `No expiration`. + * Select the `workflow` permission scope - this also selects `repo` scope. * Click "Generate token". * Copy the token and record it. It will be used below as `GH_PAT`. -1. In the forked LoopWorkspace repo, go to Settings -> Secrets -> Actions. + +## Make up a Password + +This step is common for all GitHub Browser Builds; do this step only once. This is the second of two GitHub secrets needed for your build. + +The first time you build with the GitHub Browser Build method for any DIY app, you will make up a password and record it as `MATCH_PASSWORD`. Note, if you later lose `MATCH_PASSWORD`, you will need to delete the Match-Secrets repository so that a new one can be created for you. + +## Setup GitHub LoopCaregiver Repository + +1. Fork https://github.com/LoopKit/LoopCaregiver into your account. +1. In the forked LoopCaregiver repo, go to Settings -> Secrets and variables -> Actions. 1. For each of the following secrets, tap on "New repository secret", then add the name of the secret, along with the value you recorded for it: * `TEAMID` - * `FASTLANE_KEY_ID` * `FASTLANE_ISSUER_ID` + * `FASTLANE_KEY_ID` * `FASTLANE_KEY` * `GH_PAT` - * `MATCH_PASSWORD` - just make up a password for this + * `MATCH_PASSWORD` -## Add Identifiers for Loop App +## Validate repository secrets -1. Click on the "Actions" tab of your LoopWorkspace repository. -1. Select "Add Identifiers". -1. Click "Run Workflow", and tap the green button. -1. Wait, and within a minute or two you should see a green checkmark indicating the workflow succeeded. - -## Create App Group - -If you have already built Loop via Xcode using this Apple ID, you can skip on to [Create Loop App in App Store Connect](#create-loop-app-in-app-store-connect). - -1. Go to [Register an App Group](https://developer.apple.com/account/resources/identifiers/applicationGroup/add/) on the apple developer site. -1. For Description, use "Loop App Group". -1. For Identifier, enter "group.com.TEAMID.loopkit.LoopGroup", subsituting your team id for `TEAMID`. -1. Click "Continue" and then "Register". - -## Add App Group to Bundle Identifiers +This step validates most of your six Secrets and provides error messages if it detects an issue with one or more. -1. Go to [Certificates, Identifiers & Profiles](https://developer.apple.com/account/resources/identifiers/list) on the apple developer site. -1. For each of the following identifier names: - * Loop - * Loop Intent Extension - * Loop Status Extension - * Small Status Widget -1. Click on the identifier's name. -1. On the "App Groups" capabilies, click on the "Configure" button. -1. Select the "Loop App Group" -1. Click "Continue". -1. Click "Save". -1. Click "Confirm". -1. Remember to do this for each of the identifiers above. +1. Click on the "Actions" tab of your LoopCaregiver repository and enable workflows if needed +1. On the left side, select "1. Validate Secrets". +1. On the right side, click "Run Workflow", and tap the green `Run workflow` button. +1. Wait, and within a minute or two you should see a green checkmark indicating the workflow succeeded. +1. The workflow will check if the required secrets are added and that they are correctly formatted. If errors are detected, please check the run log for details. -## Add Time Sensitive Notifications to Loop App ID -1. Go to [Certificates, Identifiers & Profiles](https://developer.apple.com/account/resources/identifiers/list) on the apple developer site. -1. Click on the "Loop" identifier -1. Scroll down to "Time Sensitive Notifications" -1. Tap the check box to enable Time Sensitive Notifications. -1. Click "Save". -1. Click "Confirm". +## Add Identifiers for LoopCaregiver App -## Create Loop App in App Store Connect +1. Click on the "Actions" tab of your LoopCaregiver repository. +1. On the left side, select "2. Add Identifiers". +1. On the right side, click "Run Workflow", and tap the green `Run workflow` button. +1. Wait, and within a minute or two you should see a green checkmark indicating the workflow succeeded. -If you have created a Loop app in App Store Connect before, you can skip this section as well. +#### Table with Name and Identifiers for LoopCaregiver -1. Go to the [apps list](https://appstoreconnect.apple.com/apps) on App Store Connect and click the blue "plus" icon to create a New App. - * Select "iOS". - * Select a name: this will have to be unique, so you may have to try a few different names here, but it will not be the name you see on your phone, so it's not that important. - * Select your primary language. - * Choose the bundle ID that matches `com.TEAMID.loopkit.Loop`, with TEAMID matching your team id. - * SKU can be anything; e.g. "123". - * Select "Full Access". -1. Click Create +| NAME | IDENTIFIER | +|-------|------------| +| LoopCaregiver | com.TEAMID.loopkit.LoopCaregiver | -You do not need to fill out the next form. That is for submitting to the app store. -## Create LoopCaregiver App in App Store Connect (Optional) +## Create LoopCaregiver App in App Store Connect -If you have created a LoopCaregiver app in App Store Connect before with a bundle ID that matches `com.TEAMID.loopkit.LoopCaregiver`, with TEAMID matching your team id, you can skip this section as well. +If you have created a LoopCaregiver app in App Store Connect before, you can skip this section. 1. Go to the [apps list](https://appstoreconnect.apple.com/apps) on App Store Connect and click the blue "plus" icon to create a New App. * Select "iOS". @@ -108,31 +113,24 @@ If you have created a LoopCaregiver app in App Store Connect before with a bundl You do not need to fill out the next form. That is for submitting to the app store. -## Create Building Certficates +## Create Building Certificates -1. Go back to the "Actions" tab of your LoopWorkspace repository in github. -1. Select "Create Certificates". -1. Click "Run Workflow", and tap the green button. +1. Go back to the "Actions" tab of your LoopCaregiver repository in GitHub. +1. On the left side, select "3. Create Certificates". +1. On the right side, click "Run Workflow", and tap the green `Run workflow` button. 1. Wait, and within a minute or two you should see a green checkmark indicating the workflow succeeded. -## Build Loop! +## Build LoopCaregiver -1. Click on the "Actions" tab of your LoopWorkspace repository. -1. Select "Build Loop". -1. Click "Run Workflow", select your branch, and tap the green button. -1. You have some time now. Go enjoy a coffee. The build should take about 20-30 minutes. +1. Click on the "Actions" tab of your LoopCaregiver repository. +1. On the left side, select "4. Build LoopCaregiver". +1. On the right side, click "Run Workflow", and tap the green `Run workflow` button. +1. You have some time now. Go enjoy a coffee. The build should take about 10-20 minutes. 1. Your app should eventually appear on [App Store Connect](https://appstoreconnect.apple.com/apps). -1. For each phone/person you would like to support Loop on: +1. For each phone/person you would like to support LoopCaregiver on: * Add them in [Users and Access](https://appstoreconnect.apple.com/access/users) on App Store Connect. * Add them to your TestFlight Internal Testing group. -## Build LoopCaregiver (Optional) +## TestFlight and Deployment Details -1. Click on the "Actions" tab of your LoopWorkspace repository. -1. Select "Build LoopCaregiver". -1. Click "Run Workflow", select your branch, and tap the green button. -1. You have some time now. Go enjoy a coffee. The build should take about 10-15 minutes. -1. Your app should eventually appear on [App Store Connect](https://appstoreconnect.apple.com/apps). -1. For each phone/person you would like to support Loop on: - * Add them in [Users and Access](https://appstoreconnect.apple.com/access/users) on App Store Connect. - * Add them to your TestFlight Internal Testing group. +Please refer to [LoopDocs: Set Up Users](https://loopkit.github.io/loopdocs/gh-actions/gh-first-time/#set-up-users-and-access-testflight) and [LoopDocs: Deploy](https://loopkit.github.io/loopdocs/gh-actions/gh-deploy/)