build_trio.yml 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291
  1. name: 4. Build Trio
  2. run-name: Build Trio (${{ github.ref_name }})
  3. on:
  4. workflow_dispatch:
  5. schedule:
  6. # Check for updates every Sunday
  7. # Later logic builds if there are updates or if it is the 2nd Sunday of the month
  8. - cron: "43 6 * * 0" # Sunday at UTC 06:43
  9. env:
  10. GH_PAT: ${{ secrets.GH_PAT }}
  11. UPSTREAM_REPO: nightscout/Trio
  12. UPSTREAM_BRANCH: ${{ github.ref_name }} # branch on upstream repository to sync from (replace with specific branch name if needed)
  13. TARGET_BRANCH: ${{ github.ref_name }} # target branch on fork to be kept in sync
  14. jobs:
  15. # use a single runner for these sequential steps
  16. check_status:
  17. runs-on: ubuntu-latest
  18. name: Check status to decide whether to build
  19. permissions:
  20. contents: write
  21. outputs:
  22. NEW_COMMITS: ${{ steps.sync.outputs.has_new_commits }}
  23. IS_SECOND_IN_MONTH: ${{ steps.date-check.outputs.is_second_instance }}
  24. # Check GH_PAT, sync repository, check day in month
  25. steps:
  26. - name: Access
  27. id: workflow-permission
  28. run: |
  29. # Validate Access Token
  30. # Ensure that gh exit codes are handled when output is piped.
  31. set -o pipefail
  32. # Define patterns to validate the access token (GH_PAT) and distinguish between classic and fine-grained tokens.
  33. GH_PAT_CLASSIC_PATTERN='^ghp_[a-zA-Z0-9]{36}$'
  34. GH_PAT_FINE_GRAINED_PATTERN='^github_pat_[a-zA-Z0-9]{22}_[a-zA-Z0-9]{59}$'
  35. # Validate Access Token (GH_PAT)
  36. if [ -z "$GH_PAT" ]; then
  37. failed=true
  38. echo "::error::The GH_PAT secret is unset or empty. Set it and try again."
  39. else
  40. if [[ $GH_PAT =~ $GH_PAT_CLASSIC_PATTERN ]]; then
  41. provides_scopes=true
  42. echo "The GH_PAT secret is a structurally valid classic token."
  43. elif [[ $GH_PAT =~ $GH_PAT_FINE_GRAINED_PATTERN ]]; then
  44. echo "The GH_PAT secret is a structurally valid fine-grained token."
  45. else
  46. unknown_format=true
  47. echo "The GH_PAT secret does not have a known token format."
  48. fi
  49. # Attempt to capture the x-oauth-scopes scopes of the token.
  50. 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
  51. failed=true
  52. if [ $unknown_format ]; then
  53. 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."
  54. else
  55. 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."
  56. fi
  57. elif [[ $scopes =~ workflow ]]; then
  58. echo "The GH_PAT secret has repo and workflow permissions."
  59. echo "has_permission=true" >> $GITHUB_OUTPUT
  60. elif [[ $scopes =~ repo ]]; then
  61. echo "The GH_PAT secret has repo (but not workflow) permissions."
  62. elif [ $provides_scopes ]; then
  63. failed=true
  64. if [ -z "$scopes" ]; then
  65. echo "The GH_PAT secret is valid and can be used to connect to GitHub, but it does not provide any permission scopes."
  66. else
  67. echo "The GH_PAT secret is valid and can be used to connect to GitHub, but it only provides the following permission scopes: $scopes"
  68. fi
  69. 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."
  70. else
  71. 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."
  72. echo "has_permission=true" >> $GITHUB_OUTPUT
  73. fi
  74. fi
  75. # Exit unsuccessfully if secret validation failed.
  76. if [ $failed ]; then
  77. exit 2
  78. fi
  79. - name: Checkout target repo
  80. if: |
  81. steps.workflow-permission.outputs.has_permission == 'true' &&
  82. (vars.SCHEDULED_BUILD != 'false' || vars.SCHEDULED_SYNC != 'false')
  83. uses: actions/checkout@v5
  84. with:
  85. token: ${{ secrets.GH_PAT }}
  86. # This syncs any target branch to upstream branch of the same name
  87. - name: Sync upstream changes
  88. if: | # do not run the upstream sync action on the upstream repository
  89. steps.workflow-permission.outputs.has_permission == 'true' &&
  90. vars.SCHEDULED_SYNC != 'false' && github.repository_owner != 'nightscout'
  91. id: sync
  92. uses: aormsby/Fork-Sync-With-Upstream-action@v3.4.2
  93. with:
  94. target_sync_branch: ${{ env.TARGET_BRANCH }}
  95. shallow_since: 6 months ago
  96. target_repo_token: ${{ secrets.GH_PAT }}
  97. upstream_sync_branch: ${{ env.UPSTREAM_BRANCH }}
  98. upstream_sync_repo: ${{ env.UPSTREAM_REPO }}
  99. # Display a sample message based on the sync output var 'has_new_commits'
  100. - name: New commits found
  101. if: |
  102. steps.workflow-permission.outputs.has_permission == 'true' &&
  103. vars.SCHEDULED_SYNC != 'false' && steps.sync.outputs.has_new_commits == 'true'
  104. run: echo "New commits were found to sync."
  105. - name: No new commits
  106. if: |
  107. steps.workflow-permission.outputs.has_permission == 'true' &&
  108. vars.SCHEDULED_SYNC != 'false' && steps.sync.outputs.has_new_commits == 'false'
  109. run: echo "There were no new commits."
  110. - name: Show value of 'has_new_commits'
  111. if: steps.workflow-permission.outputs.has_permission == 'true' && vars.SCHEDULED_SYNC != 'false'
  112. run: |
  113. echo ${{ steps.sync.outputs.has_new_commits }}
  114. echo "NEW_COMMITS=${{ steps.sync.outputs.has_new_commits }}" >> $GITHUB_OUTPUT
  115. - name: Show scheduled build configuration message
  116. if: steps.workflow-permission.outputs.has_permission != 'true'
  117. run: |
  118. echo "### :calendar: Scheduled Sync and Build Disabled :mobile_phone_off:" >> $GITHUB_STEP_SUMMARY
  119. echo "You have not yet configured the scheduled sync and build for Trio's browser build." >> $GITHUB_STEP_SUMMARY
  120. echo "Synchronizing your fork of <code>Trio</code> with the upstream repository <code>nightscout/Trio</code> will be skipped." >> $GITHUB_STEP_SUMMARY
  121. echo "If you want to enable automatic builds and updates for your Trio, please follow the instructions \
  122. under the following path <code>Trio/fastlane/testflight.md</code>." >> $GITHUB_STEP_SUMMARY
  123. # Set a logic flag if this is the second instance of this day-of-week in this month
  124. - name: Check if this is the second time this day-of-week happens this month
  125. id: date-check
  126. run: |
  127. DAY_OF_MONTH=$(date +%-d)
  128. WEEK_OF_MONTH=$(( ($(date +%-d) - 1) / 7 + 1 ))
  129. if [[ $WEEK_OF_MONTH -eq 2 ]]; then
  130. echo "is_second_instance=true" >> "$GITHUB_OUTPUT"
  131. else
  132. echo "is_second_instance=false" >> "$GITHUB_OUTPUT"
  133. fi
  134. # When a scheduled run finds nothing new to build, cancel the run so it shows
  135. # as "cancelled" instead of "success" — a green check should always mean a
  136. # new build was made.
  137. cancel_when_no_build:
  138. needs: check_status
  139. name: Cancel run when there is nothing to build
  140. runs-on: ubuntu-latest
  141. permissions:
  142. actions: write
  143. if: |
  144. github.event_name == 'schedule' &&
  145. !(vars.SCHEDULED_BUILD != 'false' && needs.check_status.outputs.IS_SECOND_IN_MONTH == 'true') &&
  146. !(vars.SCHEDULED_SYNC != 'false' && needs.check_status.outputs.NEW_COMMITS == 'true')
  147. steps:
  148. - name: Cancel the run
  149. env:
  150. GH_TOKEN: ${{ github.token }}
  151. run: |
  152. echo "Repository is up to date and no monthly build is due — cancelling this run so it is not reported as a successful build." >> "$GITHUB_STEP_SUMMARY"
  153. gh run cancel ${{ github.run_id }} --repo ${{ github.repository }}
  154. # Keep the job alive until the cancellation lands, so the run ends as
  155. # "cancelled" rather than completing successfully first.
  156. sleep 300
  157. # Checks if Distribution certificate is present and valid, optionally nukes and
  158. # creates new certs if the repository variable ENABLE_NUKE_CERTS == 'true'
  159. # only run if a build is planned
  160. check_certs:
  161. needs: [check_status]
  162. name: Check certificates
  163. uses: ./.github/workflows/create_certs.yml
  164. secrets: inherit
  165. if: |
  166. github.event_name == 'workflow_dispatch' ||
  167. (vars.SCHEDULED_BUILD != 'false' && needs.check_status.outputs.IS_SECOND_IN_MONTH == 'true') ||
  168. (vars.SCHEDULED_SYNC != 'false' && needs.check_status.outputs.NEW_COMMITS == 'true' )
  169. # Builds Trio
  170. build:
  171. name: Build
  172. needs: [check_certs, check_status]
  173. runs-on: macos-26
  174. permissions:
  175. contents: write
  176. if:
  177. | # builds with manual start; if scheduled: once a month or when new commits are found
  178. github.event_name == 'workflow_dispatch' ||
  179. (vars.SCHEDULED_BUILD != 'false' && needs.check_status.outputs.IS_SECOND_IN_MONTH == 'true') ||
  180. (vars.SCHEDULED_SYNC != 'false' && needs.check_status.outputs.NEW_COMMITS == 'true' )
  181. steps:
  182. - name: Select Xcode version
  183. run: "sudo xcode-select --switch /Applications/Xcode_26.5.app/Contents/Developer"
  184. - name: Checkout Repo for building
  185. uses: actions/checkout@v5
  186. with:
  187. token: ${{ secrets.GH_PAT }}
  188. submodules: recursive
  189. ref: ${{ env.TARGET_BRANCH }}
  190. # Customize Trio: Use patches or download and apply patches from GitHub
  191. - name: Customize Trio
  192. run: |
  193. # Trio workspace patches
  194. # -applies any patches located in the Trio/patches/ directory
  195. if $(ls ./patches/* &> /dev/null); then
  196. git apply ./patches/* --allow-empty -v --whitespace=fix
  197. fi
  198. # Download and apply Trio patches from GitHub:
  199. # Template for customizing Trio code (as opposed to submodule code)
  200. # Remove the "#" sign from the beginning of the line below to activate
  201. # and then replace the alphanumeric string with your SHA, this SHA is NOT valid
  202. #curl https://github.com/nightscout/Trio/commit/d206432b024279ef710df462b20bd464cd9682d4.patch | git apply -v --whitespace=fix
  203. # Download and apply Submodule patches from GitHub:
  204. # Template for customizing submodules (you must edit the submodule name)
  205. # This example is for G7SensorKit showing you can apply multiple commits, in the proper order
  206. # Remove the "#" sign from the beginning of the lines below to activate
  207. # This example applies 3 commits from the scan-fix folder; valid only when these are not already in Trio
  208. #curl https://github.com/loopandlearn/G7SensorKit/commit/ba44beb3d1491c453f4f438443c3f8ba29146ab3.patch | git apply --directory=G7SensorKit -v --whitespace=fix
  209. #curl https://github.com/loopandlearn/G7SensorKit/commit/d86ac8e9cd523d1267587dd70c96597125eef7ab.patch | git apply --directory=G7SensorKit -v --whitespace=fix
  210. #curl https://github.com/loopandlearn/G7SensorKit/commit/205054e7537723c2aec58d807634b4853f687244.patch | git apply --directory=G7SensorKit -v --whitespace=fix
  211. # Add patches for additional customization by following the templates above,
  212. # and make sure to specify the submodule by setting "--directory=(submodule_name)".
  213. # Several patches may be added per submodule.
  214. # Adding comments (#) is strongly recommended to easily tell the individual patches apart.
  215. # Patch Fastlane Match to not print tables
  216. - name: Patch Match Tables
  217. run: |
  218. TABLE_PRINTER_PATH=$(ruby -e 'puts Gem::Specification.find_by_name("fastlane").gem_dir')/match/lib/match/table_printer.rb
  219. if [ -f "$TABLE_PRINTER_PATH" ]; then
  220. sed -i "" "/puts(Terminal::Table.new(params))/d" "$TABLE_PRINTER_PATH"
  221. else
  222. echo "table_printer.rb not found"
  223. exit 1
  224. fi
  225. # Install project dependencies
  226. - name: Install Project Dependencies
  227. run: bundle install
  228. # Sync the GitHub runner clock with the Windows time server (workaround as suggested in https://github.com/actions/runner/issues/2996)
  229. - name: Sync clock
  230. run: sudo sntp -sS time.windows.com
  231. # Build signed Trio IPA file
  232. - name: Fastlane Build & Archive
  233. run: bundle exec fastlane build_trio
  234. env:
  235. TEAMID: ${{ secrets.TEAMID }}
  236. GH_PAT: ${{ secrets.GH_PAT }}
  237. FASTLANE_KEY_ID: ${{ secrets.FASTLANE_KEY_ID }}
  238. FASTLANE_ISSUER_ID: ${{ secrets.FASTLANE_ISSUER_ID }}
  239. FASTLANE_KEY: ${{ secrets.FASTLANE_KEY }}
  240. MATCH_PASSWORD: ${{ secrets.MATCH_PASSWORD }}
  241. # Upload to TestFlight
  242. - name: Fastlane upload to TestFlight
  243. run: bundle exec fastlane release
  244. env:
  245. TEAMID: ${{ secrets.TEAMID }}
  246. GH_PAT: ${{ secrets.GH_PAT }}
  247. FASTLANE_KEY_ID: ${{ secrets.FASTLANE_KEY_ID }}
  248. FASTLANE_ISSUER_ID: ${{ secrets.FASTLANE_ISSUER_ID }}
  249. FASTLANE_KEY: ${{ secrets.FASTLANE_KEY }}
  250. MATCH_PASSWORD: ${{ secrets.MATCH_PASSWORD }}
  251. # Upload Build artifacts
  252. - name: Upload build log, IPA and Symbol artifacts
  253. if: always()
  254. uses: actions/upload-artifact@v6
  255. with:
  256. name: build-artifacts
  257. path: |
  258. artifacts
  259. buildlog