build_LoopFollow.yml 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308
  1. name: 4. Build Loop Follow
  2. run-name: Build Loop Follow (${{ github.ref_name }})
  3. on:
  4. workflow_dispatch:
  5. ## Remove the "#" sign from the beginning of the line below to get automated builds on push (code changes in your repository)
  6. #push:
  7. schedule:
  8. # avoid starting an action at xx:00 when GitHub resources are impacted
  9. - cron: "17 12 * * 3" # Checks for updates at 12:17 UTC every Wednesday
  10. - cron: "17 10 1 * *" # Builds the app on the 1st of every month at 10:17 UTC
  11. env:
  12. UPSTREAM_REPO: loopandlearn/LoopFollow
  13. UPSTREAM_BRANCH: ${{ github.ref_name }} # branch on upstream repository to sync from (replace with specific branch name if needed)
  14. 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)
  15. ALIVE_BRANCH_MAIN: alive-main
  16. ALIVE_BRANCH_DEV: alive-dev
  17. jobs:
  18. # Checks if Distribution certificate is present and valid, optionally nukes and
  19. # creates new certs if the repository variable ENABLE_NUKE_CERTS == 'true'
  20. check_certs:
  21. name: Check certificates
  22. uses: ./.github/workflows/create_certs.yml
  23. secrets: inherit
  24. # Checks if GH_PAT holds workflow permissions
  25. # Checks for existence of alive branch; if non-existent creates it
  26. check_alive_and_permissions:
  27. needs: check_certs
  28. runs-on: ubuntu-latest
  29. name: Check alive branch and permissions
  30. permissions:
  31. contents: write
  32. outputs:
  33. WORKFLOW_PERMISSION: ${{ steps.workflow-permission.outputs.has_permission }}
  34. steps:
  35. - name: Check for workflow permissions
  36. id: workflow-permission
  37. env:
  38. TOKEN_TO_CHECK: ${{ secrets.GH_PAT }}
  39. run: |
  40. PERMISSIONS=$(curl -sS -f -I -H "Authorization: token ${{ env.TOKEN_TO_CHECK }}" https://api.github.com | grep ^x-oauth-scopes: | cut -d' ' -f2-);
  41. if [[ $PERMISSIONS =~ "workflow" || $PERMISSIONS == "" ]]; then
  42. echo "GH_PAT holds workflow permissions or is fine-grained PAT."
  43. echo "has_permission=true" >> $GITHUB_OUTPUT # Set WORKFLOW_PERMISSION to false.
  44. else
  45. echo "GH_PAT lacks workflow permissions."
  46. echo "Automated build features will be skipped!"
  47. echo "has_permission=false" >> $GITHUB_OUTPUT # Set WORKFLOW_PERMISSION to false.
  48. fi
  49. - name: Check for alive branches
  50. if: steps.workflow-permission.outputs.has_permission == 'true'
  51. env:
  52. GITHUB_TOKEN: ${{ secrets.GH_PAT }}
  53. run: |
  54. if [[ $(gh api -H "Accept: application/vnd.github+json" /repos/${{ github.repository_owner }}/LoopFollow/branches | jq --raw-output '[.[] | select(.name == "alive-main" or .name == "alive-dev")] | length > 0') == "true" ]]; then
  55. echo "Branches 'alive-main' or 'alive-dev' exist."
  56. echo "ALIVE_BRANCH_EXISTS=true" >> $GITHUB_ENV
  57. else
  58. echo "Branches 'alive-main' and 'alive-dev' do not exist."
  59. echo "ALIVE_BRANCH_EXISTS=false" >> $GITHUB_ENV
  60. fi
  61. - name: Create alive branches
  62. if: env.ALIVE_BRANCH_EXISTS == 'false'
  63. env:
  64. GITHUB_TOKEN: ${{ secrets.GH_PAT }}
  65. run: |
  66. # Get ref for UPSTREAM_REPO:main
  67. 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')
  68. # Get ref for UPSTREAM_REPO:dev
  69. 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')
  70. # Create alive-main branch in LoopFollow fork based on UPSTREAM_REPO:main
  71. gh api \
  72. --method POST \
  73. -H "Authorization: token $GITHUB_TOKEN" \
  74. -H "Accept: application/vnd.github.v3+json" \
  75. /repos/${{ github.repository_owner }}/LoopFollow/git/refs \
  76. -f ref='refs/heads/alive-main' \
  77. -f sha=$SHA_MAIN
  78. # Create alive-dev branch in LoopFollow fork based on UPSTREAM_REPO:dev
  79. gh api \
  80. --method POST \
  81. -H "Authorization: token $GITHUB_TOKEN" \
  82. -H "Accept: application/vnd.github.v3+json" \
  83. /repos/${{ github.repository_owner }}/LoopFollow/git/refs \
  84. -f ref='refs/heads/alive-dev' \
  85. -f sha=$SHA_DEV
  86. # Checks for changes in upstream repository; if changes exist prompts sync for build
  87. # Performs keepalive to avoid stale fork
  88. check_latest_from_upstream:
  89. needs: [check_certs, check_alive_and_permissions]
  90. runs-on: ubuntu-latest
  91. name: Check upstream and keep alive
  92. outputs:
  93. NEW_COMMITS: ${{ steps.sync.outputs.has_new_commits }}
  94. ABORT_SYNC: ${{ steps.check_branch.outputs.ABORT_SYNC }}
  95. steps:
  96. - name: Check if running on main or dev branch
  97. if: |
  98. needs.check_alive_and_permissions.outputs.WORKFLOW_PERMISSION == 'true' &&
  99. (vars.SCHEDULED_BUILD != 'false' || vars.SCHEDULED_SYNC != 'false')
  100. id: check_branch
  101. run: |
  102. if [ "${GITHUB_REF##*/}" = "main" ]; then
  103. echo "Running on main branch"
  104. echo "ALIVE_BRANCH=${ALIVE_BRANCH_MAIN}" >> $GITHUB_OUTPUT
  105. echo "ABORT_SYNC=false" >> $GITHUB_OUTPUT
  106. elif [ "${GITHUB_REF##*/}" = "dev" ]; then
  107. echo "Running on dev branch"
  108. echo "ALIVE_BRANCH=${ALIVE_BRANCH_DEV}" >> $GITHUB_OUTPUT
  109. echo "ABORT_SYNC=false" >> $GITHUB_OUTPUT
  110. else
  111. echo "Not running on main or dev branch"
  112. echo "ABORT_SYNC=true" >> $GITHUB_OUTPUT
  113. fi
  114. - name: Checkout target repo
  115. if: |
  116. needs.check_alive_and_permissions.outputs.WORKFLOW_PERMISSION == 'true' &&
  117. (vars.SCHEDULED_BUILD != 'false' || vars.SCHEDULED_SYNC != 'false')
  118. uses: actions/checkout@v4
  119. with:
  120. token: ${{ secrets.GH_PAT }}
  121. ref: ${{ steps.check_branch.outputs.ALIVE_BRANCH }}
  122. - name: Sync upstream changes
  123. if: | # do not run the upstream sync action on the upstream repository
  124. needs.check_alive_and_permissions.outputs.WORKFLOW_PERMISSION == 'true' &&
  125. vars.SCHEDULED_SYNC != 'false' && github.repository_owner != 'loopandlearn' && steps.check_branch.outputs.ABORT_SYNC == 'false'
  126. id: sync
  127. uses: aormsby/Fork-Sync-With-Upstream-action@v3.4.1
  128. with:
  129. target_sync_branch: ${{ steps.check_branch.outputs.ALIVE_BRANCH }}
  130. shallow_since: 6 months ago
  131. target_repo_token: ${{ secrets.GH_PAT }}
  132. upstream_sync_branch: ${{ env.UPSTREAM_BRANCH }}
  133. upstream_sync_repo: ${{ env.UPSTREAM_REPO }}
  134. # Display a sample message based on the sync output var 'has_new_commits'
  135. - name: New commits found
  136. if: |
  137. needs.check_alive_and_permissions.outputs.WORKFLOW_PERMISSION == 'true' &&
  138. vars.SCHEDULED_SYNC != 'false' && steps.sync.outputs.has_new_commits == 'true'
  139. run: echo "New commits were found to sync."
  140. - name: No new commits
  141. if: |
  142. needs.check_alive_and_permissions.outputs.WORKFLOW_PERMISSION == 'true' &&
  143. vars.SCHEDULED_SYNC != 'false' && steps.sync.outputs.has_new_commits == 'false'
  144. run: echo "There were no new commits."
  145. - name: Show value of 'has_new_commits'
  146. if: needs.check_alive_and_permissions.outputs.WORKFLOW_PERMISSION == 'true' && vars.SCHEDULED_SYNC != 'false' && steps.check_branch.outputs.ABORT_SYNC == 'false'
  147. run: |
  148. echo ${{ steps.sync.outputs.has_new_commits }}
  149. echo "NEW_COMMITS=${{ steps.sync.outputs.has_new_commits }}" >> $GITHUB_OUTPUT
  150. # Keep repository "alive": add empty commits to ALIVE_BRANCH after "time_elapsed" days of inactivity to avoid inactivation of scheduled workflows
  151. - name: Keep alive
  152. run: |
  153. echo "Keep Alive is no longer available"
  154. # if: |
  155. # needs.check_alive_and_permissions.outputs.WORKFLOW_PERMISSION == 'true' &&
  156. # (vars.SCHEDULED_BUILD != 'false' || vars.SCHEDULED_SYNC != 'false')
  157. # uses: gautamkrishnar/keepalive-workflow@v1 # using the workflow with default settings
  158. # with:
  159. # time_elapsed: 20 # Time elapsed from the previous commit to trigger a new automated commit (in days)
  160. - name: Show scheduled build configuration message
  161. if: needs.check_alive_and_permissions.outputs.WORKFLOW_PERMISSION != 'true'
  162. run: |
  163. echo "### :calendar: Scheduled Sync and Build Disabled :mobile_phone_off:" >> $GITHUB_STEP_SUMMARY
  164. echo "You have not yet configured the scheduled sync and build for LoopFollow's browser build." >> $GITHUB_STEP_SUMMARY
  165. echo "Synchronizing your fork of <code>LoopFollow</code> with the upstream repository <code>loopandlearn/LoopFollow</code> will be skipped." >> $GITHUB_STEP_SUMMARY
  166. echo "If you want to enable automatic builds and updates for your LoopFollow, please follow the instructions \
  167. under the following path <code>LoopFollow/fastlane/testflight.md</code>." >> $GITHUB_STEP_SUMMARY
  168. # Builds LoopFollow
  169. build:
  170. name: Build
  171. needs: [check_certs, check_alive_and_permissions, check_latest_from_upstream]
  172. runs-on: macos-15
  173. permissions:
  174. contents: write
  175. if:
  176. | # 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
  177. github.event_name == 'workflow_dispatch' ||
  178. (needs.check_alive_and_permissions.outputs.WORKFLOW_PERMISSION == 'true' &&
  179. (vars.SCHEDULED_BUILD != 'false' && github.event.schedule == '17 10 1 * *') ||
  180. (vars.SCHEDULED_SYNC != 'false' && needs.check_latest_from_upstream.outputs.NEW_COMMITS == 'true' )
  181. )
  182. steps:
  183. - name: Select Xcode version
  184. run: "sudo xcode-select --switch /Applications/Xcode_16.4.app/Contents/Developer"
  185. - name: Checkout Repo for syncing
  186. if: |
  187. needs.check_alive_and_permissions.outputs.WORKFLOW_PERMISSION == 'true' &&
  188. vars.SCHEDULED_SYNC != 'false'
  189. uses: actions/checkout@v4
  190. with:
  191. token: ${{ secrets.GH_PAT }}
  192. ref: ${{ env.TARGET_BRANCH }}
  193. - name: Sync upstream changes
  194. if: | # do not run the upstream sync action on the upstream repository
  195. needs.check_alive_and_permissions.outputs.WORKFLOW_PERMISSION == 'true' &&
  196. vars.SCHEDULED_SYNC != 'false' && github.repository_owner != 'loopandlearn' && needs.check_latest_from_upstream.outputs.ABORT_SYNC == 'false'
  197. id: sync
  198. uses: aormsby/Fork-Sync-With-Upstream-action@v3.4.1
  199. with:
  200. target_sync_branch: ${{ env.TARGET_BRANCH }}
  201. shallow_since: 6 months ago
  202. target_repo_token: ${{ secrets.GH_PAT }}
  203. upstream_sync_branch: ${{ env.UPSTREAM_BRANCH }}
  204. upstream_sync_repo: ${{ env.UPSTREAM_REPO }}
  205. # Display a sample message based on the sync output var 'has_new_commits'
  206. - name: New commits found
  207. if: |
  208. needs.check_alive_and_permissions.outputs.WORKFLOW_PERMISSION == 'true' &&
  209. vars.SCHEDULED_SYNC != 'false' && steps.sync.outputs.has_new_commits == 'true' && needs.check_latest_from_upstream.outputs.ABORT_SYNC == 'false'
  210. run: echo "New commits were found to sync."
  211. - name: No new commits
  212. if: |
  213. needs.check_alive_and_permissions.outputs.WORKFLOW_PERMISSION == 'true' &&
  214. vars.SCHEDULED_SYNC != 'false' && steps.sync.outputs.has_new_commits == 'false' && needs.check_latest_from_upstream.outputs.ABORT_SYNC == 'false'
  215. run: echo "There were no new commits."
  216. - name: Show value of 'has_new_commits'
  217. if: |
  218. needs.check_alive_and_permissions.outputs.WORKFLOW_PERMISSION == 'true'
  219. && vars.SCHEDULED_SYNC != 'false' && needs.check_latest_from_upstream.outputs.ABORT_SYNC == 'false'
  220. run: |
  221. echo ${{ steps.sync.outputs.has_new_commits }}
  222. echo "NEW_COMMITS=${{ steps.sync.outputs.has_new_commits }}" >> $GITHUB_OUTPUT
  223. - name: Checkout Repo for building
  224. uses: actions/checkout@v4
  225. with:
  226. token: ${{ secrets.GH_PAT }}
  227. submodules: recursive
  228. ref: ${{ env.TARGET_BRANCH }}
  229. # Patch Fastlane Match to not print tables
  230. - name: Patch Match Tables
  231. run: |
  232. TABLE_PRINTER_PATH=$(ruby -e 'puts Gem::Specification.find_by_name("fastlane").gem_dir')/match/lib/match/table_printer.rb
  233. if [ -f "$TABLE_PRINTER_PATH" ]; then
  234. sed -i "" "/puts(Terminal::Table.new(params))/d" "$TABLE_PRINTER_PATH"
  235. else
  236. echo "table_printer.rb not found"
  237. exit 1
  238. fi
  239. # Install project dependencies
  240. - name: Install Project Dependencies
  241. run: bundle install
  242. # Sync the GitHub runner clock with the Windows time server (workaround as suggested in https://github.com/actions/runner/issues/2996)
  243. - name: Sync clock
  244. run: sudo sntp -sS time.windows.com
  245. # Build signed LoopFollow IPA file
  246. - name: Fastlane Build & Archive
  247. run: bundle exec fastlane build_LoopFollow
  248. env:
  249. TEAMID: ${{ secrets.TEAMID }}
  250. GH_PAT: ${{ secrets.GH_PAT }}
  251. FASTLANE_KEY_ID: ${{ secrets.FASTLANE_KEY_ID }}
  252. FASTLANE_ISSUER_ID: ${{ secrets.FASTLANE_ISSUER_ID }}
  253. FASTLANE_KEY: ${{ secrets.FASTLANE_KEY }}
  254. MATCH_PASSWORD: ${{ secrets.MATCH_PASSWORD }}
  255. # Upload to TestFlight
  256. - name: Fastlane upload to TestFlight
  257. run: bundle exec fastlane release
  258. env:
  259. TEAMID: ${{ secrets.TEAMID }}
  260. GH_PAT: ${{ secrets.GH_PAT }}
  261. FASTLANE_KEY_ID: ${{ secrets.FASTLANE_KEY_ID }}
  262. FASTLANE_ISSUER_ID: ${{ secrets.FASTLANE_ISSUER_ID }}
  263. FASTLANE_KEY: ${{ secrets.FASTLANE_KEY }}
  264. MATCH_PASSWORD: ${{ secrets.MATCH_PASSWORD }}
  265. # Upload Build artifacts
  266. - name: Upload build log, IPA and Symbol artifacts
  267. if: always()
  268. uses: actions/upload-artifact@v4
  269. with:
  270. name: build-artifacts
  271. path: |
  272. artifacts
  273. buildlog