From 00d9795b8098d5de96062b202a03993e5bb30fbd Mon Sep 17 00:00:00 2001 From: Brandon Page Date: Thu, 28 May 2026 17:21:42 -0700 Subject: [PATCH 1/2] Update nightly schedule to weekdays at 10:30 PM PT and fix security issues. --- .github/workflows/nightly.yaml | 24 ++++- .github/workflows/pr.yaml | 45 ++++++--- .github/workflows/reusable-lib-workflow.yaml | 100 +++++++++++++------ .github/workflows/reusable-ui-workflow.yaml | 85 +++++++++++----- 4 files changed, 180 insertions(+), 74 deletions(-) diff --git a/.github/workflows/nightly.yaml b/.github/workflows/nightly.yaml index 8003de6502..222b1488b6 100644 --- a/.github/workflows/nightly.yaml +++ b/.github/workflows/nightly.yaml @@ -1,11 +1,14 @@ name: Nightly Tests -on: +on: schedule: - - cron: "0 6 * * 2,6" # cron is UTC, this translates to 10 PM PST Mon and Fri. + - cron: "30 6 * * 2-6" # cron is UTC; 10:30 PM PT every weekday (Mon-Fri). # This lets us trigger the workflow from a browser. workflow_dispatch: +permissions: + contents: read + # Run the native libraries first so that the native and Hybrid MobileSync tests # don't run simultaneously (to prevent flappers). jobs: @@ -17,7 +20,10 @@ jobs: uses: ./.github/workflows/reusable-lib-workflow.yaml with: lib: ${{ matrix.lib }} - secrets: inherit + secrets: + TEST_CREDENTIALS: ${{ secrets.TEST_CREDENTIALS }} + GCLOUD_SERVICE_KEY: ${{ secrets.GCLOUD_SERVICE_KEY }} + CODECOV_TOKEN: ${{ secrets.CODECOV_TOKEN }} android-nightly-Hybrid: if: success() || failure() needs: [android-nightly] @@ -28,7 +34,15 @@ jobs: uses: ./.github/workflows/reusable-lib-workflow.yaml with: lib: ${{ matrix.lib }} - secrets: inherit + secrets: + TEST_CREDENTIALS: ${{ secrets.TEST_CREDENTIALS }} + GCLOUD_SERVICE_KEY: ${{ secrets.GCLOUD_SERVICE_KEY }} + CODECOV_TOKEN: ${{ secrets.CODECOV_TOKEN }} android-nightly-UI-Tests: uses: ./.github/workflows/reusable-ui-workflow.yaml - secrets: inherit \ No newline at end of file + secrets: + MSDK_ANDROID_REMOTE_ACCESS_CALLBACK_URL: ${{ secrets.MSDK_ANDROID_REMOTE_ACCESS_CALLBACK_URL }} + MSDK_ANDROID_REMOTE_ACCESS_CONSUMER_KEY: ${{ secrets.MSDK_ANDROID_REMOTE_ACCESS_CONSUMER_KEY }} + UI_TEST_CONFIG: ${{ secrets.UI_TEST_CONFIG }} + GCLOUD_SERVICE_KEY: ${{ secrets.GCLOUD_SERVICE_KEY }} + CODECOV_TOKEN: ${{ secrets.CODECOV_TOKEN }} diff --git a/.github/workflows/pr.yaml b/.github/workflows/pr.yaml index cb6d61988e..af8cfb6d4c 100644 --- a/.github/workflows/pr.yaml +++ b/.github/workflows/pr.yaml @@ -1,44 +1,57 @@ name: Pull Request on: - # Dangerous without Member Check setup! - pull_request_target: + # Pull_request_target is required to grant secrets/write-permission to fork PRs. + # Mitigated by per-job Member Check (see "Check Write Permission" + "Validate Write Permission" steps). + # Reference: team Github Actions Tribal Knowledge doc. + pull_request_target: # zizmor: ignore[dangerous-triggers] branches: [dev, master] +permissions: + contents: read + jobs: test-orchestrator: runs-on: ubuntu-latest + permissions: + contents: read + pull-requests: write env: BUNDLE_GEMFILE: ${{ github.workspace }}/.github/DangerFiles/Gemfile outputs: libs: ${{ steps.test-orchestrator.outputs.libs }} steps: - name: Check Write Permission - uses: octokit/request-action@v2.x + uses: octokit/request-action@dad4362715b7fb2ddedf9772c8670824af564f0d # v2.4.0 id: check_permissions with: route: GET /repos/${{ github.repository }}/collaborators/${{ github.triggering_actor }}/permission env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} - name: Debug Permission Response + env: + PERMISSION_DATA: ${{ steps.check_permissions.outputs.data }} run: | - echo "Permission raw response: ${{ steps.check_permissions.outputs.data }}" + echo "Permission raw response: ${PERMISSION_DATA}" - name: Validate Write Permission + env: + PERMISSION: ${{ fromJson(steps.check_permissions.outputs.data).permission }} + TRIGGERING_ACTOR: ${{ github.triggering_actor }} run: | - permission=$(echo "${{ fromJson(steps.check_permissions.outputs.data).permission }}") - echo "User ${{ github.triggering_actor }} has permission: $permission" - if [[ "$permission" != "write" && "$permission" != "admin" ]]; then - echo "User ${{ github.triggering_actor }} does not have sufficient permission (write or admin) to proceed. Someone from the team needs to rerun this workflow AFTER it has been deemed safe." + echo "User ${TRIGGERING_ACTOR} has permission: ${PERMISSION}" + if [ "${PERMISSION}" != "write" ] && [ "${PERMISSION}" != "admin" ]; then + echo "User ${TRIGGERING_ACTOR} does not have sufficient permission (write or admin) to proceed. Someone from the team needs to rerun this workflow AFTER it has been deemed safe." exit 1 fi - name: Checkout - uses: actions/checkout@v4 + uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4.3.1 with: + persist-credentials: false # We need a sufficient depth or Danger will occasionally run into issues checking which files were modified. fetch-depth: 100 # This is dangerous without the member check ref: ${{ github.event.pull_request.head.sha }} - - uses: ruby/setup-ruby@v1 + - uses: ruby/setup-ruby@afeafc3d1ab54a631816aba4c914a0081c12ff2f # v1.310.0 with: ruby-version: '3.2' bundler-cache: true @@ -58,11 +71,19 @@ jobs: with: lib: ${{ matrix.lib }} is_pr: true - secrets: inherit + secrets: + TEST_CREDENTIALS: ${{ secrets.TEST_CREDENTIALS }} + GCLOUD_SERVICE_KEY: ${{ secrets.GCLOUD_SERVICE_KEY }} + CODECOV_TOKEN: ${{ secrets.CODECOV_TOKEN }} ui-tests-pr: needs: [test-orchestrator] uses: ./.github/workflows/reusable-ui-workflow.yaml with: is_pr: true - secrets: inherit \ No newline at end of file + secrets: + MSDK_ANDROID_REMOTE_ACCESS_CALLBACK_URL: ${{ secrets.MSDK_ANDROID_REMOTE_ACCESS_CALLBACK_URL }} + MSDK_ANDROID_REMOTE_ACCESS_CONSUMER_KEY: ${{ secrets.MSDK_ANDROID_REMOTE_ACCESS_CONSUMER_KEY }} + UI_TEST_CONFIG: ${{ secrets.UI_TEST_CONFIG }} + GCLOUD_SERVICE_KEY: ${{ secrets.GCLOUD_SERVICE_KEY }} + CODECOV_TOKEN: ${{ secrets.CODECOV_TOKEN }} diff --git a/.github/workflows/reusable-lib-workflow.yaml b/.github/workflows/reusable-lib-workflow.yaml index 0d080d4a9b..c1de05095a 100644 --- a/.github/workflows/reusable-lib-workflow.yaml +++ b/.github/workflows/reusable-lib-workflow.yaml @@ -7,6 +7,17 @@ on: is_pr: type: boolean default: false + secrets: + TEST_CREDENTIALS: + required: true + GCLOUD_SERVICE_KEY: + required: true + CODECOV_TOKEN: + required: true + +permissions: + contents: read + pull-requests: write jobs: test-android: @@ -14,15 +25,17 @@ jobs: env: BUNDLE_GEMFILE: ${{ github.workspace }}/.github/DangerFiles/Gemfile steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4.3.1 if: ${{ inputs.is_pr }} with: + persist-credentials: false # We need a sufficient depth or Danger will occasionally run into issues checking which files were modified. fetch-depth: 100 ref: ${{ github.event.pull_request.head.sha }} - - uses: actions/checkout@v4 + - uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4.3.1 if: ${{ ! inputs.is_pr }} with: + persist-credentials: false ref: ${{ github.head_ref }} - name: Install Dependencies env: @@ -34,13 +47,13 @@ jobs: if: ${{ inputs.lib == 'SalesforceHybrid' }} run: | npm install -g cordova - - uses: actions/setup-java@v4 + - uses: actions/setup-java@c1e323688fd81a25caa38c78aa6df2d33d3e20d9 # v4.8.0 with: distribution: 'zulu' java-version: '21' - name: Setup Android SDK - uses: android-actions/setup-android@v3 - - uses: gradle/actions/setup-gradle@v4 + uses: android-actions/setup-android@9fc6c4e9069bf8d3d10b2204b1fb8f6ef7065407 # v3.2.2 + - uses: gradle/actions/setup-gradle@748248ddd2a24f49513d8f472f81c3a07d4d50e1 # v4.4.4 with: # This is the actual Gradle version (not AGP), which can be found in the gradle-wrapper.properties file. gradle-version: "8.14.3" @@ -48,8 +61,10 @@ jobs: add-job-summary-as-pr-comment: on-failure - name: Run Lint if: ${{ inputs.is_pr }} - run: ./gradlew libs:${{ inputs.lib }}:lint - - uses: ruby/setup-ruby@v1 + env: + LIB: ${{ inputs.lib }} + run: ./gradlew "libs:${LIB}:lint" + - uses: ruby/setup-ruby@afeafc3d1ab54a631816aba4c914a0081c12ff2f # v1.310.0 if: ${{ inputs.is_pr }} with: ruby-version: '3.2' @@ -59,38 +74,53 @@ jobs: env: DANGER_GITHUB_API_TOKEN: ${{ secrets.GITHUB_TOKEN }} LIB: ${{ inputs.lib }} - run: bundle exec danger --dangerfile=.github/DangerFiles/StaticAnalysis.rb --danger_id="${{ inputs.lib }}" + run: bundle exec danger --dangerfile=.github/DangerFiles/StaticAnalysis.rb --danger_id="${LIB}" - name: Build for Testing + id: build if: success() || failure() + env: + LIB: ${{ inputs.lib }} run: | - ./gradlew libs:${{ inputs.lib }}:assembleAndroidTest - ./gradlew native:NativeSampleApps:RestExplorer:assembleDebug - - uses: 'google-github-actions/auth@v2' + mkdir -p build_logs + set -o pipefail + ./gradlew "libs:${LIB}:assembleAndroidTest" 2>&1 | tee "build_logs/assembleAndroidTest-${LIB}.log" + ./gradlew native:NativeSampleApps:RestExplorer:assembleDebug 2>&1 | tee "build_logs/assembleDebug-RestExplorer.log" + - name: Archive build logs on failure + if: failure() && steps.build.outcome == 'failure' + uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4.6.2 + with: + name: build-logs-${{ inputs.lib }} + path: build_logs/ + if-no-files-found: ignore + retention-days: 14 + - uses: 'google-github-actions/auth@c200f3691d83b41bf9bbd8638997a462592937ed' # v2.1.13 if: success() || failure() with: credentials_json: '${{ secrets.GCLOUD_SERVICE_KEY }}' - - uses: 'google-github-actions/setup-gcloud@v2' + - uses: 'google-github-actions/setup-gcloud@e427ad8a34f8676edf47cf7d7925499adf3eb74f' # v2.2.1 if: success() || failure() - name: Validate Shard Config if: success() || failure() + env: + LIB: ${{ inputs.lib }} run: | - SHARD_CONFIG=".github/test-shards/${{ inputs.lib }}.json" + SHARD_CONFIG=".github/test-shards/${LIB}.json" if [ -f "$SHARD_CONFIG" ]; then # Extract sorted class names from non-remaining shards (strip "class " prefix) CLASSES=$(jq -r '[.shards[] | select(.name != "remaining") | .targets[] | select(startswith("class ")) | ltrimstr("class ")] | sort | .[]' "$SHARD_CONFIG") - + # Extract sorted notClass names from remaining shard (strip "notClass " prefix) NOT_CLASSES=$(jq -r '[.shards[] | select(.name == "remaining") | .targets[] | select(startswith("notClass ")) | ltrimstr("notClass ")] | sort | .[]' "$SHARD_CONFIG") - + if [ "$CLASSES" != "$NOT_CLASSES" ]; then echo "::error::Shard config mismatch in $SHARD_CONFIG - classes in shards must exactly match notClass entries in remaining shard" echo "Difference:" diff <(echo "$CLASSES") <(echo "$NOT_CLASSES") || true exit 1 fi - echo "✓ Shard config valid for ${{ inputs.lib }} ($(echo "$CLASSES" | wc -l | tr -d ' ') classes)" + echo "✓ Shard config valid for ${LIB} ($(echo "$CLASSES" | wc -l | tr -d ' ') classes)" else - echo "No shard config for ${{ inputs.lib }}, skipping validation" + echo "No shard config for ${LIB}, skipping validation" fi - name: Run Tests continue-on-error: true @@ -100,17 +130,19 @@ jobs: PR_API_VERSION: "35" FULL_API_RANGE: "28 29 30 31 32 33 34 35 36" IS_PR: ${{ inputs.is_pr }} + LIB: ${{ inputs.lib }} + RUN_NUMBER: ${{ github.run_number }} run: | LEVELS_TO_TEST=$FULL_API_RANGE RETRIES=0 - if $IS_PR ; then + if [ "$IS_PR" = "true" ] ; then LEVELS_TO_TEST=$PR_API_VERSION RETRIES=1 fi # Build test-targets-for-shard arguments from config file - SHARD_CONFIG=".github/test-shards/${{ inputs.lib }}.json" + SHARD_CONFIG=".github/test-shards/${LIB}.json" SHARD_ARGS=() if [ -f "$SHARD_CONFIG" ]; then NUM_SHARDS=$(jq '.shards | length' "$SHARD_CONFIG") @@ -127,18 +159,18 @@ jobs: gcloud components install beta --quiet for LEVEL in $LEVELS_TO_TEST do - GCLOUD_RESULTS_DIR=${{ inputs.lib }}-api-${LEVEL}-build-${{github.run_number}} + GCLOUD_RESULTS_DIR="${LIB}-api-${LEVEL}-build-${RUN_NUMBER}" eval gcloud beta firebase test android run \ --project mobile-apps-firebase-test \ --type instrumentation \ --app "native/NativeSampleApps/RestExplorer/build/outputs/apk/debug/RestExplorer-debug.apk" \ - --test=libs/${{ inputs.lib }}/build/outputs/apk/androidTest/debug/${{ inputs.lib }}-debug-androidTest.apk \ - --device model=MediumPhone.arm,version=${LEVEL},locale=en,orientation=portrait \ + --test="libs/${LIB}/build/outputs/apk/androidTest/debug/${LIB}-debug-androidTest.apk" \ + --device "model=MediumPhone.arm,version=${LEVEL},locale=en,orientation=portrait" \ --environment-variables coverage=true,coverageFile="/sdcard/coverage.ec" \ --directories-to-pull=/sdcard \ - --results-dir=${GCLOUD_RESULTS_DIR} \ - --results-history-name=${{ inputs.lib }} \ + --results-dir="${GCLOUD_RESULTS_DIR}" \ + --results-history-name="${LIB}" \ --timeout=30m --no-auto-google-login --no-record-video --no-performance-metrics \ "${SHARD_ARGS[@]}" \ --num-flaky-test-attempts=${RETRIES} || true @@ -151,24 +183,26 @@ jobs: PR_API_VERSION: "35" FULL_API_RANGE: "28 29 30 31 32 33 34 35 36" IS_PR: ${{ inputs.is_pr }} + LIB: ${{ inputs.lib }} + RUN_NUMBER: ${{ github.run_number }} run: | LEVELS_TO_TEST=$FULL_API_RANGE - if $IS_PR ; then + if [ "$IS_PR" = "true" ] ; then LEVELS_TO_TEST=$PR_API_VERSION fi for LEVEL in $LEVELS_TO_TEST do - GCLOUD_RESULTS_DIR=${{ inputs.lib }}-api-${LEVEL}-build-${{github.run_number}} + GCLOUD_RESULTS_DIR="${LIB}-api-${LEVEL}-build-${RUN_NUMBER}" BUCKET_PATH="gs://test-lab-w87i9sz6q175u-kwp8ium6js0zw/${GCLOUD_RESULTS_DIR}" - gsutil ls ${BUCKET_PATH} > /dev/null 2>&1 + gsutil ls "${BUCKET_PATH}" > /dev/null 2>&1 if [ $? == 0 ] ; then # Copy XML file for test reporting if gsutil ls "${BUCKET_PATH}/*test_results_merged.xml" > /dev/null 2>&1; then # Sharded runs produce test_results_merged.xml at top level - gsutil cp "${BUCKET_PATH}/*test_results_merged.xml" firebase_results/api_${LEVEL}_test_result.xml + gsutil cp "${BUCKET_PATH}/*test_results_merged.xml" "firebase_results/api_${LEVEL}_test_result.xml" else # Pass 1: copy original (non-rerun) results for RESULT_FILE in $(gsutil ls "${BUCKET_PATH}/*/test_result_1.xml" 2>/dev/null | grep -v "rerun"); do @@ -212,7 +246,7 @@ jobs: fi done - name: Test Report - uses: mikepenz/action-junit-report@v6 + uses: mikepenz/action-junit-report@3a81627bfac62268172037048872e8ebd4207e6d # v6.4.1 if: success() || failure() with: check_name: ${{ inputs.lib }} Test Results @@ -227,15 +261,17 @@ jobs: simplified_summary: true report_paths: 'firebase_results/**.xml' - name: Archive Test Results - uses: actions/upload-artifact@v4 + uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4.6.2 if: success() || failure() with: name: test-results-${{ inputs.lib }} path: 'firebase_results/**.xml' - name: Convert Code Coverage if: success() || failure() - run: ./gradlew libs:${{ inputs.lib }}:convertCodeCoverage - - uses: codecov/codecov-action@v5 + env: + LIB: ${{ inputs.lib }} + run: ./gradlew "libs:${LIB}:convertCodeCoverage" + - uses: codecov/codecov-action@75cd11691c0faa626561e295848008c8a7dddffe # v5.5.4 if: success() || failure() with: files: libs/${{ inputs.lib }}/build/reports/jacoco/convertedCodeCoverage/convertedCodeCoverage.xml diff --git a/.github/workflows/reusable-ui-workflow.yaml b/.github/workflows/reusable-ui-workflow.yaml index 644cf76922..4e59930aa2 100644 --- a/.github/workflows/reusable-ui-workflow.yaml +++ b/.github/workflows/reusable-ui-workflow.yaml @@ -4,6 +4,21 @@ on: is_pr: type: boolean default: false + secrets: + MSDK_ANDROID_REMOTE_ACCESS_CALLBACK_URL: + required: true + MSDK_ANDROID_REMOTE_ACCESS_CONSUMER_KEY: + required: true + UI_TEST_CONFIG: + required: true + GCLOUD_SERVICE_KEY: + required: true + CODECOV_TOKEN: + required: true + +permissions: + contents: read + pull-requests: write jobs: test-android: @@ -11,15 +26,17 @@ jobs: env: BUNDLE_GEMFILE: ${{ github.workspace }}/.github/DangerFiles/Gemfile steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4.3.1 if: ${{ inputs.is_pr }} with: + persist-credentials: false # We need a sufficient depth or Danger will occasionally run into issues checking which files were modified. fetch-depth: 100 ref: ${{ github.event.pull_request.head.sha }} - - uses: actions/checkout@v4 + - uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4.3.1 if: ${{ ! inputs.is_pr }} with: + persist-credentials: false ref: ${{ github.head_ref }} - name: Install Dependencies env: @@ -29,30 +46,44 @@ jobs: run: | ./install.sh echo $UI_TEST_CONFIG > ./shared/test/ui_test_config.json - - uses: actions/setup-java@v4 + - uses: actions/setup-java@c1e323688fd81a25caa38c78aa6df2d33d3e20d9 # v4.8.0 with: distribution: 'zulu' java-version: '21' - name: Setup Android SDK - uses: android-actions/setup-android@v3 - - uses: gradle/actions/setup-gradle@v4 + uses: android-actions/setup-android@9fc6c4e9069bf8d3d10b2204b1fb8f6ef7065407 # v3.2.2 + - uses: gradle/actions/setup-gradle@748248ddd2a24f49513d8f472f81c3a07d4d50e1 # v4.4.4 with: # This is the actual Gradle version (not AGP), which can be found in the gradle-wrapper.properties file. gradle-version: "8.14.3" add-job-summary: on-failure add-job-summary-as-pr-comment: on-failure - name: Build App for Testing + id: build-app if: success() || failure() run: | - ./gradlew native:NativeSampleApps:AuthFlowTester:assembleDebug + mkdir -p build_logs + set -o pipefail + ./gradlew native:NativeSampleApps:AuthFlowTester:assembleDebug 2>&1 | tee "build_logs/assembleDebug-AuthFlowTester.log" - name: Build UI Tests + id: build-ui-tests run: | - ./gradlew native:NativeSampleApps:AuthFlowTester:assembleAndroidTest - - uses: 'google-github-actions/auth@v2' + mkdir -p build_logs + set -o pipefail + ./gradlew native:NativeSampleApps:AuthFlowTester:assembleAndroidTest 2>&1 | tee "build_logs/assembleAndroidTest-AuthFlowTester.log" + - name: Archive build logs on failure + if: failure() && (steps.build-app.outcome == 'failure' || steps.build-ui-tests.outcome == 'failure') + uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4.6.2 + with: + name: build-logs-authflowtester-ui + path: build_logs/ + if-no-files-found: ignore + retention-days: 14 + - uses: 'google-github-actions/auth@c200f3691d83b41bf9bbd8638997a462592937ed' # v2.1.13 if: success() || failure() with: credentials_json: '${{ secrets.GCLOUD_SERVICE_KEY }}' - - uses: 'google-github-actions/setup-gcloud@v2' + - uses: 'google-github-actions/setup-gcloud@e427ad8a34f8676edf47cf7d7925499adf3eb74f' # v2.2.1 if: success() || failure() - name: Run PR Tests continue-on-error: true @@ -60,9 +91,10 @@ jobs: env: # Most used according to https://gs.statcounter.com/android-version-market-share/mobile-tablet/worldwide PR_API_VERSION: "35" + RUN_NUMBER: ${{ github.run_number }} run: | - GCLOUD_RESULTS_DIR=authflowtester-pr-build-${{github.run_number}} - + GCLOUD_RESULTS_DIR="authflowtester-pr-build-${RUN_NUMBER}" + PR_TESTS="class com.salesforce.samples.authflowtester.BootConfigLoginTests#testCAOpaque_DefaultScopes_WebServerFlow, \ class com.salesforce.samples.authflowtester.ECALoginTests#testECAOpaque_DefaultScopes, \ class com.salesforce.samples.authflowtester.ECALoginTests#testECAJwt_AllScopes, \ @@ -70,7 +102,7 @@ jobs: class com.salesforce.samples.authflowtester.MultiUserLoginTests#testSameApp_SameScopes_uniqueTokens, \ class com.salesforce.samples.authflowtester.BeaconLoginTests#testBeaconOpaque_DefaultScopes, \ class com.salesforce.samples.authflowtester.AdvancedAuthBeaconLoginTests#testBeaconOpaque_DefaultScopes" - + gcloud firebase test android run \ --project mobile-apps-firebase-test \ --type instrumentation \ @@ -78,7 +110,7 @@ jobs: --environment-variables clearPackageData=true \ --app "native/NativeSampleApps/AuthFlowTester/build/outputs/apk/debug/AuthFlowTester-debug.apk" \ --test "native/NativeSampleApps/AuthFlowTester/build/outputs/apk/androidTest/debug/AuthFlowTester-debug-androidTest.apk" \ - --device model=MediumPhone.arm,version="${PR_API_VERSION}",locale=en,orientation=portrait \ + --device "model=MediumPhone.arm,version=${PR_API_VERSION},locale=en,orientation=portrait" \ --directories-to-pull=/sdcard \ --results-dir="${GCLOUD_RESULTS_DIR}" \ --results-history-name=AuthFlowTester \ @@ -91,13 +123,14 @@ jobs: if: ${{ ! inputs.is_pr }} env: FULL_API_RANGE: "28 29 30 31 32 33 34 35 36" + RUN_NUMBER: ${{ github.run_number }} run: | - GCLOUD_RESULTS_DIR=authflowtester-single-user-build-${{github.run_number}} + GCLOUD_RESULTS_DIR="authflowtester-single-user-build-${RUN_NUMBER}" DEVICE_ARGS=() for LEVEL in $FULL_API_RANGE; do DEVICE_ARGS+=(--device "model=MediumPhone.arm,version=${LEVEL},locale=en,orientation=portrait") done - + gcloud firebase test android run \ --project mobile-apps-firebase-test \ --type instrumentation \ @@ -118,13 +151,14 @@ jobs: if: ${{ ! inputs.is_pr }} env: FULL_API_RANGE: "28 29 30 31 32 33 34 35 36" + RUN_NUMBER: ${{ github.run_number }} run: | - GCLOUD_RESULTS_DIR=authflowtester-multi-user-build-${{github.run_number}} + GCLOUD_RESULTS_DIR="authflowtester-multi-user-build-${RUN_NUMBER}" DEVICE_ARGS=() for LEVEL in $FULL_API_RANGE; do DEVICE_ARGS+=(--device "model=MediumPhone.arm,version=${LEVEL},locale=en,orientation=portrait") done - + gcloud firebase test android run \ --project mobile-apps-firebase-test \ --type instrumentation \ @@ -145,6 +179,7 @@ jobs: if: success() || failure() env: IS_PR: ${{ inputs.is_pr }} + RUN_NUMBER: ${{ github.run_number }} run: | mkdir -p firebase_results BUCKET="gs://test-lab-w87i9sz6q175u-kwp8ium6js0zw" @@ -191,24 +226,24 @@ jobs: done } - if $IS_PR ; then - BUCKET_PATH="${BUCKET}/authflowtester-pr-build-${{github.run_number}}" + if [ "$IS_PR" = "true" ] ; then + BUCKET_PATH="${BUCKET}/authflowtester-pr-build-${RUN_NUMBER}" if gsutil ls "${BUCKET_PATH}" > /dev/null 2>&1; then copy_results_by_api_level "${BUCKET_PATH}" "pr" fi else - SINGLE_PATH="${BUCKET}/authflowtester-single-user-build-${{github.run_number}}" + SINGLE_PATH="${BUCKET}/authflowtester-single-user-build-${RUN_NUMBER}" if gsutil ls "${SINGLE_PATH}" > /dev/null 2>&1; then copy_results_by_api_level "${SINGLE_PATH}" "single-user" fi - MULTI_PATH="${BUCKET}/authflowtester-multi-user-build-${{github.run_number}}" + MULTI_PATH="${BUCKET}/authflowtester-multi-user-build-${RUN_NUMBER}" if gsutil ls "${MULTI_PATH}" > /dev/null 2>&1; then copy_results_by_api_level "${MULTI_PATH}" "multi-user" fi fi - name: Test Report - uses: mikepenz/action-junit-report@v6 + uses: mikepenz/action-junit-report@3a81627bfac62268172037048872e8ebd4207e6d # v6.4.1 if: success() || failure() with: check_name: AuthFlowTester Test Results @@ -224,13 +259,13 @@ jobs: report_paths: 'firebase_results/**.xml' - name: Archive APK if: success() || failure() - uses: actions/upload-artifact@v4 + uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4.6.2 with: name: AuthFlowTester-debug-${{ github.run_number }} path: native/NativeSampleApps/AuthFlowTester/build/outputs/apk/debug/AuthFlowTester-debug.apk - name: Archive Test Results - uses: actions/upload-artifact@v4 + uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4.6.2 if: success() || failure() with: name: ui-test-results - path: 'firebase_results/**.xml' \ No newline at end of file + path: 'firebase_results/**.xml' From 6e9648231bd6ef968b9d925c30486a954987878c Mon Sep 17 00:00:00 2001 From: Brandon Page Date: Thu, 28 May 2026 18:41:16 -0700 Subject: [PATCH 2/2] Add job-level permissions to reusable-workflow callers. --- .github/workflows/nightly.yaml | 9 +++++++++ .github/workflows/pr.yaml | 6 ++++++ 2 files changed, 15 insertions(+) diff --git a/.github/workflows/nightly.yaml b/.github/workflows/nightly.yaml index 222b1488b6..0182ff1c51 100644 --- a/.github/workflows/nightly.yaml +++ b/.github/workflows/nightly.yaml @@ -13,6 +13,9 @@ permissions: # don't run simultaneously (to prevent flappers). jobs: android-nightly: + permissions: + contents: read + pull-requests: write strategy: fail-fast: false matrix: @@ -25,6 +28,9 @@ jobs: GCLOUD_SERVICE_KEY: ${{ secrets.GCLOUD_SERVICE_KEY }} CODECOV_TOKEN: ${{ secrets.CODECOV_TOKEN }} android-nightly-Hybrid: + permissions: + contents: read + pull-requests: write if: success() || failure() needs: [android-nightly] strategy: @@ -39,6 +45,9 @@ jobs: GCLOUD_SERVICE_KEY: ${{ secrets.GCLOUD_SERVICE_KEY }} CODECOV_TOKEN: ${{ secrets.CODECOV_TOKEN }} android-nightly-UI-Tests: + permissions: + contents: read + pull-requests: write uses: ./.github/workflows/reusable-ui-workflow.yaml secrets: MSDK_ANDROID_REMOTE_ACCESS_CALLBACK_URL: ${{ secrets.MSDK_ANDROID_REMOTE_ACCESS_CALLBACK_URL }} diff --git a/.github/workflows/pr.yaml b/.github/workflows/pr.yaml index af8cfb6d4c..3a3482cd44 100644 --- a/.github/workflows/pr.yaml +++ b/.github/workflows/pr.yaml @@ -62,6 +62,9 @@ jobs: run: bundle exec danger --dangerfile=.github/DangerFiles/TestOrchestrator.rb --danger_id="TestOrchestrator" unit-tests-pr: + permissions: + contents: read + pull-requests: write needs: [test-orchestrator] strategy: fail-fast: false @@ -77,6 +80,9 @@ jobs: CODECOV_TOKEN: ${{ secrets.CODECOV_TOKEN }} ui-tests-pr: + permissions: + contents: read + pull-requests: write needs: [test-orchestrator] uses: ./.github/workflows/reusable-ui-workflow.yaml with: