Configure build with GitHub Actions

Also configure testing with Firebase Test Lab
This commit is contained in:
Carter Jernigan 2022-01-11 08:11:15 -05:00
parent 6c8ac20655
commit 3c575a497c
11 changed files with 666 additions and 49 deletions

87
.github/actions/setup/action.yml vendored Normal file
View File

@ -0,0 +1,87 @@
name: 'Setup Java, Rust, and Dependency Cache'
description: "Configures the build environment and caches Gradle, dependencies, and build outputs."
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: Setup Rust
shell: bash
run: |
rustup target add armv7-linux-androideabi aarch64-linux-android i686-linux-android x86_64-linux-android
- 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
# This tries to fall back to the build cache from the main branch, while ensuring that
# main branch builds repopulate the cache each time.
- name: Gradle Build Cache Main
id: gradle-build-cache-main
if: github.event.pull_request.head.sha == ''
uses: actions/cache@v2.1.7
with:
path: |
~/.gradle/caches/build-cache-1
~/.gradle/caches/transforms-3
key: ${{ runner.os }}-gradle-build-${{ github.sha }}
restore-keys: |
${{ runner.os }}-gradle-build
- name: Gradle Build Cache Pull Request
id: gradle-build-cache-pr
if: github.event.pull_request.head.sha != ''
uses: actions/cache@v2.1.7
with:
path: |
~/.gradle/caches/build-cache-1
~/.gradle/caches/transforms-3
key: ${{ runner.os }}-gradle-build-${{ github.event.pull_request.base.sha }}
restore-keys: |
${{ runner.os }}-gradle-build
- name: Rust Cache
id: rust-cache
uses: actions/cache@v2.1.7
with:
path: |
sdk-lib/target
~/.cargo
key: ${{ runner.os }}-rust-${{ hashFiles(format('{0}{1}', github.workspace, '/sdk-lib/Cargo.lock'), format('{0}{1}', github.workspace, '/sdk-lib/Cargo.toml'), format('{0}{1}', github.workspace, '/sdk-lib/build.gradle.kts'), format('{0}{1}', github.workspace, '/gradle.properties')) }}
- name: Download Gradle
if: steps.gradle-wrapper-cache.outputs.cache-hit != 'true'
shell: bash
run: |
./gradlew --version
- name: Download Gradle Dependencies
if: steps.gradle-dependency-cache.outputs.cache-hit != 'true'
shell: bash
run: |
./gradlew dependencies :sdk-lib:dependencies :demo-app:dependencies
- name: Compile
if: steps.gradle-build-cache-main.outputs.cache-hit != 'true' && steps.gradle-build-cache-pr.outputs.cache-hit != 'true'
shell: bash
env:
ORG_GRADLE_PROJECT_IS_MINIFY_APP_ENABLED: "false"
run: |
./gradlew assemble assembleAndroidTest

85
.github/workflows/deploy-release.yml vendored Normal file
View File

@ -0,0 +1,85 @@
# Expected secrets
# MAVEN_CENTRAL_USERNAME - Username for Maven Central.
# MAVEN_CENTRAL_PASSWORD - Password for Maven Central.
# MAVEN_SIGNING_KEYRING_FILE_BASE64 - Base64 encoded GPG keyring file.
# MAVEN_SIGNING_KEY_ID - ID for the key in the GPG keyring file.
# MAVEN_SIGNING_PASSWORD - Password for the key in the GPG keyring file.
name: Deploy Release
on:
workflow_dispatch:
concurrency: deploy_release
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
deploy_release:
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: 25
uses: ./.github/actions/setup
- name: Export Maven Signing Key
env:
MAVEN_SIGNING_KEYRING_FILE_BASE64: ${{ secrets.MAVEN_SIGNING_KEYRING_FILE_BASE64 }}
GPG_KEY_PATH: ${{ format('{0}/keyring.gpg', env.home) }}
shell: bash
run: |
echo ${MAVEN_SIGNING_KEYRING_FILE_BASE64} | base64 --decode > ${GPG_KEY_PATH}
# While not strictly necessary, this sanity checks the build before attempting to upload.
# This adds minimal additional build time, since most of the work is cached and re-used
# in the next step.
- name: Deploy to Maven Local
timeout-minutes: 25
run: |
./gradlew publishToMavenLocal
- name: Deploy to Maven Central
timeout-minutes: 5
env:
ORG_GRADLE_PROJECT_mavenCentralUsername: ${{ secrets.MAVEN_CENTRAL_USERNAME }}
ORG_GRADLE_PROJECT_mavenCentralPassword: ${{ secrets.MAVEN_CENTRAL_PASSWORD }}
ORG_GRADLE_PROJECT_signing.secretKeyRingFile: ${{ format('{0}/keyring.gpg', env.home) }}
ORG_GRADLE_PROJECT_signing.keyId: ${{ secrets.MAVEN_SIGNING_KEY_ID }}
ORG_GRADLE_PROJECT_signing.password: ${{ secrets.MAVEN_SIGNING_PASSWORD }}
ORG_GRADLE_PROJECT_IS_SNAPSHOT: false
run: |
./gradlew publish --no-parallel
./gradlew closeAndReleaseRepository
- name: Collect Artifacts
timeout-minutes: 1
if: ${{ always() }}
env:
ARTIFACTS_DIR_PATH: ${{ format('{0}/artifacts', env.home) }}
BINARIES_ZIP_PATH: ${{ format('{0}/artifacts/release_binaries.zip', env.home) }}
run: |
mkdir ${ARTIFACTS_DIR_PATH}
zip -r ${BINARIES_ZIP_PATH} . -i build/outputs/*
- name: Upload Artifacts
if: ${{ always() }}
uses: actions/upload-artifact@v2
timeout-minutes: 1
with:
name: Release binaries
path: ~/artifacts

82
.github/workflows/deploy-snapshot.yml vendored Normal file
View File

@ -0,0 +1,82 @@
# Expected secrets
# MAVEN_CENTRAL_USERNAME - Username for Maven Central
# MAVEN_CENTRAL_PASSWORD - Password for Maven Central
# Note that snapshot releases do not require GPG signing
name: Deploy Snapshot
on:
workflow_dispatch:
push:
branches:
- master
paths-ignore:
- '.github/ISSUE_TEMPLATE/*'
- '.github/PULL_REQUEST_TEMPLATE.md'
- 'LICENSE'
- 'README.md'
- 'docs/**'
concurrency: deploy_snapshot
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
deploy_snapshot:
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: 25
uses: ./.github/actions/setup
# While not strictly necessary, this sanity checks the build before attempting to upload.
# This adds minimal additional build time, since most of the work is cached and re-used
# in the next step.
- name: Deploy to Maven Local
timeout-minutes: 25
run: |
./gradlew publishToMavenLocal
- name: Deploy to Maven Central
timeout-minutes: 5
env:
ORG_GRADLE_PROJECT_mavenCentralUsername: ${{ secrets.MAVEN_CENTRAL_USERNAME }}
ORG_GRADLE_PROJECT_mavenCentralPassword: ${{ secrets.MAVEN_CENTRAL_PASSWORD }}
ORG_GRADLE_PROJECT_IS_SNAPSHOT: true
run: |
./gradlew publish --no-parallel
- name: Collect Artifacts
timeout-minutes: 1
if: ${{ always() }}
env:
ARTIFACTS_DIR_PATH: ${{ format('{0}/artifacts', env.home) }}
BINARIES_ZIP_PATH: ${{ format('{0}/artifacts/snapshot_binaries.zip', env.home) }}
run: |
mkdir ${ARTIFACTS_DIR_PATH}
zip -r ${BINARIES_ZIP_PATH} . -i build/outputs/*
- name: Upload Artifacts
if: ${{ always() }}
uses: actions/upload-artifact@v2
timeout-minutes: 1
with:
name: Snapshot binaries
path: ~/artifacts

300
.github/workflows/pull-request.yml vendored Normal file
View File

@ -0,0 +1,300 @@
# 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:
runs-on: ubuntu-latest
needs: validate_gradle_wrapper
permissions:
contents: read
steps:
- name: Checkout
timeout-minutes: 1
uses: actions/checkout@v2.4.0
- name: Setup
id: setup
timeout-minutes: 25
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: 25
env:
# Disable minify, since it makes lint run faster
ORG_GRADLE_PROJECT_IS_MINIFY_APP_ENABLED: false
run: |
./gradlew :sdk-lib:lintRelease :demo-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_android_modules:
if: needs.check_secrets.outputs.has-secrets == 'true'
needs: [prime_cache, check_secrets]
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: 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: |
./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
demo_app_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: 25
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 :demo-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: Demo app release binaries
path: ~/artifacts
# Performs a button mash test on the release build of the demo app
test_robo_demo_app:
if: needs.check_secrets.outputs.has-secrets == 'true'
needs: [demo_app_release_build, check_secrets]
runs-on: ubuntu-latest
permissions:
packages: read
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: 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

View File

@ -0,0 +1,15 @@
//dependencyLocking {
// lockAllConfigurations()
//}
tasks {
register("resolveAll") {
doLast {
configurations.filter {
// Add any custom filtering on the configurations to be resolved
it.isCanBeResolved
}.forEach { it.resolve() }
}
}
}

View File

@ -13,11 +13,12 @@ buildscript {
}
plugins {
id("com.github.ben-manes.versions")
id("com.osacky.fulladle")
id("io.gitlab.arturbosch.detekt")
id("org.jetbrains.dokka")
id("org.owasp.dependencycheck")
id("zcash.ktlint-conventions")
id("io.gitlab.arturbosch.detekt")
id("com.github.ben-manes.versions")
}
tasks {
@ -58,3 +59,32 @@ fun isNonStable(version: String): Boolean {
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)
}
}

View File

@ -1,9 +1,19 @@
# Publishing
Two types of artifacts can be published:
1. Snapshot — An unstable release of the SDK for testing
1. Release — A stable release of the SDK
Control of these modes of release is managed with a Gradle property `IS_SNAPSHOT`, which is
For both snapshot and release deployments, there are two ways to initiate deployment:
1. Automatically — See [ci.md](ci.md), which describes the continuous integration workflow for deployment.
2. Manually — See the remainder of this document for how to configure manual deployment.
Publishing requires:
### One time only
* Get your dev environment setup to [compile the SDK](https://github.com/zcash/zcash-android-wallet-sdk/#compiling-sources)
* Set up environment to [compile the SDK](https://github.com/zcash/zcash-android-wallet-sdk/#compiling-sources)
* Copy the GPG key to a directory with proper permissions (chmod 600). Note: If you'd like to quickly publish locally without subsequently publishing to Maven Central, configure a Gradle property `RELEASE_SIGNING_ENABLED=false`
* Create file `~/.gradle/gradle.properties` per the [instructions in this guide](https://proandroiddev.com/publishing-a-maven-artifact-3-3-step-by-step-instructions-to-mavencentral-publishing-bd661081645d)
* add your sonotype credentials with these properties
@ -15,18 +25,18 @@ Publishing requires:
* `signing.secretKeyRingFile`
### Every time
1. Update the [build number](https://github.com/zcash/zcash-android-wallet-sdk/blob/master/gradle.properties) and the [CHANGELOG](https://github.com/zcash/zcash-android-wallet-sdk/blob/master/CHANGELOG.md)
2. Build locally
1. Update the [build number](https://github.com/zcash/zcash-android-wallet-sdk/blob/master/gradle.properties) and the [CHANGELOG](https://github.com/zcash/zcash-android-wallet-sdk/blob/master/CHANGELOG.md). For release builds, suffix the Gradle invocations below with `-PIS_SNAPSHOT=false`.
3. Build locally
* This will install the files in your local maven repo at `~/.m2/repository/cash/z/ecc/android/`
```zsh
./gradlew publishToMavenLocal
```
3. Publish via the following command:
4. Publish via the following command:
```zsh
# This uploads the file to sonotypes staging area
./gradlew publish --no-daemon --no-parallel
```
4. Deploy to maven central:
5. Deploy to maven central:
```zsh
# This closes the staging repository and releases it to the world
./gradlew closeAndReleaseRepository

View File

@ -1,51 +1,46 @@
# Continuous Integration
Continuous integration is set up with GitHub Actions. The workflows are defined in this repo under [/.github/workflows](../.github/workflows).
In order to ensure code changes comply with the target integrations, the following policies should be observed.
Workflows exist for:
* Pull request - On pull request, static analysis and testing is performed.
* Snapshot deployment - On merge to the main branch, a snapshot release is deployed to Maven Central. Concurrency limits are in place, to ensure that only one snapshot deployment can happen at a time.
* Release deployment - Work in progress. Manually invoked workflow to deploy to Maven Central. Concurrency limits are in place, to ensure that only one release deployment can happen at a time.
## Changes to the GitHub project
## Setup
When forking this repository, some secrets need to be defined to set up new continuous integration builds.
The `master` branch is the git repositories default branch.
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.
Jobs are managed by PR labels. The following labels are actionable.
### 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.
|label|targets|
|----|---|
|`safe-to-ci`| `ciBuild`, `ciLintPr`
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)
### Code changes to the master branch
Once configured, these allow for generation of a temporary key which is then provided to the build through the Gradle property `ZCASH_FIREBASE_TEST_LAB_API_KEY_PATH`.
These changes are identified by modifications to files under the `src` directory or files matching `^.*\.(gradle|toml)$`
Note: Pull requests do not currently run darkside tests. See #361.
- build the project
- save artifacts to GCP bucket with short git hash, and `master`
- update code documentation via `docs` gradle target
### Snapshot deployment
* `MAVEN_CENTRAL_USERNAME` — Username for Maven Central, which maps to the Gradle property `mavenCentralUsername`.
* `MAVEN_CENTRAL_PASSWORD` — Password for Maven Central, which maps to the Gradle property `mavenCentralPassword`.
### New tag is created
GPG keys are not needed for snapshot deployment.
- build the project, save artifacts to GCP bucket with tag
- push artifacts to bintray
- send notification to Slack channel
Note: For documentation on the Gradle properties for Maven deployment, see [Gradle Maven Publish Plugin](https://github.com/vanniktech/gradle-maven-publish-plugin).
## Time based changes
Note: Snapshot builds are configured with a Gradle property `IS_SNAPSHOT`. The workflow automatically sets this property to true for snapshot deployments. This will suffix the version with `-snapshot` and will upload to the snapshot repository.
Periodic continous integration tasks
### Release deployment
* `MAVEN_CENTRAL_USERNAME` — Username for Maven Central, which maps to the Gradle property `mavenCentralUsername`.
* `MAVEN_CENTRAL_PASSWORD` — Password for Maven Central, which maps to the Gradle property `mavenCentralPassword`.
* `MAVEN_SIGNING_KEYRING_FILE_BASE64` — GPG keyring file, base64 encoded. Maps to Gradle property `signing.secretKeyRingFile`.
* `MAVEN_SIGNING_KEY_ID` — Name of key inside GPG keyring file. Maps to Gradle property `signing.keyId`.
* `MAVEN_SIGNING_PASSWORD` — Password for key inside GPG keyring file. Maps to Gradle property `signing.password`.
### Nightly
Note: For documentation on the Gradle properties for Maven deployment, see [Gradle Maven Publish Plugin](https://github.com/vanniktech/gradle-maven-publish-plugin).
- run integration tests
- send notifications for failed builds or failed nightly integrations
## Targets
Run gradle targets with the following command.
Note: it is recommended to NOT install `gradle` because the wrapper command (gradlew) takes care of that automatically.
```
./gradlew <target>
```
Where `<target>` is one of the following
- **ciBuild** : build the project and produce the AAR artifact under `build/outputs/aar`
- **ciDeploy** : deploy the AAR artifact to bintray. This will invoke **ciBuild**, if necessary.
- **ciLintPr** : lint the code in response to a PR to verify codestyle and formatting.
- **ciTestPr** : run the basic PR test suite. This will invoke **ciBuild**, if necessary.
- **ciTestNightly** : run the full nightly integration test suite
Note: Snapshot builds are configured with a Gradle property `IS_SNAPSHOT`. The workflow automatically sets this property to false for release deployments.

View File

@ -18,8 +18,10 @@ android.builder.sdkDownload=true
mavenCentralUsername=
mavenCentralPassword=
## Remove -snapshot for release publishing
LIBRARY_VERSION=1.3.0-beta19-snapshot
# Configures whether release is an unstable snapshot.
IS_SNAPSHOT=true
LIBRARY_VERSION=1.3.0-beta19
POM_NAME=Zcash Android Wallet SDK
POM_ARTIFACT_ID=zcash-android-sdk
POM_DESCRIPTION=This lightweight SDK connects Android to Zcash. It welds together Rust and Kotlin in a minimal way, allowing third-party Android apps to send and receive shielded transactions easily, securely and privately.
@ -46,6 +48,9 @@ IS_COVERAGE_ENABLED=false
# It is disabled by default, because it causes tests to take about 2x longer to run.
IS_USE_TEST_ORCHESTRATOR=false
# Optional API key for Firebase Test Lab
ZCASH_FIREBASE_TEST_LAB_API_KEY_PATH=
# Optionally configure minification
# For now, don't minify the SDK but do minify the app. Minifying the app helps us test
# the proguard-consumer rules in the SDK.
@ -69,6 +74,7 @@ ANDROID_NDK_VERSION=22.1.7171670
ANDROID_GRADLE_PLUGIN_VERSION=7.0.4
DETEKT_VERSION=1.19.0
DOKKA_VERSION=1.6.10
FULLADLE_VERSION_MATCHER=0.17.3
GRADLE_VERSIONS_PLUGIN_VERSION=0.40.0
KTLINT_VERSION=0.43.0
KSP_VERSION=1.6.10-1.0.2

View File

@ -18,14 +18,19 @@ plugins {
}
// Publishing information
val isSnapshot = project.property("IS_SNAPSHOT").toString().toBoolean()
val version = project.property("LIBRARY_VERSION").toString()
val ARTIFACT_ID = project.property("POM_ARTIFACT_ID").toString()
project.group = "cash.z.ecc.android"
project.version = version
project.version = if (isSnapshot) {
"$version-snapshot"
} else {
version
}
publishing {
// Snapshot repo for be manually configured
// Snapshot repo must be manually configured
// Release repo is configured automatically by the com.vanniktech.maven.publish plugin
if (version.contains("snapshot")) {
if (isSnapshot) {
val mavenCentralUsername = project.property("mavenCentralUsername").toString()
val mavenCentralPassword = project.property("mavenCentralPassword").toString()
if (mavenCentralUsername.isNotBlank() && mavenCentralPassword.isNotBlank()) {
@ -46,7 +51,7 @@ publishing {
}
}
if (version.contains("snapshot")) {
if (isSnapshot) {
mavenPublish {
sonatypeHost = null
}

View File

@ -10,6 +10,7 @@ pluginManagement {
plugins {
val detektVersion = extra["DETEKT_VERSION"].toString()
val dokkaVersion = extra["DOKKA_VERSION"].toString()
val fulladleVersion = extra["FULLADLE_VERSION_MATCHER"].toString()
val gradleVersionsPluginVersion = extra["GRADLE_VERSIONS_PLUGIN_VERSION"].toString()
val kotlinVersion = extra["KOTLIN_VERSION"].toString()
val kspVersion = extra["KSP_VERSION"].toString()
@ -19,6 +20,7 @@ pluginManagement {
id("com.github.ben-manes.versions") version (gradleVersionsPluginVersion) apply (false)
id("com.google.devtools.ksp") version(kspVersion) apply (false)
id("com.google.protobuf") version (protobufVersion) apply (false)
id("com.osacky.fulladle") version (fulladleVersion) apply (false)
id("io.gitlab.arturbosch.detekt") version (detektVersion) apply (false)
id("org.jetbrains.dokka") version (dokkaVersion) apply (false)
id("org.jetbrains.kotlin.plugin.allopen") version (kotlinVersion) apply (false)