Configure build with GitHub Actions
This commit is contained in:
parent
40950a0377
commit
52b6382d47
|
@ -11,12 +11,11 @@ For a Gradle dependency:
|
||||||
1. Update the dependency version in the root `gradle.properties`
|
1. Update the dependency version in the root `gradle.properties`
|
||||||
1. Update the dependency locks
|
1. Update the dependency locks
|
||||||
1. For Gradle plugins: `./gradlew dependencies --write-locks`
|
1. For Gradle plugins: `./gradlew dependencies --write-locks`
|
||||||
1. For Gradle dependencies: `./gradlew resolveAndLockAll --write-locks`
|
1. For Gradle dependencies: `./gradlew resolveAll --write-locks`
|
||||||
1. Verify no unexpected entries appear in the lockfiles. _A supply chain attack could occur during this stage. The lockfile narrows the supply chain attack window to this very moment (as opposed to every time a build occurs)_
|
1. Verify no unexpected entries appear in the lockfiles. _A supply chain attack could occur during this stage. The lockfile narrows the supply chain attack window to this very moment (as opposed to every time a build occurs)_
|
||||||
1. Are there any new APIs or possible migrations for this dependency?
|
1. Are there any new APIs or possible migrations for this dependency?
|
||||||
|
|
||||||
For Gradle itself:
|
For Gradle itself:
|
||||||
1. Run `./gradle wrapper --gradle-version $X`
|
1. Run `./gradle wrapper --gradle-version $X`
|
||||||
1. Add `distributionSha256Sum=` in `gradle/wrapper/gradle-wrapper.properties`, referencing [Gradle Release Checksums](https://gradle.org/release-checksums/)
|
1. Add `distributionSha256Sum=` in `gradle/wrapper/gradle-wrapper.properties`, referencing [Gradle Release Checksums](https://gradle.org/release-checksums/)
|
||||||
1. Update the continuous integration server environment variables with the updated SHA for the Gradle wrapper, referencing [Gradle Release Checksums](https://gradle.org/release-checksums/). _Note: Bitrise builds for other branches may temporarily fail since only a single checksum at a time is currently supported. The wrapper is not updated with every Gradle version so in practice this problem should occur infrequently._
|
|
||||||
1. Are there any new APIs or possible migrations?
|
1. Are there any new APIs or possible migrations?
|
||||||
|
|
|
@ -0,0 +1,44 @@
|
||||||
|
name: 'Setup Java and Dependency Cache'
|
||||||
|
description: "Configures the build environment and caches Gradle and dependencies."
|
||||||
|
runs:
|
||||||
|
using: "composite"
|
||||||
|
steps:
|
||||||
|
- name: Set Env
|
||||||
|
shell: bash
|
||||||
|
run: |
|
||||||
|
echo "home=${HOME}" >> "$GITHUB_ENV"
|
||||||
|
- name: Set up Java
|
||||||
|
uses: actions/setup-java@v2
|
||||||
|
with:
|
||||||
|
distribution: 'zulu'
|
||||||
|
java-version: 17
|
||||||
|
- name: Disable Gradle Daemon
|
||||||
|
shell: bash
|
||||||
|
run: |
|
||||||
|
mkdir ~/.gradle
|
||||||
|
|
||||||
|
echo "org.gradle.daemon=false" >> ~/.gradle/gradle.properties
|
||||||
|
- name: Gradle Wrapper Cache
|
||||||
|
id: gradle-wrapper-cache
|
||||||
|
uses: actions/cache@v2.1.7
|
||||||
|
with:
|
||||||
|
path: ~/.gradle/wrapper
|
||||||
|
key: ${{ runner.os }}-gradle-wrapper-${{ hashFiles(format('{0}{1}', github.workspace, '/gradle/wrapper/gradle-wrapper.properties')) }}
|
||||||
|
- name: Gradle Dependency Cache
|
||||||
|
id: gradle-dependency-cache
|
||||||
|
uses: actions/cache@v2.1.7
|
||||||
|
with:
|
||||||
|
path: ~/.gradle/caches/modules-2
|
||||||
|
key: ${{ runner.os }}-gradle-deps-${{ hashFiles(format('{0}{1}', github.workspace, '/gradle.properties')) }}
|
||||||
|
restore-keys: |
|
||||||
|
${{ runner.os }}-gradle-deps
|
||||||
|
- name: Download Gradle
|
||||||
|
if: steps.gradle-wrapper-cache.outputs.cache-hit != 'true'
|
||||||
|
shell: bash
|
||||||
|
run: |
|
||||||
|
./gradlew --version
|
||||||
|
- name: Download Dependencies
|
||||||
|
if: steps.gradle-dependency-cache.outputs.cache-hit != 'true'
|
||||||
|
shell: bash
|
||||||
|
run: |
|
||||||
|
./gradlew resolveAll
|
|
@ -0,0 +1,112 @@
|
||||||
|
# Expected secrets
|
||||||
|
# GOOGLE_PLAY_CLOUD_PROJECT - Google Cloud project associated with Google Play
|
||||||
|
# GOOGLE_PLAY_SERVICE_ACCOUNT - Email address of service account
|
||||||
|
# GOOGLE_PLAY_WORKLOAD_IDENTITY_PROVIDER - Workload identity provider to generate temporary service account key
|
||||||
|
# SIGNING_KEYSTORE_BASE_64 - The signing key for the app
|
||||||
|
# SIGNING_KEYSTORE_PASSWORD - The password for SIGNING_KEYSTORE_BASE_64
|
||||||
|
# SIGNING_KEY_ALIAS - The key alias inside SIGNING_KEYSTORE_BASE_64
|
||||||
|
# SIGNING_KEY_ALIAS_PASSWORD - The password for the key alias
|
||||||
|
|
||||||
|
name: Deploy
|
||||||
|
|
||||||
|
on:
|
||||||
|
workflow_dispatch:
|
||||||
|
push:
|
||||||
|
branches:
|
||||||
|
- main
|
||||||
|
paths-ignore:
|
||||||
|
- '.github/ISSUE_TEMPLATE/*'
|
||||||
|
- '.github/PULL_REQUEST_TEMPLATE.md'
|
||||||
|
- 'LICENSE'
|
||||||
|
- 'README.md'
|
||||||
|
- 'docs/**'
|
||||||
|
|
||||||
|
concurrency: deploy
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
validate_gradle_wrapper:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
permissions:
|
||||||
|
contents: read
|
||||||
|
steps:
|
||||||
|
- name: Checkout
|
||||||
|
timeout-minutes: 1
|
||||||
|
uses: actions/checkout@v2.4.0
|
||||||
|
# Gradle Wrapper validation can be flaky
|
||||||
|
# https://github.com/gradle/wrapper-validation-action/issues/40
|
||||||
|
- name: Gradle Wrapper Validation
|
||||||
|
timeout-minutes: 1
|
||||||
|
uses: gradle/wrapper-validation-action@v1.0.4
|
||||||
|
|
||||||
|
check_secrets:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
outputs:
|
||||||
|
has-secrets: ${{ steps.check_secrets.outputs.defined }}
|
||||||
|
steps:
|
||||||
|
- id: check_secrets
|
||||||
|
env:
|
||||||
|
GOOGLE_PLAY_CLOUD_PROJECT: ${{ secrets.GOOGLE_PLAY_CLOUD_PROJECT }}
|
||||||
|
GOOGLE_PLAY_SERVICE_ACCOUNT: ${{ secrets.GOOGLE_PLAY_SERVICE_ACCOUNT }}
|
||||||
|
GOOGLE_PLAY_WORKLOAD_IDENTITY_PROVIDER: ${{ secrets.GOOGLE_PLAY_WORKLOAD_IDENTITY_PROVIDER }}
|
||||||
|
if: "${{ env.GOOGLE_PLAY_CLOUD_PROJECT != '' && env.GOOGLE_PLAY_SERVICE_ACCOUNT != '' && env.GOOGLE_PLAY_WORKLOAD_IDENTITY_PROVIDER != '' }}"
|
||||||
|
run: echo "::set-output name=defined::true"
|
||||||
|
|
||||||
|
build_and_deploy:
|
||||||
|
if: needs.check_secrets.outputs.has-secrets == 'true'
|
||||||
|
needs: [validate_gradle_wrapper, check_secrets]
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
permissions:
|
||||||
|
contents: read
|
||||||
|
id-token: write
|
||||||
|
steps:
|
||||||
|
- name: Checkout
|
||||||
|
timeout-minutes: 1
|
||||||
|
uses: actions/checkout@v2.4.0
|
||||||
|
- name: Setup
|
||||||
|
id: setup
|
||||||
|
timeout-minutes: 8
|
||||||
|
uses: ./.github/actions/setup
|
||||||
|
- name: Authenticate to Google Cloud for Google Play
|
||||||
|
id: auth_google_play
|
||||||
|
uses: google-github-actions/auth@v0.5.0
|
||||||
|
with:
|
||||||
|
create_credentials_file: true
|
||||||
|
project_id: ${{ secrets.GOOGLE_PLAY_CLOUD_PROJECT }}
|
||||||
|
service_account: ${{ secrets.GOOGLE_PLAY_SERVICE_ACCOUNT }}
|
||||||
|
workload_identity_provider: ${{ secrets.GOOGLE_PLAY_WORKLOAD_IDENTITY_PROVIDER }}
|
||||||
|
access_token_lifetime: '1500s'
|
||||||
|
- name: Export Signing Key
|
||||||
|
env:
|
||||||
|
SIGNING_KEYSTORE_BASE_64: ${{ secrets.SIGNING_KEYSTORE_BASE_64 }}
|
||||||
|
SIGNING_KEY_PATH: ${{ format('{0}/release.jks', env.home) }}
|
||||||
|
shell: bash
|
||||||
|
run: |
|
||||||
|
echo ${SIGNING_KEYSTORE_BASE_64} | base64 --decode > ${SIGNING_KEY_PATH}
|
||||||
|
- name: Upload to Play Store
|
||||||
|
timeout-minutes: 25
|
||||||
|
env:
|
||||||
|
ZCASH_GOOGLE_PLAY_SERVICE_KEY_FILE_PATH: ${{ steps.auth_google_play.outputs.credentials_file_path }}
|
||||||
|
ORG_GRADLE_PROJECT_ZCASH_RELEASE_KEYSTORE_PATH: ${{ format('{0}/release.jks', env.home) }}
|
||||||
|
ORG_GRADLE_PROJECT_ZCASH_RELEASE_KEYSTORE_PASSWORD: ${{ secrets.SIGNING_KEYSTORE_PASSWORD }}
|
||||||
|
ORG_GRADLE_PROJECT_ZCASH_RELEASE_KEY_ALIAS: ${{ secrets.SIGNING_KEY_ALIAS }}
|
||||||
|
ORG_GRADLE_PROJECT_ZCASH_RELEASE_KEY_ALIAS_PASSWORD: ${{ secrets.SIGNING_KEY_ALIAS_PASSWORD }}
|
||||||
|
# Change to deploy once we've finished setting up the build scripts
|
||||||
|
ORG_GRADLE_PROJECT_ZCASH_GOOGLE_PLAY_DEPLOY_MODE: build
|
||||||
|
run: |
|
||||||
|
./gradlew :app:publishBundle
|
||||||
|
- name: Collect Artifacts
|
||||||
|
timeout-minutes: 1
|
||||||
|
env:
|
||||||
|
ARTIFACTS_DIR_PATH: ${{ format('{0}/artifacts', env.home) }}
|
||||||
|
BINARIES_ZIP_PATH: ${{ format('{0}/artifacts/binaries.zip', env.home) }}
|
||||||
|
MAPPINGS_ZIP_PATH: ${{ format('{0}/artifacts/mappings.zip', env.home) }}
|
||||||
|
run: |
|
||||||
|
mkdir ${ARTIFACTS_DIR_PATH}
|
||||||
|
zip -r ${BINARIES_ZIP_PATH} . -i *app/build/outputs/apk/*/release/*.apk *app/build/outputs/bundle/*/release/*.aab
|
||||||
|
zip -r ${MAPPINGS_ZIP_PATH} . -i *app/build/outputs/mapping/*/mapping.txt
|
||||||
|
- name: Upload Artifacts
|
||||||
|
uses: actions/upload-artifact@v2
|
||||||
|
timeout-minutes: 1
|
||||||
|
with:
|
||||||
|
name: Release binaries
|
||||||
|
path: ~/artifacts
|
|
@ -0,0 +1,336 @@
|
||||||
|
# Expected secrets
|
||||||
|
# FIREBASE_TEST_LAB_PROJECT - Firebase Test Lab project name
|
||||||
|
# FIREBASE_TEST_LAB_SERVICE_ACCOUNT - Email address of Firebase Test Lab service account
|
||||||
|
# FIREBASE_TEST_LAB_WORKLOAD_IDENTITY_PROVIDER - Workload identity provider to generate temporary service account key
|
||||||
|
|
||||||
|
name: Pull Request
|
||||||
|
|
||||||
|
on:
|
||||||
|
pull_request:
|
||||||
|
paths-ignore:
|
||||||
|
- '.github/ISSUE_TEMPLATE/*'
|
||||||
|
- '.github/PULL_REQUEST_TEMPLATE.md'
|
||||||
|
- 'LICENSE'
|
||||||
|
- 'README.md'
|
||||||
|
- 'docs/**'
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
validate_gradle_wrapper:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
permissions:
|
||||||
|
contents: read
|
||||||
|
steps:
|
||||||
|
- name: Checkout
|
||||||
|
timeout-minutes: 1
|
||||||
|
uses: actions/checkout@v2.4.0
|
||||||
|
# Gradle Wrapper validation can be flaky
|
||||||
|
# https://github.com/gradle/wrapper-validation-action/issues/40
|
||||||
|
- name: Gradle Wrapper Validation
|
||||||
|
timeout-minutes: 1
|
||||||
|
uses: gradle/wrapper-validation-action@v1.0.4
|
||||||
|
|
||||||
|
prime_cache:
|
||||||
|
needs: validate_gradle_wrapper
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
permissions:
|
||||||
|
contents: read
|
||||||
|
steps:
|
||||||
|
- name: Checkout
|
||||||
|
timeout-minutes: 1
|
||||||
|
uses: actions/checkout@v2.4.0
|
||||||
|
- name: Setup
|
||||||
|
id: setup
|
||||||
|
timeout-minutes: 8
|
||||||
|
uses: ./.github/actions/setup
|
||||||
|
|
||||||
|
check_secrets:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
outputs:
|
||||||
|
has-secrets: ${{ steps.check_secrets.outputs.defined }}
|
||||||
|
steps:
|
||||||
|
- id: check_secrets
|
||||||
|
env:
|
||||||
|
FIREBASE_TEST_LAB_PROJECT: ${{ secrets.FIREBASE_TEST_LAB_PROJECT }}
|
||||||
|
FIREBASE_TEST_LAB_SERVICE_ACCOUNT: ${{ secrets.FIREBASE_TEST_LAB_SERVICE_ACCOUNT }}
|
||||||
|
FIREBASE_TEST_LAB_WORKLOAD_IDENTITY_PROVIDER: ${{ secrets.FIREBASE_TEST_LAB_WORKLOAD_IDENTITY_PROVIDER }}
|
||||||
|
if: "${{ env.FIREBASE_TEST_LAB_PROJECT != '' && env.FIREBASE_TEST_LAB_SERVICE_ACCOUNT != '' && env.FIREBASE_TEST_LAB_WORKLOAD_IDENTITY_PROVIDER != '' }}"
|
||||||
|
run: echo "::set-output name=defined::true"
|
||||||
|
|
||||||
|
static_analysis_detekt:
|
||||||
|
needs: prime_cache
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
permissions:
|
||||||
|
contents: read
|
||||||
|
steps:
|
||||||
|
- name: Checkout
|
||||||
|
timeout-minutes: 1
|
||||||
|
uses: actions/checkout@v2.4.0
|
||||||
|
- name: Setup
|
||||||
|
id: setup
|
||||||
|
timeout-minutes: 5
|
||||||
|
uses: ./.github/actions/setup
|
||||||
|
- name: Detekt
|
||||||
|
timeout-minutes: 4
|
||||||
|
run: |
|
||||||
|
./gradlew detektAll
|
||||||
|
- name: Collect Artifacts
|
||||||
|
timeout-minutes: 1
|
||||||
|
if: ${{ always() }}
|
||||||
|
env:
|
||||||
|
ARTIFACTS_DIR_PATH: ${{ format('{0}/artifacts', env.home) }}
|
||||||
|
REPORTS_ZIP_PATH: ${{ format('{0}/artifacts/static_analysis_detekt.zip', env.home) }}
|
||||||
|
run: |
|
||||||
|
mkdir ${ARTIFACTS_DIR_PATH}
|
||||||
|
|
||||||
|
zip -r ${REPORTS_ZIP_PATH} . -i build/reports/detekt/*
|
||||||
|
- name: Upload Artifacts
|
||||||
|
if: ${{ always() }}
|
||||||
|
uses: actions/upload-artifact@v2
|
||||||
|
timeout-minutes: 1
|
||||||
|
with:
|
||||||
|
name: Detekt static analysis results
|
||||||
|
path: ~/artifacts
|
||||||
|
|
||||||
|
static_analysis_ktlint:
|
||||||
|
needs: prime_cache
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
permissions:
|
||||||
|
contents: read
|
||||||
|
steps:
|
||||||
|
- name: Checkout
|
||||||
|
timeout-minutes: 1
|
||||||
|
uses: actions/checkout@v2.4.0
|
||||||
|
- name: Setup
|
||||||
|
id: setup
|
||||||
|
timeout-minutes: 5
|
||||||
|
uses: ./.github/actions/setup
|
||||||
|
- name: Ktlint
|
||||||
|
timeout-minutes: 4
|
||||||
|
run: |
|
||||||
|
./gradlew ktlint
|
||||||
|
- name: Collect Artifacts
|
||||||
|
timeout-minutes: 1
|
||||||
|
if: ${{ always() }}
|
||||||
|
env:
|
||||||
|
ARTIFACTS_DIR_PATH: ${{ format('{0}/artifacts', env.home) }}
|
||||||
|
REPORTS_ZIP_PATH: ${{ format('{0}/artifacts/static_analysis_ktlint.zip', env.home) }}
|
||||||
|
run: |
|
||||||
|
mkdir ${ARTIFACTS_DIR_PATH}
|
||||||
|
|
||||||
|
zip -r ${REPORTS_ZIP_PATH} . -i build/reports/ktlint/*
|
||||||
|
- name: Upload Artifacts
|
||||||
|
if: ${{ always() }}
|
||||||
|
uses: actions/upload-artifact@v2
|
||||||
|
timeout-minutes: 1
|
||||||
|
with:
|
||||||
|
name: Ktlint static analysis results
|
||||||
|
path: ~/artifacts
|
||||||
|
|
||||||
|
static_analysis_android_lint:
|
||||||
|
needs: prime_cache
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
permissions:
|
||||||
|
contents: read
|
||||||
|
steps:
|
||||||
|
- name: Checkout
|
||||||
|
timeout-minutes: 1
|
||||||
|
uses: actions/checkout@v2.4.0
|
||||||
|
- name: Setup
|
||||||
|
id: setup
|
||||||
|
timeout-minutes: 5
|
||||||
|
uses: ./.github/actions/setup
|
||||||
|
- name: Android Lint
|
||||||
|
timeout-minutes: 15
|
||||||
|
env:
|
||||||
|
# Disable minify, since it makes lint run faster
|
||||||
|
ORG_GRADLE_PROJECT_IS_MINIFY_ENABLED: false
|
||||||
|
run: |
|
||||||
|
./gradlew :app:lintZcashmainnetRelease
|
||||||
|
- name: Collect Artifacts
|
||||||
|
timeout-minutes: 1
|
||||||
|
env:
|
||||||
|
ARTIFACTS_DIR_PATH: ${{ format('{0}/artifacts', env.home) }}
|
||||||
|
LINT_ZIP_PATH: ${{ format('{0}/artifacts/android_lint.zip', env.home) }}
|
||||||
|
run: |
|
||||||
|
mkdir ${ARTIFACTS_DIR_PATH}
|
||||||
|
zip -r ${LINT_ZIP_PATH} . -i *build/reports/*
|
||||||
|
- name: Upload Artifacts
|
||||||
|
uses: actions/upload-artifact@v2
|
||||||
|
timeout-minutes: 1
|
||||||
|
with:
|
||||||
|
name: Android Lint static analysis results
|
||||||
|
path: ~/artifacts
|
||||||
|
|
||||||
|
test_kotlin_modules:
|
||||||
|
needs: prime_cache
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
permissions:
|
||||||
|
contents: read
|
||||||
|
steps:
|
||||||
|
- name: Checkout
|
||||||
|
timeout-minutes: 1
|
||||||
|
uses: actions/checkout@v2.4.0
|
||||||
|
- name: Setup
|
||||||
|
id: setup
|
||||||
|
timeout-minutes: 5
|
||||||
|
uses: ./.github/actions/setup
|
||||||
|
- name: Test
|
||||||
|
timeout-minutes: 4
|
||||||
|
run: |
|
||||||
|
./gradlew ktlint
|
||||||
|
- name: Collect Artifacts
|
||||||
|
timeout-minutes: 1
|
||||||
|
env:
|
||||||
|
ARTIFACTS_DIR_PATH: ${{ format('{0}/artifacts', env.home) }}
|
||||||
|
RESULTS_ZIP_PATH: ${{ format('{0}/artifacts/test_kotlin.zip', env.home) }}
|
||||||
|
run: |
|
||||||
|
mkdir ${ARTIFACTS_DIR_PATH}
|
||||||
|
zip -r ${RESULTS_ZIP_PATH} . -i *build/reports/*
|
||||||
|
- name: Upload Artifacts
|
||||||
|
uses: actions/upload-artifact@v2
|
||||||
|
timeout-minutes: 1
|
||||||
|
with:
|
||||||
|
name: Test Kotlin modules results
|
||||||
|
path: ~/artifacts
|
||||||
|
|
||||||
|
test_android_modules:
|
||||||
|
if: needs.check_secrets.outputs.has-secrets == 'true'
|
||||||
|
needs: [prime_cache, check_secrets]
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
permissions:
|
||||||
|
contents: read
|
||||||
|
id-token: write
|
||||||
|
steps:
|
||||||
|
- name: Checkout
|
||||||
|
timeout-minutes: 1
|
||||||
|
uses: actions/checkout@v2.4.0
|
||||||
|
- name: Setup
|
||||||
|
id: setup
|
||||||
|
timeout-minutes: 5
|
||||||
|
uses: ./.github/actions/setup
|
||||||
|
- name: Build
|
||||||
|
timeout-minutes: 20
|
||||||
|
run: |
|
||||||
|
./gradlew assembleDebug assembleAndroidTest
|
||||||
|
- name: Authenticate to Google Cloud for Firebase Test Lab
|
||||||
|
id: auth_test_lab
|
||||||
|
uses: google-github-actions/auth@v0.5.0
|
||||||
|
with:
|
||||||
|
create_credentials_file: true
|
||||||
|
project_id: ${{ secrets.FIREBASE_TEST_LAB_PROJECT }}
|
||||||
|
service_account: ${{ secrets.FIREBASE_TEST_LAB_SERVICE_ACCOUNT }}
|
||||||
|
workload_identity_provider: ${{ secrets.FIREBASE_TEST_LAB_WORKLOAD_IDENTITY_PROVIDER }}
|
||||||
|
access_token_lifetime: '1200s'
|
||||||
|
- name: Test
|
||||||
|
timeout-minutes: 20
|
||||||
|
env:
|
||||||
|
# This first environment variable is used by Flank, since the temporary token is missing the project name
|
||||||
|
GOOGLE_CLOUD_PROJECT: ${{ secrets.FIREBASE_TEST_LAB_PROJECT }}
|
||||||
|
ORG_GRADLE_PROJECT_ZCASH_FIREBASE_TEST_LAB_API_KEY_PATH: ${{ steps.auth_test_lab.outputs.credentials_file_path }}
|
||||||
|
run: |
|
||||||
|
# NEED Firebase Test Lab API key
|
||||||
|
# ./gradlew runFlank --parallel
|
||||||
|
- name: Collect Artifacts
|
||||||
|
timeout-minutes: 1
|
||||||
|
env:
|
||||||
|
ARTIFACTS_DIR_PATH: ${{ format('{0}/artifacts', env.home) }}
|
||||||
|
TEST_RESULTS_ZIP_PATH: ${{ format('{0}/artifacts/test_results.zip', env.home) }}
|
||||||
|
run: |
|
||||||
|
mkdir ${ARTIFACTS_DIR_PATH}
|
||||||
|
|
||||||
|
zip -r ${TEST_RESULTS_ZIP_PATH} . -i *build/outputs/androidTest-results/*
|
||||||
|
- name: Upload Artifacts
|
||||||
|
uses: actions/upload-artifact@v2
|
||||||
|
timeout-minutes: 1
|
||||||
|
with:
|
||||||
|
name: Test Android modules results
|
||||||
|
path: ~/artifacts
|
||||||
|
|
||||||
|
release_build:
|
||||||
|
needs: prime_cache
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
permissions:
|
||||||
|
contents: read
|
||||||
|
steps:
|
||||||
|
- name: Checkout
|
||||||
|
timeout-minutes: 1
|
||||||
|
uses: actions/checkout@v2.4.0
|
||||||
|
- name: Setup
|
||||||
|
id: setup
|
||||||
|
timeout-minutes: 5
|
||||||
|
uses: ./.github/actions/setup
|
||||||
|
# A fake signing key to satisfy creating a "release" build
|
||||||
|
- name: Export Signing Key
|
||||||
|
env:
|
||||||
|
SIGNING_KEY_PATH: ${{ format('{0}/release.jks', env.home) }}
|
||||||
|
shell: bash
|
||||||
|
run: |
|
||||||
|
keytool -genkey -v -keystore $SIGNING_KEY_PATH -keypass android -storepass android -alias androiddebugkey -keyalg RSA -keysize 2048 -validity 100000 -dname "CN=, OU=, O=Test, L=, S=, C=" -noprompt
|
||||||
|
- name: Build
|
||||||
|
timeout-minutes: 20
|
||||||
|
env:
|
||||||
|
ORG_GRADLE_PROJECT_ZCASH_RELEASE_KEYSTORE_PATH: ${{ format('{0}/release.jks', env.home) }}
|
||||||
|
ORG_GRADLE_PROJECT_ZCASH_RELEASE_KEYSTORE_PASSWORD: android
|
||||||
|
ORG_GRADLE_PROJECT_ZCASH_RELEASE_KEY_ALIAS: androiddebugkey
|
||||||
|
ORG_GRADLE_PROJECT_ZCASH_RELEASE_KEY_ALIAS_PASSWORD: android
|
||||||
|
run: |
|
||||||
|
./gradlew :app:assembleRelease
|
||||||
|
- name: Collect Artifacts
|
||||||
|
timeout-minutes: 1
|
||||||
|
env:
|
||||||
|
ARTIFACTS_DIR_PATH: ${{ format('{0}/artifacts', env.home) }}
|
||||||
|
BINARIES_ZIP_PATH: ${{ format('{0}/artifacts/binaries.zip', env.home) }}
|
||||||
|
MAPPINGS_ZIP_PATH: ${{ format('{0}/artifacts/mappings.zip', env.home) }}
|
||||||
|
run: |
|
||||||
|
mkdir ${ARTIFACTS_DIR_PATH}
|
||||||
|
zip -r ${BINARIES_ZIP_PATH} . -i *app/build/outputs/apk/*/release/*.apk *app/build/outputs/bundle/*/release/*.aab
|
||||||
|
zip -r ${MAPPINGS_ZIP_PATH} . -i *app/build/outputs/mapping/*/mapping.txt
|
||||||
|
- name: Upload Artifacts
|
||||||
|
uses: actions/upload-artifact@v2
|
||||||
|
timeout-minutes: 1
|
||||||
|
with:
|
||||||
|
name: Release binaries
|
||||||
|
path: ~/artifacts
|
||||||
|
|
||||||
|
# Performs a button mash test on the release build of the app
|
||||||
|
# Note that we might need to help it get past the onboarding test with a script
|
||||||
|
test_robo:
|
||||||
|
if: needs.check_secrets.outputs.has-secrets == 'true'
|
||||||
|
needs: [release_build, check_secrets]
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
permissions:
|
||||||
|
packages: read
|
||||||
|
contents: read
|
||||||
|
id-token: write
|
||||||
|
steps:
|
||||||
|
- name: Checkout
|
||||||
|
timeout-minutes: 1
|
||||||
|
uses: actions/checkout@v2.4.0
|
||||||
|
- name: Setup
|
||||||
|
id: setup
|
||||||
|
timeout-minutes: 5
|
||||||
|
uses: ./.github/actions/setup
|
||||||
|
- name: Authenticate to Google Cloud for Firebase Test Lab
|
||||||
|
id: auth_test_lab
|
||||||
|
uses: google-github-actions/auth@v0.5.0
|
||||||
|
with:
|
||||||
|
create_credentials_file: true
|
||||||
|
project_id: ${{ secrets.FIREBASE_TEST_LAB_PROJECT }}
|
||||||
|
service_account: ${{ secrets.FIREBASE_TEST_LAB_SERVICE_ACCOUNT }}
|
||||||
|
workload_identity_provider: ${{ secrets.FIREBASE_TEST_LAB_WORKLOAD_IDENTITY_PROVIDER }}
|
||||||
|
access_token_lifetime: '900s'
|
||||||
|
- name: Download a single artifact
|
||||||
|
uses: actions/download-artifact@v2
|
||||||
|
with:
|
||||||
|
name: Release binaries
|
||||||
|
- name: Robo test
|
||||||
|
timeout-minutes: 15
|
||||||
|
env:
|
||||||
|
# Path depends on `release_build` job, plus path of `Download a single artifact` step
|
||||||
|
BINARIES_ZIP_PATH: binaries.zip
|
||||||
|
# This first environment variable is used by Flank, since the temporary token is missing the project name
|
||||||
|
GOOGLE_CLOUD_PROJECT: ${{ secrets.FIREBASE_TEST_LAB_PROJECT }}
|
||||||
|
ORG_GRADLE_PROJECT_ZCASH_FIREBASE_TEST_LAB_API_KEY_PATH: ${{ steps.auth_test_lab.outputs.credentials_file_path }}
|
||||||
|
run: |
|
||||||
|
unzip ${BINARIES_ZIP_PATH}
|
||||||
|
./gradlew :app:runFlankSanityConfig
|
|
@ -28,6 +28,8 @@ If you plan to fork the project to create a new app of your own, please make the
|
||||||
1. ui-lib/src/main/res/common/ - All of the the ic_launcher assets
|
1. ui-lib/src/main/res/common/ - All of the the ic_launcher assets
|
||||||
1. Change the package name
|
1. Change the package name
|
||||||
1. Under [app/build.gradle.kts](app/build.gradle.kts), change the package name of the application
|
1. Under [app/build.gradle.kts](app/build.gradle.kts), change the package name of the application
|
||||||
|
1. Optional
|
||||||
|
1. Configure secrets for [Continuous Integration](docs/CI.md).
|
||||||
|
|
||||||
# Known Issues
|
# Known Issues
|
||||||
|
|
||||||
|
|
|
@ -2,7 +2,6 @@ dependencyLocking {
|
||||||
lockAllConfigurations()
|
lockAllConfigurations()
|
||||||
}
|
}
|
||||||
|
|
||||||
// Just resolves dependencies
|
|
||||||
tasks {
|
tasks {
|
||||||
register("resolveAll") {
|
register("resolveAll") {
|
||||||
doLast {
|
doLast {
|
||||||
|
@ -12,17 +11,4 @@ tasks {
|
||||||
}.forEach { it.resolve() }
|
}.forEach { it.resolve() }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Invoke with `./gradlew resolveAndLockAll --write-locks`
|
|
||||||
register("resolveAndLockAll") {
|
|
||||||
doFirst {
|
|
||||||
require(gradle.startParameter.isWriteDependencyLocks)
|
|
||||||
}
|
|
||||||
doLast {
|
|
||||||
configurations.filter {
|
|
||||||
// Add any custom filtering on the configurations to be resolved
|
|
||||||
it.isCanBeResolved
|
|
||||||
}.forEach { it.resolve() }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -7,6 +7,7 @@ buildscript {
|
||||||
|
|
||||||
plugins {
|
plugins {
|
||||||
id("com.github.ben-manes.versions")
|
id("com.github.ben-manes.versions")
|
||||||
|
id("com.osacky.fulladle")
|
||||||
id("io.gitlab.arturbosch.detekt")
|
id("io.gitlab.arturbosch.detekt")
|
||||||
id("zcash.ktlint-conventions")
|
id("zcash.ktlint-conventions")
|
||||||
}
|
}
|
||||||
|
@ -49,6 +50,34 @@ fun isNonStable(version: String): Boolean {
|
||||||
return unstableKeywords.any { versionLowerCase.contains(it) }
|
return unstableKeywords.any { versionLowerCase.contains(it) }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Firebase Test Lab has min and max values that might differ from our project's
|
||||||
|
// These are determined by `gcloud firebase test android models list`
|
||||||
|
@Suppress("MagicNumber", "PropertyName", "VariableNaming")
|
||||||
|
val FIREBASE_TEST_LAB_MIN_API = 23
|
||||||
|
@Suppress("MagicNumber", "PropertyName", "VariableNaming")
|
||||||
|
val FIREBASE_TEST_LAB_MAX_API = 30
|
||||||
|
|
||||||
|
val firebaseTestLabKeyPath = project.properties["ZCASH_FIREBASE_TEST_LAB_API_KEY_PATH"].toString()
|
||||||
|
if (firebaseTestLabKeyPath.isNotBlank()) {
|
||||||
|
val minSdkVersion = run {
|
||||||
|
val buildMinSdk = project.properties["ANDROID_MIN_SDK_VERSION"].toString().toInt()
|
||||||
|
buildMinSdk.coerceAtLeast(FIREBASE_TEST_LAB_MIN_API).toString()
|
||||||
|
}
|
||||||
|
val targetSdkVersion = run {
|
||||||
|
val buildTargetSdk = project.properties["ANDROID_TARGET_SDK_VERSION"].toString().toInt()
|
||||||
|
buildTargetSdk.coerceAtMost(FIREBASE_TEST_LAB_MAX_API).toString()
|
||||||
|
}
|
||||||
|
fladle {
|
||||||
|
serviceAccountCredentials.set(File(firebaseTestLabKeyPath))
|
||||||
|
devices.addAll(
|
||||||
|
mapOf("model" to "NexusLowRes", "version" to minSdkVersion),
|
||||||
|
mapOf("model" to "NexusLowRes", "version" to targetSdkVersion)
|
||||||
|
)
|
||||||
|
|
||||||
|
@Suppress("MagicNumber")
|
||||||
|
flakyTestAttempts.set(2)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// All of this should be refactored to build-conventions
|
// All of this should be refactored to build-conventions
|
||||||
subprojects {
|
subprojects {
|
||||||
|
|
|
@ -68,6 +68,8 @@ com.google.protobuf:protobuf-java:3.10.0=classpath
|
||||||
com.google.testing.platform:core-proto:0.0.8-alpha07=classpath
|
com.google.testing.platform:core-proto:0.0.8-alpha07=classpath
|
||||||
com.googlecode.json-simple:json-simple:1.1=classpath
|
com.googlecode.json-simple:json-simple:1.1=classpath
|
||||||
com.googlecode.juniversalchardet:juniversalchardet:1.0.3=classpath
|
com.googlecode.juniversalchardet:juniversalchardet:1.0.3=classpath
|
||||||
|
com.osacky.flank.gradle:fladle:0.17.3=classpath
|
||||||
|
com.osacky.fulladle:com.osacky.fulladle.gradle.plugin:0.17.3=classpath
|
||||||
com.squareup:javapoet:1.10.0=classpath
|
com.squareup:javapoet:1.10.0=classpath
|
||||||
com.squareup:javawriter:2.5.0=classpath
|
com.squareup:javawriter:2.5.0=classpath
|
||||||
com.sun.activation:javax.activation:1.2.0=classpath
|
com.sun.activation:javax.activation:1.2.0=classpath
|
||||||
|
|
|
@ -0,0 +1,44 @@
|
||||||
|
# Continuous Integration
|
||||||
|
Continuous integration is set up with GitHub Actions. The workflows are defined in this repo under [/.github/workflows](../.github/workflows).
|
||||||
|
|
||||||
|
Workflows exist for:
|
||||||
|
* Pull request - On pull request, static analysis and testing is performed.
|
||||||
|
* Deploy - Work in progress. On merge to the main branch, a release build is automatically deployed. Concurrency limits are in place, to ensure that only one release deployment can happen at a time.
|
||||||
|
|
||||||
|
## Setup
|
||||||
|
When forking this repository, some secrets need to be defined to set up new continuous integration builds.
|
||||||
|
|
||||||
|
The secrets passed to GitHub Actions then map to Gradle properties set up within our build scripts. Necessary secrets are documented at the top of each GitHub workflow yml file, as well as reiterated here.
|
||||||
|
|
||||||
|
To enhance security, [OpenID Connect](https://docs.github.com/en/actions/deployment/security-hardening-your-deployments/configuring-openid-connect-in-google-cloud-platform) is used to generate temporary access tokens for each build.
|
||||||
|
|
||||||
|
### Pull request
|
||||||
|
* `FIREBASE_TEST_LAB_PROJECT` - Firebase Test Lab project name.
|
||||||
|
* `FIREBASE_TEST_LAB_SERVICE_ACCOUNT` - Email address of Firebase Test Lab service account.
|
||||||
|
* `FIREBASE_TEST_LAB_WORKLOAD_IDENTITY_PROVIDER` - Workload identity provider to generate temporary service account key.
|
||||||
|
|
||||||
|
To obtain the values for these, you'll need to enable the necessary Google Cloud APIs to enable automated access to Firebase Test Lab.
|
||||||
|
* Configure Firebase Test Lab. Google has [documentation for Jenkins](https://firebase.google.com/docs/test-lab/android/continuous). Although we're using GitHub Actions, the initial requirements are the same.
|
||||||
|
* Configure [workload identity federation](https://github.com/google-github-actions/auth#setting-up-workload-identity-federation)
|
||||||
|
|
||||||
|
Note that pull requests will create a "release" build with a temporary fake signing key. This simplifies configuration of CI for forks who simply want to run tests and not do release deployments. The limitations of this approach are:
|
||||||
|
- These builds cannot be used for testing of app upgrade compatibility (since signing key is different each time)
|
||||||
|
- Firebase, Google Play Services, and Google Maps won't work since they use the signing key to restrict API access. The app does not currently use these services, so this is a non-issue for now.
|
||||||
|
|
||||||
|
### Release deployment
|
||||||
|
* `GOOGLE_PLAY_CLOUD_PROJECT` - Google Cloud project associated with Google Play.
|
||||||
|
* `GOOGLE_PLAY_SERVICE_ACCOUNT` - Email address of service account.
|
||||||
|
* `GOOGLE_PLAY_WORKLOAD_IDENTITY_PROVIDER` - Workload identity provider to generate temporary service account key
|
||||||
|
* `SIGNING_KEYSTORE_BASE_64` — Base64 encoded upload keystore.
|
||||||
|
* `SIGNING_KEYSTORE_PASSWORD` — Password for upload keystore.
|
||||||
|
* `SIGNING_KEY_ALIAS` — Name of key inside upload keystore.
|
||||||
|
* `SIGNING_KEY_ALIAS_PASSWORD` — Password for key alias.
|
||||||
|
|
||||||
|
To obtain the values for the Google Play deployment, you'll need to
|
||||||
|
|
||||||
|
* Create a service account with access to your Google Play account. Recommended permissions are to "edit and delete draft apps" and "release apps to testing tracks".
|
||||||
|
* Configure [workload identity federation](https://github.com/google-github-actions/auth#setting-up-workload-identity-federation)
|
||||||
|
|
||||||
|
Note that security of release deployments is enhanced via two mechanisms:
|
||||||
|
- CI signs the app with the upload keystore and not the final release keystore. If the upload keystore is ever leaked, it can be rotated without impacting end user security.
|
||||||
|
- Deployment to Google Play can only be made to testing tracks. Release to production requires manual human login under a different account with greater permissions.
|
|
@ -21,6 +21,9 @@ IS_COVERAGE_ENABLED=false
|
||||||
# It is disabled by default, because it causes tests to take about 2x longer to run.
|
# It is disabled by default, because it causes tests to take about 2x longer to run.
|
||||||
IS_USE_TEST_ORCHESTRATOR=false
|
IS_USE_TEST_ORCHESTRATOR=false
|
||||||
|
|
||||||
|
# Optional API key for Firebase Test Lab
|
||||||
|
ZCASH_FIREBASE_TEST_LAB_API_KEY_PATH=
|
||||||
|
|
||||||
# Optionally disable minification
|
# Optionally disable minification
|
||||||
IS_MINIFY_ENABLED=true
|
IS_MINIFY_ENABLED=true
|
||||||
|
|
||||||
|
@ -60,6 +63,7 @@ ANDROID_NDK_VERSION=23.0.7599858
|
||||||
|
|
||||||
ANDROID_GRADLE_PLUGIN_VERSION=7.1.0
|
ANDROID_GRADLE_PLUGIN_VERSION=7.1.0
|
||||||
DETEKT_VERSION=1.19.0
|
DETEKT_VERSION=1.19.0
|
||||||
|
FULLADLE_VERSION_MATCHER=0.17.3
|
||||||
GRADLE_VERSIONS_PLUGIN_VERSION=0.39.0
|
GRADLE_VERSIONS_PLUGIN_VERSION=0.39.0
|
||||||
KTLINT_VERSION=0.43.1
|
KTLINT_VERSION=0.43.1
|
||||||
JGIT_VERSION=5.12.0.202106070339-r
|
JGIT_VERSION=5.12.0.202106070339-r
|
||||||
|
|
|
@ -34,6 +34,7 @@ pluginManagement {
|
||||||
plugins {
|
plugins {
|
||||||
val androidGradlePluginVersion = extra["ANDROID_GRADLE_PLUGIN_VERSION"].toString()
|
val androidGradlePluginVersion = extra["ANDROID_GRADLE_PLUGIN_VERSION"].toString()
|
||||||
val detektVersion = extra["DETEKT_VERSION"].toString()
|
val detektVersion = extra["DETEKT_VERSION"].toString()
|
||||||
|
val fulladleVersion = extra["FULLADLE_VERSION_MATCHER"].toString()
|
||||||
val gradleVersionsPluginVersion = extra["GRADLE_VERSIONS_PLUGIN_VERSION"].toString()
|
val gradleVersionsPluginVersion = extra["GRADLE_VERSIONS_PLUGIN_VERSION"].toString()
|
||||||
val kotlinVersion = extra["KOTLIN_VERSION"].toString()
|
val kotlinVersion = extra["KOTLIN_VERSION"].toString()
|
||||||
val playPublisherVersion = extra["PLAY_PUBLISHER_PLUGIN_VERSION_MATCHER"].toString()
|
val playPublisherVersion = extra["PLAY_PUBLISHER_PLUGIN_VERSION_MATCHER"].toString()
|
||||||
|
@ -42,6 +43,7 @@ pluginManagement {
|
||||||
id("com.android.library") version (androidGradlePluginVersion) apply (false)
|
id("com.android.library") version (androidGradlePluginVersion) apply (false)
|
||||||
id("com.github.ben-manes.versions") version (gradleVersionsPluginVersion) apply (false)
|
id("com.github.ben-manes.versions") version (gradleVersionsPluginVersion) apply (false)
|
||||||
id("com.github.triplet.play") version (playPublisherVersion) apply (false)
|
id("com.github.triplet.play") version (playPublisherVersion) apply (false)
|
||||||
|
id("com.osacky.fulladle") version (fulladleVersion) apply (false)
|
||||||
id("io.gitlab.arturbosch.detekt") version (detektVersion) apply (false)
|
id("io.gitlab.arturbosch.detekt") version (detektVersion) apply (false)
|
||||||
kotlin("android") version (kotlinVersion) apply (false)
|
kotlin("android") version (kotlinVersion) apply (false)
|
||||||
kotlin("jvm") version (kotlinVersion)
|
kotlin("jvm") version (kotlinVersion)
|
||||||
|
@ -54,7 +56,7 @@ dependencyResolutionManagement {
|
||||||
repositories {
|
repositories {
|
||||||
val isRepoRestrictionEnabled = true
|
val isRepoRestrictionEnabled = true
|
||||||
|
|
||||||
maven("https://dl.google.com/dl/android/maven2/") { // google()
|
google {
|
||||||
if (isRepoRestrictionEnabled) {
|
if (isRepoRestrictionEnabled) {
|
||||||
content {
|
content {
|
||||||
includeGroup("android.arch.lifecycle")
|
includeGroup("android.arch.lifecycle")
|
||||||
|
@ -65,7 +67,7 @@ dependencyResolutionManagement {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
maven("https://repo.maven.apache.org/maven2/") { // mavenCentral()
|
mavenCentral {
|
||||||
if (isRepoRestrictionEnabled) {
|
if (isRepoRestrictionEnabled) {
|
||||||
content {
|
content {
|
||||||
excludeGroup("android.arch.lifecycle")
|
excludeGroup("android.arch.lifecycle")
|
||||||
|
|
Loading…
Reference in New Issue