Skip to content

Actions PR pipeline

Actions PR pipeline #3

Workflow file for this run

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:
# Determine which apps to build based on PR body
setup:
name: setup-matrix
runs-on: ubuntu-latest
outputs:
apps: ${{ steps.set-matrix.outputs.apps }}
should-run-build: ${{ steps.set-matrix.outputs.should-run-build }}
should-run-ui-tests: ${{ steps.set-matrix.outputs.should-run-ui-tests }}
should-run-e2e-tests: ${{ steps.set-matrix.outputs.should-run-e2e-tests }}
steps:
- name: Determine matrix
id: set-matrix
run: |
# Check which apps are mentioned in PR body
PR_BODY="${{ github.event.pull_request.body }}"
APPS="["
FIRST=true
if echo "$PR_BODY" | grep -q "Parent"; then
if [ "$FIRST" = false ]; then APPS="${APPS},"; fi
APPS="${APPS}{\"type\":\"Parent\",\"type-lower\":\"parent\"}"
FIRST=false
fi
if echo "$PR_BODY" | grep -q "Student"; then
if [ "$FIRST" = false ]; then APPS="${APPS},"; fi
APPS="${APPS}{\"type\":\"Student\",\"type-lower\":\"student\"}"
FIRST=false
fi
if echo "$PR_BODY" | grep -q "Teacher"; then
if [ "$FIRST" = false ]; then APPS="${APPS},"; fi
APPS="${APPS}{\"type\":\"Teacher\",\"type-lower\":\"teacher\"}"
FIRST=false
fi
APPS="${APPS}]"
echo "apps=$APPS" >> $GITHUB_OUTPUT
# Determine if we should run builds
if [[ "${{ github.event.action }}" == "opened" || "${{ github.event.action }}" == "synchronize" ]]; then
echo "should-run-build=true" >> $GITHUB_OUTPUT
elif [[ "${{ github.event.action }}" == "labeled" ]] && [[ "${{ contains(github.event.pull_request.labels.*.name, 'run-ui-tests') }}" == "true" || "${{ contains(github.event.pull_request.labels.*.name, 'run-e2e-tests') }}" == "true" ]]; then
echo "should-run-build=true" >> $GITHUB_OUTPUT
else
echo "should-run-build=false" >> $GITHUB_OUTPUT
fi
# Determine test types
if [[ "${{ contains(github.event.pull_request.labels.*.name, 'run-ui-tests') }}" == "true" ]]; then
echo "should-run-ui-tests=true" >> $GITHUB_OUTPUT
else
echo "should-run-ui-tests=false" >> $GITHUB_OUTPUT
fi
if [[ "${{ contains(github.event.pull_request.labels.*.name, 'run-e2e-tests') }}" == "true" ]]; then
echo "should-run-e2e-tests=true" >> $GITHUB_OUTPUT
else
echo "should-run-e2e-tests=false" >> $GITHUB_OUTPUT
fi
build:
name: ${{ matrix.app.type-lower }}-build
runs-on: ubuntu-latest
needs: setup
if: needs.setup.outputs.should-run-build == 'true' && needs.setup.outputs.apps != '[]'
strategy:
fail-fast: true
matrix:
app: ${{ fromJson(needs.setup.outputs.apps) }}
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', '**/*.gradle.kts', '**/gradle-wrapper.properties', '**/gradle.properties', 'apps/buildSrc/src/**/*.kt') }}
restore-keys: |
${{ runner.os }}-gradle-
- name: Cache Gradle Build Cache
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: Cache npm global packages
uses: actions/cache@v4
with:
path: ~/.npm
key: ${{ runner.os }}-npm-global-firebase
restore-keys: |
${{ runner.os }}-npm-global-
- name: Decode Release Keystore
run: |
echo "${{ secrets.ANDROID_RELEASE_KEYSTORE_B64 }}" | base64 --decode > release.jks
- name: Setup Service account
env:
FIREBASE_SERVICE_ACCOUNT_KEY: ${{ secrets.FIREBASE_SERVICE_ACCOUNT_KEY }}
run: |
echo "${FIREBASE_SERVICE_ACCOUNT_KEY}" > service-account-key.json
- name: Build Release Notes
id: get_release_notes
run: |
echo "RELEASE_NOTES<<EOF" >> $GITHUB_OUTPUT
echo "${{ github.event.pull_request.title }}" >> $GITHUB_OUTPUT
echo "EOF" >> $GITHUB_OUTPUT
- name: Install Firebase CLI
run: npm install -g firebase-tools
- name: Setup Firebase App Id
run: |
case "${{ matrix.app.type }}" in
Parent)
echo "${{ secrets.FIREBASE_ANDROID_PARENT_APP_ID }}" > firebase_app_id.txt
;;
Student)
echo "${{ secrets.FIREBASE_ANDROID_STUDENT_APP_ID }}" > firebase_app_id.txt
;;
Teacher)
echo "${{ secrets.FIREBASE_ANDROID_TEACHER_APP_ID }}" > firebase_app_id.txt
;;
esac
# Building Artifacts
- name: Build debug and test APKs
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 \
--configuration-cache \
--no-daemon \
-Dorg.gradle.jvmargs="-Xmx4g -XX:+HeapDumpOnOutOfMemoryError" \
-Dkotlin.compiler.execution.strategy=in-process \
-Pandroid.injected.signing.store.file=$(pwd)/release.jks
# Uploading Artifacts to GitHub
- name: Upload APKs
uses: actions/upload-artifact@v4
with:
name: ${{ matrix.app.type-lower }}-apks
path: |
apps/${{ matrix.app.type-lower }}/build/outputs/apk/qa/debug/${{ matrix.app.type-lower }}-qa-debug.apk
apps/${{ matrix.app.type-lower }}/build/outputs/apk/androidTest/qa/debug/${{ matrix.app.type-lower }}-qa-debug-androidTest.apk
apps/${{ matrix.app.type-lower }}/build/outputs/apk/dev/debugMinify/${{ matrix.app.type-lower }}-dev-debugMinify.apk
compression-level: 6
retention-days: 5
# Uploading Artifacts to Firebase App Distribution
- name: Distribute app to Firebase App Distribution
env:
GOOGLE_APPLICATION_CREDENTIALS: ${{ github.workspace }}/service-account-key.json
run: |
firebase --version
FIREBASE_APP_ID=$(cat firebase_app_id.txt)
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
cat result.txt
- name: Prepare Comment Body
id: prepare_comment
run: |
INSTALL_URL=$(cat result.txt | grep appdistribution.firebase | sed 's/.*\(https:.*\)/\1/')
# URL encode using printf and sed instead of jq
INSTALL_URL_ESCAPED=$(printf '%s' "$INSTALL_URL" | sed 's/:/%3A/g; s/\//%2F/g; s/?/%3F/g; s/=/%3D/g; s/&/%26/g')
COMMENT_BODY=$(cat <<EOF
<!-- firebase-qr-${{ matrix.app.type-lower }} -->
## 📱 ${{ matrix.app.type }} App - Firebase Distribution
<img src="https://api.qrserver.com/v1/create-qr-code/?data=${INSTALL_URL_ESCAPED}" width="200">
**[Open Install Page]($INSTALL_URL)**
_Last updated: $(date -u '+%Y-%m-%d %H:%M:%S UTC')_
_Build: [\`${{ github.sha }}\`](${{ github.server_url }}/${{ github.repository }}/commit/${{ github.sha }})_
EOF
)
echo "body<<EOF" >> $GITHUB_OUTPUT
echo "$COMMENT_BODY" >> $GITHUB_OUTPUT
echo "EOF" >> $GITHUB_OUTPUT
- name: Find Previous Comment
id: find_comment
uses: peter-evans/find-comment@v2
with:
issue-number: ${{ github.event.number }}
comment-author: 'github-actions[bot]'
body-includes: '<!-- firebase-qr-${{ matrix.app.type-lower }} -->'
- name: Create or Update Comment
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
submodule-build-and-test:
name: submodule-build-and-test
runs-on: ubuntu-latest
needs: setup
if: needs.setup.outputs.should-run-build == 'true' && needs.setup.outputs.apps != '[]'
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', '**/*.gradle.kts', '**/gradle-wrapper.properties', '**/gradle.properties', 'apps/buildSrc/src/**/*.kt') }}
restore-keys: |
${{ runner.os }}-gradle-
- name: Cache Gradle Build Cache
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-
# Building Artifacts
- name: Build test and app APKs
run: |
./gradle/gradlew -p apps :pandautils:assembleDebugAndroidTest \
--build-cache \
--parallel \
--max-workers=4 \
--configuration-cache \
--no-daemon \
-Dorg.gradle.jvmargs="-Xmx4g -XX:+HeapDumpOnOutOfMemoryError" \
-Dkotlin.compiler.execution.strategy=in-process
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 \
--build-cache \
--parallel \
--max-workers=4 \
--configuration-cache \
--no-daemon \
-Dorg.gradle.jvmargs="-Xmx4g -XX:+HeapDumpOnOutOfMemoryError" \
-Dkotlin.compiler.execution.strategy=in-process
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 \
--build-cache \
--parallel \
--max-workers=4 \
--configuration-cache \
--no-daemon \
-Dorg.gradle.jvmargs="-Xmx4g -XX:+HeapDumpOnOutOfMemoryError" \
-Dkotlin.compiler.execution.strategy=in-process
# Uploading Artifacts to GitHub
- name: Upload test APK
uses: actions/upload-artifact@v4
with:
name: pandautils-test.apk
path: libs/pandautils/pandautils-test.apk
retention-days: 5
- name: Upload app APK
uses: actions/upload-artifact@v4
with:
name: pandautils-app.apk
path: libs/pandautils/pandautils-app.apk
retention-days: 5
unit-tests:
name: ${{ matrix.app.type-lower }}-unit-tests
runs-on: ubuntu-latest
needs: setup
if: needs.setup.outputs.should-run-build == 'true' && needs.setup.outputs.apps != '[]'
strategy:
fail-fast: true
matrix:
app: ${{ fromJson(needs.setup.outputs.apps) }}
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', '**/*.gradle.kts', '**/gradle-wrapper.properties', '**/gradle.properties', 'apps/buildSrc/src/**/*.kt') }}
restore-keys: |
${{ runner.os }}-gradle-
- name: Cache Gradle Build Cache
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: Run unit tests
run: |
./gradle/gradlew -p apps :${{ matrix.app.type-lower }}:testDevDebugUnitTest \
--build-cache \
--parallel \
--max-workers=4 \
--configuration-cache \
--no-daemon \
-Dorg.gradle.jvmargs="-Xmx4g -XX:+HeapDumpOnOutOfMemoryError" \
-Dkotlin.compiler.execution.strategy=in-process
ui-tests:
name: ${{ matrix.app.type-lower }}-${{ matrix.orientation }}-ui-tests
runs-on: ubuntu-latest
needs: [setup, build]
if: needs.setup.outputs.should-run-ui-tests == 'true' && needs.setup.outputs.apps != '[]'
strategy:
fail-fast: false
matrix:
app: ${{ fromJson(needs.setup.outputs.apps) }}
orientation: [portrait, landscape]
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: echo "${FIREBASE_SERVICE_ACCOUNT_KEY}" > service-account-key.json
- name: Setup Flank config
run: |
if [ "${{ matrix.orientation }}" == "portrait" ]; then
cp ./apps/${{ matrix.app.type-lower }}/flank.yml ./flank.yml
else
cp ./apps/${{ matrix.app.type-lower }}/flank_landscape.yml ./flank.yml
fi
- name: Copy APKs to expected locations
run: |
if [ -d "${{ matrix.app.type-lower }}-apks" ]; then
mkdir -p apps/${{ matrix.app.type-lower }}/build/outputs/apk/qa/debug
mkdir -p apps/${{ matrix.app.type-lower }}/build/outputs/apk/androidTest/qa/debug
mkdir -p apps/${{ matrix.app.type-lower }}/build/outputs/apk/dev/debugMinify
find ${{ matrix.app.type-lower }}-apks -name "*-qa-debug.apk" -not -name "*androidTest*" -exec mv {} apps/${{ matrix.app.type-lower }}/build/outputs/apk/qa/debug/ \;
find ${{ matrix.app.type-lower }}-apks -name "*-qa-debug-androidTest.apk" -exec mv {} apps/${{ matrix.app.type-lower }}/build/outputs/apk/androidTest/qa/debug/ \;
find ${{ matrix.app.type-lower }}-apks -name "*-dev-debugMinify.apk" -exec mv {} apps/${{ matrix.app.type-lower }}/build/outputs/apk/dev/debugMinify/ \;
rm -rf ${{ matrix.app.type-lower }}-apks
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 ${{ matrix.app.type-lower }} 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 }}
e2e-tests:
name: ${{ matrix.app.type-lower }}-e2e-tests
runs-on: ubuntu-latest
needs: [setup, build]
if: needs.setup.outputs.should-run-e2e-tests == 'true' && needs.setup.outputs.apps != '[]'
strategy:
fail-fast: false
matrix:
app: ${{ fromJson(needs.setup.outputs.apps) }}
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: echo "${FIREBASE_SERVICE_ACCOUNT_KEY}" > service-account-key.json
- name: Setup Flank config
run: cp ./apps/${{ matrix.app.type-lower }}/flank_e2e.yml ./flank.yml
- name: Copy APKs to expected locations
run: |
if [ -d "${{ matrix.app.type-lower }}-apks" ]; then
mkdir -p apps/${{ matrix.app.type-lower }}/build/outputs/apk/qa/debug
mkdir -p apps/${{ matrix.app.type-lower }}/build/outputs/apk/androidTest/qa/debug
mkdir -p apps/${{ matrix.app.type-lower }}/build/outputs/apk/dev/debugMinify
find ${{ matrix.app.type-lower }}-apks -name "*-qa-debug.apk" -not -name "*androidTest*" -exec mv {} apps/${{ matrix.app.type-lower }}/build/outputs/apk/qa/debug/ \;
find ${{ matrix.app.type-lower }}-apks -name "*-qa-debug-androidTest.apk" -exec mv {} apps/${{ matrix.app.type-lower }}/build/outputs/apk/androidTest/qa/debug/ \;
find ${{ matrix.app.type-lower }}-apks -name "*-dev-debugMinify.apk" -exec mv {} apps/${{ matrix.app.type-lower }}/build/outputs/apk/dev/debugMinify/ \;
rm -rf ${{ matrix.app.type-lower }}-apks
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 ${{ matrix.app.type-lower }} 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 }}
submodule-ui-tests:
name: submodule-ui-tests
runs-on: ubuntu-latest
needs: [setup, submodule-build-and-test]
if: needs.setup.outputs.should-run-ui-tests == 'true' && needs.setup.outputs.apps != '[]'
steps:
- name: Checkout repository
uses: actions/checkout@v4
with:
fetch-depth: 1
- 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'