From 3d5a22759e15cef33f6c7431e7116b3f1bc3c4a5 Mon Sep 17 00:00:00 2001 From: Lukasz Modzelewski Date: Fri, 2 Jan 2026 11:29:26 +0100 Subject: [PATCH 01/11] run testBuild.yaml on pull_request --- .github/workflows/testBuild.yml | 38 ++++++++++++++++++++++----------- 1 file changed, 26 insertions(+), 12 deletions(-) diff --git a/.github/workflows/testBuild.yml b/.github/workflows/testBuild.yml index e189817609e9..055baa229c1d 100644 --- a/.github/workflows/testBuild.yml +++ b/.github/workflows/testBuild.yml @@ -1,6 +1,10 @@ name: Build and deploy apps for testing on: + pull_request: + types: [closed] + paths-ignore: ['docs/**', 'contributingGuides/**', 'help/**', '.github/**', 'scripts/**', 'tests/**'] + workflow_dispatch: inputs: # If not specified, only build iOS and Android apps from the main branch of Expensify/App @@ -31,8 +35,13 @@ on: type: boolean default: true +concurrency: + group: test-build-${{ github.event.pull_request.number || github.run_id }} + cancel-in-progress: false + jobs: prep: + if: github.event_name == 'workflow_dispatch' || (github.event_name == 'pull_request' && github.event.pull_request.merged == true) runs-on: ubuntu-latest outputs: APP_REF: ${{ steps.getHeadRef.outputs.REF || 'main' }} @@ -50,21 +59,25 @@ jobs: OS_BOTIFY_TOKEN: ${{ secrets.OS_BOTIFY_COMMIT_TOKEN }} - name: Validate that the user reviewed the pull request before running a test build - if: ${{ !inputs.REVIEWED_CODE }} + if: ${{ github.event_name == 'workflow_dispatch' && !inputs.REVIEWED_CODE }} run: | echo "::error::🕵️‍♀️ Please carefully review the pull request before running a test build to ensure it does not contain any malicious code" exit 1 - - name: Extract App PR number from URL + - name: Extract App PR number id: extractAppPRNumber - if: ${{ inputs.APP_PULL_REQUEST_URL != '' }} + if: ${{ github.event_name == 'pull_request' || inputs.APP_PULL_REQUEST_URL != '' }} run: | - PR_NUMBER=$(echo '${{ inputs.APP_PULL_REQUEST_URL }}' | sed -E 's|.*/pull/([0-9]+).*|\1|') - if ! [[ "$PR_NUMBER" =~ ^[0-9]+$ ]]; then - echo "::error::❌ Could not extract PR number from URL. Please provide a valid GitHub PR URL (e.g., https://github.com/Expensify/App/pull/12345)" - exit 1 + if [ "${{ github.event_name }}" == "pull_request" ]; then + echo "PR_NUMBER=${{ github.event.pull_request.number }}" >> "$GITHUB_OUTPUT" + elif [ "${{ github.event_name }}" == "workflow_dispatch" ] && [ -n "${{ inputs.APP_PULL_REQUEST_URL }}" ]; then + PR_NUMBER=$(echo '${{ inputs.APP_PULL_REQUEST_URL }}' | sed -E 's|.*/pull/([0-9]+).*|\1|') + if ! [[ "$PR_NUMBER" =~ ^[0-9]+$ ]]; then + echo "::error::❌ Could not extract PR number from URL. Please provide a valid GitHub PR URL (e.g., https://github.com/Expensify/App/pull/12345)" + exit 1 + fi + echo "PR_NUMBER=$PR_NUMBER" >> "$GITHUB_OUTPUT" fi - echo "PR_NUMBER=$PR_NUMBER" >> "$GITHUB_OUTPUT" - name: Extract Mobile-Expensify PR number from URL id: extractMobilePRNumber @@ -113,7 +126,7 @@ jobs: repo: context.repo.repo, pull_number: '${{ needs.prep.outputs.APP_PR_NUMBER }}', }); - + const body = pullRequest.data.body; const regex = /MOBILE-EXPENSIFY:\s*https:\/\/github.com\/Expensify\/Mobile-Expensify\/pull\/(?\d+)/; const found = body.match(regex)?.groups?.prNumber || ""; @@ -132,6 +145,7 @@ jobs: - name: Check if Expensify/Mobile-Expensify pull request number is correct id: getHeadRef + if: github.event_name == 'workflow_dispatch' run: | set -e if [[ -z "${{ needs.prep.outputs.MOBILE_PR_NUMBER }}" && -z "${{ needs.getMobileExpensifyPR.outputs.MOBILE_EXPENSIFY_PR }}" ]]; then @@ -180,7 +194,7 @@ jobs: web: name: Build and deploy Web - if: ${{ inputs.WEB && needs.prep.outputs.APP_PR_NUMBER }} + if: ${{ (github.event_name == 'pull_request' || inputs.WEB) && needs.prep.outputs.APP_PR_NUMBER }} needs: [prep] runs-on: ubuntu-latest-xl env: @@ -219,7 +233,7 @@ jobs: androidHybrid: name: Build Android HybridApp - if: ${{ inputs.ANDROID }} + if: ${{ github.event_name == 'pull_request' || inputs.ANDROID }} needs: [prep, getMobileExpensifyPR, getMobileExpensifyRef] runs-on: ubuntu-latest-xl env: @@ -327,7 +341,7 @@ jobs: iosHybrid: name: Build and deploy iOS for testing - if: ${{ inputs.IOS }} + if: ${{ github.event_name == 'pull_request' || inputs.IOS }} needs: [prep, getMobileExpensifyPR, getMobileExpensifyRef] env: DEVELOPER_DIR: /Applications/Xcode_26.0.app/Contents/Developer From 310343a1479b3d63ac967575b9ee08d7e87149e7 Mon Sep 17 00:00:00 2001 From: Lukasz Modzelewski Date: Thu, 22 Jan 2026 09:57:58 +0100 Subject: [PATCH 02/11] run testBuild.yaml on push instead of pull_request --- .github/workflows/testBuild.yml | 35 ++++++++++++++++++++++----------- 1 file changed, 23 insertions(+), 12 deletions(-) diff --git a/.github/workflows/testBuild.yml b/.github/workflows/testBuild.yml index 055baa229c1d..8ae0ee72550b 100644 --- a/.github/workflows/testBuild.yml +++ b/.github/workflows/testBuild.yml @@ -1,9 +1,9 @@ name: Build and deploy apps for testing on: - pull_request: - types: [closed] - paths-ignore: ['docs/**', 'contributingGuides/**', 'help/**', '.github/**', 'scripts/**', 'tests/**'] + push: + branches: [main] + paths-ignore: ['docs/**', 'contributingGuides/**', 'help/**', '.github/**', 'scripts/**', 'tests/**', 'jest/**', '.claude/**'] workflow_dispatch: inputs: @@ -36,15 +36,14 @@ on: default: true concurrency: - group: test-build-${{ github.event.pull_request.number || github.run_id }} + group: test-build-${{ github.sha || github.run_id }} cancel-in-progress: false jobs: prep: - if: github.event_name == 'workflow_dispatch' || (github.event_name == 'pull_request' && github.event.pull_request.merged == true) runs-on: ubuntu-latest outputs: - APP_REF: ${{ steps.getHeadRef.outputs.REF || 'main' }} + APP_REF: ${{ steps.getHeadRef.outputs.REF || steps.setAppRef.outputs.REF || 'main' }} APP_PR_NUMBER: ${{ steps.extractAppPRNumber.outputs.PR_NUMBER }} MOBILE_PR_NUMBER: ${{ steps.extractMobilePRNumber.outputs.PR_NUMBER }} steps: @@ -64,12 +63,19 @@ jobs: echo "::error::🕵️‍♀️ Please carefully review the pull request before running a test build to ensure it does not contain any malicious code" exit 1 + - name: Get merged pull request + id: getMergedPullRequest + if: ${{ github.event_name == 'push' }} + uses: actions-ecosystem/action-get-merged-pull-request@59afe90821bb0b555082ce8ff1e36b03f91553d9 + with: + github_token: ${{ github.token }} + - name: Extract App PR number id: extractAppPRNumber - if: ${{ github.event_name == 'pull_request' || inputs.APP_PULL_REQUEST_URL != '' }} + if: ${{ github.event_name == 'push' || inputs.APP_PULL_REQUEST_URL != '' }} run: | - if [ "${{ github.event_name }}" == "pull_request" ]; then - echo "PR_NUMBER=${{ github.event.pull_request.number }}" >> "$GITHUB_OUTPUT" + if [ "${{ github.event_name }}" == "push" ]; then + echo "PR_NUMBER=${{ steps.getMergedPullRequest.outputs.number }}" >> "$GITHUB_OUTPUT" elif [ "${{ github.event_name }}" == "workflow_dispatch" ] && [ -n "${{ inputs.APP_PULL_REQUEST_URL }}" ]; then PR_NUMBER=$(echo '${{ inputs.APP_PULL_REQUEST_URL }}' | sed -E 's|.*/pull/([0-9]+).*|\1|') if ! [[ "$PR_NUMBER" =~ ^[0-9]+$ ]]; then @@ -103,6 +109,11 @@ jobs: env: GITHUB_TOKEN: ${{ github.token }} + - name: Set App ref for push + id: setAppRef + if: ${{ github.event_name == 'push' }} + run: echo "REF=${{ github.sha }}" >> "$GITHUB_OUTPUT" + getMobileExpensifyPR: runs-on: ubuntu-latest needs: [prep] @@ -194,7 +205,7 @@ jobs: web: name: Build and deploy Web - if: ${{ (github.event_name == 'pull_request' || inputs.WEB) && needs.prep.outputs.APP_PR_NUMBER }} + if: ${{ (github.event_name == 'push' || inputs.WEB) && needs.prep.outputs.APP_PR_NUMBER }} needs: [prep] runs-on: ubuntu-latest-xl env: @@ -233,7 +244,7 @@ jobs: androidHybrid: name: Build Android HybridApp - if: ${{ github.event_name == 'pull_request' || inputs.ANDROID }} + if: ${{ github.event_name == 'push' || inputs.ANDROID }} needs: [prep, getMobileExpensifyPR, getMobileExpensifyRef] runs-on: ubuntu-latest-xl env: @@ -341,7 +352,7 @@ jobs: iosHybrid: name: Build and deploy iOS for testing - if: ${{ github.event_name == 'pull_request' || inputs.IOS }} + if: ${{ github.event_name == 'push' || inputs.IOS }} needs: [prep, getMobileExpensifyPR, getMobileExpensifyRef] env: DEVELOPER_DIR: /Applications/Xcode_26.0.app/Contents/Developer From 7fe4f1ca67da12aa7c5a1672601a03953b9c7472 Mon Sep 17 00:00:00 2001 From: Lukasz Modzelewski Date: Thu, 22 Jan 2026 12:34:17 +0100 Subject: [PATCH 03/11] add guard if MERGED_PR_NUMBER does not exist --- .github/workflows/testBuild.yml | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/.github/workflows/testBuild.yml b/.github/workflows/testBuild.yml index 8ae0ee72550b..9e66abadf161 100644 --- a/.github/workflows/testBuild.yml +++ b/.github/workflows/testBuild.yml @@ -75,7 +75,12 @@ jobs: if: ${{ github.event_name == 'push' || inputs.APP_PULL_REQUEST_URL != '' }} run: | if [ "${{ github.event_name }}" == "push" ]; then - echo "PR_NUMBER=${{ steps.getMergedPullRequest.outputs.number }}" >> "$GITHUB_OUTPUT" + MERGED_PR_NUMBER="${{ steps.getMergedPullRequest.outputs.number }}" + if [ -z "$MERGED_PR_NUMBER" ]; then + echo "::error::❌ No merged PR found on main for sha ${{ github.sha }}. Aborting build." + exit 1 + fi + echo "PR_NUMBER=$MERGED_PR_NUMBER" >> "$GITHUB_OUTPUT" elif [ "${{ github.event_name }}" == "workflow_dispatch" ] && [ -n "${{ inputs.APP_PULL_REQUEST_URL }}" ]; then PR_NUMBER=$(echo '${{ inputs.APP_PULL_REQUEST_URL }}' | sed -E 's|.*/pull/([0-9]+).*|\1|') if ! [[ "$PR_NUMBER" =~ ^[0-9]+$ ]]; then From 37d83e75d0266a3b83e855680c49acf510e01e0a Mon Sep 17 00:00:00 2001 From: Lukasz Modzelewski Date: Thu, 22 Jan 2026 14:49:40 +0100 Subject: [PATCH 04/11] do getMobileExpensifyPR only if workflow_dispatch --- .github/workflows/testBuild.yml | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/.github/workflows/testBuild.yml b/.github/workflows/testBuild.yml index 9e66abadf161..db38add7dbb3 100644 --- a/.github/workflows/testBuild.yml +++ b/.github/workflows/testBuild.yml @@ -127,6 +127,7 @@ jobs: steps: - name: Check if author specified Expensify/Mobile-Expensify PR id: mobileExpensifyPR + if: ${{ github.event_name == 'workflow_dispatch' }} # v7 uses: actions/github-script@60a0d83039c74a4aee543508d2ffcb1c3799cdea with: @@ -156,12 +157,13 @@ jobs: MOBILE_EXPENSIFY_REF: ${{ steps.getHeadRef.outputs.REF || 'main' }} steps: - name: Checkout + if: ${{ github.event_name == 'workflow_dispatch' }} # v4 uses: actions/checkout@8ade135a41bc03ea155e62e844d188df1ea18608 - name: Check if Expensify/Mobile-Expensify pull request number is correct id: getHeadRef - if: github.event_name == 'workflow_dispatch' + if: ${{ github.event_name == 'workflow_dispatch' }} run: | set -e if [[ -z "${{ needs.prep.outputs.MOBILE_PR_NUMBER }}" && -z "${{ needs.getMobileExpensifyPR.outputs.MOBILE_EXPENSIFY_PR }}" ]]; then From e39ef7427c5998b8e6192e11367fbb2322475ab5 Mon Sep 17 00:00:00 2001 From: Lukasz Modzelewski Date: Fri, 23 Jan 2026 09:04:50 +0100 Subject: [PATCH 05/11] remove concurrency --- .github/workflows/testBuild.yml | 4 ---- 1 file changed, 4 deletions(-) diff --git a/.github/workflows/testBuild.yml b/.github/workflows/testBuild.yml index db38add7dbb3..932e4e71d120 100644 --- a/.github/workflows/testBuild.yml +++ b/.github/workflows/testBuild.yml @@ -35,10 +35,6 @@ on: type: boolean default: true -concurrency: - group: test-build-${{ github.sha || github.run_id }} - cancel-in-progress: false - jobs: prep: runs-on: ubuntu-latest From 8f1f6362734273b60db9e7d5f10addec47ee1063 Mon Sep 17 00:00:00 2001 From: Lukasz Modzelewski Date: Fri, 23 Jan 2026 14:20:13 +0100 Subject: [PATCH 06/11] handle submodule updates from OSBotify --- .github/workflows/testBuild.yml | 60 +++++++++++++++++++++++---------- 1 file changed, 43 insertions(+), 17 deletions(-) diff --git a/.github/workflows/testBuild.yml b/.github/workflows/testBuild.yml index 932e4e71d120..8cb1fdb673ca 100644 --- a/.github/workflows/testBuild.yml +++ b/.github/workflows/testBuild.yml @@ -42,6 +42,9 @@ jobs: APP_REF: ${{ steps.getHeadRef.outputs.REF || steps.setAppRef.outputs.REF || 'main' }} APP_PR_NUMBER: ${{ steps.extractAppPRNumber.outputs.PR_NUMBER }} MOBILE_PR_NUMBER: ${{ steps.extractMobilePRNumber.outputs.PR_NUMBER }} + BUILD_WEB: ${{ steps.detectOSBotifyPush.outputs.BUILD_WEB || 'true' }} + BUILD_MOBILE: ${{ steps.detectOSBotifyPush.outputs.BUILD_MOBILE || 'true' }} + POST_COMMENTS: ${{ steps.detectOSBotifyPush.outputs.POST_COMMENTS || 'true' }} steps: - name: Checkout # v4 @@ -59,33 +62,55 @@ jobs: echo "::error::🕵️‍♀️ Please carefully review the pull request before running a test build to ensure it does not contain any malicious code" exit 1 + - name: Detect OSBotify automated push + id: detectOSBotifyPush + if: ${{ github.event_name == 'push' && github.actor == 'OSBotify' }} + run: | + COMMIT_MESSAGE="${{ github.event.head_commit.message }}" + if [[ "$COMMIT_MESSAGE" == Update\ version\ to* ]]; then + echo "::notice::⏭️ OSBotify version bump detected. Skipping workflow." + echo "BUILD_MOBILE=false" >> "$GITHUB_OUTPUT" + echo "BUILD_WEB=false" >> "$GITHUB_OUTPUT" + echo "POST_COMMENTS=false" >> "$GITHUB_OUTPUT" + elif [[ "$COMMIT_MESSAGE" == Update\ Mobile-Expensify\ submodule* ]]; then + echo "::notice::✅ OSBotify Mobile-Expensify submodule update detected. Building native only." + echo "BUILD_WEB=false" >> "$GITHUB_OUTPUT" + echo "POST_COMMENTS=false" >> "$GITHUB_OUTPUT" + fi + - name: Get merged pull request id: getMergedPullRequest - if: ${{ github.event_name == 'push' }} + if: ${{ github.event_name == 'push' && github.actor != 'OSBotify' }} uses: actions-ecosystem/action-get-merged-pull-request@59afe90821bb0b555082ce8ff1e36b03f91553d9 with: github_token: ${{ github.token }} - name: Extract App PR number id: extractAppPRNumber - if: ${{ github.event_name == 'push' || inputs.APP_PULL_REQUEST_URL != '' }} + if: ${{ (github.event_name == 'push' && github.actor != 'OSBotify') || (github.event_name == 'workflow_dispatch' && inputs.APP_PULL_REQUEST_URL != '') }} run: | - if [ "${{ github.event_name }}" == "push" ]; then - MERGED_PR_NUMBER="${{ steps.getMergedPullRequest.outputs.number }}" - if [ -z "$MERGED_PR_NUMBER" ]; then - echo "::error::❌ No merged PR found on main for sha ${{ github.sha }}. Aborting build." - exit 1 - fi - echo "PR_NUMBER=$MERGED_PR_NUMBER" >> "$GITHUB_OUTPUT" - elif [ "${{ github.event_name }}" == "workflow_dispatch" ] && [ -n "${{ inputs.APP_PULL_REQUEST_URL }}" ]; then + # Handle workflow_dispatch with App PR URL + if [ "${{ github.event_name }}" == "workflow_dispatch" ] && [ -n "${{ inputs.APP_PULL_REQUEST_URL }}" ]; then PR_NUMBER=$(echo '${{ inputs.APP_PULL_REQUEST_URL }}' | sed -E 's|.*/pull/([0-9]+).*|\1|') if ! [[ "$PR_NUMBER" =~ ^[0-9]+$ ]]; then echo "::error::❌ Could not extract PR number from URL. Please provide a valid GitHub PR URL (e.g., https://github.com/Expensify/App/pull/12345)" exit 1 fi + echo "::notice::✅ App PR number from input: $PR_NUMBER" echo "PR_NUMBER=$PR_NUMBER" >> "$GITHUB_OUTPUT" + exit 0 fi + # Regular push - require merged PR + MERGED_PR_NUMBER="${{ steps.getMergedPullRequest.outputs.number }}" + if [ -z "$MERGED_PR_NUMBER" ]; then + echo "::error::❌ Could not find merged PR number for this commit." + exit 1 + fi + + echo "::notice::✅ Merged PR number found: $MERGED_PR_NUMBER" + echo "PR_NUMBER=$MERGED_PR_NUMBER" >> "$GITHUB_OUTPUT" + - name: Extract Mobile-Expensify PR number from URL id: extractMobilePRNumber if: ${{ inputs.MOBILE_EXPENSIFY_PULL_REQUEST_URL != '' }} @@ -174,7 +199,8 @@ jobs: postGitHubCommentBuildStarted: name: Post build started comment runs-on: ubuntu-latest - needs: [prep, getMobileExpensifyPR, getMobileExpensifyRef] + if: ${{ needs.prep.outputs.POST_COMMENTS == 'true' }} + needs: [prep, getMobileExpensifyPR] steps: - name: Add build start comment to Expensify/App PR if: ${{ needs.prep.outputs.APP_PR_NUMBER != '' }} @@ -208,7 +234,7 @@ jobs: web: name: Build and deploy Web - if: ${{ (github.event_name == 'push' || inputs.WEB) && needs.prep.outputs.APP_PR_NUMBER }} + if: ${{ needs.prep.outputs.BUILD_WEB == 'true' && (github.event_name == 'push' || inputs.WEB) && needs.prep.outputs.APP_PR_NUMBER }} needs: [prep] runs-on: ubuntu-latest-xl env: @@ -247,7 +273,7 @@ jobs: androidHybrid: name: Build Android HybridApp - if: ${{ github.event_name == 'push' || inputs.ANDROID }} + if: ${{ needs.prep.outputs.BUILD_MOBILE == 'true' && (github.event_name == 'push' || inputs.ANDROID) }} needs: [prep, getMobileExpensifyPR, getMobileExpensifyRef] runs-on: ubuntu-latest-xl env: @@ -264,7 +290,7 @@ jobs: token: ${{ secrets.OS_BOTIFY_TOKEN }} - name: Checkout Mobile-Expensify to specified branch or commit - if: ${{ needs.getMobileExpensifyRef.outputs.MOBILE_EXPENSIFY_REF != '' }} + if: ${{ github.event_name == 'workflow_dispatch' && needs.getMobileExpensifyRef.outputs.MOBILE_EXPENSIFY_REF != '' }} run: | cd Mobile-Expensify git fetch origin ${{ needs.getMobileExpensifyRef.outputs.MOBILE_EXPENSIFY_REF }} @@ -355,7 +381,7 @@ jobs: iosHybrid: name: Build and deploy iOS for testing - if: ${{ github.event_name == 'push' || inputs.IOS }} + if: ${{ needs.prep.outputs.BUILD_MOBILE == 'true' && (github.event_name == 'push' || inputs.IOS) }} needs: [prep, getMobileExpensifyPR, getMobileExpensifyRef] env: DEVELOPER_DIR: /Applications/Xcode_26.0.app/Contents/Developer @@ -373,7 +399,7 @@ jobs: token: ${{ secrets.OS_BOTIFY_TOKEN }} - name: Checkout Mobile-Expensify to specified branch or commit - if: ${{ needs.getMobileExpensifyRef.outputs.MOBILE_EXPENSIFY_REF != '' }} + if: ${{ github.event_name == 'workflow_dispatch' && needs.getMobileExpensifyRef.outputs.MOBILE_EXPENSIFY_REF != '' }} run: | cd Mobile-Expensify git fetch origin ${{ needs.getMobileExpensifyRef.outputs.MOBILE_EXPENSIFY_REF }} @@ -477,7 +503,7 @@ jobs: postGithubComment: runs-on: ubuntu-latest - if: always() + if: ${{ always() && needs.prep.outputs.POST_COMMENTS == 'true' }} name: Post a GitHub comment with app download links for testing needs: [prep, getMobileExpensifyPR, web, androidHybrid, iosHybrid] steps: From cf810e60c996ca29f4a62a087aaa8dfa69eb13ce Mon Sep 17 00:00:00 2001 From: Lukasz Modzelewski Date: Fri, 23 Jan 2026 14:57:04 +0100 Subject: [PATCH 07/11] combine set App ref step --- .github/workflows/testBuild.yml | 22 ++++++++++------------ 1 file changed, 10 insertions(+), 12 deletions(-) diff --git a/.github/workflows/testBuild.yml b/.github/workflows/testBuild.yml index 8cb1fdb673ca..f418eb2ddba7 100644 --- a/.github/workflows/testBuild.yml +++ b/.github/workflows/testBuild.yml @@ -39,7 +39,7 @@ jobs: prep: runs-on: ubuntu-latest outputs: - APP_REF: ${{ steps.getHeadRef.outputs.REF || steps.setAppRef.outputs.REF || 'main' }} + APP_REF: ${{ steps.getHeadRef.outputs.REF || 'main' }} APP_PR_NUMBER: ${{ steps.extractAppPRNumber.outputs.PR_NUMBER }} MOBILE_PR_NUMBER: ${{ steps.extractMobilePRNumber.outputs.PR_NUMBER }} BUILD_WEB: ${{ steps.detectOSBotifyPush.outputs.BUILD_WEB || 'true' }} @@ -122,24 +122,22 @@ jobs: fi echo "PR_NUMBER=$PR_NUMBER" >> "$GITHUB_OUTPUT" - - name: Check if App pull request number is correct - if: ${{ github.event_name == 'workflow_dispatch' }} + - name: Set App ref id: getHeadRef run: | set -e - if [ -z "${{ steps.extractAppPRNumber.outputs.PR_NUMBER }}" ]; then - echo "REF=" >> "$GITHUB_OUTPUT" - else - echo "REF=$(gh pr view ${{ steps.extractAppPRNumber.outputs.PR_NUMBER }} --json headRefOid --jq '.headRefOid')" >> "$GITHUB_OUTPUT" + if [ "${{ github.event_name }}" == "workflow_dispatch" ]; then + if [ -z "${{ steps.extractAppPRNumber.outputs.PR_NUMBER }}" ]; then + echo "REF=" >> "$GITHUB_OUTPUT" + else + echo "REF=$(gh pr view ${{ steps.extractAppPRNumber.outputs.PR_NUMBER }} --json headRefOid --jq '.headRefOid')" >> "$GITHUB_OUTPUT" + fi + elif [ "${{ github.event_name }}" == "push" ]; then + echo "REF=${{ github.sha }}" >> "$GITHUB_OUTPUT" fi env: GITHUB_TOKEN: ${{ github.token }} - - name: Set App ref for push - id: setAppRef - if: ${{ github.event_name == 'push' }} - run: echo "REF=${{ github.sha }}" >> "$GITHUB_OUTPUT" - getMobileExpensifyPR: runs-on: ubuntu-latest needs: [prep] From 9dbe2febcfecca6489a21b3b7cf1308d6e483aee Mon Sep 17 00:00:00 2001 From: Lukasz Modzelewski Date: Fri, 23 Jan 2026 16:00:25 +0100 Subject: [PATCH 08/11] add testBuildOnPush workflow --- .github/workflows/testBuild.yml | 96 ++----- .github/workflows/testBuildOnPush.yml | 354 ++++++++++++++++++++++++++ 2 files changed, 376 insertions(+), 74 deletions(-) create mode 100644 .github/workflows/testBuildOnPush.yml diff --git a/.github/workflows/testBuild.yml b/.github/workflows/testBuild.yml index f418eb2ddba7..b56cf6db5993 100644 --- a/.github/workflows/testBuild.yml +++ b/.github/workflows/testBuild.yml @@ -1,10 +1,6 @@ name: Build and deploy apps for testing on: - push: - branches: [main] - paths-ignore: ['docs/**', 'contributingGuides/**', 'help/**', '.github/**', 'scripts/**', 'tests/**', 'jest/**', '.claude/**'] - workflow_dispatch: inputs: # If not specified, only build iOS and Android apps from the main branch of Expensify/App @@ -42,9 +38,6 @@ jobs: APP_REF: ${{ steps.getHeadRef.outputs.REF || 'main' }} APP_PR_NUMBER: ${{ steps.extractAppPRNumber.outputs.PR_NUMBER }} MOBILE_PR_NUMBER: ${{ steps.extractMobilePRNumber.outputs.PR_NUMBER }} - BUILD_WEB: ${{ steps.detectOSBotifyPush.outputs.BUILD_WEB || 'true' }} - BUILD_MOBILE: ${{ steps.detectOSBotifyPush.outputs.BUILD_MOBILE || 'true' }} - POST_COMMENTS: ${{ steps.detectOSBotifyPush.outputs.POST_COMMENTS || 'true' }} steps: - name: Checkout # v4 @@ -57,59 +50,21 @@ jobs: OS_BOTIFY_TOKEN: ${{ secrets.OS_BOTIFY_COMMIT_TOKEN }} - name: Validate that the user reviewed the pull request before running a test build - if: ${{ github.event_name == 'workflow_dispatch' && !inputs.REVIEWED_CODE }} + if: ${{ !inputs.REVIEWED_CODE }} run: | echo "::error::🕵️‍♀️ Please carefully review the pull request before running a test build to ensure it does not contain any malicious code" exit 1 - - name: Detect OSBotify automated push - id: detectOSBotifyPush - if: ${{ github.event_name == 'push' && github.actor == 'OSBotify' }} - run: | - COMMIT_MESSAGE="${{ github.event.head_commit.message }}" - if [[ "$COMMIT_MESSAGE" == Update\ version\ to* ]]; then - echo "::notice::⏭️ OSBotify version bump detected. Skipping workflow." - echo "BUILD_MOBILE=false" >> "$GITHUB_OUTPUT" - echo "BUILD_WEB=false" >> "$GITHUB_OUTPUT" - echo "POST_COMMENTS=false" >> "$GITHUB_OUTPUT" - elif [[ "$COMMIT_MESSAGE" == Update\ Mobile-Expensify\ submodule* ]]; then - echo "::notice::✅ OSBotify Mobile-Expensify submodule update detected. Building native only." - echo "BUILD_WEB=false" >> "$GITHUB_OUTPUT" - echo "POST_COMMENTS=false" >> "$GITHUB_OUTPUT" - fi - - - name: Get merged pull request - id: getMergedPullRequest - if: ${{ github.event_name == 'push' && github.actor != 'OSBotify' }} - uses: actions-ecosystem/action-get-merged-pull-request@59afe90821bb0b555082ce8ff1e36b03f91553d9 - with: - github_token: ${{ github.token }} - - - name: Extract App PR number + - name: Extract App PR number from URL id: extractAppPRNumber - if: ${{ (github.event_name == 'push' && github.actor != 'OSBotify') || (github.event_name == 'workflow_dispatch' && inputs.APP_PULL_REQUEST_URL != '') }} + if: ${{ inputs.APP_PULL_REQUEST_URL != '' }} run: | - # Handle workflow_dispatch with App PR URL - if [ "${{ github.event_name }}" == "workflow_dispatch" ] && [ -n "${{ inputs.APP_PULL_REQUEST_URL }}" ]; then - PR_NUMBER=$(echo '${{ inputs.APP_PULL_REQUEST_URL }}' | sed -E 's|.*/pull/([0-9]+).*|\1|') - if ! [[ "$PR_NUMBER" =~ ^[0-9]+$ ]]; then - echo "::error::❌ Could not extract PR number from URL. Please provide a valid GitHub PR URL (e.g., https://github.com/Expensify/App/pull/12345)" - exit 1 - fi - echo "::notice::✅ App PR number from input: $PR_NUMBER" - echo "PR_NUMBER=$PR_NUMBER" >> "$GITHUB_OUTPUT" - exit 0 - fi - - # Regular push - require merged PR - MERGED_PR_NUMBER="${{ steps.getMergedPullRequest.outputs.number }}" - if [ -z "$MERGED_PR_NUMBER" ]; then - echo "::error::❌ Could not find merged PR number for this commit." + PR_NUMBER=$(echo '${{ inputs.APP_PULL_REQUEST_URL }}' | sed -E 's|.*/pull/([0-9]+).*|\1|') + if ! [[ "$PR_NUMBER" =~ ^[0-9]+$ ]]; then + echo "::error::❌ Could not extract PR number from URL. Please provide a valid GitHub PR URL (e.g., https://github.com/Expensify/App/pull/12345)" exit 1 fi - - echo "::notice::✅ Merged PR number found: $MERGED_PR_NUMBER" - echo "PR_NUMBER=$MERGED_PR_NUMBER" >> "$GITHUB_OUTPUT" + echo "PR_NUMBER=$PR_NUMBER" >> "$GITHUB_OUTPUT" - name: Extract Mobile-Expensify PR number from URL id: extractMobilePRNumber @@ -122,18 +77,15 @@ jobs: fi echo "PR_NUMBER=$PR_NUMBER" >> "$GITHUB_OUTPUT" - - name: Set App ref + - name: Check if App pull request number is correct + if: ${{ github.event_name == 'workflow_dispatch' }} id: getHeadRef run: | set -e - if [ "${{ github.event_name }}" == "workflow_dispatch" ]; then - if [ -z "${{ steps.extractAppPRNumber.outputs.PR_NUMBER }}" ]; then - echo "REF=" >> "$GITHUB_OUTPUT" - else - echo "REF=$(gh pr view ${{ steps.extractAppPRNumber.outputs.PR_NUMBER }} --json headRefOid --jq '.headRefOid')" >> "$GITHUB_OUTPUT" - fi - elif [ "${{ github.event_name }}" == "push" ]; then - echo "REF=${{ github.sha }}" >> "$GITHUB_OUTPUT" + if [ -z "${{ steps.extractAppPRNumber.outputs.PR_NUMBER }}" ]; then + echo "REF=" >> "$GITHUB_OUTPUT" + else + echo "REF=$(gh pr view ${{ steps.extractAppPRNumber.outputs.PR_NUMBER }} --json headRefOid --jq '.headRefOid')" >> "$GITHUB_OUTPUT" fi env: GITHUB_TOKEN: ${{ github.token }} @@ -146,7 +98,6 @@ jobs: steps: - name: Check if author specified Expensify/Mobile-Expensify PR id: mobileExpensifyPR - if: ${{ github.event_name == 'workflow_dispatch' }} # v7 uses: actions/github-script@60a0d83039c74a4aee543508d2ffcb1c3799cdea with: @@ -162,7 +113,7 @@ jobs: repo: context.repo.repo, pull_number: '${{ needs.prep.outputs.APP_PR_NUMBER }}', }); - + const body = pullRequest.data.body; const regex = /MOBILE-EXPENSIFY:\s*https:\/\/github.com\/Expensify\/Mobile-Expensify\/pull\/(?\d+)/; const found = body.match(regex)?.groups?.prNumber || ""; @@ -176,13 +127,11 @@ jobs: MOBILE_EXPENSIFY_REF: ${{ steps.getHeadRef.outputs.REF || 'main' }} steps: - name: Checkout - if: ${{ github.event_name == 'workflow_dispatch' }} # v4 uses: actions/checkout@8ade135a41bc03ea155e62e844d188df1ea18608 - name: Check if Expensify/Mobile-Expensify pull request number is correct id: getHeadRef - if: ${{ github.event_name == 'workflow_dispatch' }} run: | set -e if [[ -z "${{ needs.prep.outputs.MOBILE_PR_NUMBER }}" && -z "${{ needs.getMobileExpensifyPR.outputs.MOBILE_EXPENSIFY_PR }}" ]]; then @@ -197,8 +146,7 @@ jobs: postGitHubCommentBuildStarted: name: Post build started comment runs-on: ubuntu-latest - if: ${{ needs.prep.outputs.POST_COMMENTS == 'true' }} - needs: [prep, getMobileExpensifyPR] + needs: [prep, getMobileExpensifyPR, getMobileExpensifyRef] steps: - name: Add build start comment to Expensify/App PR if: ${{ needs.prep.outputs.APP_PR_NUMBER != '' }} @@ -232,7 +180,7 @@ jobs: web: name: Build and deploy Web - if: ${{ needs.prep.outputs.BUILD_WEB == 'true' && (github.event_name == 'push' || inputs.WEB) && needs.prep.outputs.APP_PR_NUMBER }} + if: ${{ inputs.WEB && needs.prep.outputs.APP_PR_NUMBER }} needs: [prep] runs-on: ubuntu-latest-xl env: @@ -271,7 +219,7 @@ jobs: androidHybrid: name: Build Android HybridApp - if: ${{ needs.prep.outputs.BUILD_MOBILE == 'true' && (github.event_name == 'push' || inputs.ANDROID) }} + if: ${{ inputs.ANDROID }} needs: [prep, getMobileExpensifyPR, getMobileExpensifyRef] runs-on: ubuntu-latest-xl env: @@ -288,7 +236,7 @@ jobs: token: ${{ secrets.OS_BOTIFY_TOKEN }} - name: Checkout Mobile-Expensify to specified branch or commit - if: ${{ github.event_name == 'workflow_dispatch' && needs.getMobileExpensifyRef.outputs.MOBILE_EXPENSIFY_REF != '' }} + if: ${{ needs.getMobileExpensifyRef.outputs.MOBILE_EXPENSIFY_REF != '' }} run: | cd Mobile-Expensify git fetch origin ${{ needs.getMobileExpensifyRef.outputs.MOBILE_EXPENSIFY_REF }} @@ -379,7 +327,7 @@ jobs: iosHybrid: name: Build and deploy iOS for testing - if: ${{ needs.prep.outputs.BUILD_MOBILE == 'true' && (github.event_name == 'push' || inputs.IOS) }} + if: ${{ inputs.IOS }} needs: [prep, getMobileExpensifyPR, getMobileExpensifyRef] env: DEVELOPER_DIR: /Applications/Xcode_26.0.app/Contents/Developer @@ -397,7 +345,7 @@ jobs: token: ${{ secrets.OS_BOTIFY_TOKEN }} - name: Checkout Mobile-Expensify to specified branch or commit - if: ${{ github.event_name == 'workflow_dispatch' && needs.getMobileExpensifyRef.outputs.MOBILE_EXPENSIFY_REF != '' }} + if: ${{ needs.getMobileExpensifyRef.outputs.MOBILE_EXPENSIFY_REF != '' }} run: | cd Mobile-Expensify git fetch origin ${{ needs.getMobileExpensifyRef.outputs.MOBILE_EXPENSIFY_REF }} @@ -501,7 +449,7 @@ jobs: postGithubComment: runs-on: ubuntu-latest - if: ${{ always() && needs.prep.outputs.POST_COMMENTS == 'true' }} + if: always() name: Post a GitHub comment with app download links for testing needs: [prep, getMobileExpensifyPR, web, androidHybrid, iosHybrid] steps: @@ -540,4 +488,4 @@ jobs: ANDROID: ${{ needs.androidHybrid.result }} IOS: ${{ needs.iosHybrid.result }} ANDROID_LINK: ${{ needs.androidHybrid.outputs.ROCK_ANDROID_ADHOC_INDEX_URL }} - IOS_LINK: ${{ needs.iosHybrid.outputs.ROCK_IOS_ADHOC_INDEX_URL }} + IOS_LINK: ${{ needs.iosHybrid.outputs.ROCK_IOS_ADHOC_INDEX_URL }} \ No newline at end of file diff --git a/.github/workflows/testBuildOnPush.yml b/.github/workflows/testBuildOnPush.yml new file mode 100644 index 000000000000..6661e50fc5e3 --- /dev/null +++ b/.github/workflows/testBuildOnPush.yml @@ -0,0 +1,354 @@ +name: Build and deploy apps for testing on push + +on: + push: + branches: [main] + paths-ignore: ['docs/**', 'contributingGuides/**', 'help/**', '.github/**', 'scripts/**', 'tests/**', 'jest/**', '.claude/**'] + +jobs: + prep: + runs-on: ubuntu-latest + outputs: + APP_REF: ${{ github.sha }} + APP_PR_NUMBER: ${{ steps.getMergedPullRequest.outputs.number }} + BUILD_WEB: ${{ steps.detectOSBotifyPush.outputs.BUILD_WEB || 'true' }} + BUILD_MOBILE: ${{ steps.detectOSBotifyPush.outputs.BUILD_MOBILE || 'true' }} + POST_COMMENTS: ${{ steps.detectOSBotifyPush.outputs.POST_COMMENTS || 'true' }} + steps: + - name: Checkout + # v4 + uses: actions/checkout@8ade135a41bc03ea155e62e844d188df1ea18608 + + - name: Validate that user is an Expensify employee + uses: ./.github/actions/composite/validateActor + with: + REQUIRE_APP_DEPLOYER: false + OS_BOTIFY_TOKEN: ${{ secrets.OS_BOTIFY_COMMIT_TOKEN }} + + - name: Detect OSBotify automated push + id: detectOSBotifyPush + if: ${{ github.actor == 'OSBotify' }} + env: + COMMIT_MESSAGE: ${{ github.event.head_commit.message }} + run: | + if [[ "$COMMIT_MESSAGE" == Update\ Mobile-Expensify\ submodule* ]]; then + echo "::notice::✅ OSBotify Mobile-Expensify submodule update detected. Building native only." + { + echo "BUILD_WEB=false" + echo "POST_COMMENTS=false" + } >> "$GITHUB_OUTPUT" + else + echo "::notice::⏭️ OSBotify automated push detected but without Mobile-Expensify submodule update. Skipping workflow." + { + echo "BUILD_MOBILE=false" + echo "BUILD_WEB=false" + echo "POST_COMMENTS=false" + } >> "$GITHUB_OUTPUT" + fi + + - name: Get merged pull request + id: getMergedPullRequest + if: ${{ github.actor != 'OSBotify' }} + uses: actions-ecosystem/action-get-merged-pull-request@59afe90821bb0b555082ce8ff1e36b03f91553d9 + with: + github_token: ${{ github.token }} + + postGitHubCommentBuildStarted: + name: Post build started comment + runs-on: ubuntu-latest + if: ${{ needs.prep.outputs.POST_COMMENTS == 'true' }} + needs: [prep] + steps: + - name: Add build start comment to Expensify/App PR + if: ${{ needs.prep.outputs.APP_PR_NUMBER != '' }} + # v7 + uses: actions/github-script@60a0d83039c74a4aee543508d2ffcb1c3799cdea + with: + github-token: ${{ github.token }} + script: | + const workflowURL = `https://github.com/${context.repo.owner}/${context.repo.repo}/actions/runs/${context.runId}`; + github.rest.issues.createComment({ + owner: context.repo.owner, + repo: context.repo.repo, + issue_number: ${{ needs.prep.outputs.APP_PR_NUMBER }}, + body: `🚧 @${{ github.actor }} has triggered a test Expensify/App build. You can view the [workflow run here](${workflowURL}).` + }); + + web: + name: Build and deploy Web + if: ${{ needs.prep.outputs.BUILD_WEB == 'true' && needs.prep.outputs.APP_PR_NUMBER }} + needs: [prep] + runs-on: ubuntu-latest-xl + env: + PULL_REQUEST_NUMBER: ${{ needs.prep.outputs.APP_PR_NUMBER || '' }} + steps: + - name: Checkout + # v4 + uses: actions/checkout@8ade135a41bc03ea155e62e844d188df1ea18608 + with: + ref: ${{ needs.prep.outputs.APP_REF }} + + - name: Create .env.adhoc file based on staging and add PULL_REQUEST_NUMBER env to it + run: | + cp .env.staging .env.adhoc + sed -i 's/ENVIRONMENT=staging/ENVIRONMENT=adhoc/' .env.adhoc + echo "PULL_REQUEST_NUMBER=$PULL_REQUEST_NUMBER" >> .env.adhoc + + - name: Setup Node + uses: ./.github/actions/composite/setupNode + + - name: Configure AWS Credentials + # v4 + uses: aws-actions/configure-aws-credentials@ececac1a45f3b08a01d2dd070d28d111c5fe6722 + with: + aws-access-key-id: ${{ secrets.AWS_ACCESS_KEY_ID }} + aws-secret-access-key: ${{ secrets.AWS_SECRET_ACCESS_KEY }} + aws-region: us-east-1 + + - name: Build web for testing + run: npm run build-adhoc + env: + SENTRY_AUTH_TOKEN: ${{ secrets.SENTRY_AUTH_TOKEN }} + + - name: Deploy to S3 for internal testing + run: aws s3 cp --recursive --acl public-read "$GITHUB_WORKSPACE"/dist s3://ad-hoc-expensify-cash/web/"$PULL_REQUEST_NUMBER" + + androidHybrid: + name: Build Android HybridApp + if: ${{ needs.prep.outputs.BUILD_MOBILE == 'true' }} + needs: [prep] + runs-on: ubuntu-latest-xl + env: + PULL_REQUEST_NUMBER: ${{ needs.prep.outputs.APP_PR_NUMBER || '' }} + outputs: + ROCK_ANDROID_ADHOC_INDEX_URL: ${{ steps.set-artifact-url.outputs.ARTIFACT_URL }} + steps: + - name: Checkout + # v4 + uses: actions/checkout@8ade135a41bc03ea155e62e844d188df1ea18608 + with: + submodules: true + ref: ${{ needs.prep.outputs.APP_REF }} + token: ${{ secrets.OS_BOTIFY_TOKEN }} + + - name: Configure MapBox SDK + run: ./scripts/setup-mapbox-sdk.sh ${{ secrets.MAPBOX_SDK_DOWNLOAD_TOKEN }} + + - name: Setup Node + id: setup-node + uses: ./.github/actions/composite/setupNode + with: + IS_HYBRID_BUILD: 'true' + + - name: Run grunt build + run: | + cd Mobile-Expensify + npm run grunt:build:shared + + - name: Setup dotenv + run: | + cp .env.staging .env.adhoc + sed -i 's/ENVIRONMENT=staging/ENVIRONMENT=adhoc/' .env.adhoc + echo "APP_PULL_REQUEST_NUMBER=$PULL_REQUEST_NUMBER" >> .env.adhoc + + - name: Setup 1Password CLI and certificates + uses: Expensify/GitHub-Actions/setup-certificate-1p@main + with: + OP_SERVICE_ACCOUNT_TOKEN: ${{ secrets.OP_SERVICE_ACCOUNT_TOKEN }} + SHOULD_LOAD_SSL_CERTIFICATES: 'false' + + - name: Load files from 1Password + env: + OP_SERVICE_ACCOUNT_TOKEN: ${{ secrets.OP_SERVICE_ACCOUNT_TOKEN }} + run: | + op read "op://${{ vars.OP_VAULT }}/upload-key.keystore/upload-key.keystore" --force --out-file ./upload-key.keystore + op read "op://${{ vars.OP_VAULT }}/android-fastlane-json-key.json/android-fastlane-json-key.json" --force --out-file ./android-fastlane-json-key.json + + # Copy the keystore to the Android directory for Fullstory + cp ./upload-key.keystore Mobile-Expensify/Android + + - name: Load Android upload keystore credentials from 1Password + id: load-credentials + # v2 + uses: 1password/load-secrets-action@581a835fb51b8e7ec56b71cf2ffddd7e68bb25e0 + with: + export-env: false + env: + OP_SERVICE_ACCOUNT_TOKEN: ${{ secrets.OP_SERVICE_ACCOUNT_TOKEN }} + ANDROID_UPLOAD_KEYSTORE_PASSWORD: op://${{ vars.OP_VAULT }}/Repository-Secrets/ANDROID_UPLOAD_KEYSTORE_PASSWORD + ANDROID_UPLOAD_KEYSTORE_ALIAS: op://${{ vars.OP_VAULT }}/Repository-Secrets/ANDROID_UPLOAD_KEYSTORE_ALIAS + ANDROID_UPLOAD_KEY_PASSWORD: op://${{ vars.OP_VAULT }}/Repository-Secrets/ANDROID_UPLOAD_KEY_PASSWORD + + - name: Configure AWS Credentials + # v4 + uses: aws-actions/configure-aws-credentials@ececac1a45f3b08a01d2dd070d28d111c5fe6722 + with: + aws-access-key-id: ${{ secrets.AWS_ACCESS_KEY_ID }} + aws-secret-access-key: ${{ secrets.AWS_SECRET_ACCESS_KEY }} + aws-region: us-east-1 + + - name: Rock Remote Build - Android + id: rock-remote-build-android + uses: callstackincubator/android@0bbc1b7c2e1a8be1ecb4d6c744c211869823fd65 + env: + GITHUB_TOKEN: ${{ github.token }} + SENTRY_AUTH_TOKEN: ${{ secrets.SENTRY_AUTH_TOKEN }} + IS_HYBRID_APP: true + with: + variant: 'Adhoc' + sign: true + re-sign: true + ad-hoc: true + keystore-file: './upload-key.keystore' + keystore-store-file: 'upload-key.keystore' + keystore-store-password: ${{ steps.load-credentials.outputs.ANDROID_UPLOAD_KEYSTORE_PASSWORD }} + keystore-key-alias: ${{ steps.load-credentials.outputs.ANDROID_UPLOAD_KEYSTORE_ALIAS }} + keystore-key-password: ${{ steps.load-credentials.outputs.ANDROID_UPLOAD_KEY_PASSWORD }} + # Specify the path (relative to the Android source directory) where the keystore should be placed. + keystore-path: '../tools/buildtools/upload-key.keystore' + comment-bot: false + rock-build-extra-params: '--extra-params -PreactNativeArchitectures=arm64-v8a,x86_64' + + - name: Set artifact URL output + id: set-artifact-url + run: echo "ARTIFACT_URL=$ARTIFACT_URL" >> "$GITHUB_OUTPUT" + + iosHybrid: + name: Build and deploy iOS for testing + if: ${{ needs.prep.outputs.BUILD_MOBILE == 'true' }} + needs: [prep] + env: + DEVELOPER_DIR: /Applications/Xcode_26.0.app/Contents/Developer + PULL_REQUEST_NUMBER: ${{ needs.prep.outputs.APP_PR_NUMBER || '' }} + runs-on: macos-15-xlarge + outputs: + ROCK_IOS_ADHOC_INDEX_URL: ${{ steps.set-artifact-url.outputs.ARTIFACT_URL }} + steps: + - name: Checkout + # v4 + uses: actions/checkout@8ade135a41bc03ea155e62e844d188df1ea18608 + with: + submodules: true + ref: ${{ needs.prep.outputs.APP_REF }} + token: ${{ secrets.OS_BOTIFY_TOKEN }} + + - name: Configure MapBox SDK + run: ./scripts/setup-mapbox-sdk.sh ${{ secrets.MAPBOX_SDK_DOWNLOAD_TOKEN }} + + - name: Setup Node + id: setup-node + uses: ./.github/actions/composite/setupNode + with: + IS_HYBRID_BUILD: 'true' + + - name: Create .env.adhoc file based on staging and add PULL_REQUEST_NUMBER env to it + run: | + cp .env.staging .env.adhoc + sed -i '' 's/ENVIRONMENT=staging/ENVIRONMENT=adhoc/' .env.adhoc + echo "PULL_REQUEST_NUMBER=$PULL_REQUEST_NUMBER" >> .env.adhoc + + - name: Setup 1Password CLI and certificates + uses: Expensify/GitHub-Actions/setup-certificate-1p@main + with: + OP_SERVICE_ACCOUNT_TOKEN: ${{ secrets.OP_SERVICE_ACCOUNT_TOKEN }} + SHOULD_LOAD_SSL_CERTIFICATES: 'false' + + - name: Load files from 1Password + env: + OP_SERVICE_ACCOUNT_TOKEN: ${{ secrets.OP_SERVICE_ACCOUNT_TOKEN }} + run: | + op read "op://${{ vars.OP_VAULT }}/OldApp_AdHoc/OldApp_AdHoc.mobileprovision" --force --out-file ./OldApp_AdHoc.mobileprovision + op read "op://${{ vars.OP_VAULT }}/OldApp_AdHoc_Share_Extension/OldApp_AdHoc_Share_Extension.mobileprovision" --force --out-file ./OldApp_AdHoc_Share_Extension.mobileprovision + op read "op://${{ vars.OP_VAULT }}/OldApp_AdHoc_Notification_Service/OldApp_AdHoc_Notification_Service.mobileprovision" --force --out-file ./OldApp_AdHoc_Notification_Service.mobileprovision + op read "op://${{ vars.OP_VAULT }}/New Expensify Distribution Certificate/Certificates.p12" --force --out-file ./Certificates.p12 + + - name: Create ExportOptions.plist + run: | + cat > Mobile-Expensify/iOS/ExportOptions.plist << 'EOF' + + + + + method + ad-hoc + provisioningProfiles + + com.expensify.expensifylite.adhoc + (OldApp) AdHoc + com.expensify.expensifylite.adhoc.SmartScanExtension + (OldApp) AdHoc: Share Extension + com.expensify.expensifylite.adhoc.NotificationServiceExtension + (OldApp) AdHoc: Notification Service + + + + EOF + + - name: Configure AWS Credentials + # v4 + uses: aws-actions/configure-aws-credentials@ececac1a45f3b08a01d2dd070d28d111c5fe6722 + with: + aws-access-key-id: ${{ secrets.AWS_ACCESS_KEY_ID }} + aws-secret-access-key: ${{ secrets.AWS_SECRET_ACCESS_KEY }} + aws-region: us-east-1 + + - name: Rock Remote Build - iOS + id: rock-remote-build-ios + uses: callstackincubator/ios@8dcef6cc275e0cf3299f5a97cde5ebd635c887d7 + env: + GITHUB_TOKEN: ${{ github.token }} + SENTRY_AUTH_TOKEN: ${{ secrets.SENTRY_AUTH_TOKEN }} + IS_HYBRID_APP: true + with: + destination: device + re-sign: true + ad-hoc: true + scheme: 'Expensify AdHoc' + configuration: 'AdHoc' + certificate-file: './Certificates.p12' + provisioning-profiles: | + [ + { + "name": "(OldApp) AdHoc", + "file": "./OldApp_AdHoc.mobileprovision" + }, + { + "name": "(OldApp) AdHoc: Share Extension", + "file": "./OldApp_AdHoc_Share_Extension.mobileprovision" + }, + { + "name": "(OldApp) AdHoc: Notification Service", + "file": "./OldApp_AdHoc_Notification_Service.mobileprovision" + } + ] + comment-bot: false + + - name: Set artifact URL output + id: set-artifact-url + run: echo "ARTIFACT_URL=$ARTIFACT_URL" >> "$GITHUB_OUTPUT" + + postGithubComment: + runs-on: ubuntu-latest + if: ${{ always() && needs.prep.outputs.POST_COMMENTS == 'true' }} + name: Post a GitHub comment with app download links for testing + needs: [prep, web, androidHybrid, iosHybrid] + steps: + - name: Checkout + # v4 + uses: actions/checkout@8ade135a41bc03ea155e62e844d188df1ea18608 + with: + ref: ${{ needs.prep.outputs.APP_REF }} + + - name: Publish links to apps for download on Expensify/App PR + if: ${{ needs.prep.outputs.APP_PR_NUMBER }} + uses: ./.github/actions/javascript/postTestBuildComment + with: + REPO: App + APP_PR_NUMBER: ${{ needs.prep.outputs.APP_PR_NUMBER }} + GITHUB_TOKEN: ${{ github.token }} + ANDROID: ${{ needs.androidHybrid.result }} + IOS: ${{ needs.iosHybrid.result }} + WEB: ${{ needs.web.result }} + ANDROID_LINK: ${{ needs.androidHybrid.outputs.ROCK_ANDROID_ADHOC_INDEX_URL || ''}} + IOS_LINK: ${{ needs.iosHybrid.outputs.ROCK_IOS_ADHOC_INDEX_URL || ''}} + WEB_LINK: https://${{ needs.prep.outputs.APP_PR_NUMBER }}.pr-testing.expensify.com From 4ae8ffa4b37088489b33eb1d7b963fb6677b5e20 Mon Sep 17 00:00:00 2001 From: Lukasz Modzelewski Date: Tue, 27 Jan 2026 13:51:17 +0100 Subject: [PATCH 09/11] fix warning, add buildSummary --- .github/workflows/testBuildOnPush.yml | 131 +++++++++++++++++++++----- 1 file changed, 110 insertions(+), 21 deletions(-) diff --git a/.github/workflows/testBuildOnPush.yml b/.github/workflows/testBuildOnPush.yml index 6661e50fc5e3..f66ff116b676 100644 --- a/.github/workflows/testBuildOnPush.yml +++ b/.github/workflows/testBuildOnPush.yml @@ -10,10 +10,11 @@ jobs: runs-on: ubuntu-latest outputs: APP_REF: ${{ github.sha }} - APP_PR_NUMBER: ${{ steps.getMergedPullRequest.outputs.number }} + APP_PR_NUMBER: ${{ steps.getMergedPullRequest.outputs.PR_NUMBER }} + PR_URL: ${{ steps.getMergedPullRequest.outputs.PR_URL }} BUILD_WEB: ${{ steps.detectOSBotifyPush.outputs.BUILD_WEB || 'true' }} BUILD_MOBILE: ${{ steps.detectOSBotifyPush.outputs.BUILD_MOBILE || 'true' }} - POST_COMMENTS: ${{ steps.detectOSBotifyPush.outputs.POST_COMMENTS || 'true' }} + POST_COMMENTS: ${{ steps.detectOSBotifyPush.outputs.POST_COMMENTS || steps.getMergedPullRequest.outputs.POST_COMMENTS || 'true' }} steps: - name: Checkout # v4 @@ -28,30 +29,47 @@ jobs: - name: Detect OSBotify automated push id: detectOSBotifyPush if: ${{ github.actor == 'OSBotify' }} - env: - COMMIT_MESSAGE: ${{ github.event.head_commit.message }} - run: | - if [[ "$COMMIT_MESSAGE" == Update\ Mobile-Expensify\ submodule* ]]; then - echo "::notice::✅ OSBotify Mobile-Expensify submodule update detected. Building native only." - { - echo "BUILD_WEB=false" - echo "POST_COMMENTS=false" - } >> "$GITHUB_OUTPUT" - else - echo "::notice::⏭️ OSBotify automated push detected but without Mobile-Expensify submodule update. Skipping workflow." - { - echo "BUILD_MOBILE=false" - echo "BUILD_WEB=false" - echo "POST_COMMENTS=false" - } >> "$GITHUB_OUTPUT" - fi + # v7 + uses: actions/github-script@60a0d83039c74a4aee543508d2ffcb1c3799cdea + with: + github-token: ${{ github.token }} + script: | + const commitMessage = context.payload?.head_commit?.message || ''; + + if (commitMessage.startsWith('Update Mobile-Expensify submodule')) { + core.notice(`${context.actor} automated push with Mobile-Expensify submodule update detected. Skipping web build and post comments. No PR associated with this commit.`); + core.setOutput('BUILD_WEB', 'false'); + core.setOutput('POST_COMMENTS', 'false'); + } else { + core.warning(`OSBotify automated push detected but without Mobile-Expensify submodule update commit phrase. Skipping rest of the workflow. No PR associated with this commit.`); + core.setOutput('BUILD_MOBILE', 'false'); + core.setOutput('BUILD_WEB', 'false'); + core.setOutput('POST_COMMENTS', 'false'); + } - name: Get merged pull request id: getMergedPullRequest if: ${{ github.actor != 'OSBotify' }} - uses: actions-ecosystem/action-get-merged-pull-request@59afe90821bb0b555082ce8ff1e36b03f91553d9 + # v7 + uses: actions/github-script@60a0d83039c74a4aee543508d2ffcb1c3799cdea with: - github_token: ${{ github.token }} + github-token: ${{ github.token }} + script: | + const prData = await github.rest.repos.listPullRequestsAssociatedWithCommit({ + owner: context.repo.owner, + repo: context.repo.repo, + commit_sha: context.sha, + }); + const prNumber = prData?.data?.find(p => p.state === 'closed' && p.merged_at)?.number?.toString(); + if (prNumber) { + const prUrl = `https://github.com/${context.repo.owner}/${context.repo.repo}/pull/${prNumber}`; + core.notice(`Found merged pull request: [#${prNumber}](${prUrl})`); + core.setOutput('PR_URL', prUrl); + core.setOutput('PR_NUMBER', prNumber); + } else { + core.setOutput('POST_COMMENTS', 'false'); + core.setFailed(`Commit pushed by non-OSBotify actor. No merged pull request found for commit ${context.sha}`); + } postGitHubCommentBuildStarted: name: Post build started comment @@ -352,3 +370,74 @@ jobs: ANDROID_LINK: ${{ needs.androidHybrid.outputs.ROCK_ANDROID_ADHOC_INDEX_URL || ''}} IOS_LINK: ${{ needs.iosHybrid.outputs.ROCK_IOS_ADHOC_INDEX_URL || ''}} WEB_LINK: https://${{ needs.prep.outputs.APP_PR_NUMBER }}.pr-testing.expensify.com + + buildSummary: + runs-on: ubuntu-latest + if: ${{ always() && (needs.prep.outputs.APP_PR_NUMBER != '' || needs.androidHybrid.outputs.ROCK_ANDROID_ADHOC_INDEX_URL != '' || needs.iosHybrid.outputs.ROCK_IOS_ADHOC_INDEX_URL != '') }} + name: Build Summary + needs: [prep, web, androidHybrid, iosHybrid] + steps: + - name: Checkout + # v4 + uses: actions/checkout@8ade135a41bc03ea155e62e844d188df1ea18608 + with: + ref: ${{ needs.prep.outputs.APP_REF }} + + - name: Get Mobile-Expensify submodule SHA + id: getSubmoduleSHA + run: | + if [ -d "Mobile-Expensify" ]; then + SUBMODULE_SHA=$(git ls-tree HEAD Mobile-Expensify | awk '{print $3}') + echo "SHA=$SUBMODULE_SHA" >> "$GITHUB_OUTPUT" + fi + + - name: Create build summary + # v7 + uses: actions/github-script@60a0d83039c74a4aee543508d2ffcb1c3799cdea + with: + github-token: ${{ github.token }} + script: | + const prNumber = '${{ needs.prep.outputs.APP_PR_NUMBER }}'; + const webLink = prNumber && '${{ needs.web.result }}' === 'success' ? `https://${prNumber}.pr-testing.expensify.com` : ''; + const androidLink = '${{ needs.androidHybrid.outputs.ROCK_ANDROID_ADHOC_INDEX_URL }}' || ''; + const iosLink = '${{ needs.iosHybrid.outputs.ROCK_IOS_ADHOC_INDEX_URL }}' || ''; + const submoduleSHA = '${{ steps.getSubmoduleSHA.outputs.SHA }}' || ''; + + const webStatus = webLink ? '✅ Success' : '${{ needs.web.result }}' === 'failure' ? '❌ Failed' : '⏭️ Skipped'; + const androidStatus = androidLink ? '✅ Success' : '${{ needs.androidHybrid.result }}' === 'failure' ? '❌ Failed' : '⏭️ Skipped'; + const iosStatus = iosLink ? '✅ Success' : '${{ needs.iosHybrid.result }}' === 'failure' ? '❌ Failed' : '⏭️ Skipped'; + + const summary = core.summary + .addTable([ + [{data: 'Platform', header: true}, {data: 'Status', header: true}, {data: 'Download', header: true}], + [ + 'Web', + webStatus, + webLink ? `${webLink}` : '-' + ], + [ + 'Android', + androidStatus, + androidLink ? `${androidLink}` : '-' + ], + [ + 'iOS', + iosStatus, + iosLink ? `${iosLink}` : '-' + ], + ]); + + if (submoduleSHA) { + const submoduleUrl = `https://github.com/Expensify/Mobile-Expensify/commit/${submoduleSHA}`; + summary.addRaw(`\n**Mobile-Expensify Submodule SHA:** [${submoduleSHA}](${submoduleUrl})`); + } + + + const prUrl = '${{ needs.prep.outputs.PR_URL }}'; + if (prUrl) { + summary.addRaw(`\n**PR Number:** [${prNumber}](${prUrl})`); + } else { + summary.addRaw(`\n**PR Number:** No PR associated with this commit.`); + } + + summary.write(); From c1304b345c4b69fd0109106a59efefeb354e8e55 Mon Sep 17 00:00:00 2001 From: Lukasz Modzelewski Date: Tue, 27 Jan 2026 15:04:30 +0100 Subject: [PATCH 10/11] fix summary PR Link text --- .github/workflows/testBuildOnPush.yml | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/.github/workflows/testBuildOnPush.yml b/.github/workflows/testBuildOnPush.yml index f66ff116b676..608b750e4906 100644 --- a/.github/workflows/testBuildOnPush.yml +++ b/.github/workflows/testBuildOnPush.yml @@ -434,10 +434,12 @@ jobs: const prUrl = '${{ needs.prep.outputs.PR_URL }}'; + if (prUrl) { - summary.addRaw(`\n**PR Number:** [${prNumber}](${prUrl})`); + summary.addRaw(`\n**PR Link:** ${prUrl}`); } else { - summary.addRaw(`\n**PR Number:** No PR associated with this commit.`); + summary.addRaw(`\n**PR Link:** No PR associated with this commit.`); } + summary.write(); From a96a985fe396f97bbadbf852d6e47ec6df8c8952 Mon Sep 17 00:00:00 2001 From: Lukasz Modzelewski Date: Thu, 29 Jan 2026 17:49:07 +0100 Subject: [PATCH 11/11] update xcode version --- .github/workflows/testBuildOnPush.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/testBuildOnPush.yml b/.github/workflows/testBuildOnPush.yml index 608b750e4906..c8d8daa3444a 100644 --- a/.github/workflows/testBuildOnPush.yml +++ b/.github/workflows/testBuildOnPush.yml @@ -236,7 +236,7 @@ jobs: if: ${{ needs.prep.outputs.BUILD_MOBILE == 'true' }} needs: [prep] env: - DEVELOPER_DIR: /Applications/Xcode_26.0.app/Contents/Developer + DEVELOPER_DIR: /Applications/Xcode_26.2.app/Contents/Developer PULL_REQUEST_NUMBER: ${{ needs.prep.outputs.APP_PR_NUMBER || '' }} runs-on: macos-15-xlarge outputs: