Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 8 additions & 1 deletion .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,14 @@ jobs:
uses: gradle/actions/setup-gradle@v5

- name: Decode google-services.json
run: echo "$GOOGLE_SERVICES_JSON_BASE64" | base64 -d > app/google-services.json
run: |
set -euo pipefail
if [ -z "${GOOGLE_SERVICES_JSON_BASE64:-}" ]; then
echo "GOOGLE_SERVICES_JSON_BASE64 is empty; using checked-in placeholder."
exit 0
fi
mkdir -p app/src/debug
printf '%s' "$GOOGLE_SERVICES_JSON_BASE64" | base64 -d > app/src/debug/google-services.json
Comment thread
ovitrif marked this conversation as resolved.
env:
GOOGLE_SERVICES_JSON_BASE64: ${{ secrets.GOOGLE_SERVICES_JSON_BASE64 }}

Expand Down
18 changes: 16 additions & 2 deletions .github/workflows/e2e.yml
Original file line number Diff line number Diff line change
Expand Up @@ -57,7 +57,14 @@ jobs:
uses: gradle/actions/setup-gradle@v5

- name: Decode google-services.json
run: echo "$GOOGLE_SERVICES_JSON_BASE64" | base64 -d > app/google-services.json
run: |
set -euo pipefail
if [ -z "${GOOGLE_SERVICES_JSON_BASE64:-}" ]; then
echo "GOOGLE_SERVICES_JSON_BASE64 is empty; using checked-in placeholder."
exit 0
fi
mkdir -p app/src/debug
printf '%s' "$GOOGLE_SERVICES_JSON_BASE64" | base64 -d > app/src/debug/google-services.json
env:
GOOGLE_SERVICES_JSON_BASE64: ${{ secrets.GOOGLE_SERVICES_JSON_BASE64 }}

Expand Down Expand Up @@ -100,7 +107,14 @@ jobs:
uses: gradle/actions/setup-gradle@v5

- name: Decode google-services.json
run: echo "$GOOGLE_SERVICES_JSON_BASE64" | base64 -d > app/google-services.json
run: |
set -euo pipefail
if [ -z "${GOOGLE_SERVICES_JSON_BASE64:-}" ]; then
echo "GOOGLE_SERVICES_JSON_BASE64 is empty; using checked-in placeholder."
exit 0
fi
mkdir -p app/src/debug
printf '%s' "$GOOGLE_SERVICES_JSON_BASE64" | base64 -d > app/src/debug/google-services.json
env:
GOOGLE_SERVICES_JSON_BASE64: ${{ secrets.GOOGLE_SERVICES_JSON_BASE64 }}

Expand Down
9 changes: 8 additions & 1 deletion .github/workflows/e2e_migration.yml
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,14 @@ jobs:
uses: gradle/actions/setup-gradle@v5

- name: Decode google-services.json
run: echo "$GOOGLE_SERVICES_JSON_BASE64" | base64 -d > app/google-services.json
run: |
set -euo pipefail
if [ -z "${GOOGLE_SERVICES_JSON_BASE64:-}" ]; then
echo "GOOGLE_SERVICES_JSON_BASE64 is empty; using checked-in placeholder."
exit 0
fi
mkdir -p app/src/debug
printf '%s' "$GOOGLE_SERVICES_JSON_BASE64" | base64 -d > app/src/debug/google-services.json
env:
GOOGLE_SERVICES_JSON_BASE64: ${{ secrets.GOOGLE_SERVICES_JSON_BASE64 }}

Expand Down
99 changes: 99 additions & 0 deletions .github/workflows/release-internal.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,99 @@
name: Release Internal

on:
workflow_dispatch:

concurrency:
group: ${{ github.workflow }}-${{ github.ref }}
cancel-in-progress: false

env:
TERM: xterm-256color
FORCE_COLOR: 1

jobs:
build-internal:
if: github.ref == 'refs/heads/master' || startsWith(github.ref, 'refs/heads/release-') || startsWith(github.ref, 'refs/tags/v')
runs-on: ubuntu-latest
timeout-minutes: 45
environment: release-internal

permissions:
contents: read
packages: read

steps:
- name: Checkout
uses: actions/checkout@v6

- name: Setup Java
uses: actions/setup-java@v5
with:
java-version: '17'
distribution: 'adopt'

- name: Setup Gradle
uses: gradle/actions/setup-gradle@v5

- name: Decode mainnet release google-services.json
env:
MAINNET_RELEASE_GOOGLE_SERVICES_JSON_BASE64: ${{ secrets.MAINNET_RELEASE_GOOGLE_SERVICES_JSON_BASE64 }}
run: |
set -euo pipefail
test -n "$MAINNET_RELEASE_GOOGLE_SERVICES_JSON_BASE64"
mkdir -p app/src/mainnetRelease
printf '%s' "$MAINNET_RELEASE_GOOGLE_SERVICES_JSON_BASE64" | base64 --decode > app/src/mainnetRelease/google-services.json

- name: Decode internal keystore
env:
INTERNAL_KEYSTORE_BASE64: ${{ secrets.INTERNAL_KEYSTORE_BASE64 }}
run: |
set -euo pipefail
test -n "$INTERNAL_KEYSTORE_BASE64"
umask 077
keystore_path="$RUNNER_TEMP/internal.keystore"
printf '%s' "$INTERNAL_KEYSTORE_BASE64" | base64 --decode > "$keystore_path"
echo "KEYSTORE_FILE=$keystore_path" >> "$GITHUB_ENV"

- name: Build internal release APK
env:
GPR_USER: ${{ secrets.GPR_USER || github.actor }}
GPR_TOKEN: ${{ secrets.GPR_TOKEN || github.token }}
GITHUB_TOKEN: ${{ secrets.GPR_TOKEN || github.token }}
KEYSTORE_PASSWORD: ${{ secrets.INTERNAL_KEYSTORE_PASSWORD }}
KEY_ALIAS: ${{ secrets.INTERNAL_KEY_ALIAS }}
KEY_PASSWORD: ${{ secrets.INTERNAL_KEY_PASSWORD }}
run: ./gradlew assembleMainnetRelease --no-daemon --stacktrace

- name: Verify internal release signature
run: |
set -euo pipefail
android_sdk_root="${ANDROID_HOME:-${ANDROID_SDK_ROOT:-}}"
test -n "$android_sdk_root"
apksigner_path="$(find "$android_sdk_root/build-tools" -name apksigner -type f | sort -V | tail -n 1)"
test -n "$apksigner_path"

apk_count=0
while IFS= read -r -d '' apk_path; do
apk_count=$((apk_count + 1))
"$apksigner_path" verify --verbose --print-certs "$apk_path"
done < <(find app/build/outputs/apk/mainnet/release -name 'bitkit-mainnet-release-*.apk' -print0)
test "$apk_count" -gt 0

- name: Collect internal artifacts
id: artifacts
run: |
set -euo pipefail
artifact_dir="$RUNNER_TEMP/internal-release"
mkdir -p "$artifact_dir"
find app/build/outputs/apk/mainnet/release -name 'bitkit-mainnet-release-*.apk' -print0 |
xargs -0 -I {} cp {} "$artifact_dir/"
(cd "$artifact_dir" && sha256sum *.apk > SHA256SUMS.txt)
echo "artifact_dir=$artifact_dir" >> "$GITHUB_OUTPUT"

- name: Upload internal artifacts
uses: actions/upload-artifact@v6
with:
name: bitkit-internal-release-${{ github.run_number }}
path: ${{ steps.artifacts.outputs.artifact_dir }}
retention-days: 30
123 changes: 123 additions & 0 deletions .github/workflows/release.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,123 @@
name: Release

on:
workflow_dispatch:

concurrency:
group: ${{ github.workflow }}-${{ github.ref }}
cancel-in-progress: false

env:
TERM: xterm-256color
FORCE_COLOR: 1

jobs:
build-release:
if: github.ref == 'refs/heads/master' || startsWith(github.ref, 'refs/heads/release-') || startsWith(github.ref, 'refs/tags/v')
runs-on: ubuntu-latest
timeout-minutes: 45
environment: release

permissions:
contents: read
packages: read

steps:
- name: Checkout
uses: actions/checkout@v6

- name: Setup Java
uses: actions/setup-java@v5
with:
java-version: '17'
distribution: 'adopt'

- name: Setup Gradle
uses: gradle/actions/setup-gradle@v5

- name: Decode mainnet release google-services.json
env:
MAINNET_RELEASE_GOOGLE_SERVICES_JSON_BASE64: ${{ secrets.MAINNET_RELEASE_GOOGLE_SERVICES_JSON_BASE64 }}
run: |
set -euo pipefail
test -n "$MAINNET_RELEASE_GOOGLE_SERVICES_JSON_BASE64"
mkdir -p app/src/mainnetRelease
printf '%s' "$MAINNET_RELEASE_GOOGLE_SERVICES_JSON_BASE64" | base64 --decode > app/src/mainnetRelease/google-services.json

- name: Decode release keystore
env:
BITKIT_KEYSTORE_BASE64: ${{ secrets.BITKIT_KEYSTORE_BASE64 }}
run: |
set -euo pipefail
test -n "$BITKIT_KEYSTORE_BASE64"
umask 077
keystore_path="$RUNNER_TEMP/bitkit.keystore"
printf '%s' "$BITKIT_KEYSTORE_BASE64" | base64 --decode > "$keystore_path"
echo "KEYSTORE_FILE=$keystore_path" >> "$GITHUB_ENV"

- name: Build release artifacts
env:
GPR_USER: ${{ secrets.GPR_USER || github.actor }}
GPR_TOKEN: ${{ secrets.GPR_TOKEN || github.token }}
GITHUB_TOKEN: ${{ secrets.GPR_TOKEN || github.token }}
KEYSTORE_PASSWORD: ${{ secrets.BITKIT_KEYSTORE_PASSWORD }}
KEY_ALIAS: ${{ secrets.BITKIT_KEY_ALIAS }}
KEY_PASSWORD: ${{ secrets.BITKIT_KEY_PASSWORD }}
run: ./gradlew assembleMainnetRelease bundleMainnetRelease --no-daemon --stacktrace

- name: Verify release signatures
run: |
set -euo pipefail
android_sdk_root="${ANDROID_HOME:-${ANDROID_SDK_ROOT:-}}"
test -n "$android_sdk_root"
apksigner_path="$(find "$android_sdk_root/build-tools" -name apksigner -type f | sort -V | tail -n 1)"
test -n "$apksigner_path"

apk_count=0
while IFS= read -r -d '' apk_path; do
apk_count=$((apk_count + 1))
"$apksigner_path" verify --verbose --print-certs "$apk_path"
done < <(find app/build/outputs/apk/mainnet/release -name 'bitkit-mainnet-release-*.apk' -print0)
test "$apk_count" -gt 0

bundle_count=0
while IFS= read -r -d '' bundle_path; do
bundle_count=$((bundle_count + 1))
verify_output="$(mktemp)"
if ! jarsigner -verify -verbose -certs "$bundle_path" 2>&1 | tee "$verify_output"; then
rm -f "$verify_output"
exit 1
fi
if grep -qi "jar is unsigned" "$verify_output"; then
echo "Unsigned bundle: $bundle_path"
rm -f "$verify_output"
exit 1
fi
if ! grep -qi "jar verified" "$verify_output"; then
echo "Bundle signature verification did not report success: $bundle_path"
rm -f "$verify_output"
exit 1
fi
rm -f "$verify_output"
done < <(find app/build/outputs/bundle/mainnetRelease -name 'bitkit-mainnet-release-*.aab' -print0)
test "$bundle_count" -gt 0

- name: Collect release artifacts
id: artifacts
run: |
set -euo pipefail
artifact_dir="$RUNNER_TEMP/release"
mkdir -p "$artifact_dir"
find app/build/outputs/bundle/mainnetRelease -name 'bitkit-mainnet-release-*.aab' -print0 |
xargs -0 -I {} cp {} "$artifact_dir/"
find app/build/outputs/apk/mainnet/release -name 'bitkit-mainnet-release-*.apk' -print0 |
xargs -0 -I {} cp {} "$artifact_dir/"
(cd "$artifact_dir" && sha256sum *.aab *.apk > SHA256SUMS.txt)
echo "artifact_dir=$artifact_dir" >> "$GITHUB_OUTPUT"

- name: Upload release artifacts
uses: actions/upload-artifact@v6
with:
name: bitkit-release-${{ github.run_number }}
path: ${{ steps.artifacts.outputs.artifact_dir }}
retention-days: 30
9 changes: 8 additions & 1 deletion .github/workflows/ui-tests.yml
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,14 @@ jobs:
gradle-${{ runner.os }}-
- name: Decode google-services.json
run: echo "$GOOGLE_SERVICES_JSON_BASE64" | base64 -d > app/google-services.json
run: |
set -euo pipefail
if [ -z "${GOOGLE_SERVICES_JSON_BASE64:-}" ]; then
echo "GOOGLE_SERVICES_JSON_BASE64 is empty; using checked-in placeholder."
exit 0
fi
mkdir -p app/src/debug
printf '%s' "$GOOGLE_SERVICES_JSON_BASE64" | base64 -d > app/src/debug/google-services.json
env:
GOOGLE_SERVICES_JSON_BASE64: ${{ secrets.GOOGLE_SERVICES_JSON_BASE64 }}

Expand Down
2 changes: 2 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,8 @@ local.properties
!*.local.template*
# Secrets
google-services.json
# Tracked fallback config used only so debug variants compile from a fresh clone.
!app/google-services.json
.env
.env.*
!.env.example
Expand Down
10 changes: 7 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,11 +10,15 @@ This repository contains the **native Android app** for Bitkit.

#### 1. Firebase Configuration

Download `google-services.json` from the Firebase Console for each of the following build flavor groups,:
- dev/tnet/mainnetDebug: Place in `app/google-services.json`
Dev and testnet debug builds use a checked-in placeholder at `app/google-services.json` so a fresh clone can compile without private Firebase files.

Download `google-services.json` from the Firebase Console when you need real Firebase integration for push notifications testing:
- Debug builds: Place in `app/src/debug/google-services.json`
- mainnetRelease: Place in `app/src/mainnetRelease/google-services.json`

> **Note**: Each flavor requires its own Firebase project configuration. The mainnet flavor will fail to build without its dedicated `google-services.json` file.
The debug file above is ignored by Git and takes precedence over the checked-in placeholder. To use real Firebase integration across debug variants, make sure it includes each application ID you build.

> **Note**: Placeholder config is only for local dev and testnet debug builds. FCM token registration and push notifications require real Firebase configuration. The mainnet release flavor should always use the real `mainnetRelease/google-services.json` file.

#### 2. GitHub Packages setup

Expand Down
48 changes: 48 additions & 0 deletions app/google-services.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
{
"project_info": {
"project_number": "000000000000",
"project_id": "bitkit-debug-placeholder",
"storage_bucket": "bitkit-debug-placeholder.appspot.com"
},
"client": [
{
"client_info": {
"mobilesdk_app_id": "1:000000000000:android:0000000000000000000000",
"android_client_info": {
"package_name": "to.bitkit.dev"
}
},
"oauth_client": [],
"api_key": [
{
"current_key": "AIzaSyDebugPlaceholderKeyForLocalBuilds000"
}
],
"services": {
"appinvite_service": {
"other_platform_oauth_client": []
}
}
},
{
"client_info": {
"mobilesdk_app_id": "1:000000000000:android:0000000000000000000002",
"android_client_info": {
"package_name": "to.bitkit.tnet"
}
},
"oauth_client": [],
"api_key": [
{
"current_key": "AIzaSyDebugPlaceholderKeyForLocalBuilds002"
}
],
"services": {
"appinvite_service": {
"other_platform_oauth_client": []
}
}
}
],
"configuration_version": "1"
}
Comment thread
ovitrif marked this conversation as resolved.
6 changes: 4 additions & 2 deletions settings.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -12,10 +12,12 @@ fun getGithubCredentials(
passKey: String = "gpr.key",
userKey: String = "gpr.user",
): Pair<String?, String?> {
val user = System.getenv("GITHUB_ACTOR")
val user = System.getenv("GPR_USER")
?: System.getenv("GITHUB_ACTOR")
?: providers.gradleProperty(userKey).orNull
?: localProperties.getProperty(userKey)
val key = System.getenv("GITHUB_TOKEN")
val key = System.getenv("GPR_TOKEN")
?: System.getenv("GITHUB_TOKEN")
?: providers.gradleProperty(passKey).orNull
?: localProperties.getProperty(passKey)
return user to key
Expand Down
Loading