diff --git a/.github/workflows/add_identifiers.yml b/.github/workflows/add_identifiers.yml index 77faff9e4..9d8232bad 100644 --- a/.github/workflows/add_identifiers.yml +++ b/.github/workflows/add_identifiers.yml @@ -1,36 +1,37 @@ name: 2. Add Identifiers -run-name: Add Identifiers +run-name: Add Identifiers (${{ github.ref_name }}) on: workflow_dispatch: jobs: - secrets: + validate: + name: Validate uses: ./.github/workflows/validate_secrets.yml secrets: inherit identifiers: - needs: secrets + needs: validate runs-on: macos-13 steps: # Uncomment to manually select Xcode version if needed #- name: Select Xcode version - # run: "sudo xcode-select --switch /Applications/Xcode_14.1.app/Contents/Developer" + # run: "sudo xcode-select --switch /Applications/Xcode_15.0.1.app/Contents/Developer" # Checks-out the repo - name: Checkout Repo - uses: actions/checkout@v3 + uses: actions/checkout@v4 # 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" - # 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 + # Install project dependencies + - name: Install Project Dependencies + run: bundle install # Create or update identifiers for app - name: Fastlane Provision - run: fastlane identifiers + run: bundle exec fastlane identifiers env: TEAMID: ${{ secrets.TEAMID }} GH_PAT: ${{ secrets.GH_PAT }} diff --git a/.github/workflows/build_Open-iAPS.yml b/.github/workflows/build_Open-iAPS.yml new file mode 100644 index 000000000..59697972f --- /dev/null +++ b/.github/workflows/build_Open-iAPS.yml @@ -0,0 +1,270 @@ +name: 4. Build Open-iAPS +run-name: Build Open-iAPS (${{ 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: '30 04 1 * *' # Runs at 04:30 UTC on the 1st every month + - 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: nightscout/Open-iAPS + 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 + +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: + needs: validate + runs-on: ubuntu-latest + name: Check alive branch and permissions + permissions: + contents: write + outputs: + WORKFLOW_PERMISSION: ${{ steps.workflow-permission.outputs.has_permission }} + + steps: + - name: Check for workflow permissions + id: workflow-permission + 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.GITHUB_TOKEN }} + run: | + if [[ "$(gh api -H "Accept: application/vnd.github+json" /repos/${{ github.repository }}/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 nightscout/Open-iAPS:main + response=$(curl --request GET \ + --url "https://api.github.com/repos/${{ env.UPSTREAM_REPO }}/git/refs/heads/main" \ + --header "Authorization: Bearer $GITHUB_TOKEN" \ + --silent) + echo "API Response: $response" + SHA=$(echo "$response" | jq -r '.object.sha') + if [ "$SHA" = "null" ]; then + echo "Error: Unable to retrieve SHA for the main branch." + exit 1 + fi + echo "SHA of main branch: $SHA"; + + # Create alive branch based on nightscout/Open-iAPS:main + gh api \ + --method POST \ + -H "Authorization: token $GITHUB_TOKEN" \ + -H "Accept: application/vnd.github.v3+json" \ + /repos/${{ github.repository }}/git/refs \ + -f ref='refs/heads/alive' \ + -f sha=$SHA + + # Checks for changes in upstream repository; if changes exist prompts sync for build + # Performs keepalive to avoid stale fork + check_latest_from_upstream: + needs: [validate, check_alive_and_permissions] + runs-on: ubuntu-latest + name: Check upstream and keep alive + outputs: + NEW_COMMITS: ${{ steps.sync.outputs.has_new_commits }} + + 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 != 'nightscout' + 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 Open-iAPS's browser build." >> $GITHUB_STEP_SUMMARY + echo "Synchronizing your fork of Open-iAPS with the upstream repository nightscout/Open-iAPS will be skipped." >> $GITHUB_STEP_SUMMARY + echo "If you want to enable automatic builds and updates for your Open-iAPS, please follow the instructions \ + under the following path Open-iAPS/fastlane/testflight.md." >> $GITHUB_STEP_SUMMARY + + + # Builds Open-iAPS + build: + name: Build + needs: [validate, check_alive_and_permissions, check_latest_from_upstream] + runs-on: macos-13 + 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' ) + ) + steps: + # Uncomment to manually select Xcode version if needed + #- name: Select Xcode version + # run: "sudo xcode-select --switch /Applications/Xcode_15.0.1.app/Contents/Developer" + + - name: Checkout Repo for syncing + if: | + needs.check_alive_and_permissions.outputs.WORKFLOW_PERMISSION == 'true' && + vars.SCHEDULED_SYNC != 'false' + uses: actions/checkout@v4 + with: + token: ${{ secrets.GH_PAT }} + ref: ${{ env.TARGET_BRANCH }} + + - name: Sync upstream changes + if: | # do not run the upstream sync action on the upstream repository + needs.check_alive_and_permissions.outputs.WORKFLOW_PERMISSION == 'true' && + vars.SCHEDULED_SYNC != 'false' && github.repository_owner != 'nightscout' + id: sync + uses: aormsby/Fork-Sync-With-Upstream-action@v3.4.1 + with: + target_sync_branch: ${{ env.TARGET_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 + + - name: Checkout Repo for building + uses: actions/checkout@v4 + with: + token: ${{ secrets.GH_PAT }} + submodules: recursive + ref: ${{ env.TARGET_BRANCH }} + + # 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" + + # Install project dependencies + - name: Install project dependencies + run: bundle install + + # Build signed Open-iAPS IPA file + - name: Fastlane Build & Archive + run: bundle exec fastlane build_iAPS + env: + TEAMID: ${{ secrets.TEAMID }} + GH_PAT: ${{ secrets.GH_PAT }} + FASTLANE_KEY_ID: ${{ secrets.FASTLANE_KEY_ID }} + 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 + env: + TEAMID: ${{ secrets.TEAMID }} + GH_PAT: ${{ secrets.GH_PAT }} + FASTLANE_KEY_ID: ${{ secrets.FASTLANE_KEY_ID }} + FASTLANE_ISSUER_ID: ${{ secrets.FASTLANE_ISSUER_ID }} + FASTLANE_KEY: ${{ secrets.FASTLANE_KEY }} + MATCH_PASSWORD: ${{ secrets.MATCH_PASSWORD }} + + # Upload Build artifacts + - name: Upload build log, IPA and Symbol artifacts + if: always() + uses: actions/upload-artifact@v3 + with: + name: build-artifacts + path: | + artifacts + buildlog diff --git a/.github/workflows/build_iAPS.yml b/.github/workflows/build_iAPS.yml deleted file mode 100644 index 260856fe1..000000000 --- a/.github/workflows/build_iAPS.yml +++ /dev/null @@ -1,71 +0,0 @@ -name: 4. Build iAPS -run-name: Build iAPS (${{ 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: - - ## Remove the "#" sign from the beginning of the two lines below to get automated builds every two months - #schedule: - #- cron: '0 17 1 */2 *' # Runs at 17:00 UTC on the 1st in Jan, Mar, May, Jul, Sep and Nov. - - -jobs: - secrets: - uses: ./.github/workflows/validate_secrets.yml - secrets: inherit - - build: - needs: secrets - runs-on: macos-13 - steps: - # Uncomment to manually select Xcode version if needed - - name: Select Xcode version - run: "sudo xcode-select --switch /Applications/Xcode_15.0.1.app/Contents/Developer" - - # Checks-out the repo - - name: Checkout Repo - uses: actions/checkout@v3 - with: - submodules: recursive - - # 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" - - # 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 iAPS IPA file - - name: Fastlane Build & Archive - run: fastlane build_iAPS - env: - TEAMID: ${{ secrets.TEAMID }} - GH_PAT: ${{ secrets.GH_PAT }} - FASTLANE_KEY_ID: ${{ secrets.FASTLANE_KEY_ID }} - 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: fastlane release - env: - TEAMID: ${{ secrets.TEAMID }} - GH_PAT: ${{ secrets.GH_PAT }} - FASTLANE_KEY_ID: ${{ secrets.FASTLANE_KEY_ID }} - FASTLANE_ISSUER_ID: ${{ secrets.FASTLANE_ISSUER_ID }} - FASTLANE_KEY: ${{ secrets.FASTLANE_KEY }} - MATCH_PASSWORD: ${{ secrets.MATCH_PASSWORD }} - - # Upload Build artifacts - - name: Upload build log, IPA and Symbol artifacts - if: always() - uses: actions/upload-artifact@v3 - with: - name: build-artifacts - path: | - artifacts - buildlog diff --git a/.github/workflows/create_certs.yml b/.github/workflows/create_certs.yml index 7675f5d5c..d418b51a2 100644 --- a/.github/workflows/create_certs.yml +++ b/.github/workflows/create_certs.yml @@ -1,15 +1,17 @@ name: 3. Create Certificates -run-name: Create Certificates +run-name: Create Certificates (${{ github.ref_name }}) on: workflow_dispatch: jobs: - secrets: + validate: + name: Validate uses: ./.github/workflows/validate_secrets.yml secrets: inherit - + certificates: - needs: secrets + name: Create Certificates + needs: validate runs-on: macos-13 steps: # Uncomment to manually select Xcode version if needed @@ -18,19 +20,19 @@ jobs: # Checks-out the repo - name: Checkout Repo - uses: actions/checkout@v3 + uses: actions/checkout@v4 # 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" - # 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 + # Install project dependencies + - name: Install Project Dependencies + run: bundle install # Create or update certificates for app - name: Create Certificates - run: fastlane certs + run: bundle exec fastlane certs env: TEAMID: ${{ secrets.TEAMID }} GH_PAT: ${{ secrets.GH_PAT }} diff --git a/.github/workflows/validate_secrets.yml b/.github/workflows/validate_secrets.yml index 151f2c421..a35f77f11 100644 --- a/.github/workflows/validate_secrets.yml +++ b/.github/workflows/validate_secrets.yml @@ -1,74 +1,194 @@ name: 1. Validate Secrets -run-name: Validate Secrets +run-name: Validate Secrets (${{ github.ref_name }}) on: [workflow_call, workflow_dispatch] jobs: - validate: + validate-access-token: + name: Access runs-on: macos-13 + env: + GH_PAT: ${{ secrets.GH_PAT }} + GH_TOKEN: ${{ secrets.GH_PAT }} + outputs: + HAS_WORKFLOW_PERMISSION: ${{ steps.access-token.outputs.has_workflow_permission }} + steps: + - name: Validate Access Token + id: access-token + run: | + # Validate Access Token + + # Ensure that gh exit codes are handled when output is piped. + set -o pipefail + + # Define patterns to validate the access token (GH_PAT) and distinguish between classic and fine-grained tokens. + GH_PAT_CLASSIC_PATTERN='^ghp_[a-zA-Z0-9]{36}$' + GH_PAT_FINE_GRAINED_PATTERN='^github_pat_[a-zA-Z0-9]{22}_[a-zA-Z0-9]{59}$' + + # Validate Access Token (GH_PAT) + if [ -z "$GH_PAT" ]; then + failed=true + echo "::error::The GH_PAT secret is unset or empty. Set it and try again." + else + if [[ $GH_PAT =~ $GH_PAT_CLASSIC_PATTERN ]]; then + provides_scopes=true + echo "The GH_PAT secret is a structurally valid classic token." + elif [[ $GH_PAT =~ $GH_PAT_FINE_GRAINED_PATTERN ]]; then + echo "The GH_PAT secret is a structurally valid fine-grained token." + else + unknown_format=true + echo "The GH_PAT secret does not have a known token format." + fi + + # Attempt to capture the x-oauth-scopes scopes of the token. + if ! scopes=$(curl -sS -f -I -H "Authorization: token $GH_PAT" https://api.github.com | { grep -i '^x-oauth-scopes:' || true; } | cut -d ' ' -f2- | tr -d '\r'); then + failed=true + if [ $unknown_format ]; then + echo "::error::Unable to connect to GitHub using the GH_PAT secret. Verify that it is set correctly (including the 'ghp_' or 'github_pat_' prefix) and try again." + else + echo "::error::Unable to connect to GitHub using the GH_PAT secret. Verify that the token exists and has not expired at https://github.com/settings/tokens. If necessary, regenerate or create a new token (and update the secret), then try again." + fi + elif [[ $scopes =~ workflow ]]; then + echo "The GH_PAT secret has repo and workflow permissions." + echo "has_workflow_permission=true" >> $GITHUB_OUTPUT + elif [[ $scopes =~ repo ]]; then + echo "The GH_PAT secret has repo (but not workflow) permissions." + elif [ $provides_scopes ]; then + failed=true + if [ -z "$scopes" ]; then + echo "The GH_PAT secret is valid and can be used to connect to GitHub, but it does not provide any permission scopes." + else + echo "The GH_PAT secret is valid and can be used to connect to GitHub, but it only provides the following permission scopes: $scopes" + fi + echo "::error::The GH_PAT secret is lacking at least the 'repo' permission scope required to access the Match-Secrets repository. Update the token permissions at https://github.com/settings/tokens (to include the 'repo' and 'workflow' scopes) and try again." + else + echo "The GH_PAT secret is valid and can be used to connect to GitHub, but it does not provide inspectable scopes. Assuming that the 'repo' and 'workflow' permission scopes required to access the Match-Secrets repository and perform automations are present." + echo "has_workflow_permission=true" >> $GITHUB_OUTPUT + fi + fi + + # Exit unsuccessfully if secret validation failed. + if [ $failed ]; then + exit 2 + fi + + validate-match-secrets: + name: Match-Secrets + needs: validate-access-token + runs-on: macos-13 + env: + GH_TOKEN: ${{ secrets.GH_PAT }} + steps: + - name: Validate Match-Secrets + run: | + # Validate Match-Secrets + + # Ensure that gh exit codes are handled when output is piped. + set -o pipefail + + # If a Match-Secrets repository does not exist, attempt to create one. + if ! visibility=$(gh repo view ${{ github.repository_owner }}/Match-Secrets --json visibility | jq --raw-output '.visibility | ascii_downcase'); then + echo "A '${{ github.repository_owner }}/Match-Secrets' repository could not be found using the GH_PAT secret. Attempting to create one..." + + # Create a private Match-Secrets repository and verify that it exists and that it is private. + if gh repo create ${{ github.repository_owner }}/Match-Secrets --private >/dev/null && [ "$(gh repo view ${{ github.repository_owner }}/Match-Secrets --json visibility | jq --raw-output '.visibility | ascii_downcase')" == "private" ]; then + echo "Created a private '${{ github.repository_owner }}/Match-Secrets' repository." + else + failed=true + echo "::error::Unable to create a private '${{ github.repository_owner }}/Match-Secrets' repository. Create a private 'Match-Secrets' repository manually and try again. If a private 'Match-Secrets' repository already exists, verify that the token permissions of the GH_PAT are set correctly (or update them) at https://github.com/settings/tokens and try again." + fi + # Otherwise, if a Match-Secrets repository exists, but it is public, cause validation to fail. + elif [[ "$visibility" == "public" ]]; then + failed=true + echo "::error::A '${{ github.repository_owner }}/Match-Secrets' repository was found, but it is public. Change the repository visibility to private (or delete it) and try again. If necessary, a private repository will be created for you." + else + echo "Found a private '${{ github.repository_owner }}/Match-Secrets' repository to use." + fi + + # Exit unsuccessfully if secret validation failed. + if [ $failed ]; then + exit 2 + fi + + validate-fastlane-secrets: + name: Fastlane + needs: [validate-access-token, validate-match-secrets] + runs-on: macos-13 + env: + GH_PAT: ${{ secrets.GH_PAT }} + GH_TOKEN: ${{ secrets.GH_PAT }} + FASTLANE_ISSUER_ID: ${{ secrets.FASTLANE_ISSUER_ID }} + FASTLANE_KEY_ID: ${{ secrets.FASTLANE_KEY_ID }} + FASTLANE_KEY: ${{ secrets.FASTLANE_KEY }} + MATCH_PASSWORD: ${{ secrets.MATCH_PASSWORD }} + TEAMID: ${{ secrets.TEAMID }} steps: - # Checks-out the repo - name: Checkout Repo - uses: actions/checkout@v3 + uses: actions/checkout@v4 - # 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 + # Install project dependencies + - name: Install Project Dependencies + run: bundle install - # Validates the repo secrets - - name: Validate Secrets + - name: Validate Fastlane Secrets run: | - # Validate Secrets - echo Validating Repository Secrets... - + # Validate Fastlane Secrets + # Validate TEAMID if [ -z "$TEAMID" ]; then failed=true - echo "::error::TEAMID secret is unset or empty. Set it and try again." + echo "::error::The TEAMID secret is unset or empty. Set it and try again." elif [ ${#TEAMID} -ne 10 ]; then failed=true - echo "::error::TEAMID secret is set but has wrong length. Verify that it is set correctly and try again." - fi - - # Validate GH_PAT - if [ -z "$GH_PAT" ]; then + echo "::error::The TEAMID secret is set but has wrong length. Verify that it is set correctly and try again." + elif ! [[ $TEAMID =~ ^[A-Z0-9]+$ ]]; then failed=true - echo "::error::GH_PAT secret is unset or empty. Set it and try again." - elif [ "$(gh api -H "Accept: application/vnd.github+json" /repos/${{ github.repository_owner }}/Match-Secrets | jq --raw-output '.permissions.push')" != "true" ]; then + 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::GH_PAT secret is set but invalid or lacking appropriate privileges on the ${{ github.repository_owner }}/Match-Secrets repository. Verify that it is set correctly and try again." + echo "::error::The MATCH_PASSWORD secret is unset or empty. Set it and try again." fi - + + # Ensure that fastlane exit codes are handled when output is piped. + set -o pipefail + # Validate FASTLANE_ISSUER_ID, FASTLANE_KEY_ID, and FASTLANE_KEY + FASTLANE_KEY_ID_PATTERN='^[A-Z0-9]+$' + FASTLANE_ISSUER_ID_PATTERN='^\{?[A-F0-9a-f]{8}-[A-F0-9a-f]{4}-[A-F0-9a-f]{4}-[A-F0-9a-f]{4}-[A-F0-9a-f]{12}\}?$' + if [ -z "$FASTLANE_ISSUER_ID" ] || [ -z "$FASTLANE_KEY_ID" ] || [ -z "$FASTLANE_KEY" ]; then failed=true [ -z "$FASTLANE_ISSUER_ID" ] && echo "::error::The FASTLANE_ISSUER_ID secret is unset or empty. Set it and try again." [ -z "$FASTLANE_KEY_ID" ] && echo "::error::The FASTLANE_KEY_ID secret is unset or empty. Set it and try again." [ -z "$FASTLANE_KEY" ] && echo "::error::The FASTLANE_KEY secret is unset or empty. Set it and try again." - elif ! echo "$FASTLANE_KEY" | openssl pkcs8 -nocrypt >/dev/null; then + elif [ ${#FASTLANE_KEY_ID} -ne 10 ]; then failed=true - echo "::error::The FASTLANE_KEY secret is set but invalid. Verify that it is set correctly and try again." - elif ! fastlane validate_secrets; then + echo "::error::The FASTLANE_KEY_ID secret is set but has wrong length. Verify that you copied it correctly from the 'Keys' tab at https://appstoreconnect.apple.com/access/api and try again." + elif ! [[ $FASTLANE_KEY_ID =~ $FASTLANE_KEY_ID_PATTERN ]]; then failed=true - echo "::error::Unable to create a valid authorization token for the App Store Connect API.\ - Verify that the FASTLANE_ISSUER_ID, FASTLANE_KEY_ID, and FASTLANE_KEY secrets are set correctly and try again." - fi - - # Validate MATCH_PASSWORD - if [ -z "$MATCH_PASSWORD" ]; then + echo "::error::The FASTLANE_KEY_ID secret is set but invalid. Verify that you copied it correctly from the 'Keys' tab at https://appstoreconnect.apple.com/access/api and try again." + elif ! [[ $FASTLANE_ISSUER_ID =~ $FASTLANE_ISSUER_ID_PATTERN ]]; then failed=true - echo "::error::The MATCH_PASSWORD secret is unset or empty. Set it and try again." + echo "::error::The FASTLANE_ISSUER_ID secret is set but invalid. Verify that you copied it correctly from the 'Keys' tab at https://appstoreconnect.apple.com/access/api and try again." + elif ! echo "$FASTLANE_KEY" | openssl pkcs8 -nocrypt >/dev/null; then + failed=true + echo "::error::The FASTLANE_KEY secret is set but invalid. Verify that you copied it correctly from the API Key file (*.p8) you downloaded and try again." + elif ! bundle exec fastlane validate_secrets 2>&1 | tee fastlane.log; then + if grep -q "bad decrypt" fastlane.log; then + failed=true + echo "::error::Unable to decrypt the Match-Secrets repository using the MATCH_PASSWORD secret. Verify that it is set correctly and try again." + elif grep -q -e "required agreement" -e "license agreement" fastlane.log; then + failed=true + echo "::error::Unable to create a valid authorization token for the App Store Connect API. Verify that the latest developer program license agreement has been accepted at https://developer.apple.com/account (review and accept any updated agreement), then wait a few minutes for changes to propagate and try again." + elif ! grep -q -e "No code signing identity found" -e "Could not install WWDR certificate" fastlane.log; then + failed=true + echo "::error::Unable to create a valid authorization token for the App Store Connect API. Verify that the FASTLANE_ISSUER_ID, FASTLANE_KEY_ID, and FASTLANE_KEY secrets are set correctly and try again." + fi fi - + # Exit unsuccessfully if secret validation failed. if [ $failed ]; then exit 2 fi - shell: bash - env: - TEAMID: ${{ secrets.TEAMID }} - GH_PAT: ${{ secrets.GH_PAT }} - FASTLANE_ISSUER_ID: ${{ secrets.FASTLANE_ISSUER_ID }} - FASTLANE_KEY_ID: ${{ secrets.FASTLANE_KEY_ID }} - FASTLANE_KEY: ${{ secrets.FASTLANE_KEY }} - MATCH_PASSWORD: ${{ secrets.MATCH_PASSWORD }} - GH_TOKEN: ${{ secrets.GH_PAT }} \ No newline at end of file diff --git a/Config.xcconfig b/Config.xcconfig index 30297014c..266012d65 100644 --- a/Config.xcconfig +++ b/Config.xcconfig @@ -1,12 +1,12 @@ -APP_DISPLAY_NAME = iAPS +APP_DISPLAY_NAME = OiAPS APP_VERSION = 2.3.3 APP_BUILD_NUMBER = 1 COPYRIGHT_NOTICE = DEVELOPER_TEAM = ##TEAM_ID## BUNDLE_IDENTIFIER = ru.artpancreas.$(DEVELOPMENT_TEAM).FreeAPS APP_GROUP_ID = group.com.$(DEVELOPMENT_TEAM).loopkit.LoopGroup -APP_ICON = pod_colorful +APP_ICON = OiAPS_Icon APP_URL_SCHEME = freeaps-x +#include? "../../ConfigOverride.xcconfig" #include? "ConfigOverride.xcconfig" -//#include? "../../ConfigOverride.xcconfig" diff --git a/FreeAPS/Resources/Assets.xcassets/OiAPS_Icon.appiconset/Contents.json b/FreeAPS/Resources/Assets.xcassets/OiAPS_Icon.appiconset/Contents.json new file mode 100644 index 000000000..867109049 --- /dev/null +++ b/FreeAPS/Resources/Assets.xcassets/OiAPS_Icon.appiconset/Contents.json @@ -0,0 +1,20 @@ +{ + "images" : [ + { + "filename" : "OiAPS App Icon.png", + "idiom" : "universal", + "platform" : "ios", + "size" : "1024x1024" + }, + { + "filename" : "OiAPS Watch Icon.png", + "idiom" : "universal", + "platform" : "watchos", + "size" : "1024x1024" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/FreeAPS/Resources/Assets.xcassets/OiAPS_Icon.appiconset/OiAPS App Icon.png b/FreeAPS/Resources/Assets.xcassets/OiAPS_Icon.appiconset/OiAPS App Icon.png new file mode 100644 index 000000000..22c99017c Binary files /dev/null and b/FreeAPS/Resources/Assets.xcassets/OiAPS_Icon.appiconset/OiAPS App Icon.png differ diff --git a/FreeAPS/Resources/Assets.xcassets/OiAPS_Icon.appiconset/OiAPS Watch Icon.png b/FreeAPS/Resources/Assets.xcassets/OiAPS_Icon.appiconset/OiAPS Watch Icon.png new file mode 100644 index 000000000..ea3880b84 Binary files /dev/null and b/FreeAPS/Resources/Assets.xcassets/OiAPS_Icon.appiconset/OiAPS Watch Icon.png differ diff --git a/FreeAPS/Sources/Models/Icons.swift b/FreeAPS/Sources/Models/Icons.swift index 16056b66b..567dbe3fc 100644 --- a/FreeAPS/Sources/Models/Icons.swift +++ b/FreeAPS/Sources/Models/Icons.swift @@ -3,11 +3,12 @@ import Foundation import UIKit enum Icon_: String, CaseIterable, Identifiable { - case primary = "pod_colorful" + case primary = "OiAPS_Icon" case text = "iAPS" case podBlack = "podTemplateBlack" case popWhite = "podTemplateWhite" case cyan = "iAPS_Loop_Cyan_Text" + case podColorful = "pod_colorful" case podPurple case catWithPod case catWithPodWhite = "catWithPodWhiteBG" diff --git a/Gemfile.lock b/Gemfile.lock index 95b509e74..b7e7dc231 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -1,52 +1,53 @@ GEM remote: https://rubygems.org/ specs: - CFPropertyList (3.0.4) + CFPropertyList (3.0.6) rexml - addressable (2.8.0) - public_suffix (>= 2.0.2, < 5.0) + addressable (2.8.5) + public_suffix (>= 2.0.2, < 6.0) artifactory (3.0.15) atomos (0.1.3) aws-eventstream (1.2.0) - aws-partitions (1.516.0) - aws-sdk-core (3.121.2) + aws-partitions (1.824.0) + aws-sdk-core (3.181.1) aws-eventstream (~> 1, >= 1.0.2) - aws-partitions (~> 1, >= 1.239.0) + aws-partitions (~> 1, >= 1.651.0) + aws-sigv4 (~> 1.5) + jmespath (~> 1, >= 1.6.1) + aws-sdk-kms (1.71.0) + aws-sdk-core (~> 3, >= 3.177.0) aws-sigv4 (~> 1.1) - jmespath (~> 1.0) - aws-sdk-kms (1.50.0) - aws-sdk-core (~> 3, >= 3.121.2) - aws-sigv4 (~> 1.1) - aws-sdk-s3 (1.104.0) - aws-sdk-core (~> 3, >= 3.121.2) + aws-sdk-s3 (1.134.0) + aws-sdk-core (~> 3, >= 3.181.0) aws-sdk-kms (~> 1) - aws-sigv4 (~> 1.4) - aws-sigv4 (1.4.0) + aws-sigv4 (~> 1.6) + aws-sigv4 (1.6.0) aws-eventstream (~> 1, >= 1.0.2) babosa (1.0.4) - claide (1.0.3) + claide (1.1.0) colored (1.2) colored2 (3.1.2) commander (4.6.0) highline (~> 2.0.0) declarative (0.0.20) - digest-crc (0.6.4) + digest-crc (0.6.5) rake (>= 12.0.0, < 14.0.0) domain_name (0.5.20190701) unf (>= 0.0.5, < 1.0.0) - dotenv (2.7.6) + dotenv (2.8.1) emoji_regex (3.2.3) - excon (0.87.0) - faraday (1.8.0) + excon (0.103.0) + faraday (1.10.3) faraday-em_http (~> 1.0) faraday-em_synchrony (~> 1.0) faraday-excon (~> 1.1) - faraday-httpclient (~> 1.0.1) + faraday-httpclient (~> 1.0) + faraday-multipart (~> 1.0) faraday-net_http (~> 1.0) - faraday-net_http_persistent (~> 1.1) + faraday-net_http_persistent (~> 1.0) faraday-patron (~> 1.0) faraday-rack (~> 1.0) - multipart-post (>= 1.2, < 3) + faraday-retry (~> 1.0) ruby2_keywords (>= 0.0.4) faraday-cookie_jar (0.0.7) faraday (>= 0.8.0) @@ -55,14 +56,17 @@ GEM faraday-em_synchrony (1.0.0) faraday-excon (1.1.0) faraday-httpclient (1.0.1) + faraday-multipart (1.0.4) + multipart-post (~> 2) faraday-net_http (1.0.1) faraday-net_http_persistent (1.2.0) faraday-patron (1.0.0) faraday-rack (1.0.0) + faraday-retry (1.0.3) faraday_middleware (1.2.0) faraday (~> 1.0) - fastimage (2.2.5) - fastlane (2.196.0) + fastimage (2.2.7) + fastlane (2.215.0) CFPropertyList (>= 2.3, < 4.0.0) addressable (>= 2.8, < 3.0.0) artifactory (~> 3.0) @@ -83,10 +87,11 @@ GEM google-apis-playcustomapp_v1 (~> 0.1) google-cloud-storage (~> 1.31) highline (~> 2.0) + http-cookie (~> 1.0.5) json (< 3.0.0) jwt (>= 2.1.0, < 3) mini_magick (>= 4.9.4, < 5.0.0) - multipart-post (~> 2.0.0) + multipart-post (>= 2.0.0, < 3.0.0) naturally (~> 2.2) optparse (~> 0.1.1) plist (>= 3.1.0, < 4.0.0) @@ -94,7 +99,7 @@ GEM security (= 0.1.3) simctl (~> 1.6.3) terminal-notifier (>= 2.0.0, < 3.0.0) - terminal-table (>= 1.4.5, < 2.0.0) + terminal-table (~> 3) tty-screen (>= 0.6.3, < 1.0.0) tty-spinner (>= 0.8.0, < 1.0.0) word_wrap (~> 1.0.0) @@ -102,9 +107,9 @@ GEM xcpretty (~> 0.3.0) xcpretty-travis-formatter (>= 0.0.3) gh_inspector (1.1.3) - google-apis-androidpublisher_v3 (0.12.0) - google-apis-core (>= 0.4, < 2.a) - google-apis-core (0.4.1) + google-apis-androidpublisher_v3 (0.49.0) + google-apis-core (>= 0.11.0, < 2.a) + google-apis-core (0.11.1) addressable (~> 2.5, >= 2.5.1) googleauth (>= 0.16.2, < 2.a) httpclient (>= 2.8.1, < 3.a) @@ -113,74 +118,72 @@ GEM retriable (>= 2.0, < 4.a) rexml webrick - google-apis-iamcredentials_v1 (0.7.0) - google-apis-core (>= 0.4, < 2.a) - google-apis-playcustomapp_v1 (0.5.0) - google-apis-core (>= 0.4, < 2.a) - google-apis-storage_v1 (0.8.0) - google-apis-core (>= 0.4, < 2.a) + google-apis-iamcredentials_v1 (0.17.0) + google-apis-core (>= 0.11.0, < 2.a) + google-apis-playcustomapp_v1 (0.13.0) + google-apis-core (>= 0.11.0, < 2.a) + google-apis-storage_v1 (0.19.0) + google-apis-core (>= 0.9.0, < 2.a) google-cloud-core (1.6.0) google-cloud-env (~> 1.0) google-cloud-errors (~> 1.0) - google-cloud-env (1.5.0) - faraday (>= 0.17.3, < 2.0) - google-cloud-errors (1.2.0) - google-cloud-storage (1.34.1) - addressable (~> 2.5) + google-cloud-env (1.6.0) + faraday (>= 0.17.3, < 3.0) + google-cloud-errors (1.3.1) + google-cloud-storage (1.44.0) + addressable (~> 2.8) digest-crc (~> 0.4) google-apis-iamcredentials_v1 (~> 0.1) - google-apis-storage_v1 (~> 0.1) + google-apis-storage_v1 (~> 0.19.0) google-cloud-core (~> 1.6) googleauth (>= 0.16.2, < 2.a) mini_mime (~> 1.0) - googleauth (1.0.0) - faraday (>= 0.17.3, < 2.0) + googleauth (1.8.0) + faraday (>= 0.17.3, < 3.a) jwt (>= 1.4, < 3.0) - memoist (~> 0.16) multi_json (~> 1.11) os (>= 0.9, < 2.0) signet (>= 0.16, < 2.a) highline (2.0.3) - http-cookie (1.0.4) + http-cookie (1.0.5) domain_name (~> 0.5) httpclient (2.8.3) - jmespath (1.4.0) - json (2.6.0) - jwt (2.3.0) - memoist (0.16.2) - mini_magick (4.11.0) - mini_mime (1.1.2) + jmespath (1.6.2) + json (2.6.3) + jwt (2.7.1) + mini_magick (4.12.0) + mini_mime (1.1.5) multi_json (1.15.0) - multipart-post (2.0.0) + multipart-post (2.3.0) nanaimo (0.3.0) naturally (2.2.1) optparse (0.1.1) - os (1.1.1) - plist (3.6.0) - public_suffix (4.0.6) + os (1.1.4) + plist (3.7.0) + public_suffix (5.0.3) rake (13.0.6) - representable (3.1.1) + representable (3.2.0) declarative (< 0.1.0) trailblazer-option (>= 0.1.1, < 0.2.0) uber (< 0.2.0) retriable (3.1.2) - rexml (3.2.5) + rexml (3.2.6) rouge (2.0.7) ruby2_keywords (0.0.5) rubyzip (2.3.2) security (0.1.3) - signet (0.16.0) + signet (0.18.0) addressable (~> 2.8) - faraday (>= 0.17.3, < 2.0) + faraday (>= 0.17.5, < 3.a) jwt (>= 1.5, < 3.0) multi_json (~> 1.10) - simctl (1.6.8) + simctl (1.6.10) CFPropertyList naturally terminal-notifier (2.0.0) - terminal-table (1.8.0) - unicode-display_width (~> 1.1, >= 1.1.1) - trailblazer-option (0.1.1) + terminal-table (3.0.2) + unicode-display_width (>= 1.1.1, < 3) + trailblazer-option (0.1.2) tty-cursor (0.7.1) tty-screen (0.8.1) tty-spinner (0.9.3) @@ -188,11 +191,11 @@ GEM uber (0.1.0) unf (0.1.4) unf_ext - unf_ext (0.0.8) - unicode-display_width (1.8.0) - webrick (1.7.0) + unf_ext (0.0.8.2) + unicode-display_width (2.4.2) + webrick (1.8.1) word_wrap (1.0.0) - xcodeproj (1.21.0) + xcodeproj (1.22.0) CFPropertyList (>= 2.3.3, < 4.0) atomos (~> 0.1.3) claide (>= 1.0.2, < 2.0) @@ -206,10 +209,11 @@ GEM PLATFORMS arm64-darwin-21 + arm64-darwin-22 x86_64-darwin-19 DEPENDENCIES fastlane BUNDLED WITH - 2.3.26 + 2.4.19 \ No newline at end of file diff --git a/fastlane/Fastfile b/fastlane/Fastfile index f487b5c9c..171acb86e 100644 --- a/fastlane/Fastfile +++ b/fastlane/Fastfile @@ -233,6 +233,12 @@ platform :ios do end find_bundle_id("#{BUNDLE_ID}") + + match( + type: "appstore", + git_basic_authorization: Base64.strict_encode64("#{GITHUB_REPOSITORY_OWNER}:#{GH_PAT}"), + app_identifier: [], + ) end desc "Nuke Certs" diff --git a/fastlane/testflight.md b/fastlane/testflight.md index 4e0cdffe5..5187168a9 100644 --- a/fastlane/testflight.md +++ b/fastlane/testflight.md @@ -1,77 +1,118 @@ # Using Github Actions + FastLane to deploy to TestFlight: the "Browser Build" method -These instructions allow you to build iAPS without having access to a Mac. They also allow you to easily install iAPS on phones that are not connected to your computer. So you can send builds and updates to those you care for easily, or have an easy to access backup if you run iAPS for yourself. You do not need to worry about correct Xcode/Mac versions either. An app built using this method can easily be deployed to newer versions of iOS, as soon as they are available. +These instructions allow you to build Open-iAPS without having access to a Mac. + +* You can install Open-iAPS on phones via TestFlight that are not connected to your computer +* You can send builds and updates to those you care for +* You can install Open-iAPS 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** +> +> The browser build defaults to automatically updating and building a new version of Open-iAPS 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 Open-iAPS log appears - if the time is very short, it did not need to build - only the long actions (>10 minutes) built a new Open-iAPS app +> +> It also creates an alive branch, if you don't already have one. See [Why do I have an alive branch?](#why-do-i-have-an-alive-branch). +> + +## Introduction The setup steps are somewhat involved, but nearly all are one time steps. Subsequent builds are trivial. Your app must be updated once every 90 days, but it's a simple click to make a new build and can be done from anywhere. Note that TestFlight requires apple id accounts 13 years or older. This can be circumvented by logging into Media & Purchase on the child's phone with an adult's account. More details on this can be found in [LoopDocs](https://loopkit.github.io/loopdocs/gh-actions/gh-deploy/#install-testflight-loop-for-child). -This method for building without a Mac was ported from Loop. If you have used this method for Loop or one of the other DIY apps (Loop, Loop Caregiver, Loop Follow, Xdrip4iOS), some of the steps can be re-used and the full set of instructions does not need to be repeated. This will be mentioned in relevant sections below. +This method for building without a Mac was ported from Loop. If you have used this method for Loop or one of the other DIY apps (Loop Caregiver, Loop Follow, Xdrip4iOS), some of the steps can be re-used and the full set of instructions does not need to be repeated. This will be mentioned in relevant sections below. There are more detailed instructions in LoopDocs for doing Browser Builds of Loop and other apps, including troubleshooting and build errors. Please refer to [LoopDocs](https://loopkit.github.io/loopdocs/gh-actions/gh-other-apps/) for more details. ## Prerequisites -* A [github account](https://github.com/signup). The free level comes with plenty of storage and free compute time to build iAPS, multiple times a day, if you wanted to. -* A paid [Apple Developer account](https://developer.apple.com). You may be able to use the free version, but that has not been tested. +* A [github account](https://github.com/signup). The free level comes with plenty of storage and free compute time to build Open-iAPS, multiple times a day, if you wanted to. +* A paid [Apple Developer account](https://developer.apple.com). * Some time. Set aside a couple of hours to perform the setup. * Use the same GitHub account for all "Browser Builds" of the various DIY apps. +## Save 6 Secrets + +You require 6 Secrets (alphanumeric items) to use the GitHub build method and if you use the GitHub method to build other apps, e.g., Loop Follow or Xdrip4iOS, you will use the same 6 Secrets for each app you build with this method. Each secret is indentified below by `ALL_CAPITAL_LETTER_NAMES`. + +* Four Secrets are from your Apple Account +* Two Secrets are from your GitHub account +* Be sure to save the 6 Secrets in a text file using a text editor + - Do **NOT** use a smart editor, which might auto-correct and change case, because these Secrets are case sensitive ## Generate App Store Connect API Key -This step is common for all "Browser Builds", and should be done only once. Please save the API key somewhere safe, so it can be re-used for other builds, or if needing to start from scratch. +This step is common for all GitHub Browser Builds; do this step only once. You will be saving 4 Secrets from your Apple Account in this step. 1. Sign in to the [Apple developer portal page](https://developer.apple.com/account/resources/certificates/list). -1. Copy the team id from the upper right of the screen. Record this as your `TEAMID`. -1. Go to the [App Store Connect](https://appstoreconnect.apple.com/access/api) interface, click the "Keys" tab, and create a new key with "Admin" access. Give it a name like "FastLane API Key". -1. Record the key id; this will be used for `FASTLANE_KEY_ID`. +1. Copy the Team ID from the upper right of the screen. Record this as your `TEAMID`. +1. Go to the [App Store Connect](https://appstoreconnect.apple.com/access/integrations/api) interface, click the "Integrations" tab, and create a new key with "Admin" access. Give it the name: "FastLane API Key". 1. Record the issuer id; this will be used for `FASTLANE_ISSUER_ID`. +1. Record the key id; this will be used for `FASTLANE_KEY_ID`. 1. Download the API key itself, and open it in a text editor. The contents of this file will be used for `FASTLANE_KEY`. Copy the full text, including the "-----BEGIN PRIVATE KEY-----" and "-----END PRIVATE KEY-----" lines. -## Setup Github Match-Secrets repository +## Create GitHub Personal Access Token -This is also a common step for all "browser builds", do this step only once -1. Create a [new empty repository](https://github.com/new) titled `Match-Secrets`. It should be private. +If you have previously built another app using the "browser build" method, you can can re-use your previous personal access token (`GH_PAT`) and skip this step. -## Setup Github iAPS repository -1. Fork https://github.com/Artificial-Pancreas/iAPS into your account. If you already have a fork of iAPS in GitHub, you can't make another one. You can continue to work with your existing fork, or delete that from GitHub and then and fork https://github.com/Artificial-Pancreas/iAPS. +Log into your GitHub account to create a personal access token; this is one of two GitHub secrets needed for your build. -If you have previously built Loop or another app using the "browser build" method, you can can re-use your previous personal access token (`GH_PAT`) and skip ahead to `step 2`. 1. Create a [new personal access token](https://github.com/settings/tokens/new): - * Enter a name for your token. Something like "FastLane Access Token". - * The default Expiration time is 30 days - but you should select `No Expiration` - * Select the `repo` permission scope. + * Enter a name for your token, use "FastLane Access Token". + * Change the Expiration selection to `No expiration`. + * Select the `workflow` permission scope - this also selects `repo` scope. * Click "Generate token". * Copy the token and record it. It will be used below as `GH_PAT`. -1. In the forked iAPS repo, go to Settings -> Secrets -> Actions. + +## Make up a Password + +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. + +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. + +## Setup Github Open-iAPS repository +1. Fork https://github.com/nightscout/Open-iAPS into your account. If you already have a fork of Open-iAPS in GitHub, you can't make another one. You can continue to work with your existing fork, or delete that from GitHub and then and fork https://github.com/nightscout/Open-iAPS. +1. In the forked Open-iAPS repo, go to Settings -> Secrets and variables -> Actions. 1. For each of the following secrets, tap on "New repository secret", then add the name of the secret, along with the value you recorded for it: * `TEAMID` - * `FASTLANE_KEY_ID` * `FASTLANE_ISSUER_ID` + * `FASTLANE_KEY_ID` * `FASTLANE_KEY` * `GH_PAT` - * `MATCH_PASSWORD` - just make up a password for this + * `MATCH_PASSWORD` ## Validate repository secrets -1. Click on the "Actions" tab of your iAPS repository. -1. Select "1. Validate Secrets". -1. Click "Run Workflow", and tap the green button. +This step validates most of your six Secrets and provides error messages if it detects an issue with one or more. + +1. Click on the "Actions" tab of your Open-iAPS repository and enable workflows if needed +1. On the left side, select "1. Validate Secrets". +1. On the right side, click "Run Workflow", and tap the green `Run workflow` button. 1. Wait, and within a minute or two you should see a green checkmark indicating the workflow succeeded. -1. The workflow will check if the required secrets are added and that they are correctly formatted. If errors are detected, please check the run log for details. +1. The workflow will check if the required secrets are added and that they are correctly formatted. If errors are detected, please check the run log for details. -## Add Identifiers for iAPS App +## Add Identifiers for Open-iAPS App -1. Click on the "Actions" tab of your iAPS repository. -1. Select "2. Add Identifiers". -1. Click "Run Workflow", and tap the green button. +1. Click on the "Actions" tab of your Open-iAPS repository. +1. On the left side, select "2. Add Identifiers". +1. On the right side, click "Run Workflow", and tap the green `Run workflow` button. 1. Wait, and within a minute or two you should see a green checkmark indicating the workflow succeeded. ## Create App Group -If you have already built iAPS via Xcode using this Apple ID, you can skip on to [Create iAPS App in App Store Connect](#create-FreeAPS-X-app-in-app-store-connect). -_Please note that in default builds of iAPS, the app group is actually identical to the one used with Loop, so please enter these details exactly as described below. This is to ease the setup of apps such as Xdrip4iOS. It may require some caution if transfering between iAPS and Loop._ +If you have already built Open-iAPS via Xcode using this Apple ID, you can skip on to [Create Open-iAPS App in App Store Connect](#create-FreeAPS-X-app-in-app-store-connect). +_Please note that in default builds of Open-iAPS, the app group is actually identical to the one used with Loop, so please enter these details exactly as described below. This is to ease the setup of apps such as Xdrip4iOS. It may require some caution if transfering between Open-iAPS and Loop._ 1. Go to [Register an App Group](https://developer.apple.com/account/resources/identifiers/applicationGroup/add/) on the apple developer site. 1. For Description, use "Loop App Group". @@ -93,9 +134,9 @@ _Please note that in default builds of iAPS, the app group is actually identical 1. Click "Confirm". 1. Remember to do this for each of the identifiers above. -## Create iAPS App in App Store Connect +## Create Open-iAPS App in App Store Connect -If you have created a iAPS app in App Store Connect before, you can skip this section as well. +If you have created a Open-iAPS app in App Store Connect before, you can skip this section as well. 1. Go to the [apps list](https://appstoreconnect.apple.com/apps) on App Store Connect and click the blue "plus" icon to create a New App. * Select "iOS". @@ -110,18 +151,32 @@ You do not need to fill out the next form. That is for submitting to the app sto ## Create Building Certficates -1. Go back to the "Actions" tab of your iAPS repository in github. +1. Go back to the "Actions" tab of your Open-iAPS repository in github. 1. Select "3. Create Certificates". 1. Click "Run Workflow", and tap the green button. 1. Wait, and within a minute or two you should see a green checkmark indicating the workflow succeeded. -## Build iAPS! +## Build Open-iAPS! -1. Click on the "Actions" tab of your iAPS repository. -1. Select "4. Build iAPS". _Are you working on a previuos fork of iAPS and not seeing any GitHub workflows in the Actions tab? You may have to change the default branch so that it contains the .github/workflows files, or merge these changes into your default branch (typically `master`)._ +1. Click on the "Actions" tab of your Open-iAPS repository. +1. On the left side, select "4. Build Open-iAPS". 1. Click "Run Workflow", select your branch, and tap the green button. 1. You have some time now. Go enjoy a coffee. The build should take about 15 minutes. 1. Your app should eventually appear on [App Store Connect](https://appstoreconnect.apple.com/apps). -1. For each phone/person you would like to support iAPS on: +1. For each phone/person you would like to support Open-iAPS on: * Add them in [Users and Access](https://appstoreconnect.apple.com/access/users) on App Store Connect. * Add them to your TestFlight Internal Testing group. + +## TestFlight and Deployment Details + +For more details, please refer to [LoopDocs: Set Up Users](https://loopkit.github.io/loopdocs/gh-actions/gh-first-time/#set-up-users-and-access-testflight) and [LoopDocs: Deploy](https://loopkit.github.io/loopdocs/gh-actions/gh-deploy/) + +## Automatic Build FAQs + +### Why do I have an `alive` branch? + +If a GitHub repository has no activity (no commits are made) in 60 days, then GitHub disables the ability to use automated actions for that repository. We need to take action more frequently than that or the automated build process won't work. + +The updated `build_Open-iAPS.yml` file uses a special branch called `alive` and adds a dummy commit to the `alive` branch at regular intervals. This "trick" keeps the Actions enabled so the automated build works. + +The branch `alive` is created automatically for you. Do not delete or rename it! Do not modify `alive` yourself; it is not used for building the app.