diff --git a/.github/workflows/claude-code-review.yml b/.github/workflows/claude-code-review.yml index 1f33912b41..d756544331 100644 --- a/.github/workflows/claude-code-review.yml +++ b/.github/workflows/claude-code-review.yml @@ -39,6 +39,7 @@ jobs: prompt: | REPO: ${{ github.repository }} PR NUMBER: ${{ github.event.pull_request.number }} + EVENT ACTION: ${{ github.event.action }} Please review this pull request and provide inline feedback using the GitHub review system. @@ -52,11 +53,37 @@ jobs: Use the repository's CLAUDE.md for guidance on style and conventions. Be constructive and helpful in your feedback. Instructions: + ${{ github.event.action == 'synchronize' && ' + ## SYNCHRONIZE EVENT - UPDATE EXISTING REVIEW + This is an update to an existing PR. You must: + 1. Use the GitHub MCP tools to fetch your previous reviews on this PR + 2. Fetch the latest PR diff and identify what has changed since your last review + 3. Update your EXISTING review comments - DO NOT create a new review summary + 4. Use checkboxes to track progress on previously identified issues: + - [ ] Unresolved issue + - [x] Resolved issue + 5. For each previously identified issue: + - If it has been addressed: Mark the checkbox as complete [x] and add a note + - If it is still present: Keep the checkbox unchecked [ ] + - If new issues are found: Add new checkboxes [ ] + 6. Update inline comments: + - Resolve or update threads that have been addressed + - Add new inline comments ONLY for new issues that require changes + - Do NOT add inline comments for positive changes or improvements + 7. Keep all positive feedback in the summary section only + + DO NOT create a new review from scratch. Update the existing one.' || ' + ## NEW REVIEW EVENT + This is a new PR or initial review. You must: 1. Use the GitHub MCP tools to fetch the PR diff - 2. Add inline comments using the appropriate MCP tools for each specific piece of feedback on particular lines - 3. Submit the review with event type 'COMMENT' (not 'REQUEST_CHANGES') to publish as non-blocking feedback + 2. Create a review summary with checkboxes for any issues found: + - [ ] Issue description and location + 3. Add inline comments ONLY for specific code that needs changes + 4. DO NOT add inline comments for positive feedback - include positive feedback in the summary section only + 5. Submit the review with event type COMMENT (not REQUEST_CHANGES) to publish as non-blocking feedback + ' }} # See https://github.com/anthropics/claude-code-action/blob/main/docs/usage.md # or https://docs.claude.com/en/docs/claude-code/cli-reference for available options - claude_args: '--allowedTools "mcp__github__create_pending_pull_request_review,mcp__github__add_comment_to_pending_review,mcp__github__submit_pending_pull_request_review,mcp__github__get_pull_request_diff"' + claude_args: '--allowedTools "mcp__github__create_pending_pull_request_review,mcp__github__add_comment_to_pending_review,mcp__github__submit_pending_pull_request_review,mcp__github__get_pull_request_diff,mcp__github__list_reviews,mcp__github__get_review,mcp__github__list_review_comments,mcp__github__update_review_comment,mcp__github__create_or_update_pull_request_review_comment"' diff --git a/.github/workflows/pr-pipeline.yml b/.github/workflows/pr-pipeline.yml new file mode 100644 index 0000000000..5faf5418c9 --- /dev/null +++ b/.github/workflows/pr-pipeline.yml @@ -0,0 +1,1115 @@ +name: Pull Request + +on: + pull_request: + types: [opened, synchronize, labeled] + branches-ignore: + - 'release/**' + +concurrency: + group: ${{ github.head_ref || github.run_id }} + cancel-in-progress: true + +jobs: + build: + name: ${{ matrix.app-type-lower }}-build + runs-on: ubuntu-latest + if: >- + ( + (github.event.action == 'opened' || github.event.action == 'synchronize') || + (github.event.action == 'labeled' && (contains(github.event.pull_request.labels.*.name, 'run-ui-tests') || contains(github.event.pull_request.labels.*.name, 'run-e2e-tests'))) + ) && ( + contains(github.event.pull_request.body, 'Parent') || + contains(github.event.pull_request.body, 'Student') || + contains(github.event.pull_request.body, 'Teacher') + ) + strategy: + fail-fast: true + matrix: + include: + - app-type: Parent + app-type-lower: parent + should_run: ${{ contains(github.event.pull_request.body, 'Parent') }} + - app-type: Student + app-type-lower: student + should_run: ${{ contains(github.event.pull_request.body, 'Student') }} + - app-type: Teacher + app-type-lower: teacher + should_run: ${{ contains(github.event.pull_request.body, 'Teacher') }} + steps: + - name: Dump Pull Request Event Context + run: | + echo "body: ${{ github.event.pull_request.body }}" + echo "${{ toJson(github.event.pull_request) }}" + + - name: Checkout repository + if: ${{ matrix.should_run }} + uses: actions/checkout@v4 + with: + submodules: 'recursive' + fetch-depth: 1 + token: ${{ secrets.ACCESS_TOKEN }} + + - name: Set up JDK 17 + if: ${{ matrix.should_run }} + uses: actions/setup-java@v4 + with: + java-version: '17' + distribution: 'temurin' + + - name: Cache Gradle packages + if: ${{ matrix.should_run }} + uses: actions/cache@v4 + with: + path: | + ~/.gradle/caches + ~/.gradle/wrapper + key: ${{ runner.os }}-gradle-${{ hashFiles('**/gradle-wrapper.properties') }} + restore-keys: | + ${{ runner.os }}-gradle- + + - name: Cache Gradle Build Cache + if: ${{ matrix.should_run }} + uses: actions/cache@v4 + with: + path: | + ~/.gradle/caches/build-cache-* + .gradle + key: ${{ runner.os }}-gradle-build-cache-${{ github.sha }} + restore-keys: | + ${{ runner.os }}-gradle-build-cache- + + - name: Decode Release Keystore + if: ${{ matrix.should_run }} + run: | + echo "${{ secrets.ANDROID_RELEASE_KEYSTORE_B64 }}" | base64 --decode > release.jks + chmod 600 release.jks + + - name: Setup Service account + if: ${{ matrix.should_run }} + env: + FIREBASE_SERVICE_ACCOUNT_KEY: ${{ secrets.FIREBASE_SERVICE_ACCOUNT_KEY }} + run: | + if [ -z "${FIREBASE_SERVICE_ACCOUNT_KEY}" ]; then + echo "Error: FIREBASE_SERVICE_ACCOUNT_KEY is not set" + exit 1 + fi + echo "${FIREBASE_SERVICE_ACCOUNT_KEY}" > service-account-key.json + chmod 600 service-account-key.json + + - name: Build Release Notes + if: ${{ matrix.should_run }} + id: get_release_notes + run: | + echo "RELEASE_NOTES<> $GITHUB_OUTPUT + echo "${{ github.event.pull_request.title }}" >> $GITHUB_OUTPUT + echo "EOF" >> $GITHUB_OUTPUT + + - name: Install Firebase CLI + if: ${{ matrix.should_run }} + run: npm install -g firebase-tools + + - name: Setup Parent Firebase App Id + if: ${{ matrix.should_run && matrix.app-type == 'Parent' }} + run: | + if [ -z "${{ secrets.FIREBASE_ANDROID_PARENT_APP_ID }}" ]; then + echo "Error: FIREBASE_ANDROID_PARENT_APP_ID is not set" + exit 1 + fi + echo "${{ secrets.FIREBASE_ANDROID_PARENT_APP_ID }}" > firebase_app_id.txt + + - name: Setup Student Firebase App Id + if: ${{ matrix.should_run && matrix.app-type == 'Student' }} + run: | + if [ -z "${{ secrets.FIREBASE_ANDROID_STUDENT_APP_ID }}" ]; then + echo "Error: FIREBASE_ANDROID_STUDENT_APP_ID is not set" + exit 1 + fi + echo "${{ secrets.FIREBASE_ANDROID_STUDENT_APP_ID }}" > firebase_app_id.txt + + - name: Setup Teacher Firebase App Id + if: ${{ matrix.should_run && matrix.app-type == 'Teacher' }} + run: | + if [ -z "${{ secrets.FIREBASE_ANDROID_TEACHER_APP_ID }}" ]; then + echo "Error: FIREBASE_ANDROID_TEACHER_APP_ID is not set" + exit 1 + fi + echo "${{ secrets.FIREBASE_ANDROID_TEACHER_APP_ID }}" > firebase_app_id.txt + + # Building Artifacts + - name: Build debug and test APKs + if: ${{ matrix.should_run }} + run: | + ./gradle/gradlew -p apps :${{ matrix.app-type-lower }}:assembleQaDebug \ + :${{ matrix.app-type-lower }}:assembleQaDebugAndroidTest \ + :${{ matrix.app-type-lower }}:assembleDevDebugMinify \ + --build-cache \ + --parallel \ + --max-workers=4 \ + --no-daemon \ + -Dorg.gradle.jvmargs="-Xmx6g -XX:+HeapDumpOnOutOfMemoryError" \ + -Dkotlin.compiler.execution.strategy=in-process \ + -Pandroid.injected.signing.store.file=$(pwd)/release.jks + + # Uploading Artifacts to GitHub + - name: Upload QA debug APK + if: ${{ matrix.should_run }} + uses: actions/upload-artifact@v4 + with: + name: ${{ matrix.app-type-lower }}-qa-debug.apk + path: apps/${{ matrix.app-type-lower }}/build/outputs/apk/qa/debug/${{ matrix.app-type-lower }}-qa-debug.apk + + - name: Upload QA test APK + if: ${{ matrix.should_run }} + uses: actions/upload-artifact@v4 + with: + name: ${{ matrix.app-type-lower }}-qa-debug-androidTest.apk + path: apps/${{ matrix.app-type-lower }}/build/outputs/apk/androidTest/qa/debug/${{ matrix.app-type-lower }}-qa-debug-androidTest.apk + + - name: Upload Dev debug APK + if: ${{ matrix.should_run }} + uses: actions/upload-artifact@v4 + with: + name: ${{ matrix.app-type-lower }}-dev-debugMinify.apk + path: apps/${{ matrix.app-type-lower }}/build/outputs/apk/dev/debugMinify/${{ matrix.app-type-lower }}-dev-debugMinify.apk + + # Uploading Artifacts to Firebase App Distribution + - name: Distribute app to Firebase App Distribution + if: ${{ matrix.should_run }} + env: + GOOGLE_APPLICATION_CREDENTIALS: ${{ github.workspace }}/service-account-key.json + run: | + firebase --version + FIREBASE_APP_ID=$(cat firebase_app_id.txt) + + if ! firebase appdistribution:distribute "apps/${{ matrix.app-type-lower }}/build/outputs/apk/dev/debugMinify/${{ matrix.app-type-lower }}-dev-debugMinify.apk" \ + --app "$FIREBASE_APP_ID" \ + --release-notes "${{ steps.get_release_notes.outputs.RELEASE_NOTES }}" \ + --groups "Testers" > result.txt 2>&1; then + echo "Firebase distribution failed:" + cat result.txt + exit 1 + fi + cat result.txt + + - name: Prepare Comment Body + if: ${{ matrix.should_run }} + id: prepare_comment + run: | + INSTALL_URL=$(grep -o 'https://appdistribution\.firebase[^[:space:]]*' result.txt | head -1) + + if [ -z "$INSTALL_URL" ]; then + echo "Error: Could not extract install URL from Firebase output" + cat result.txt + exit 1 + fi + + INSTALL_URL_ESCAPED=$(printf '%s' "$INSTALL_URL" | sed 's/:/%3A/g; s/\//%2F/g; s/?/%3F/g; s/=/%3D/g; s/&/%26/g') + { + echo "body<" + echo "

${{ matrix.app-type }} Install Page

" + echo "EOF" + } >> $GITHUB_OUTPUT + + - name: Find Previous Comment + if: ${{ matrix.should_run }} + id: find_comment + uses: peter-evans/find-comment@v2 + with: + issue-number: ${{ github.event.number }} + comment-author: 'github-actions[bot]' + body-includes: '' + + - name: Create or Update Comment + if: ${{ matrix.should_run }} + uses: peter-evans/create-or-update-comment@v4 + with: + comment-id: ${{ steps.find_comment.outputs.comment-id }} + issue-number: ${{ github.event.number }} + body: ${{ steps.prepare_comment.outputs.body }} + edit-mode: replace + + - name: Cleanup sensitive files + if: always() && matrix.should_run + run: | + rm -f release.jks + rm -f service-account-key.json + rm -f firebase_app_id.txt + rm -f result.txt + + submodule-build-and-test: + name: submodule-build-and-test + runs-on: ubuntu-latest + if: >- + ( + (github.event.action == 'opened' || github.event.action == 'synchronize') || + (github.event.action == 'labeled' && (contains(github.event.pull_request.labels.*.name, 'run-ui-tests') || contains(github.event.pull_request.labels.*.name, 'run-e2e-tests'))) + ) && ( + contains(github.event.pull_request.body, 'Parent') || + contains(github.event.pull_request.body, 'Student') || + contains(github.event.pull_request.body, 'Teacher') + ) + steps: + - name: Checkout repository + uses: actions/checkout@v4 + with: + submodules: 'recursive' + token: ${{ secrets.ACCESS_TOKEN }} + + - name: Set up JDK 17 + uses: actions/setup-java@v4 + with: + java-version: '17' + distribution: 'temurin' + + - name: Cache Gradle packages + uses: actions/cache@v4 + with: + path: | + ~/.gradle/caches + ~/.gradle/wrapper + key: ${{ runner.os }}-gradle-${{ hashFiles('**/gradle-wrapper.properties') }} + restore-keys: | + ${{ runner.os }}-gradle- + + # Building Artifacts + - name: Build test and app APKs + run: | + ./gradle/gradlew -p apps :pandautils:assembleDebugAndroidTest + mv ./libs/pandautils/build/outputs/apk/androidTest/debug/pandautils-debug-androidTest.apk ./libs/pandautils/pandautils-test.apk + ./gradle/gradlew -p apps :pandautils:assembleDebugAndroidTest -DtestApplicationId=com.instructure.pandautils + mv ./libs/pandautils/build/outputs/apk/androidTest/debug/pandautils-debug-androidTest.apk ./libs/pandautils/pandautils-app.apk + + - name: Run submodule unit tests + run: | + ./gradle/gradlew -p apps testDebugUnitTest -x :dataseedingapi:test -x :teacher:test -x :student:test + + # Uploading Artifacts to GitHub + - name: Upload test APK + uses: actions/upload-artifact@v4 + with: + name: pandautils-test.apk + path: libs/pandautils/pandautils-test.apk + + - name: Upload app APK + uses: actions/upload-artifact@v4 + with: + name: pandautils-app.apk + path: libs/pandautils/pandautils-app.apk + + parent-unit-tests: + name: parent-unit-tests + runs-on: ubuntu-latest + if: >- + ( + (github.event.action == 'opened' || github.event.action == 'synchronize') || + (github.event.action == 'labeled' && (contains(github.event.pull_request.labels.*.name, 'run-ui-tests') || contains(github.event.pull_request.labels.*.name, 'run-e2e-tests'))) + ) && contains(github.event.pull_request.body, 'Parent') + steps: + - name: Checkout repository + uses: actions/checkout@v4 + with: + submodules: 'recursive' + fetch-depth: 1 + token: ${{ secrets.ACCESS_TOKEN }} + + - name: Set up JDK 17 + uses: actions/setup-java@v4 + with: + java-version: '17' + distribution: 'temurin' + + - name: Cache Gradle packages + uses: actions/cache@v4 + with: + path: | + ~/.gradle/caches + ~/.gradle/wrapper + key: ${{ runner.os }}-gradle-${{ hashFiles('**/gradle-wrapper.properties') }} + restore-keys: | + ${{ runner.os }}-gradle- + + - name: Run unit tests + run: | + ./gradle/gradlew -p apps :parent:testDevDebugUnitTest \ + --build-cache \ + --parallel \ + --max-workers=4 \ + --no-daemon \ + -Dorg.gradle.jvmargs="-Xmx6g -XX:+HeapDumpOnOutOfMemoryError" \ + -Dkotlin.compiler.execution.strategy=in-process + + student-unit-tests: + name: student-unit-tests + runs-on: ubuntu-latest + if: >- + ( + (github.event.action == 'opened' || github.event.action == 'synchronize') || + (github.event.action == 'labeled' && (contains(github.event.pull_request.labels.*.name, 'run-ui-tests') || contains(github.event.pull_request.labels.*.name, 'run-e2e-tests'))) + ) && contains(github.event.pull_request.body, 'Student') + steps: + - name: Checkout repository + uses: actions/checkout@v4 + with: + submodules: 'recursive' + fetch-depth: 1 + token: ${{ secrets.ACCESS_TOKEN }} + + - name: Set up JDK 17 + uses: actions/setup-java@v4 + with: + java-version: '17' + distribution: 'temurin' + + - name: Cache Gradle packages + uses: actions/cache@v4 + with: + path: | + ~/.gradle/caches + ~/.gradle/wrapper + key: ${{ runner.os }}-gradle-${{ hashFiles('**/gradle-wrapper.properties') }} + restore-keys: | + ${{ runner.os }}-gradle- + + - name: Run unit tests + run: | + ./gradle/gradlew -p apps :student:testDevDebugUnitTest \ + --build-cache \ + --parallel \ + --max-workers=4 \ + --no-daemon \ + -Dorg.gradle.jvmargs="-Xmx6g -XX:+HeapDumpOnOutOfMemoryError" \ + -Dkotlin.compiler.execution.strategy=in-process + + teacher-unit-tests: + name: teacher-unit-tests + runs-on: ubuntu-latest + if: >- + ( + (github.event.action == 'opened' || github.event.action == 'synchronize') || + (github.event.action == 'labeled' && (contains(github.event.pull_request.labels.*.name, 'run-ui-tests') || contains(github.event.pull_request.labels.*.name, 'run-e2e-tests'))) + ) && contains(github.event.pull_request.body, 'Teacher') + steps: + - name: Checkout repository + uses: actions/checkout@v4 + with: + submodules: 'recursive' + fetch-depth: 1 + token: ${{ secrets.ACCESS_TOKEN }} + + - name: Set up JDK 17 + uses: actions/setup-java@v4 + with: + java-version: '17' + distribution: 'temurin' + + - name: Cache Gradle packages + uses: actions/cache@v4 + with: + path: | + ~/.gradle/caches + ~/.gradle/wrapper + key: ${{ runner.os }}-gradle-${{ hashFiles('**/gradle-wrapper.properties') }} + restore-keys: | + ${{ runner.os }}-gradle- + + - name: Run unit tests + run: | + ./gradle/gradlew -p apps :teacher:testDevDebugUnitTest \ + --build-cache \ + --parallel \ + --max-workers=4 \ + --no-daemon \ + -Dorg.gradle.jvmargs="-Xmx6g -XX:+HeapDumpOnOutOfMemoryError" \ + -Dkotlin.compiler.execution.strategy=in-process + + parent-portrait-ui-tests: + name: parent-portrait-ui-tests + runs-on: ubuntu-latest + if: >- + ( + (github.event.action == 'opened' || github.event.action == 'synchronize') || + (github.event.action == 'labeled' && (contains(github.event.pull_request.labels.*.name, 'run-ui-tests') || contains(github.event.pull_request.labels.*.name, 'run-e2e-tests'))) + ) && contains(github.event.pull_request.body, 'Parent') + needs: [build, parent-unit-tests] + steps: + - name: Checkout repository + uses: actions/checkout@v4 + with: + fetch-depth: 1 + + - name: Download artifacts + uses: actions/download-artifact@v4 + + - name: Setup Service account + env: + FIREBASE_SERVICE_ACCOUNT_KEY: ${{ secrets.GCLOUD_KEY }} + run: | + if [ -z "${FIREBASE_SERVICE_ACCOUNT_KEY}" ]; then + echo "Error: GCLOUD_KEY secret is not set" + exit 1 + fi + echo "${FIREBASE_SERVICE_ACCOUNT_KEY}" > service-account-key.json + chmod 600 service-account-key.json + + - name: Setup Flank config + run: cp ./apps/parent/flank.yml ./flank.yml + + - name: Copy APKs to expected locations + run: | + if [ -d "parent-qa-debug.apk" ]; then + mkdir -p apps/parent/build/outputs/apk/qa/debug + mv parent-qa-debug.apk/parent-qa-debug.apk apps/parent/build/outputs/apk/qa/debug/ + rm -rf parent-qa-debug.apk + fi + if [ -d "parent-qa-debug-androidTest.apk" ]; then + mkdir -p apps/parent/build/outputs/apk/androidTest/qa/debug + mv parent-qa-debug-androidTest.apk/parent-qa-debug-androidTest.apk apps/parent/build/outputs/apk/androidTest/qa/debug/ + rm -rf parent-qa-debug-androidTest.apk + fi + if [ -d "parent-dev-debugMinify.apk" ]; then + mkdir -p apps/parent/build/outputs/apk/dev/debugMinify + mv parent-dev-debugMinify.apk/parent-dev-debugMinify.apk apps/parent/build/outputs/apk/dev/debugMinify/ + rm -rf parent-dev-debugMinify.apk + fi + + - name: Run Flank UI tests + uses: Flank/flank@v23.10.1 + with: + version: 'v23.07.0' + platform: 'android' + service_account: './service-account-key.json' + flank_configuration_file: './flank.yml' + + - name: Report test results to Splunk + run: ./apps/postProcessTestRun.bash parent results/`ls results` + env: + SPLUNK_MOBILE_TOKEN: ${{ secrets.SPLUNK_MOBILE_TOKEN }} + OBSERVE_MOBILE_TOKEN: ${{ secrets.OBSERVE_MOBILE_TOKEN }} + BITRISE_TRIGGERED_WORKFLOW_ID: ${{ github.workflow }} + BITRISE_GIT_BRANCH: ${{ github.ref_name }} + BITRISE_BUILD_URL: ${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }} + + - name: Cleanup sensitive files + if: always() + run: rm -f service-account-key.json + + parent-landscape-ui-tests: + name: parent-landscape-ui-tests + runs-on: ubuntu-latest + if: >- + ( + (github.event.action == 'opened' || github.event.action == 'synchronize') || + (github.event.action == 'labeled' && (contains(github.event.pull_request.labels.*.name, 'run-ui-tests') || contains(github.event.pull_request.labels.*.name, 'run-e2e-tests'))) + ) && contains(github.event.pull_request.body, 'Parent') + needs: [build, parent-unit-tests] + steps: + - name: Checkout repository + uses: actions/checkout@v4 + with: + fetch-depth: 1 + + - name: Download artifacts + uses: actions/download-artifact@v4 + + - name: Setup Service account + env: + FIREBASE_SERVICE_ACCOUNT_KEY: ${{ secrets.GCLOUD_KEY }} + run: | + if [ -z "${FIREBASE_SERVICE_ACCOUNT_KEY}" ]; then + echo "Error: GCLOUD_KEY secret is not set" + exit 1 + fi + echo "${FIREBASE_SERVICE_ACCOUNT_KEY}" > service-account-key.json + chmod 600 service-account-key.json + + - name: Setup Flank config + run: cp ./apps/parent/flank_landscape.yml ./flank.yml + + - name: Copy APKs to expected locations + run: | + if [ -d "parent-qa-debug.apk" ]; then + mkdir -p apps/parent/build/outputs/apk/qa/debug + mv parent-qa-debug.apk/parent-qa-debug.apk apps/parent/build/outputs/apk/qa/debug/ + rm -rf parent-qa-debug.apk + fi + if [ -d "parent-qa-debug-androidTest.apk" ]; then + mkdir -p apps/parent/build/outputs/apk/androidTest/qa/debug + mv parent-qa-debug-androidTest.apk/parent-qa-debug-androidTest.apk apps/parent/build/outputs/apk/androidTest/qa/debug/ + rm -rf parent-qa-debug-androidTest.apk + fi + if [ -d "parent-dev-debugMinify.apk" ]; then + mkdir -p apps/parent/build/outputs/apk/dev/debugMinify + mv parent-dev-debugMinify.apk/parent-dev-debugMinify.apk apps/parent/build/outputs/apk/dev/debugMinify/ + rm -rf parent-dev-debugMinify.apk + fi + + - name: Run Flank UI tests + uses: Flank/flank@v23.10.1 + with: + version: 'v23.07.0' + platform: 'android' + service_account: './service-account-key.json' + flank_configuration_file: './flank.yml' + + - name: Report test results to Splunk + run: ./apps/postProcessTestRun.bash parent results/`ls results` + env: + SPLUNK_MOBILE_TOKEN: ${{ secrets.SPLUNK_MOBILE_TOKEN }} + OBSERVE_MOBILE_TOKEN: ${{ secrets.OBSERVE_MOBILE_TOKEN }} + BITRISE_TRIGGERED_WORKFLOW_ID: ${{ github.workflow }} + BITRISE_GIT_BRANCH: ${{ github.ref_name }} + BITRISE_BUILD_URL: ${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }} + + - name: Cleanup sensitive files + if: always() + run: rm -f service-account-key.json + + student-portrait-ui-tests: + name: student-portrait-ui-tests + runs-on: ubuntu-latest + if: >- + ( + (github.event.action == 'opened' || github.event.action == 'synchronize') || + (github.event.action == 'labeled' && (contains(github.event.pull_request.labels.*.name, 'run-ui-tests') || contains(github.event.pull_request.labels.*.name, 'run-e2e-tests'))) + ) && contains(github.event.pull_request.body, 'Student') + needs: [build, student-unit-tests] + steps: + - name: Checkout repository + uses: actions/checkout@v4 + with: + fetch-depth: 1 + + - name: Download artifacts + uses: actions/download-artifact@v4 + + - name: Setup Service account + env: + FIREBASE_SERVICE_ACCOUNT_KEY: ${{ secrets.GCLOUD_KEY }} + run: | + if [ -z "${FIREBASE_SERVICE_ACCOUNT_KEY}" ]; then + echo "Error: GCLOUD_KEY secret is not set" + exit 1 + fi + echo "${FIREBASE_SERVICE_ACCOUNT_KEY}" > service-account-key.json + chmod 600 service-account-key.json + + - name: Setup Flank config + run: cp ./apps/student/flank.yml ./flank.yml + + - name: Copy APKs to expected locations + run: | + if [ -d "student-qa-debug.apk" ]; then + mkdir -p apps/student/build/outputs/apk/qa/debug + mv student-qa-debug.apk/student-qa-debug.apk apps/student/build/outputs/apk/qa/debug/ + rm -rf student-qa-debug.apk + fi + if [ -d "student-qa-debug-androidTest.apk" ]; then + mkdir -p apps/student/build/outputs/apk/androidTest/qa/debug + mv student-qa-debug-androidTest.apk/student-qa-debug-androidTest.apk apps/student/build/outputs/apk/androidTest/qa/debug/ + rm -rf student-qa-debug-androidTest.apk + fi + if [ -d "student-dev-debugMinify.apk" ]; then + mkdir -p apps/student/build/outputs/apk/dev/debugMinify + mv student-dev-debugMinify.apk/student-dev-debugMinify.apk apps/student/build/outputs/apk/dev/debugMinify/ + rm -rf student-dev-debugMinify.apk + fi + + - name: Run Flank UI tests + uses: Flank/flank@v23.10.1 + with: + version: 'v23.07.0' + platform: 'android' + service_account: './service-account-key.json' + flank_configuration_file: './flank.yml' + + - name: Report test results to Splunk + run: ./apps/postProcessTestRun.bash student results/`ls results` + env: + SPLUNK_MOBILE_TOKEN: ${{ secrets.SPLUNK_MOBILE_TOKEN }} + OBSERVE_MOBILE_TOKEN: ${{ secrets.OBSERVE_MOBILE_TOKEN }} + BITRISE_TRIGGERED_WORKFLOW_ID: ${{ github.workflow }} + BITRISE_GIT_BRANCH: ${{ github.ref_name }} + BITRISE_BUILD_URL: ${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }} + + - name: Cleanup sensitive files + if: always() + run: rm -f service-account-key.json + + student-landscape-ui-tests: + name: student-landscape-ui-tests + runs-on: ubuntu-latest + if: >- + ( + (github.event.action == 'opened' || github.event.action == 'synchronize') || + (github.event.action == 'labeled' && (contains(github.event.pull_request.labels.*.name, 'run-ui-tests') || contains(github.event.pull_request.labels.*.name, 'run-e2e-tests'))) + ) && contains(github.event.pull_request.body, 'Student') + needs: [build, student-unit-tests] + steps: + - name: Checkout repository + uses: actions/checkout@v4 + with: + fetch-depth: 1 + + - name: Download artifacts + uses: actions/download-artifact@v4 + + - name: Setup Service account + env: + FIREBASE_SERVICE_ACCOUNT_KEY: ${{ secrets.GCLOUD_KEY }} + run: | + if [ -z "${FIREBASE_SERVICE_ACCOUNT_KEY}" ]; then + echo "Error: GCLOUD_KEY secret is not set" + exit 1 + fi + echo "${FIREBASE_SERVICE_ACCOUNT_KEY}" > service-account-key.json + chmod 600 service-account-key.json + + - name: Setup Flank config + run: cp ./apps/student/flank_landscape.yml ./flank.yml + + - name: Copy APKs to expected locations + run: | + if [ -d "student-qa-debug.apk" ]; then + mkdir -p apps/student/build/outputs/apk/qa/debug + mv student-qa-debug.apk/student-qa-debug.apk apps/student/build/outputs/apk/qa/debug/ + rm -rf student-qa-debug.apk + fi + if [ -d "student-qa-debug-androidTest.apk" ]; then + mkdir -p apps/student/build/outputs/apk/androidTest/qa/debug + mv student-qa-debug-androidTest.apk/student-qa-debug-androidTest.apk apps/student/build/outputs/apk/androidTest/qa/debug/ + rm -rf student-qa-debug-androidTest.apk + fi + if [ -d "student-dev-debugMinify.apk" ]; then + mkdir -p apps/student/build/outputs/apk/dev/debugMinify + mv student-dev-debugMinify.apk/student-dev-debugMinify.apk apps/student/build/outputs/apk/dev/debugMinify/ + rm -rf student-dev-debugMinify.apk + fi + + - name: Run Flank UI tests + uses: Flank/flank@v23.10.1 + with: + version: 'v23.07.0' + platform: 'android' + service_account: './service-account-key.json' + flank_configuration_file: './flank.yml' + + - name: Report test results to Splunk + run: ./apps/postProcessTestRun.bash student results/`ls results` + env: + SPLUNK_MOBILE_TOKEN: ${{ secrets.SPLUNK_MOBILE_TOKEN }} + OBSERVE_MOBILE_TOKEN: ${{ secrets.OBSERVE_MOBILE_TOKEN }} + BITRISE_TRIGGERED_WORKFLOW_ID: ${{ github.workflow }} + BITRISE_GIT_BRANCH: ${{ github.ref_name }} + BITRISE_BUILD_URL: ${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }} + + - name: Cleanup sensitive files + if: always() + run: rm -f service-account-key.json + + teacher-portrait-ui-tests: + name: teacher-portrait-ui-tests + runs-on: ubuntu-latest + if: >- + ( + (github.event.action == 'opened' || github.event.action == 'synchronize') || + (github.event.action == 'labeled' && (contains(github.event.pull_request.labels.*.name, 'run-ui-tests') || contains(github.event.pull_request.labels.*.name, 'run-e2e-tests'))) + ) && contains(github.event.pull_request.body, 'Teacher') + needs: [build, teacher-unit-tests] + steps: + - name: Checkout repository + uses: actions/checkout@v4 + with: + fetch-depth: 1 + + - name: Download artifacts + uses: actions/download-artifact@v4 + + - name: Setup Service account + env: + FIREBASE_SERVICE_ACCOUNT_KEY: ${{ secrets.GCLOUD_KEY }} + run: | + if [ -z "${FIREBASE_SERVICE_ACCOUNT_KEY}" ]; then + echo "Error: GCLOUD_KEY secret is not set" + exit 1 + fi + echo "${FIREBASE_SERVICE_ACCOUNT_KEY}" > service-account-key.json + chmod 600 service-account-key.json + + - name: Setup Flank config + run: cp ./apps/teacher/flank.yml ./flank.yml + + - name: Copy APKs to expected locations + run: | + if [ -d "teacher-qa-debug.apk" ]; then + mkdir -p apps/teacher/build/outputs/apk/qa/debug + mv teacher-qa-debug.apk/teacher-qa-debug.apk apps/teacher/build/outputs/apk/qa/debug/ + rm -rf teacher-qa-debug.apk + fi + if [ -d "teacher-qa-debug-androidTest.apk" ]; then + mkdir -p apps/teacher/build/outputs/apk/androidTest/qa/debug + mv teacher-qa-debug-androidTest.apk/teacher-qa-debug-androidTest.apk apps/teacher/build/outputs/apk/androidTest/qa/debug/ + rm -rf teacher-qa-debug-androidTest.apk + fi + if [ -d "teacher-dev-debugMinify.apk" ]; then + mkdir -p apps/teacher/build/outputs/apk/dev/debugMinify + mv teacher-dev-debugMinify.apk/teacher-dev-debugMinify.apk apps/teacher/build/outputs/apk/dev/debugMinify/ + rm -rf teacher-dev-debugMinify.apk + fi + + - name: Run Flank UI tests + uses: Flank/flank@v23.10.1 + with: + version: 'v23.07.0' + platform: 'android' + service_account: './service-account-key.json' + flank_configuration_file: './flank.yml' + + - name: Report test results to Splunk + run: ./apps/postProcessTestRun.bash teacher results/`ls results` + env: + SPLUNK_MOBILE_TOKEN: ${{ secrets.SPLUNK_MOBILE_TOKEN }} + OBSERVE_MOBILE_TOKEN: ${{ secrets.OBSERVE_MOBILE_TOKEN }} + BITRISE_TRIGGERED_WORKFLOW_ID: ${{ github.workflow }} + BITRISE_GIT_BRANCH: ${{ github.ref_name }} + BITRISE_BUILD_URL: ${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }} + + - name: Cleanup sensitive files + if: always() + run: rm -f service-account-key.json + + teacher-landscape-ui-tests: + name: teacher-landscape-ui-tests + runs-on: ubuntu-latest + if: >- + ( + (github.event.action == 'opened' || github.event.action == 'synchronize') || + (github.event.action == 'labeled' && (contains(github.event.pull_request.labels.*.name, 'run-ui-tests') || contains(github.event.pull_request.labels.*.name, 'run-e2e-tests'))) + ) && contains(github.event.pull_request.body, 'Teacher') + needs: [build, teacher-unit-tests] + steps: + - name: Checkout repository + uses: actions/checkout@v4 + with: + fetch-depth: 1 + + - name: Download artifacts + uses: actions/download-artifact@v4 + + - name: Setup Service account + env: + FIREBASE_SERVICE_ACCOUNT_KEY: ${{ secrets.GCLOUD_KEY }} + run: | + if [ -z "${FIREBASE_SERVICE_ACCOUNT_KEY}" ]; then + echo "Error: GCLOUD_KEY secret is not set" + exit 1 + fi + echo "${FIREBASE_SERVICE_ACCOUNT_KEY}" > service-account-key.json + chmod 600 service-account-key.json + + - name: Setup Flank config + run: cp ./apps/teacher/flank_landscape.yml ./flank.yml + + - name: Copy APKs to expected locations + run: | + if [ -d "teacher-qa-debug.apk" ]; then + mkdir -p apps/teacher/build/outputs/apk/qa/debug + mv teacher-qa-debug.apk/teacher-qa-debug.apk apps/teacher/build/outputs/apk/qa/debug/ + rm -rf teacher-qa-debug.apk + fi + if [ -d "teacher-qa-debug-androidTest.apk" ]; then + mkdir -p apps/teacher/build/outputs/apk/androidTest/qa/debug + mv teacher-qa-debug-androidTest.apk/teacher-qa-debug-androidTest.apk apps/teacher/build/outputs/apk/androidTest/qa/debug/ + rm -rf teacher-qa-debug-androidTest.apk + fi + if [ -d "teacher-dev-debugMinify.apk" ]; then + mkdir -p apps/teacher/build/outputs/apk/dev/debugMinify + mv teacher-dev-debugMinify.apk/teacher-dev-debugMinify.apk apps/teacher/build/outputs/apk/dev/debugMinify/ + rm -rf teacher-dev-debugMinify.apk + fi + + - name: Run Flank UI tests + uses: Flank/flank@v23.10.1 + with: + version: 'v23.07.0' + platform: 'android' + service_account: './service-account-key.json' + flank_configuration_file: './flank.yml' + + - name: Report test results to Splunk + run: ./apps/postProcessTestRun.bash teacher results/`ls results` + env: + SPLUNK_MOBILE_TOKEN: ${{ secrets.SPLUNK_MOBILE_TOKEN }} + OBSERVE_MOBILE_TOKEN: ${{ secrets.OBSERVE_MOBILE_TOKEN }} + BITRISE_TRIGGERED_WORKFLOW_ID: ${{ github.workflow }} + BITRISE_GIT_BRANCH: ${{ github.ref_name }} + BITRISE_BUILD_URL: ${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }} + + - name: Cleanup sensitive files + if: always() + run: rm -f service-account-key.json + + parent-e2e-tests: + name: parent-e2e-tests + runs-on: ubuntu-latest + if: >- + ( + (github.event.action == 'opened' || github.event.action == 'synchronize') || + (github.event.action == 'labeled' && (contains(github.event.pull_request.labels.*.name, 'run-ui-tests') || contains(github.event.pull_request.labels.*.name, 'run-e2e-tests'))) + ) && contains(github.event.pull_request.body, 'Parent') && contains(github.event.pull_request.body, 'Run E2E test suite') + needs: [build, parent-unit-tests] + steps: + - name: Checkout repository + uses: actions/checkout@v4 + with: + fetch-depth: 1 + + - name: Download artifacts + uses: actions/download-artifact@v4 + + - name: Setup Service account + env: + FIREBASE_SERVICE_ACCOUNT_KEY: ${{ secrets.GCLOUD_KEY }} + run: | + if [ -z "${FIREBASE_SERVICE_ACCOUNT_KEY}" ]; then + echo "Error: GCLOUD_KEY secret is not set" + exit 1 + fi + echo "${FIREBASE_SERVICE_ACCOUNT_KEY}" > service-account-key.json + chmod 600 service-account-key.json + + - name: Setup Flank config + run: cp ./apps/parent/flank_e2e.yml ./flank.yml + + - name: Copy APKs to expected locations + run: | + if [ -d "parent-qa-debug.apk" ]; then + mkdir -p apps/parent/build/outputs/apk/qa/debug + mv parent-qa-debug.apk/parent-qa-debug.apk apps/parent/build/outputs/apk/qa/debug/ + rm -rf parent-qa-debug.apk + fi + if [ -d "parent-qa-debug-androidTest.apk" ]; then + mkdir -p apps/parent/build/outputs/apk/androidTest/qa/debug + mv parent-qa-debug-androidTest.apk/parent-qa-debug-androidTest.apk apps/parent/build/outputs/apk/androidTest/qa/debug/ + rm -rf parent-qa-debug-androidTest.apk + fi + if [ -d "parent-dev-debugMinify.apk" ]; then + mkdir -p apps/parent/build/outputs/apk/dev/debugMinify + mv parent-dev-debugMinify.apk/parent-dev-debugMinify.apk apps/parent/build/outputs/apk/dev/debugMinify/ + rm -rf parent-dev-debugMinify.apk + fi + + - name: Run Flank E2E tests + uses: Flank/flank@v23.10.1 + with: + version: 'v23.07.0' + platform: 'android' + service_account: './service-account-key.json' + flank_configuration_file: './flank.yml' + + - name: Report test results to Splunk + run: ./apps/postProcessTestRun.bash parent results/`ls results` + env: + SPLUNK_MOBILE_TOKEN: ${{ secrets.SPLUNK_MOBILE_TOKEN }} + OBSERVE_MOBILE_TOKEN: ${{ secrets.OBSERVE_MOBILE_TOKEN }} + BITRISE_TRIGGERED_WORKFLOW_ID: ${{ github.workflow }} + BITRISE_GIT_BRANCH: ${{ github.ref_name }} + BITRISE_BUILD_URL: ${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }} + + - name: Cleanup sensitive files + if: always() + run: rm -f service-account-key.json + + student-e2e-tests: + name: student-e2e-tests + runs-on: ubuntu-latest + if: >- + ( + (github.event.action == 'opened' || github.event.action == 'synchronize') || + (github.event.action == 'labeled' && (contains(github.event.pull_request.labels.*.name, 'run-ui-tests') || contains(github.event.pull_request.labels.*.name, 'run-e2e-tests'))) + ) && contains(github.event.pull_request.body, 'Student') && contains(github.event.pull_request.body, 'Run E2E test suite') + needs: [build, student-unit-tests] + steps: + - name: Checkout repository + uses: actions/checkout@v4 + with: + fetch-depth: 1 + + - name: Download artifacts + uses: actions/download-artifact@v4 + + - name: Setup Service account + env: + FIREBASE_SERVICE_ACCOUNT_KEY: ${{ secrets.GCLOUD_KEY }} + run: | + if [ -z "${FIREBASE_SERVICE_ACCOUNT_KEY}" ]; then + echo "Error: GCLOUD_KEY secret is not set" + exit 1 + fi + echo "${FIREBASE_SERVICE_ACCOUNT_KEY}" > service-account-key.json + chmod 600 service-account-key.json + + - name: Setup Flank config + run: cp ./apps/student/flank_e2e.yml ./flank.yml + + - name: Copy APKs to expected locations + run: | + if [ -d "student-qa-debug.apk" ]; then + mkdir -p apps/student/build/outputs/apk/qa/debug + mv student-qa-debug.apk/student-qa-debug.apk apps/student/build/outputs/apk/qa/debug/ + rm -rf student-qa-debug.apk + fi + if [ -d "student-qa-debug-androidTest.apk" ]; then + mkdir -p apps/student/build/outputs/apk/androidTest/qa/debug + mv student-qa-debug-androidTest.apk/student-qa-debug-androidTest.apk apps/student/build/outputs/apk/androidTest/qa/debug/ + rm -rf student-qa-debug-androidTest.apk + fi + if [ -d "student-dev-debugMinify.apk" ]; then + mkdir -p apps/student/build/outputs/apk/dev/debugMinify + mv student-dev-debugMinify.apk/student-dev-debugMinify.apk apps/student/build/outputs/apk/dev/debugMinify/ + rm -rf student-dev-debugMinify.apk + fi + + - name: Run Flank E2E tests + uses: Flank/flank@v23.10.1 + with: + version: 'v23.07.0' + platform: 'android' + service_account: './service-account-key.json' + flank_configuration_file: './flank.yml' + + - name: Report test results to Splunk + run: ./apps/postProcessTestRun.bash student results/`ls results` + env: + SPLUNK_MOBILE_TOKEN: ${{ secrets.SPLUNK_MOBILE_TOKEN }} + OBSERVE_MOBILE_TOKEN: ${{ secrets.OBSERVE_MOBILE_TOKEN }} + BITRISE_TRIGGERED_WORKFLOW_ID: ${{ github.workflow }} + BITRISE_GIT_BRANCH: ${{ github.ref_name }} + BITRISE_BUILD_URL: ${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }} + + - name: Cleanup sensitive files + if: always() + run: rm -f service-account-key.json + + teacher-e2e-tests: + name: teacher-e2e-tests + runs-on: ubuntu-latest + if: >- + ( + (github.event.action == 'opened' || github.event.action == 'synchronize') || + (github.event.action == 'labeled' && (contains(github.event.pull_request.labels.*.name, 'run-ui-tests') || contains(github.event.pull_request.labels.*.name, 'run-e2e-tests'))) + ) && contains(github.event.pull_request.body, 'Teacher') && contains(github.event.pull_request.body, 'Run E2E test suite') + needs: [build, teacher-unit-tests] + steps: + - name: Checkout repository + uses: actions/checkout@v4 + with: + fetch-depth: 1 + + - name: Download artifacts + uses: actions/download-artifact@v4 + + - name: Setup Service account + env: + FIREBASE_SERVICE_ACCOUNT_KEY: ${{ secrets.GCLOUD_KEY }} + run: | + if [ -z "${FIREBASE_SERVICE_ACCOUNT_KEY}" ]; then + echo "Error: GCLOUD_KEY secret is not set" + exit 1 + fi + echo "${FIREBASE_SERVICE_ACCOUNT_KEY}" > service-account-key.json + chmod 600 service-account-key.json + + - name: Setup Flank config + run: cp ./apps/teacher/flank_e2e.yml ./flank.yml + + - name: Copy APKs to expected locations + run: | + if [ -d "teacher-qa-debug.apk" ]; then + mkdir -p apps/teacher/build/outputs/apk/qa/debug + mv teacher-qa-debug.apk/teacher-qa-debug.apk apps/teacher/build/outputs/apk/qa/debug/ + rm -rf teacher-qa-debug.apk + fi + if [ -d "teacher-qa-debug-androidTest.apk" ]; then + mkdir -p apps/teacher/build/outputs/apk/androidTest/qa/debug + mv teacher-qa-debug-androidTest.apk/teacher-qa-debug-androidTest.apk apps/teacher/build/outputs/apk/androidTest/qa/debug/ + rm -rf teacher-qa-debug-androidTest.apk + fi + if [ -d "teacher-dev-debugMinify.apk" ]; then + mkdir -p apps/teacher/build/outputs/apk/dev/debugMinify + mv teacher-dev-debugMinify.apk/teacher-dev-debugMinify.apk apps/teacher/build/outputs/apk/dev/debugMinify/ + rm -rf teacher-dev-debugMinify.apk + fi + + - name: Run Flank E2E tests + uses: Flank/flank@v23.10.1 + with: + version: 'v23.07.0' + platform: 'android' + service_account: './service-account-key.json' + flank_configuration_file: './flank.yml' + + - name: Report test results to Splunk + run: ./apps/postProcessTestRun.bash teacher results/`ls results` + env: + SPLUNK_MOBILE_TOKEN: ${{ secrets.SPLUNK_MOBILE_TOKEN }} + OBSERVE_MOBILE_TOKEN: ${{ secrets.OBSERVE_MOBILE_TOKEN }} + BITRISE_TRIGGERED_WORKFLOW_ID: ${{ github.workflow }} + BITRISE_GIT_BRANCH: ${{ github.ref_name }} + BITRISE_BUILD_URL: ${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }} + + - name: Cleanup sensitive files + if: always() + run: rm -f service-account-key.json + + submodule-ui-tests: + name: submodule-ui-tests + runs-on: ubuntu-latest + if: >- + ( + (github.event.action == 'opened' || github.event.action == 'synchronize') || + (github.event.action == 'labeled' && (contains(github.event.pull_request.labels.*.name, 'run-ui-tests') || contains(github.event.pull_request.labels.*.name, 'run-e2e-tests'))) + ) && ( + contains(github.event.pull_request.body, 'Parent') || + contains(github.event.pull_request.body, 'Student') || + contains(github.event.pull_request.body, 'Teacher') + ) + needs: submodule-build-and-test + steps: + - name: Checkout repository + uses: actions/checkout@v4 + + - name: Download test APK artifact + uses: actions/download-artifact@v4 + with: + name: pandautils-test.apk + path: . + + - name: Download app APK artifact + uses: actions/download-artifact@v4 + with: + name: pandautils-app.apk + path: . + + - name: Setup Service account + env: + FIREBASE_SERVICE_ACCOUNT_KEY: ${{ secrets.GCLOUD_KEY }} + run: echo "${FIREBASE_SERVICE_ACCOUNT_KEY}" > service-account-key.json + + - name: Setup Flank config + run: cp ./libs/pandautils/flank.yml ./flank.yml + + - name: Copy APKs to expected locations + run: | + mkdir -p libs/pandautils + mv pandautils-test.apk libs/pandautils/ + mv pandautils-app.apk libs/pandautils/ + + - name: Run Flank E2E tests + uses: Flank/flank@v23.10.1 + with: + version: 'v23.07.0' + platform: 'android' + service_account: './service-account-key.json' + flank_configuration_file: './flank.yml' \ No newline at end of file diff --git a/apps/student/build.gradle b/apps/student/build.gradle index 0f87999743..c756f47af9 100644 --- a/apps/student/build.gradle +++ b/apps/student/build.gradle @@ -338,9 +338,6 @@ dependencies { implementation Libs.GLANCE_APPWIDGET_PREVIEW } -// Comment out this line if the reporting logic starts going wonky. -gradle.addListener new TimingsListener(project) - apply plugin: 'com.google.gms.google-services' if (coverageEnabled) {