From ab1c67e17c7d564a6b6020fae839d003a8b1102b Mon Sep 17 00:00:00 2001 From: marionbarker Date: Sat, 25 Jan 2025 14:54:21 -0800 Subject: [PATCH 1/7] update Browser Build features to match main branch --- .github/workflows/add_identifiers.yml | 11 +- .github/workflows/build_loop.yml | 325 ++++++++++++++----------- .github/workflows/create_certs.yml | 11 +- .github/workflows/validate_secrets.yml | 40 +-- fastlane/Fastfile | 1 - 5 files changed, 219 insertions(+), 169 deletions(-) diff --git a/.github/workflows/add_identifiers.yml b/.github/workflows/add_identifiers.yml index aab334ab6..8ff87a55f 100644 --- a/.github/workflows/add_identifiers.yml +++ b/.github/workflows/add_identifiers.yml @@ -24,8 +24,15 @@ jobs: # 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" - + run: | + TABLE_PRINTER_PATH=$(ruby -e 'puts Gem::Specification.find_by_name("fastlane").gem_dir')/match/lib/match/table_printer.rb + if [ -f "$TABLE_PRINTER_PATH" ]; then + sed -i "" "/puts(Terminal::Table.new(params))/d" "$TABLE_PRINTER_PATH" + else + echo "table_printer.rb not found" + exit 1 + fi + # Install project dependencies - name: Install Project Dependencies run: bundle install diff --git a/.github/workflows/build_loop.yml b/.github/workflows/build_loop.yml index dc84dbee8..254c4015f 100644 --- a/.github/workflows/build_loop.yml +++ b/.github/workflows/build_loop.yml @@ -2,26 +2,27 @@ name: 4. Build Loop run-name: Build Loop (${{ github.ref_name }}) on: workflow_dispatch: - + ## 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 UTC every Wednesday - - cron: '0 6 1 * *' # Builds the app on the 1st of every month at 06:00 UTC + - cron: "0 8 * * 3" # Checks for updates at 08:00 UTC every Wednesday + - cron: "0 6 1 * *" # Builds the app on the 1st of every month at 06:00 UTC env: UPSTREAM_REPO: LoopKit/LoopWorkspace UPSTREAM_BRANCH: ${{ github.ref_name }} # branch on upstream repository to sync from (replace with specific branch name if needed) TARGET_BRANCH: ${{ github.ref_name }} # target branch on fork to be kept in sync, and target branch on upstream to be kept alive (replace with specific branch name if needed) - ALIVE_BRANCH: alive + ALIVE_BRANCH_MAIN: alive-main + ALIVE_BRANCH_DEV: alive-dev jobs: 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: @@ -32,126 +33,155 @@ jobs: contents: write outputs: WORKFLOW_PERMISSION: ${{ steps.workflow-permission.outputs.has_permission }} - + steps: - - name: Check for workflow permissions - id: workflow-permission - env: - TOKEN_TO_CHECK: ${{ secrets.GH_PAT }} - run: | - PERMISSIONS=$(curl -sS -f -I -H "Authorization: token ${{ env.TOKEN_TO_CHECK }}" https://api.github.com | grep ^x-oauth-scopes: | cut -d' ' -f2-); - - if [[ $PERMISSIONS =~ "workflow" || $PERMISSIONS == "" ]]; then - echo "GH_PAT holds workflow permissions or is fine-grained PAT." - echo "has_permission=true" >> $GITHUB_OUTPUT # Set WORKFLOW_PERMISSION to false. - else - echo "GH_PAT lacks workflow permissions." - 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: - GITHUB_TOKEN: ${{ secrets.GH_PAT }} - run: | - if [[ "$(gh api -H "Accept: application/vnd.github+json" /repos/${{ github.repository_owner }}/LoopWorkspace/branches | jq --raw-output 'any(.name=="alive")')" == "true" ]]; then - echo "Branch 'alive' exists." - echo "ALIVE_BRANCH_EXISTS=true" >> $GITHUB_ENV # Set ALIVE_BRANCH_EXISTS to true - else - 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 == 'false' - env: - GITHUB_TOKEN: ${{ secrets.GH_PAT }} - run: | - # Get ref for LoopKit/LoopWorkspace:dev - SHA=$(curl -sS https://api.github.com/repos/${{ env.UPSTREAM_REPO }}/git/refs \ - | jq '.[] | select(.ref == "refs/heads/dev" ) | .object.sha' \ - | tr -d '"' - ); - - # Create alive branch based on LoopKit/LoopWorkspace:dev - gh api \ - --method POST \ - -H "Authorization: token $GITHUB_TOKEN" \ - -H "Accept: application/vnd.github.v3+json" \ - /repos/${{ github.repository_owner }}/LoopWorkspace/git/refs \ - -f ref='refs/heads/alive' \ - -f sha=$SHA - + - name: Check for workflow permissions + id: workflow-permission + env: + TOKEN_TO_CHECK: ${{ secrets.GH_PAT }} + run: | + PERMISSIONS=$(curl -sS -f -I -H "Authorization: token ${{ env.TOKEN_TO_CHECK }}" https://api.github.com | grep ^x-oauth-scopes: | cut -d' ' -f2-); + + if [[ $PERMISSIONS =~ "workflow" || $PERMISSIONS == "" ]]; then + echo "GH_PAT holds workflow permissions or is fine-grained PAT." + echo "has_permission=true" >> $GITHUB_OUTPUT # Set WORKFLOW_PERMISSION to false. + else + echo "GH_PAT lacks workflow permissions." + echo "Automated build features will be skipped!" + echo "has_permission=false" >> $GITHUB_OUTPUT # Set WORKFLOW_PERMISSION to false. + fi + + - name: Check for alive branches + if: steps.workflow-permission.outputs.has_permission == 'true' + env: + GITHUB_TOKEN: ${{ secrets.GH_PAT }} + run: | + if [[ $(gh api -H "Accept: application/vnd.github+json" /repos/${{ github.repository_owner }}/LoopWorkspace/branches | jq --raw-output '[.[] | select(.name == "alive-main" or .name == "alive-dev")] | length > 0') == "true" ]]; then + echo "Branches 'alive-main' or 'alive-dev' exist." + echo "ALIVE_BRANCH_EXISTS=true" >> $GITHUB_ENV + else + echo "Branches 'alive-main' and 'alive-dev' do not exist." + echo "ALIVE_BRANCH_EXISTS=false" >> $GITHUB_ENV + fi + + - name: Create alive branches + if: env.ALIVE_BRANCH_EXISTS == 'false' + env: + GITHUB_TOKEN: ${{ secrets.GH_PAT }} + run: | + # Get ref for LoopKit/LoopWorkspace:main + SHA_MAIN=$(curl -sS -H "Authorization: token $GITHUB_TOKEN" https://api.github.com/repos/${{ env.UPSTREAM_REPO }}/git/refs/heads/main | jq -r '.object.sha') + + # Get ref for LoopKit/LoopWorkspace:dev + SHA_DEV=$(curl -sS -H "Authorization: token $GITHUB_TOKEN" https://api.github.com/repos/${{ env.UPSTREAM_REPO }}/git/refs/heads/dev | jq -r '.object.sha') + + # Create alive-main branch based on LoopKit/LoopWorkspace:main + gh api \ + --method POST \ + -H "Authorization: token $GITHUB_TOKEN" \ + -H "Accept: application/vnd.github.v3+json" \ + /repos/${{ github.repository_owner }}/LoopWorkspace/git/refs \ + -f ref='refs/heads/alive-main' \ + -f sha=$SHA_MAIN + + # Create alive-dev branch based on LoopKit/LoopWorkspace:dev + gh api \ + --method POST \ + -H "Authorization: token $GITHUB_TOKEN" \ + -H "Accept: application/vnd.github.v3+json" \ + /repos/${{ github.repository_owner }}/LoopWorkspace/git/refs \ + -f ref='refs/heads/alive-dev' \ + -f sha=$SHA_DEV + # Checks for changes in upstream repository; if changes exist prompts sync for build # Performs keepalive to avoid stale fork check_latest_from_upstream: needs: [validate, check_alive_and_permissions] runs-on: ubuntu-latest name: Check upstream and keep alive - outputs: + outputs: NEW_COMMITS: ${{ steps.sync.outputs.has_new_commits }} - + ABORT_SYNC: ${{ steps.check_branch.outputs.ABORT_SYNC }} + steps: - - name: Checkout target repo - if: | - needs.check_alive_and_permissions.outputs.WORKFLOW_PERMISSION == 'true' && - (vars.SCHEDULED_BUILD != 'false' || vars.SCHEDULED_SYNC != 'false') - uses: actions/checkout@v4 - 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' && - vars.SCHEDULED_SYNC != 'false' && github.repository_owner != 'LoopKit' - id: sync - uses: aormsby/Fork-Sync-With-Upstream-action@v3.4.1 - with: - target_sync_branch: ${{ env.ALIVE_BRANCH }} - shallow_since: 6 months ago - 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: | - needs.check_alive_and_permissions.outputs.WORKFLOW_PERMISSION == 'true' && - vars.SCHEDULED_SYNC != 'false' && steps.sync.outputs.has_new_commits == 'true' - run: echo "New commits were found to sync." - - - name: No new commits - if: | - 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: | - echo ${{ steps.sync.outputs.has_new_commits }} - echo "NEW_COMMITS=${{ steps.sync.outputs.has_new_commits }}" >> $GITHUB_OUTPUT - - # Keep repository "alive": add empty commits to ALIVE_BRANCH after "time_elapsed" days of inactivity to avoid inactivation of scheduled workflows - - name: Keep alive - if: | - needs.check_alive_and_permissions.outputs.WORKFLOW_PERMISSION == 'true' && - (vars.SCHEDULED_BUILD != 'false' || vars.SCHEDULED_SYNC != 'false') - 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: | - echo "### :calendar: Scheduled Sync and Build Disabled :mobile_phone_off:" >> $GITHUB_STEP_SUMMARY - echo "You have not yet configured the scheduled sync and build for Loop's browser build." >> $GITHUB_STEP_SUMMARY - echo "Synchronizing your fork of LoopWorkspace with the upstream repository LoopKit/LoopWorkspace will be skipped." >> $GITHUB_STEP_SUMMARY - echo "If you want to enable automatic builds and updates for your Loop, please follow the instructions \ - under the following path LoopWorkspace/fastlane/testflight.md." >> $GITHUB_STEP_SUMMARY - + - name: Check if running on main or dev branch + if: | + needs.check_alive_and_permissions.outputs.WORKFLOW_PERMISSION == 'true' && + (vars.SCHEDULED_BUILD != 'false' || vars.SCHEDULED_SYNC != 'false') + id: check_branch + run: | + if [ "${GITHUB_REF##*/}" = "main" ]; then + echo "Running on main branch" + echo "ALIVE_BRANCH=${ALIVE_BRANCH_MAIN}" >> $GITHUB_OUTPUT + echo "ABORT_SYNC=false" >> $GITHUB_OUTPUT + elif [ "${GITHUB_REF##*/}" = "dev" ]; then + echo "Running on dev branch" + echo "ALIVE_BRANCH=${ALIVE_BRANCH_DEV}" >> $GITHUB_OUTPUT + echo "ABORT_SYNC=false" >> $GITHUB_OUTPUT + else + echo "Not running on main or dev branch" + echo "ABORT_SYNC=true" >> $GITHUB_OUTPUT + fi + + - name: Checkout target repo + if: | + needs.check_alive_and_permissions.outputs.WORKFLOW_PERMISSION == 'true' && + (vars.SCHEDULED_BUILD != 'false' || vars.SCHEDULED_SYNC != 'false') + uses: actions/checkout@v4 + with: + token: ${{ secrets.GH_PAT }} + ref: ${{ steps.check_branch.outputs.ALIVE_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' && + vars.SCHEDULED_SYNC != 'false' && github.repository_owner != 'LoopKit' && steps.check_branch.outputs.ABORT_SYNC == 'false' + id: sync + uses: aormsby/Fork-Sync-With-Upstream-action@v3.4.1 + with: + target_sync_branch: ${{ steps.check_branch.outputs.ALIVE_BRANCH }} + shallow_since: 6 months ago + 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: | + needs.check_alive_and_permissions.outputs.WORKFLOW_PERMISSION == 'true' && + vars.SCHEDULED_SYNC != 'false' && steps.sync.outputs.has_new_commits == 'true' + run: echo "New commits were found to sync." + + - name: No new commits + if: | + 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' && steps.check_branch.outputs.ABORT_SYNC == 'false' + run: | + echo ${{ steps.sync.outputs.has_new_commits }} + echo "NEW_COMMITS=${{ steps.sync.outputs.has_new_commits }}" >> $GITHUB_OUTPUT + + # Keep repository "alive": add empty commits to ALIVE_BRANCH after "time_elapsed" days of inactivity to avoid inactivation of scheduled workflows + - name: Keep alive + if: | + needs.check_alive_and_permissions.outputs.WORKFLOW_PERMISSION == 'true' && + (vars.SCHEDULED_BUILD != 'false' || vars.SCHEDULED_SYNC != 'false') + 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: | + echo "### :calendar: Scheduled Sync and Build Disabled :mobile_phone_off:" >> $GITHUB_STEP_SUMMARY + echo "You have not yet configured the scheduled sync and build for Loop's browser build." >> $GITHUB_STEP_SUMMARY + echo "Synchronizing your fork of LoopWorkspace with the upstream repository LoopKit/LoopWorkspace will be skipped." >> $GITHUB_STEP_SUMMARY + echo "If you want to enable automatic builds and updates for your Loop, please follow the instructions \ + under the following path LoopWorkspace/fastlane/testflight.md." >> $GITHUB_STEP_SUMMARY + # Builds Loop build: name: Build @@ -159,16 +189,17 @@ jobs: runs-on: macos-14 permissions: contents: write - if: | # runs if started manually, or if sync schedule is set and enabled and scheduled on the first Saturday each month, or if sync schedule is set and enabled and new commits were found - github.event_name == 'workflow_dispatch' || - (needs.check_alive_and_permissions.outputs.WORKFLOW_PERMISSION == 'true' && - (vars.SCHEDULED_BUILD != 'false' && github.event.schedule == '0 6 1 * *') || - (vars.SCHEDULED_SYNC != 'false' && needs.check_latest_from_upstream.outputs.NEW_COMMITS == 'true' ) - ) + if: + | # runs if started manually, or if sync schedule is set and enabled and scheduled on the first Saturday each month, or if sync schedule is set and enabled and new commits were found + github.event_name == 'workflow_dispatch' || + (needs.check_alive_and_permissions.outputs.WORKFLOW_PERMISSION == 'true' && + (vars.SCHEDULED_BUILD != 'false' && github.event.schedule == '0 6 1 * *') || + (vars.SCHEDULED_SYNC != 'false' && needs.check_latest_from_upstream.outputs.NEW_COMMITS == 'true' ) + ) steps: - name: Select Xcode version run: "sudo xcode-select --switch /Applications/Xcode_15.4.app/Contents/Developer" - + - name: Checkout Repo for syncing if: | needs.check_alive_and_permissions.outputs.WORKFLOW_PERMISSION == 'true' && @@ -176,12 +207,12 @@ jobs: uses: actions/checkout@v4 with: token: ${{ secrets.GH_PAT }} - ref: ${{ env.TARGET_BRANCH }} - + 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' && - vars.SCHEDULED_SYNC != 'false' && github.repository_owner != 'LoopKit' + vars.SCHEDULED_SYNC != 'false' && github.repository_owner != 'LoopKit' && needs.check_latest_from_upstream.outputs.ABORT_SYNC == 'false' id: sync uses: aormsby/Fork-Sync-With-Upstream-action@v3.4.1 with: @@ -190,24 +221,24 @@ 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: | needs.check_alive_and_permissions.outputs.WORKFLOW_PERMISSION == 'true' && - vars.SCHEDULED_SYNC != 'false' && steps.sync.outputs.has_new_commits == 'true' + vars.SCHEDULED_SYNC != 'false' && steps.sync.outputs.has_new_commits == 'true' && needs.check_latest_from_upstream.outputs.ABORT_SYNC == 'false' run: echo "New commits were found to sync." - + - name: No new commits if: | needs.check_alive_and_permissions.outputs.WORKFLOW_PERMISSION == 'true' && - vars.SCHEDULED_SYNC != 'false' && steps.sync.outputs.has_new_commits == 'false' + vars.SCHEDULED_SYNC != 'false' && steps.sync.outputs.has_new_commits == 'false' && needs.check_latest_from_upstream.outputs.ABORT_SYNC == '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' + && vars.SCHEDULED_SYNC != 'false' && needs.check_latest_from_upstream.outputs.ABORT_SYNC == 'false' run: | echo ${{ steps.sync.outputs.has_new_commits }} echo "NEW_COMMITS=${{ steps.sync.outputs.has_new_commits }}" >> $GITHUB_OUTPUT @@ -218,7 +249,7 @@ jobs: token: ${{ secrets.GH_PAT }} submodules: recursive ref: ${{ env.TARGET_BRANCH }} - + # Customize Loop: Download and apply patches - name: Customize Loop run: | @@ -233,24 +264,30 @@ jobs: # Template for customizing submodule Loop (changes Loop app name to "CustomLoop") # Remove the "#" sign from the beginning of the line below to activate: #curl https://github.com/loopnlearn/Loop/commit/d206432b024279ef710df462b20bd464cd9682d4.patch | git apply --directory=Loop -v --whitespace=fix - + # Submodule LoopKit patches: # General template for customizing submodule LoopKit # Copy url from a GitHub commit or pull request and insert below, and remove the "#" sign from the beginning of the line to activate: #curl url_to_github_commit.patch | git apply --directory=LoopKit -v --whitespace=fix - + # Submodule xxxxx patches: # Add patches for customization of additional submodules by following the templates above, # and make sure to specify the submodule by setting "--directory=(submodule_name)". # Several patches may be added per submodule. # Adding comments (#) may be useful to easily tell the individual patches apart. - - + # 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" - + run: | + TABLE_PRINTER_PATH=$(ruby -e 'puts Gem::Specification.find_by_name("fastlane").gem_dir')/match/lib/match/table_printer.rb + if [ -f "$TABLE_PRINTER_PATH" ]; then + sed -i "" "/puts(Terminal::Table.new(params))/d" "$TABLE_PRINTER_PATH" + else + echo "table_printer.rb not found" + exit 1 + fi + # Install project dependencies - name: Install Project Dependencies run: bundle install @@ -258,10 +295,10 @@ jobs: # Sync the GitHub runner clock with the Windows time server (workaround as suggested in https://github.com/actions/runner/issues/2996) - name: Sync clock run: sudo sntp -sS time.windows.com - + # Build signed Loop IPA file - name: Fastlane Build & Archive - run: bundle exec fastlane build_loop + run: bundle exec fastlane build_loop env: TEAMID: ${{ secrets.TEAMID }} GH_PAT: ${{ secrets.GH_PAT }} @@ -269,7 +306,7 @@ jobs: FASTLANE_ISSUER_ID: ${{ secrets.FASTLANE_ISSUER_ID }} FASTLANE_KEY: ${{ secrets.FASTLANE_KEY }} MATCH_PASSWORD: ${{ secrets.MATCH_PASSWORD }} - + # Upload to TestFlight - name: Fastlane upload to TestFlight run: bundle exec fastlane release @@ -289,4 +326,4 @@ jobs: name: build-artifacts path: | artifacts - buildlog + buildlog \ No newline at end of file diff --git a/.github/workflows/create_certs.yml b/.github/workflows/create_certs.yml index 90bb4f75b..9c4b51722 100644 --- a/.github/workflows/create_certs.yml +++ b/.github/workflows/create_certs.yml @@ -24,8 +24,15 @@ jobs: # 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" - + run: | + TABLE_PRINTER_PATH=$(ruby -e 'puts Gem::Specification.find_by_name("fastlane").gem_dir')/match/lib/match/table_printer.rb + if [ -f "$TABLE_PRINTER_PATH" ]; then + sed -i "" "/puts(Terminal::Table.new(params))/d" "$TABLE_PRINTER_PATH" + else + echo "table_printer.rb not found" + exit 1 + fi + # Install project dependencies - name: Install Project Dependencies run: bundle install diff --git a/.github/workflows/validate_secrets.yml b/.github/workflows/validate_secrets.yml index 5ad976a01..15562a740 100644 --- a/.github/workflows/validate_secrets.yml +++ b/.github/workflows/validate_secrets.yml @@ -16,14 +16,14 @@ jobs: 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 @@ -65,12 +65,12 @@ jobs: 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 @@ -81,10 +81,10 @@ jobs: - 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..." @@ -103,12 +103,12 @@ jobs: 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] @@ -124,18 +124,18 @@ jobs: steps: - name: Checkout Repo uses: actions/checkout@v4 - + - name: Install Project Dependencies run: bundle install # Sync the GitHub runner clock with the Windows time server (workaround as suggested in https://github.com/actions/runner/issues/2996) - name: Sync clock run: sudo sntp -sS time.windows.com - + - name: Validate Fastlane Secrets run: | # Validate Fastlane Secrets - + # Validate TEAMID if [ -z "$TEAMID" ]; then failed=true @@ -147,20 +147,20 @@ jobs: 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." @@ -168,13 +168,13 @@ jobs: [ -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." + 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." 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." + 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." 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." + 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." 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." @@ -190,7 +190,7 @@ jobs: 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 diff --git a/fastlane/Fastfile b/fastlane/Fastfile index a8cb9278e..f918b3aa4 100644 --- a/fastlane/Fastfile +++ b/fastlane/Fastfile @@ -156,7 +156,6 @@ platform :ios do skip_submission: false, ipa: "Loop.ipa", skip_waiting_for_build_processing: true, - changelog: git_branch+" "+last_git_commit[:abbreviated_commit_hash], ) end From 72b08874981a3a69dce58591081601fefd152c01 Mon Sep 17 00:00:00 2001 From: marionbarker Date: Sat, 25 Jan 2025 17:00:17 -0800 Subject: [PATCH 2/7] update Loop: restore TestFlight expiration date --- Loop | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Loop b/Loop index 1aaee2db4..ae76f9095 160000 --- a/Loop +++ b/Loop @@ -1 +1 @@ -Subproject commit 1aaee2db4953dbcdbe71a15cad967dbb190dc238 +Subproject commit ae76f90955843df0e1e4297f6c7c669d21e5eae2 From 0a2104b0a90adfc612e9b97b7ac618e8a9192f44 Mon Sep 17 00:00:00 2001 From: marionbarker Date: Sat, 25 Jan 2025 15:00:59 -0800 Subject: [PATCH 3/7] update LibreTransmitter: add new libre 2 plus EU sensors --- LibreTransmitter | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/LibreTransmitter b/LibreTransmitter index c01eba63e..f6c2d0f55 160000 --- a/LibreTransmitter +++ b/LibreTransmitter @@ -1 +1 @@ -Subproject commit c01eba63e94e9f6f2841a8835680c4e39c61b18d +Subproject commit f6c2d0f552e07aa778ec639d3faca571eaa0a97b From af935829d77c2f7267cf8b937e9140308419bde1 Mon Sep 17 00:00:00 2001 From: marionbarker Date: Sat, 25 Jan 2025 15:03:30 -0800 Subject: [PATCH 4/7] update OmniXXX: pod improvements, protect against 0x31 --- OmniBLE | 2 +- OmniKit | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/OmniBLE b/OmniBLE index b82d2e1ff..1fa287441 160000 --- a/OmniBLE +++ b/OmniBLE @@ -1 +1 @@ -Subproject commit b82d2e1fff5f0fccc42145fcbbe4731b80898fff +Subproject commit 1fa2874419225c8c7af0d9afbd9faf823cda34e5 diff --git a/OmniKit b/OmniKit index 3a75484ba..48a35efa5 160000 --- a/OmniKit +++ b/OmniKit @@ -1 +1 @@ -Subproject commit 3a75484ba062f647e9c5ff0291948859c77ba9b2 +Subproject commit 48a35efa52f42e0b72fe2e984f60d4482a11a75f From 01d91a438d14b784ddaf43a88d3df6095ae2750d Mon Sep 17 00:00:00 2001 From: marionbarker Date: Sat, 25 Jan 2025 15:04:46 -0800 Subject: [PATCH 5/7] update LoopKit: improve visibility for options to select --- LoopKit | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/LoopKit b/LoopKit index 3ce43ded2..a03be5768 160000 --- a/LoopKit +++ b/LoopKit @@ -1 +1 @@ -Subproject commit 3ce43ded238a776f7c8a8a9bea339db47adc0c20 +Subproject commit a03be5768e8d9cec87d93f0af9e72c97b7200b9a From 7e4fba15b257ed2737e3363b9ce5c4358ed896ec Mon Sep 17 00:00:00 2001 From: marionbarker Date: Sat, 25 Jan 2025 15:06:23 -0800 Subject: [PATCH 6/7] update to Fastlane 2.225.0 --- Gemfile.lock | 38 +++++++++++++++++++++----------------- 1 file changed, 21 insertions(+), 17 deletions(-) diff --git a/Gemfile.lock b/Gemfile.lock index 8caeede21..6196cbe67 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -10,20 +10,20 @@ GEM artifactory (3.0.17) atomos (0.1.3) aws-eventstream (1.3.0) - aws-partitions (1.981.0) - aws-sdk-core (3.209.1) + aws-partitions (1.1007.0) + aws-sdk-core (3.213.0) aws-eventstream (~> 1, >= 1.3.0) - aws-partitions (~> 1, >= 1.651.0) + aws-partitions (~> 1, >= 1.992.0) aws-sigv4 (~> 1.9) jmespath (~> 1, >= 1.6.1) - aws-sdk-kms (1.94.0) - aws-sdk-core (~> 3, >= 3.207.0) + aws-sdk-kms (1.95.0) + aws-sdk-core (~> 3, >= 3.210.0) aws-sigv4 (~> 1.5) - aws-sdk-s3 (1.166.0) - aws-sdk-core (~> 3, >= 3.207.0) + aws-sdk-s3 (1.171.0) + aws-sdk-core (~> 3, >= 3.210.0) aws-sdk-kms (~> 1) aws-sigv4 (~> 1.5) - aws-sigv4 (1.10.0) + aws-sigv4 (1.10.1) aws-eventstream (~> 1, >= 1.0.2) babosa (1.0.4) base64 (0.2.0) @@ -69,7 +69,7 @@ GEM faraday_middleware (1.2.1) faraday (~> 1.0) fastimage (2.3.1) - fastlane (2.223.1) + fastlane (2.225.0) CFPropertyList (>= 2.3, < 4.0.0) addressable (>= 2.8, < 3.0.0) artifactory (~> 3.0) @@ -85,6 +85,7 @@ GEM faraday-cookie_jar (~> 0.0.6) faraday_middleware (~> 1.0) fastimage (>= 2.1.0, < 3.0.0) + fastlane-sirp (>= 1.0.0) gh_inspector (>= 1.1.2, < 2.0.0) google-apis-androidpublisher_v3 (~> 0.3) google-apis-playcustomapp_v1 (~> 0.1) @@ -110,6 +111,8 @@ GEM xcodeproj (>= 1.13.0, < 2.0.0) xcpretty (~> 0.3.0) xcpretty-travis-formatter (>= 0.0.3, < 2.0.0) + fastlane-sirp (1.0.0) + sysrandom (~> 1.0) gh_inspector (1.1.3) google-apis-androidpublisher_v3 (0.54.0) google-apis-core (>= 0.11.0, < 2.a) @@ -152,17 +155,17 @@ GEM domain_name (~> 0.5) httpclient (2.8.3) jmespath (1.6.2) - json (2.7.2) - jwt (2.9.1) + json (2.7.6) + jwt (2.9.3) base64 mini_magick (4.13.2) mini_mime (1.1.5) multi_json (1.15.0) multipart-post (2.4.1) - nanaimo (0.3.0) + nanaimo (0.4.0) naturally (2.2.1) nkf (0.2.0) - optparse (0.5.0) + optparse (0.6.0) os (1.1.4) plist (3.7.1) public_suffix (5.1.1) @@ -172,7 +175,7 @@ GEM trailblazer-option (>= 0.1.1, < 0.2.0) uber (< 0.2.0) retriable (3.1.2) - rexml (3.3.7) + rexml (3.3.9) rouge (2.0.7) ruby2_keywords (0.0.5) rubyzip (2.3.2) @@ -185,6 +188,7 @@ GEM simctl (1.6.10) CFPropertyList naturally + sysrandom (1.0.5) terminal-notifier (2.0.0) terminal-table (3.0.2) unicode-display_width (>= 1.1.1, < 3) @@ -197,13 +201,13 @@ GEM unf (0.2.0) unicode-display_width (2.6.0) word_wrap (1.0.0) - xcodeproj (1.25.0) + xcodeproj (1.27.0) CFPropertyList (>= 2.3.3, < 4.0) atomos (~> 0.1.3) claide (>= 1.0.2, < 2.0) colored2 (~> 3.1) - nanaimo (~> 0.3.0) - rexml (>= 3.3.2, < 4.0) + nanaimo (~> 0.4.0) + rexml (>= 3.3.6, < 4.0) xcpretty (0.3.0) rouge (~> 2.0.7) xcpretty-travis-formatter (1.0.1) From 967d5eccdceee660dec7c7120727b9a4a261b7f4 Mon Sep 17 00:00:00 2001 From: marionbarker Date: Sat, 25 Jan 2025 16:45:05 -0800 Subject: [PATCH 7/7] update using Trio (PR 453) to automate new certificates, credit @bjornoleh --- .github/workflows/build_loop.yml | 14 ++-- .github/workflows/create_certs.yml | 105 ++++++++++++++++++++----- .github/workflows/validate_secrets.yml | 9 +-- fastlane/Fastfile | 57 +++++++++++++- fastlane/testflight.md | 49 ++++++++---- 5 files changed, 186 insertions(+), 48 deletions(-) diff --git a/.github/workflows/build_loop.yml b/.github/workflows/build_loop.yml index 254c4015f..137b367fc 100644 --- a/.github/workflows/build_loop.yml +++ b/.github/workflows/build_loop.yml @@ -18,15 +18,17 @@ env: ALIVE_BRANCH_DEV: alive-dev jobs: - validate: - name: Validate - uses: ./.github/workflows/validate_secrets.yml + # Checks if Distribution certificate is present and valid, optionally nukes and + # creates new certs if the repository variable ENABLE_NUKE_CERTS == 'true' + check_certs: + name: Check certificates + uses: ./.github/workflows/create_certs.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: validate + needs: check_certs runs-on: ubuntu-latest name: Check alive branch and permissions permissions: @@ -96,7 +98,7 @@ jobs: # Checks for changes in upstream repository; if changes exist prompts sync for build # Performs keepalive to avoid stale fork check_latest_from_upstream: - needs: [validate, check_alive_and_permissions] + needs: [check_certs, check_alive_and_permissions] runs-on: ubuntu-latest name: Check upstream and keep alive outputs: @@ -185,7 +187,7 @@ jobs: # Builds Loop build: name: Build - needs: [validate, check_alive_and_permissions, check_latest_from_upstream] + needs: [check_certs, check_alive_and_permissions, check_latest_from_upstream] runs-on: macos-14 permissions: contents: write diff --git a/.github/workflows/create_certs.yml b/.github/workflows/create_certs.yml index 9c4b51722..025c0cb27 100644 --- a/.github/workflows/create_certs.yml +++ b/.github/workflows/create_certs.yml @@ -1,23 +1,32 @@ name: 3. Create Certificates run-name: Create Certificates (${{ github.ref_name }}) -on: - workflow_dispatch: + +on: [workflow_call, workflow_dispatch] + +env: + TEAMID: ${{ secrets.TEAMID }} + GH_PAT: ${{ secrets.GH_PAT }} + GH_TOKEN: ${{ secrets.GH_PAT }} + MATCH_PASSWORD: ${{ secrets.MATCH_PASSWORD }} + FASTLANE_KEY_ID: ${{ secrets.FASTLANE_KEY_ID }} + FASTLANE_ISSUER_ID: ${{ secrets.FASTLANE_ISSUER_ID }} + FASTLANE_KEY: ${{ secrets.FASTLANE_KEY }} jobs: validate: name: Validate uses: ./.github/workflows/validate_secrets.yml secrets: inherit - - certificates: - name: Create Certificates + + + create_certs: + name: Certificates needs: validate - runs-on: macos-14 + runs-on: macos-15 + outputs: + new_certificate_needed: ${{ steps.set_output.outputs.new_certificate_needed }} + steps: - # Uncomment to manually select latest Xcode if needed - #- name: Select Latest Xcode - # run: "sudo xcode-select --switch /Applications/Xcode_13.0.app/Contents/Developer" - # Checks-out the repo - name: Checkout Repo uses: actions/checkout@v4 @@ -41,13 +50,69 @@ jobs: - name: Sync clock run: sudo sntp -sS time.windows.com - # Create or update certificates for app - - name: Create Certificates - run: bundle exec fastlane certs - env: - TEAMID: ${{ secrets.TEAMID }} - GH_PAT: ${{ secrets.GH_PAT }} - MATCH_PASSWORD: ${{ secrets.MATCH_PASSWORD }} - FASTLANE_KEY_ID: ${{ secrets.FASTLANE_KEY_ID }} - FASTLANE_ISSUER_ID: ${{ secrets.FASTLANE_ISSUER_ID }} - FASTLANE_KEY: ${{ secrets.FASTLANE_KEY }} + # Create or update Distribution certificate and provisioning profiles + - name: Check and create or update Distribution certificate and profiles if needed + run: | + echo "Running Fastlane certs lane..." + bundle exec fastlane certs || true # ignore and continue on errors without annotating an exit code + - name: Check Distribution certificate and launch Nuke certificates if needed + run: bundle exec fastlane check_and_renew_certificates + id: check_certs + + - name: Set output and annotations based on Fastlane result + id: set_output + run: | + CERT_STATUS_FILE="${{ github.workspace }}/fastlane/new_certificate_needed.txt" + ENABLE_NUKE_CERTS=${{ vars.ENABLE_NUKE_CERTS }} + + if [ -f "$CERT_STATUS_FILE" ]; then + CERT_STATUS=$(cat "$CERT_STATUS_FILE" | tr -d '\n' | tr -d '\r') # Read file content and strip newlines + echo "new_certificate_needed: $CERT_STATUS" + echo "new_certificate_needed=$CERT_STATUS" >> $GITHUB_OUTPUT + else + echo "Certificate status file not found. Defaulting to false." + echo "new_certificate_needed=false" >> $GITHUB_OUTPUT + fi + # Check if ENABLE_NUKE_CERTS is not set to true when certs are valid + if [ "$CERT_STATUS" != "true" ] && [ "$ENABLE_NUKE_CERTS" != "true" ]; then + echo "::notice::🔔 Automated renewal of certificates is disabled because the repository variable ENABLE_NUKE_CERTS is not set to 'true'." + fi + # Check if ENABLE_NUKE_CERTS is not set to true when certs are not valid + if [ "$CERT_STATUS" = "true" ] && [ "$ENABLE_NUKE_CERTS" != "true" ]; then + 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'." + exit 1 + fi + # Check if vars.FORCE_NUKE_CERTS is not set to true + if [ vars.FORCE_NUKE_CERTS = "true" ]; then + echo "::warning::‼️ Nuking of certificates was forced because the repository variable FORCE_NUKE_CERTS is set to 'true'." + fi + # 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 + nuke_certs: + name: Nuke certificates + needs: [validate, create_certs] + runs-on: macos-14 + if: ${{ (needs.create_certs.outputs.new_certificate_needed == 'true' && vars.ENABLE_NUKE_CERTS == 'true') || vars.FORCE_NUKE_CERTS == 'true' }} + steps: + - name: Output from step id 'check_certs' + run: echo "new_certificate_needed=${{ needs.create_certs.outputs.new_certificate_needed }}" + + - name: Checkout repository + uses: actions/checkout@v4 + + - name: Install dependencies + run: bundle install + + - name: Run Fastlane nuke_certs + run: | + set -e # Set error immediately after this step if error occurs + bundle exec fastlane nuke_certs + - name: Recreate Distribution certificate after nuking + run: | + set -e # Set error immediately after this step if error occurs + bundle exec fastlane certs + - name: Add success annotations for nuke and certificate recreation + if: ${{ success() }} + run: | + echo "::warning::⚠️ All Distribution certificates and TestFlight profiles have been revoked and recreated." + 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." + echo "::warning::✅ But don't worry about your existing TestFlight builds, they will keep working!" diff --git a/.github/workflows/validate_secrets.yml b/.github/workflows/validate_secrets.yml index 15562a740..dbbec0f5e 100644 --- a/.github/workflows/validate_secrets.yml +++ b/.github/workflows/validate_secrets.yml @@ -178,16 +178,15 @@ jobs: 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 ! bundle exec fastlane validate_secrets 2>&1 | tee fastlane.log; then + 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 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." + 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." + elif grep -q "Your certificate .* is not valid" fastlane.log; then + echo "::notice::Your Distribution certificate is invalid or expired. Automated renewal of the certificate will be attempted." fi fi diff --git a/fastlane/Fastfile b/fastlane/Fastfile index f918b3aa4..e71a3ee16 100644 --- a/fastlane/Fastfile +++ b/fastlane/Fastfile @@ -220,7 +220,8 @@ platform :ios do match( type: "appstore", - force: true, + force: false, + verbose: true, git_basic_authorization: Base64.strict_encode64("#{GITHUB_REPOSITORY_OWNER}:#{GH_PAT}"), app_identifier: [ "com.#{TEAMID}.loopkit.Loop", @@ -276,4 +277,56 @@ platform :ios do git_basic_authorization: Base64.strict_encode64("#{GITHUB_REPOSITORY_OWNER}:#{GH_PAT}") ) end -end + + desc "Check Certificates and Trigger Workflow for Expired or Missing Certificates" + lane :check_and_renew_certificates do + setup_ci if ENV['CI'] + ENV["MATCH_READONLY"] = false.to_s + + # Authenticate using App Store Connect API Key + api_key = app_store_connect_api_key( + key_id: ENV["FASTLANE_KEY_ID"], + issuer_id: ENV["FASTLANE_ISSUER_ID"], + key_content: ENV["FASTLANE_KEY"] # Ensure valid key content + ) + + # Initialize flag to track if renewal of certificates is needed + new_certificate_needed = false + + # Fetch all certificates + certificates = Spaceship::ConnectAPI::Certificate.all + + # Filter for Distribution Certificates + distribution_certs = certificates.select { |cert| cert.certificate_type == "DISTRIBUTION" } + + # Handle case where no distribution certificates are found + if distribution_certs.empty? + puts "No Distribution certificates found! Triggering action to create certificate." + new_certificate_needed = true + else + # Check for expiration + distribution_certs.each do |cert| + expiration_date = Time.parse(cert.expiration_date) + + puts "Current Distribution Certificate: #{cert.id}, Expiration date: #{expiration_date}" + + if expiration_date < Time.now + puts "Distribution Certificate #{cert.id} is expired! Triggering action to renew certificate." + new_certificate_needed = true + else + puts "Distribution certificate #{cert.id} is valid. No action required." + end + end + end + + # Write result to new_certificate_needed.txt + file_path = File.expand_path('new_certificate_needed.txt') + File.write(file_path, new_certificate_needed ? 'true' : 'false') + + # Log the absolute path and contents of the new_certificate_needed.txt file + puts "" + puts "Absolute path of new_certificate_needed.txt: #{file_path}" + new_certificate_needed_content = File.read(file_path) + puts "Certificate creation or renewal needed: #{new_certificate_needed_content}" + end +end \ No newline at end of file diff --git a/fastlane/testflight.md b/fastlane/testflight.md index abe99314e..c344e91dc 100644 --- a/fastlane/testflight.md +++ b/fastlane/testflight.md @@ -2,14 +2,14 @@ These instructions allow you to build Loop without having access to a Mac. -* You can install Loop on phones via TestFlight that are not connected to your computer +* You can install Loop on phones using TestFlight that are not connected to your computer * You can send builds and updates to those you care for * You can install Loop 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 ## **Automatic Builds** > -> This new version of the browser build **defaults to** automatically updating and building a new version of Loop according to this schedule: +> The browser build **defaults to** automatically updating and building a new version of Loop according to this schedule: > - automatically checks for updates weekly on Wednesdays and if updates are found, it will build a new version of the app > - automatically builds once a month regardless of whether there are updates on the first of the month > - with each scheduled run (weekly or monthly), a successful Build Loop log appears - if the time is very short, it did not need to build - only the long actions (>20 minutes) built a new Loop app @@ -33,6 +33,8 @@ There are more detailed instructions in LoopDocs for using GitHub for Browser Bu 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 Loop. More details on this can be found in [LoopDocs](https://loopkit.github.io/loopdocs/gh-actions/gh-deploy/#install-testflight-loop-for-child). +If you build multiple apps, it is strongly recommended that you configure a free *GitHub* organization and do all your building in the organization. This means you enter items one time for the organization (6 SECRETS required to build and 1 VARIABLE required to automatically update your certificates annually). Otherwise, those 6 SECRETS must be entered for every repository. Please refer to [LoopDocs: Use a *GitHub* Organization Account](https://loopkit.github.io/loopdocs/gh-actions/gh-other-apps/#use-a-github-organization-account). + ## 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. @@ -48,6 +50,8 @@ You require 6 Secrets (alphanumeric items) to use the GitHub build method and if * 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 +Refer to [LoopDocs: Make a Secrets Reference File](https://loopkit.github.io/loopdocs/gh-actions/gh-first-time/#make-a-secrets-reference-file) for a handy template to use when saving your Secrets. + ## 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. @@ -61,6 +65,8 @@ This step is common for all GitHub Browser Builds; do this step only once. You w ## Create GitHub Personal Access Token +If you have previously built another app using the "browser build" method, you use the same personal access token (`GH_PAT`), so skip this step. If you use a free GitHub organization to build, you still use the same personal access token. This is created using your personal GitHub username. + Log into your GitHub account to create a personal access token; this is one of two GitHub secrets needed for your build. 1. Create a [new personal access token](https://github.com/settings/tokens/new): @@ -76,25 +82,26 @@ This is the second one 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 and make a new Match-Secrets repository (next step). -## Setup GitHub Match-Secrets Repository - -The creation of the Match-Secrets repository is a common step for all GitHub Browser Builds; do this step only once. You must be logged into your GitHub account. +## GitHub Match-Secrets Repository -1. Create a [new empty repository](https://github.com/new) titled `Match-Secrets`. It should be private. - -Once created, you will not take any direct actions with this repository; it needs to be there for the GitHub to use as you progress through the steps. +A private Match-Secrets repository is automatically created under your GitHub username the first time you run a GitHub Action. Because it is a private repository - only you can see it. You will not take any direct actions with this repository; it needs to be there for GitHub to use as you progress through the steps. ## Setup GitHub LoopWorkspace Repository -1. Fork https://github.com/LoopKit/LoopWorkspace into your account. -1. In the forked LoopWorkspace 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: +1. Fork https://github.com/LoopKit/LoopWorkspace into your GitHub username (using your organization if you have one). If you already have a fork of Trio in that username, you should not make another one. Do not rename the repository. You can continue to work with your existing fork, or delete that from GitHub and then fork again. +1. If you are using an organization, do this step at the organization level, e.g., username-org. If you are not using an organization, do this step at the repository level, e.g., username/LoopWorkspace: + * Go to Settings -> Secrets and variables -> Actions and make sure the Secrets tab is open +1. For each of the following secrets, tap on "New organization secret" or "New repository secret", then add the name of the secret, along with the value you recorded for it: * `TEAMID` * `FASTLANE_ISSUER_ID` * `FASTLANE_KEY_ID` * `FASTLANE_KEY` * `GH_PAT` * `MATCH_PASSWORD` +1. If you are using an organization, do this step at the organization level, e.g., username-org. If you are not using an organization, do this step at the repository level, e.g., username/LoopWorkspace: + * Go to Settings -> Secrets and variables -> Actions and make sure the Variables tab is open +1. Tap on "Create new organization variable" or "Create new repository variable", then add the name below and enter the value true. Unlike secrets variables are visible and can be edited. + * `ENABLE_NUKE_CERTS` ## Validate repository secrets @@ -106,6 +113,8 @@ This step validates most of your six Secrets and provides error messages if it d 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. +There can be a delay after you start a workflow before the screen changes. Refresh your browser to see if it started. And if it seems to take a long time to finish - refresh your browser to see if it is done. + ## Add Identifiers for Loop App 1. Click on the "Actions" tab of your LoopWorkspace repository. @@ -171,10 +180,9 @@ You do not need to fill out the next form. That is for submitting to the app sto ## Create Building Certificates -1. Go back to the "Actions" tab of your LoopWorkspace 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. +This step is no longer required. The Build Loop function now takes care of this for you. It does not hurt to run it but is not needed. + +Once a year, you will get an email from Apple indicating your certificate will expire in 30 days. You can ignore that email. When it does expire, the next time an automatic or manual build happens, the expired certificate information will be removed (nuked) from your Match-Secrets repository and a new one created. This should happen without you needing to take any action. ## Build Loop @@ -267,3 +275,14 @@ Your build will run on the following conditions: - If you disable any automation (both variables set to `false`), no updates, keep-alive or building happens when Build Loop runs - If you disabled just scheduled synchronization (`SCHEDULED_SYNC` set to`false`), it will only run once a month, on the first of the month, no update will happen; keep-alive will run - If you disabled just scheduled build (`SCHEDULED_BUILD` set to`false`), it will run once weekly, every Wednesday, to check for changes; if there are changes, it will update and build; keep-alive will run + +## What if I build using more than one GitHub username + +This is not typical. But if you do use more than one GitHub username, follow these steps at the time of the annual certificate renewal. + +1. After the certificates were removed (nuked) from username1 Match-Secrets storage, you need to switch to username2 +1. Add the variable FORCE_NUKE_CERTS=true to the username2/Trio repository +1. Run the action Create Certificate (or Build, but Create is faster) +1. Immediately set FORCE_NUKE_CERTS=false or delete the variable + +Now certificates for username2 have been cleared out of Match-Secrets storage for username2. Building can proceed as usual for both username1 and username2.