From ab1c67e17c7d564a6b6020fae839d003a8b1102b Mon Sep 17 00:00:00 2001 From: marionbarker Date: Sat, 25 Jan 2025 14:54:21 -0800 Subject: [PATCH 01/11] 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 aab334ab6c..8ff87a55f5 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 dc84dbee88..254c4015fb 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 90bb4f75b0..9c4b517228 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 5ad976a01f..15562a7406 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 a8cb9278e9..f918b3aa41 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 02/11] update Loop: restore TestFlight expiration date --- Loop | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Loop b/Loop index 1aaee2db49..ae76f90955 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 03/11] 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 c01eba63e9..f6c2d0f552 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 04/11] 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 b82d2e1fff..1fa2874419 160000 --- a/OmniBLE +++ b/OmniBLE @@ -1 +1 @@ -Subproject commit b82d2e1fff5f0fccc42145fcbbe4731b80898fff +Subproject commit 1fa2874419225c8c7af0d9afbd9faf823cda34e5 diff --git a/OmniKit b/OmniKit index 3a75484ba0..48a35efa52 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 05/11] update LoopKit: improve visibility for options to select --- LoopKit | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/LoopKit b/LoopKit index 3ce43ded23..a03be5768e 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 06/11] 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 8caeede216..6196cbe67e 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 07/11] 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 254c4015fb..137b367fc6 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 9c4b517228..025c0cb277 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 15562a7406..dbbec0f5e5 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 f918b3aa41..e71a3ee16c 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 abe99314eb..c344e91dc5 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. From 77dc12ea9d73b03995aad572ffc42747ae1c74e3 Mon Sep 17 00:00:00 2001 From: marionbarker Date: Tue, 11 Feb 2025 19:57:33 -0800 Subject: [PATCH 08/11] modify time of day for automatic run, deconflict possible nuke cert from other OS apps --- .github/workflows/build_loop.yml | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/.github/workflows/build_loop.yml b/.github/workflows/build_loop.yml index 137b367fc6..c64d752b5f 100644 --- a/.github/workflows/build_loop.yml +++ b/.github/workflows/build_loop.yml @@ -6,9 +6,12 @@ on: ## Remove the "#" sign from the beginning of the line below to get automated builds on push (code changes in your repository) #push: + # Automated builds now include automatic certificate update - the nuke certs part of that process could + # affect other OS apps if run simultaneously. + # Each OS needs a time of day distinct from other apps, LoopWorkspace uses 9 every Wed and 7 every 1st of month 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 9 * * 3" # Checks for updates at 09:00 UTC every Wednesday + - cron: "0 7 1 * *" # Builds the app on the 1st of every month at 07:00 UTC env: UPSTREAM_REPO: LoopKit/LoopWorkspace @@ -195,7 +198,7 @@ jobs: | # 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_BUILD != 'false' && github.event.schedule == '0 7 1 * *') || (vars.SCHEDULED_SYNC != 'false' && needs.check_latest_from_upstream.outputs.NEW_COMMITS == 'true' ) ) steps: From 9f14aaceaa5c0c45dcc54ed508ec28bc98323566 Mon Sep 17 00:00:00 2001 From: marionbarker Date: Wed, 12 Feb 2025 14:23:17 -0800 Subject: [PATCH 09/11] update OmniXXX: fix podSuspend logic error, incorporate tryToValidateComms --- OmniBLE | 2 +- OmniKit | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/OmniBLE b/OmniBLE index 1fa2874419..6f65cbae4c 160000 --- a/OmniBLE +++ b/OmniBLE @@ -1 +1 @@ -Subproject commit 1fa2874419225c8c7af0d9afbd9faf823cda34e5 +Subproject commit 6f65cbae4c8089a892911e273204edfc4cc81e9d diff --git a/OmniKit b/OmniKit index 48a35efa52..39915b05fe 160000 --- a/OmniKit +++ b/OmniKit @@ -1 +1 @@ -Subproject commit 48a35efa52f42e0b72fe2e984f60d4482a11a75f +Subproject commit 39915b05fe46b5d73eca52e156dd7efd11193ee8 From c13c5ce258a2fe2516247f47c813c9e80dbaabec Mon Sep 17 00:00:00 2001 From: marionbarker Date: Mon, 3 Mar 2025 13:49:21 -0800 Subject: [PATCH 10/11] update actions to use macos 15 and Xcode_16.2 --- .github/workflows/add_identifiers.yml | 2 +- .github/workflows/build_loop.yml | 4 ++-- .github/workflows/create_certs.yml | 2 +- .github/workflows/validate_secrets.yml | 6 +++--- 4 files changed, 7 insertions(+), 7 deletions(-) diff --git a/.github/workflows/add_identifiers.yml b/.github/workflows/add_identifiers.yml index 8ff87a55f5..4628862995 100644 --- a/.github/workflows/add_identifiers.yml +++ b/.github/workflows/add_identifiers.yml @@ -12,7 +12,7 @@ jobs: identifiers: name: Add Identifiers needs: validate - runs-on: macos-14 + runs-on: macos-15 steps: # Uncomment to manually select latest Xcode if needed #- name: Select Latest Xcode diff --git a/.github/workflows/build_loop.yml b/.github/workflows/build_loop.yml index c64d752b5f..8648335a94 100644 --- a/.github/workflows/build_loop.yml +++ b/.github/workflows/build_loop.yml @@ -191,7 +191,7 @@ jobs: build: name: Build needs: [check_certs, check_alive_and_permissions, check_latest_from_upstream] - runs-on: macos-14 + runs-on: macos-15 permissions: contents: write if: @@ -203,7 +203,7 @@ jobs: ) steps: - name: Select Xcode version - run: "sudo xcode-select --switch /Applications/Xcode_15.4.app/Contents/Developer" + run: "sudo xcode-select --switch /Applications/Xcode_16.2.app/Contents/Developer" - name: Checkout Repo for syncing if: | diff --git a/.github/workflows/create_certs.yml b/.github/workflows/create_certs.yml index 025c0cb277..d20001b2ca 100644 --- a/.github/workflows/create_certs.yml +++ b/.github/workflows/create_certs.yml @@ -90,7 +90,7 @@ jobs: nuke_certs: name: Nuke certificates needs: [validate, create_certs] - runs-on: macos-14 + runs-on: macos-15 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' diff --git a/.github/workflows/validate_secrets.yml b/.github/workflows/validate_secrets.yml index dbbec0f5e5..7f747773ea 100644 --- a/.github/workflows/validate_secrets.yml +++ b/.github/workflows/validate_secrets.yml @@ -5,7 +5,7 @@ on: [workflow_call, workflow_dispatch] jobs: validate-access-token: name: Access - runs-on: macos-14 + runs-on: macos-15 env: GH_PAT: ${{ secrets.GH_PAT }} GH_TOKEN: ${{ secrets.GH_PAT }} @@ -74,7 +74,7 @@ jobs: validate-match-secrets: name: Match-Secrets needs: validate-access-token - runs-on: macos-14 + runs-on: macos-15 env: GH_TOKEN: ${{ secrets.GH_PAT }} steps: @@ -112,7 +112,7 @@ jobs: validate-fastlane-secrets: name: Fastlane needs: [validate-access-token, validate-match-secrets] - runs-on: macos-14 + runs-on: macos-15 env: GH_PAT: ${{ secrets.GH_PAT }} GH_TOKEN: ${{ secrets.GH_PAT }} From e7e54e58e72429575eb9a5c7ffae82eef4dc185a Mon Sep 17 00:00:00 2001 From: marionbarker Date: Fri, 21 Mar 2025 08:08:30 -0700 Subject: [PATCH 11/11] update Loop: Adjust provisioning profile path in script for Xcode 16 compatibility --- Loop | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Loop b/Loop index ae76f90955..f63edf9b70 160000 --- a/Loop +++ b/Loop @@ -1 +1 @@ -Subproject commit ae76f90955843df0e1e4297f6c7c669d21e5eae2 +Subproject commit f63edf9b70177c141d5d67fbaff568b4baa6d3a6