Initial commit: Pezkuwi Wallet Android

Security hardened release:
- Code obfuscation enabled (minifyEnabled=true, shrinkResources=true)
- Sensitive files excluded (google-services.json, keystores)
- Branch.io key moved to BuildConfig placeholder
- Updated dependencies: OkHttp 4.12.0, Gson 2.10.1, BouncyCastle 1.77
- Comprehensive ProGuard rules for crypto wallet
- Navigation 2.7.7, Lifecycle 2.7.0, ConstraintLayout 2.1.4
This commit is contained in:
2026-02-12 05:19:41 +03:00
commit a294aa1a6b
7687 changed files with 441811 additions and 0 deletions
+72
View File
@@ -0,0 +1,72 @@
import os
import sys
import re
from datetime import datetime, timezone
import requests
ALLOWED_SEVERITIES = {"Major", "Critical", "Normal"}
# Matches: "Release severity: <value>" (case-insensitive, flexible spaces)
SEVERITY_LINE_RE = re.compile(r"^release\s+severity\s*:\s*(.+)$", re.IGNORECASE)
def parse_base_params(comment_link: str) -> None:
if not comment_link:
print("COMMENT_LINK is not set. Provide a valid PR comment API URL in env var COMMENT_LINK.")
sys.exit(1)
env_file = os.getenv("GITHUB_ENV")
if not env_file:
print("GITHUB_ENV is not set. This script expects GitHub Actions environment.")
sys.exit(1)
try:
resp = requests.get(comment_link, timeout=10)
resp.raise_for_status()
payload = resp.json()
except requests.RequestException as e:
print(f"Failed to fetch PR comment: {e}")
sys.exit(1)
except ValueError:
print("Response is not valid JSON.")
sys.exit(1)
body = payload.get("body")
if not isinstance(body, str) or not body.strip():
print("PR comment body is empty. Add 'Release severity: Major | Critical | Normal'.")
sys.exit(1)
lines = [line.strip() for line in body.splitlines()]
severity_raw = ""
for line in lines:
m = SEVERITY_LINE_RE.match(line)
if m:
severity_raw = m.group(1).strip()
break
if not severity_raw:
print("Release severity is missing. Add a line 'Release severity: Major | Critical | Normal'.")
sys.exit(1)
if severity_raw not in ALLOWED_SEVERITIES:
print(f"Invalid severity '{severity_raw}'. Allowed values: Major, Critical, Normal.")
sys.exit(1)
severity = severity_raw
time_iso = datetime.now(timezone.utc).strftime("%Y-%m-%dT%H:%M:%SZ")
try:
with open(env_file, "a", encoding="utf-8") as f:
f.write(f"TIME={time_iso}\n")
f.write(f"SEVERITY={severity}\n")
except OSError as e:
print(f"Failed to write to GITHUB_ENV: {e}")
sys.exit(1)
if __name__ == "__main__":
parse_base_params(os.getenv("COMMENT_LINK"))
+52
View File
@@ -0,0 +1,52 @@
#!/usr/bin/env bash
adb devices
# Install debug app
adb -s emulator-5554 install app/debug/app-debug.apk
# Install instrumental tests
adb -s emulator-5554 install app/androidTest/debug/app-debug-androidTest.apk
# Run tests
adb logcat -c &&
python - <<END
import os
import re
import subprocess as sp
import sys
import threading
import time
done = False
def update():
# prevent CI from killing the process for inactivity
while not done:
time.sleep(5)
print ("Running...")
t = threading.Thread(target=update)
t.dameon = True
t.start()
def run():
os.system('adb wait-for-device')
p = sp.Popen('adb shell am instrument -w -m -e debug false -e class "io.novafoundation.nova.balances.BalancesIntegrationTest" io.novafoundation.nova.debug.test/io.qameta.allure.android.runners.AllureAndroidJUnitRunner',
shell=True, stdout=sp.PIPE, stderr=sp.PIPE, stdin=sp.PIPE)
return p.communicate()
success = re.compile(r'OK \(\d+ tests\)')
stdout, stderr = run()
stdout = stdout.decode('ISO-8859-1')
stderr = stderr.decode('ISO-8859-1')
done = True
print (stderr)
print (stdout)
if success.search(stderr + stdout):
sys.exit(0)
else:
sys.exit(1) # make sure we fail if the tests fail
END
EXIT_CODE=$?
adb logcat -d '*:E'
# Export results
adb exec-out run-as io.novafoundation.nova.debug sh -c 'cd /data/data/io.novafoundation.nova.debug/files && tar cf - allure-results' > allure-results.tar
exit $EXIT_CODE
+52
View File
@@ -0,0 +1,52 @@
#!/usr/bin/env bash
adb devices
# Install debug app
adb -s emulator-5554 install app/debug/app-debug.apk
# Install instrumental tests
adb -s emulator-5554 install test-app/androidTest/debug/app-debug-androidTest.apk
# Run tests
adb logcat -c &&
python - <<END
import os
import re
import subprocess as sp
import sys
import threading
import time
done = False
def update():
# prevent CI from killing the process for inactivity
while not done:
time.sleep(5)
print ("Running...")
t = threading.Thread(target=update)
t.dameon = True
t.start()
def run():
os.system('adb wait-for-device')
p = sp.Popen('adb shell am instrument -w -m -e notClass io.novafoundation.nova.balances.BalancesIntegrationTest -e package io.novafoundation.nova.debug io.novafoundation.nova.debug.test/io.qameta.allure.android.runners.AllureAndroidJUnitRunner',
shell=True, stdout=sp.PIPE, stderr=sp.PIPE, stdin=sp.PIPE)
return p.communicate()
success = re.compile(r'OK \(\d+ tests\)')
stdout, stderr = run()
stdout = stdout.decode('ISO-8859-1')
stderr = stderr.decode('ISO-8859-1')
done = True
print (stderr)
print (stdout)
if success.search(stderr + stdout):
sys.exit(0)
else:
sys.exit(1) # make sure we fail if the tests fail
END
EXIT_CODE=$?
adb logcat -d '*:E'
# Export results
adb exec-out run-as io.novafoundation.nova.debug sh -c 'cd /data/data/io.novafoundation.nova.debug/files && tar cf - allure-results' > allure-results.tar
exit $EXIT_CODE
+204
View File
@@ -0,0 +1,204 @@
name: Reusable workflow for build Android
on:
workflow_call:
inputs:
branch:
required: true
default: main
type: string
gradlew-command:
required: false
type: string
default: "false"
run-tests:
required: false
type: boolean
default: true
keystore-file-name:
required: false
type: string
default: "false"
keystore-file-base64:
required: false
type: string
default: "false"
upload-name:
required: false
type: string
default: "apk"
build-debug-tests:
required: false
type: boolean
default: false
secrets:
# Crowdloan secrets - NOT NEEDED for Pezkuwi (own blockchain)
ACALA_PROD_AUTH_TOKEN:
required: false
ACALA_TEST_AUTH_TOKEN:
required: false
MOONBEAM_PROD_AUTH_TOKEN:
required: false
MOONBEAM_TEST_AUTH_TOKEN:
required: false
# Fiat on-ramp - OPTIONAL (future update)
MOONPAY_PRODUCTION_SECRET:
required: false
MOONPAY_TEST_SECRET:
required: false
# EVM chain support - REQUIRED for cross-chain
EHTERSCAN_API_KEY_MOONBEAM:
required: true
EHTERSCAN_API_KEY_MOONRIVER:
required: true
EHTERSCAN_API_KEY_ETHEREUM:
required: true
INFURA_API_KEY:
required: true
# RPC provider - use own nodes or Dwellir
DWELLIR_API_KEY:
required: false
# WalletConnect - REQUIRED for dApp connections
WALLET_CONNECT_PROJECT_ID:
required: true
# Google OAuth - REQUIRED for cloud backup
DEBUG_GOOGLE_OAUTH_ID:
required: true
RELEASE_GOOGLE_OAUTH_ID:
required: true
# Special secrets for signing:
CI_MARKET_KEYSTORE_PASS:
required: false
CI_MARKET_KEYSTORE_KEY_ALIAS:
required: false
CI_MARKET_KEYSTORE_KEY_PASS:
required: false
CI_MARKET_KEY_FILE:
required: false
CI_KEYSTORE_PASS:
required: false
CI_KEYSTORE_KEY_ALIAS:
required: false
CI_KEYSTORE_KEY_PASS:
required: false
CI_GITHUB_KEYSTORE_PASS:
required: false
CI_GITHUB_KEYSTORE_KEY_ALIAS:
required: false
CI_GITHUB_KEYSTORE_KEY_PASS:
required: false
# Secrets for google-services:
CI_DEVELOP_GOOGLE_SERVICES:
required: true
CI_PRODUCTION_GOOGLE_SERVICES:
required: true
env:
ACALA_PROD_AUTH_TOKEN: ${{ secrets.ACALA_PROD_AUTH_TOKEN }}
ACALA_TEST_AUTH_TOKEN: ${{ secrets.ACALA_TEST_AUTH_TOKEN }}
MOONBEAM_PROD_AUTH_TOKEN: ${{ secrets.MOONBEAM_PROD_AUTH_TOKEN }}
MOONBEAM_TEST_AUTH_TOKEN: ${{ secrets.MOONBEAM_TEST_AUTH_TOKEN }}
MOONPAY_PRODUCTION_SECRET: ${{ secrets.MOONPAY_PRODUCTION_SECRET }}
MOONPAY_TEST_SECRET: ${{ secrets.MOONPAY_TEST_SECRET }}
MERCURYO_PRODUCTION_SECRET: ${{ secrets.MERCURYO_PRODUCTION_SECRET }}
MERCURYO_TEST_SECRET: ${{ secrets.MERCURYO_TEST_SECRET }}
EHTERSCAN_API_KEY_MOONBEAM: ${{ secrets.EHTERSCAN_API_KEY_MOONBEAM }}
EHTERSCAN_API_KEY_MOONRIVER: ${{ secrets.EHTERSCAN_API_KEY_MOONRIVER }}
EHTERSCAN_API_KEY_ETHEREUM: ${{ secrets.EHTERSCAN_API_KEY_ETHEREUM }}
INFURA_API_KEY: ${{ secrets.INFURA_API_KEY }}
DWELLIR_API_KEY: ${{ secrets.DWELLIR_API_KEY }}
WALLET_CONNECT_PROJECT_ID: ${{ secrets.WALLET_CONNECT_PROJECT_ID }}
DEBUG_GOOGLE_OAUTH_ID: ${{ secrets.DEBUG_GOOGLE_OAUTH_ID }}
RELEASE_GOOGLE_OAUTH_ID: ${{ secrets.RELEASE_GOOGLE_OAUTH_ID }}
CI_MARKET_KEYSTORE_PASS: ${{ secrets.CI_MARKET_KEYSTORE_PASS }}
CI_MARKET_KEYSTORE_KEY_ALIAS: ${{ secrets.CI_MARKET_KEYSTORE_KEY_ALIAS }}
CI_MARKET_KEYSTORE_KEY_PASS: ${{ secrets.CI_MARKET_KEYSTORE_KEY_PASS }}
CI_MARKET_KEY_FILE: ${{ secrets.RELEASE_MARKET_KEY_FILE }}
CI_KEYSTORE_PASS: ${{ secrets.CI_KEYSTORE_PASS }}
CI_KEYSTORE_KEY_ALIAS: ${{ secrets.CI_KEYSTORE_KEY_ALIAS }}
CI_KEYSTORE_KEY_PASS: ${{ secrets.CI_KEYSTORE_KEY_PASS }}
CI_GITHUB_KEYSTORE_PASS: ${{ secrets.CI_GITHUB_KEYSTORE_PASS }}
CI_GITHUB_KEYSTORE_KEY_ALIAS: ${{ secrets.CI_GITHUB_KEYSTORE_KEY_ALIAS }}
CI_GITHUB_KEYSTORE_KEY_PASS: ${{ secrets.CI_GITHUB_KEYSTORE_KEY_PASS }}
CI_GITHUB_KEYSTORE_KEY_FILE: ${{ secrets.BASE64_GITHUB_KEYSTORE_FILE }}
CI_DEVELOP_GOOGLE_SERVICES_FILE: ${{ secrets.CI_DEVELOP_GOOGLE_SERVICES }}
CI_PRODUCTION_GOOGLE_SERVICES_FILE: ${{ secrets.CI_PRODUCTION_GOOGLE_SERVICES }}
POLKASSEMBLY_SUMMARY_API_KEY: ${{ secrets.POLKASSEMBLY_SUMMARY_API_KEY }}
CI_BUILD_ID: ${{ github.run_number }}
concurrency:
group: ${{ github.workflow }}-${{ github.ref }}-${{inputs.upload-name}}
cancel-in-progress: true
jobs:
build-app:
name: Build app and test
runs-on: ubuntu-24.04
timeout-minutes: 90
steps:
- name: Checkout particular branch
uses: actions/checkout@v4
with:
ref: ${{ inputs.branch }}
- name: 📂 Set up DEV Google Services
uses: davidSchuppa/base64Secret-toFile-action@v3
with:
secret: ${{ env.CI_DEVELOP_GOOGLE_SERVICES_FILE }}
filename: google-services.json
destination-path: ./app/
- name: 📂 Set up PROD Google Services
uses: davidSchuppa/base64Secret-toFile-action@v3
with:
secret: ${{ env.CI_PRODUCTION_GOOGLE_SERVICES_FILE }}
filename: google-services.json
destination-path: ./app/src/release/
- name: 🔧 Install dependencies
uses: ./.github/workflows/install/
- name: 🧪 Run tests
if: ${{ inputs.run-tests }}
run: ./gradlew runTest
- name: 🔐 Getting github sign key
if: ${{ startsWith(inputs.keystore-file-name, 'github_key.jks') }}
uses: timheuer/base64-to-file@v1.1
with:
fileName: ${{ inputs.keystore-file-name }}
fileDir: './app/'
encodedString: ${{ env.CI_GITHUB_KEYSTORE_KEY_FILE }}
- name: 🔐 Getting market sign key
if: ${{ startsWith(inputs.keystore-file-name, 'market_key.jks') }}
uses: timheuer/base64-to-file@v1.1
with:
fileName: ${{ inputs.keystore-file-name }}
fileDir: './app/'
encodedString: ${{ env.CI_MARKET_KEY_FILE }}
- name: 🏗 Build app
if: ${{ !startsWith(inputs.gradlew-command, 'false') }}
run: ./gradlew ${{ inputs.gradlew-command }}
- name: 🏗 Build debug tests
if: ${{ inputs.build-debug-tests }}
run: ./gradlew assembleDebugAndroidTest
- name: 🧹 Delete key after building
if: ${{ !startsWith(inputs.keystore-file-name, 'false') }}
run: rm ./app/${{ inputs.keystore-file-name }}
- name: ➡️ Upload build artifacts
if: ${{ !startsWith(inputs.gradlew-command, 'false') }}
uses: actions/upload-artifact@v4
with:
name: ${{ inputs.upload-name }}
path: app/build/outputs/apk/
+68
View File
@@ -0,0 +1,68 @@
name: Appium Mobile Tests
on:
workflow_call:
inputs:
app_url:
type: string
description: URL to download the app from
required: true
test_grep:
type: string
description: Test pattern to run (pytest marker or test name)
required: false
default: "android"
allure_job_run_id:
type: string
description: ALLURE_JOB_RUN_ID service parameter. Leave blank.
required: false
default: ""
allure_username:
type: string
description: ALLURE_USERNAME service parameter. Leave blank.
required: false
default: ""
secrets:
WORKFLOW_TOKEN:
required: true
ALLURE_TOKEN:
required: false
env:
PYTHON_VERSION: '3.9'
CI: true
ALLURE_ENDPOINT: https://pezkuwi.testops.cloud/
ALLURE_PROJECT_ID: 103
jobs:
trigger-tests:
runs-on: ubuntu-latest
steps:
- name: Trigger mobile tests in test repository
uses: actions/github-script@v7
with:
github-token: ${{ secrets.WORKFLOW_TOKEN }}
script: |
const response = await github.rest.actions.createWorkflowDispatch({
owner: 'pezkuwichain',
repo: 'appium-mobile-tests',
workflow_id: 'browserstack-tests.yml',
ref: 'master',
inputs: {
app_url: '${{ inputs.app_url }}',
ALLURE_JOB_RUN_ID: '${{ inputs.allure_job_run_id }}',
ALLURE_USERNAME: '${{ inputs.allure_username }}'
}
});
console.log('Mobile tests triggered successfully');
console.log('App URL:', '${{ inputs.app_url }}');
- name: Wait for test completion (optional)
if: false
uses: actions/github-script@v7
with:
github-token: ${{ secrets.WORKFLOW_TOKEN }}
script: |
console.log('Waiting for test completion...');
+93
View File
@@ -0,0 +1,93 @@
name: Run balances tests
on:
workflow_dispatch:
schedule:
- cron: '0 */8 * * *'
jobs:
build-app:
uses: pezkuwichain/pezkuwi-wallet-android/.github/workflows/android_build.yml@main
with:
branch: ${{github.head_ref}}
gradlew-command: assembleDebug
upload-name: develop-apk
run-tests: false
build-debug-tests: true
secrets: inherit
run-tests:
needs: [build-app]
runs-on: macos-13
steps:
- uses: actions/checkout@v4
- name: Download built artifact
uses: actions/download-artifact@v4
with:
name: develop-apk
path: app
- name: Debug path
run: |
ls -laR app
- name: Add permissions
run: chmod +x .github/scripts/run_balances_test.sh
- name: Run tests
uses: reactivecircus/android-emulator-runner@v2
with:
disable-animations: true
profile: Nexus 6
api-level: 29
script: .github/scripts/run_balances_test.sh
- uses: actions/upload-artifact@v4
if: always()
with:
name: anroid-results
path: ./allure-results.tar
report:
needs: [run-tests]
if: ${{ always() }}
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Download artifact
uses: actions/download-artifact@v4
with:
path: artifacts
- name: Unzip results
run: |
find artifacts -name allure-results.tar -exec tar -xvf {} \;
- name: Debug path
run: |
ls -laR
- name: Generate report
uses: ./.github/workflows/report/
with:
token: ${{ secrets.ACTIONS_DEPLOY_KEY }}
keep-reports-history: 30
telegram-notification:
needs: [report]
runs-on: ubuntu-latest
if: failure()
steps:
- name: Notify Telegram channel
uses: appleboy/telegram-action@master
with:
to: ${{ secrets.TELEGRAM_TO }}
token: ${{ secrets.TELEGRAM_TOKEN }}
format: html
message: |
💸 Balances tests failed.
Test Results: https://pezkuwichain.github.io/balances_test_result/${{ github.run_number }}/index.html
Github run: https://github.com/${{ github.repository }}/actions/runs/${{ github.run_id }}
@@ -0,0 +1,56 @@
name: Distribute app to Play Store
on:
workflow_dispatch:
inputs:
app_version:
description: 'Version of application'
required: true
default: v*.*.*
branch:
description: 'From which branch the application will be built'
required: true
default: main
jobs:
build:
uses: pezkuwichain/pezkuwi-wallet-android/.github/workflows/android_build.yml@main
with:
branch: ${{ github.event.inputs.branch }}
gradlew-command: assembleReleaseMarket
keystore-file-name: market_key.jks
secrets: inherit
upload:
runs-on: ubuntu-latest
needs: build
steps:
- uses: actions/checkout@v4
- name: Set Environment Variables
uses: tw3lveparsecs/github-actions-setvars@v0.1
with:
envFilePath: .github/workflows/variables/android.env
- name: Download built artifact
uses: actions/download-artifact@v4
with:
name: apk
path: app
- name: Rename artifacts
run: mv app/releaseMarket/app-releaseMarket.apk app/releaseMarket/pezkuwi-wallet-android-${{ github.event.inputs.app_version }}.apk
- name: Market publication
uses: r0adkll/upload-google-play@v1
with:
serviceAccountJsonPlainText: ${{ secrets.SERVICE_ACCOUNT_JSON }} # The contents of your service-account.json
packageName: io.pezkuwichain.wallet
releaseFiles: app/releaseMarket/pezkuwi-wallet-android-${{ github.event.inputs.app_version }}.apk
track: production # One of production, beta, alpha, internalsharing, internal, or a custom track name (case sensitive)
status: draft # One of "completed", "inProgress", "halted", "draft"
inAppUpdatePriority: 2
userFraction: 1.0 # Percentage of users who should get the staged version of the app. Defaults to 1.0
whatsNewDirectory: distribution/whatsnew # The directory of localized "whats new" files to upload as the release notes. The files contained in the whatsNewDirectory MUST use the pattern whatsnew-<LOCALE> where LOCALE is using the BCP 47 format
mappingFile: app/build/outputs/mapping/release/mapping.txt # The mapping.txt file used to de-obfuscate your stack traces from crash reports
debugSymbols: app/intermediates/merged_native_libs/release/out/lib
+37
View File
@@ -0,0 +1,37 @@
name: Install dependencies for Android build
description: Contains all dependencies for Android build
runs:
using: "composite"
steps:
- name: ☕️ Install Java
uses: actions/setup-java@v4.0.0
with:
distribution: 'temurin'
java-version: '17'
cache: 'gradle'
- name: Setup Android SDK
uses: android-actions/setup-android@v3
with:
cmdline-tools-version: 12266719
- name: Install NDK
run: echo "y" | sudo ${ANDROID_SDK_ROOT}/cmdline-tools/16.0/bin/sdkmanager --install "ndk;26.1.10909125" --sdk_root=${ANDROID_SDK_ROOT}
shell: bash
- name: Set ndk.dir in local.properties
run: echo "ndk.dir=${{ steps.setup-ndk.outputs.ndk-path }}" >> local.properties
shell: bash
- name: 🦀 Install Rust
uses: dtolnay/rust-toolchain@stable
with:
components: rustfmt, clippy
- name: Add targets
run: |
rustup target add armv7-linux-androideabi
rustup target add i686-linux-android
rustup target add x86_64-linux-android
rustup target add aarch64-linux-android
shell: bash
@@ -0,0 +1,42 @@
name: Manual Firebase distribution
on:
workflow_dispatch:
inputs:
firebase_group:
description: 'Firebase group'
required: true
default: dev-team
branch:
description: 'From which branch the application will be built'
required: true
default: main
jobs:
build:
uses: pezkuwichain/pezkuwi-wallet-android/.github/workflows/android_build.yml@main
with:
branch: ${{ github.event.inputs.branch }}
gradlew-command: assembleDevelop
secrets: inherit
upload:
runs-on: ubuntu-latest
needs: build
steps:
- uses: actions/checkout@v4
- name: Download built artifact
uses: actions/download-artifact@v4
with:
name: apk
path: app
- name: 🗳 Upload to Firebase
uses: ./.github/workflows/upload-to-firebase
with:
appId: ${{ secrets.ANDROID_DEVELOP_FIREBASE_APP_ID }}
firebase-token: ${{ secrets.CREDENTIAL_FILE_CONTENT }}
releaseNotes: ${{ github.event.head_commit.message }}
test-groups: ${{ github.event.inputs.firebase_group }}
upload-file: app/develop/app-develop.apk
+76
View File
@@ -0,0 +1,76 @@
name: PR Workflow
on:
pull_request:
branches:
- 'master'
pull_request_review_comment:
types: [created, edited, deleted]
jobs:
checkRef:
if: github.event.pull_request.base.ref == 'master' || github.event_name == 'pull_request'
runs-on: ubuntu-latest
outputs:
is_rc: ${{ steps.check_ref.outputs.ref_contains_rc }}
steps:
- uses: actions/checkout@v4
- name: Check if "rc" or "hotfix" is present in github.ref
id: check_ref
run: |
echo ${{ github.head_ref || github.ref_name }}
if [[ "${{ github.head_ref || github.ref_name }}" == "rc/"* || "${{ github.head_ref || github.ref_name }}" == "hotfix/"* ]]; then
echo "ref_contains_rc=1" >> $GITHUB_OUTPUT
else
echo "ref_contains_rc=0" >> $GITHUB_OUTPUT
fi
- name: Output check result
run: |
echo "Output: ${{ steps.check_ref.outputs.ref_contains_rc }}"
make-or-update-pr:
runs-on: ubuntu-latest
permissions: write-all
needs: checkRef
if: needs.checkRef.outputs.is_rc == '1'
steps:
- uses: actions/checkout@v4
- name: Find Comment
uses: peter-evans/find-comment@v3
id: fc
with:
issue-number: ${{ github.event.pull_request.number }}
body-includes: Release notes
- name: Create comment link
id: create_link
run: |
echo "COMMENT_LINK=https://api.github.com/repos/${{ github.repository }}/issues/comments/${{ steps.fc.outputs.comment-id }}" >> $GITHUB_ENV
shell: bash
- name: Extract version from branch name
id: extract_version
run: |
VERSION=${{ github.head_ref }}
VERSION=${VERSION/hotfix/rc} # Replace "hotfix" with "rc"
echo "version=${VERSION#*rc/}" >> $GITHUB_OUTPUT
- uses: tibdex/github-app-token@v2
id: generate-token
with:
app_id: ${{ secrets.PR_APP_ID }}
private_key: ${{ secrets.PR_APP_TOKEN }}
- name: Run Python script
run: python .github/scripts/pr_comment_extract_data.py
- name: Create new branch and file in pezkuwi-wallet-android-releases repo
uses: peter-evans/repository-dispatch@v3
with:
token: ${{ steps.generate-token.outputs.token }}
repository: pezkuwichain/pezkuwi-wallet-android-releases
event-type: create-pr
client-payload: '{"version": "${{ steps.extract_version.outputs.version }}", "comment_link": "${{ env.COMMENT_LINK }}", "time": "${{ env.TIME }}", "severity": "${{ env.SEVERITY }}"}'
@@ -0,0 +1,88 @@
name: Publish GitHub release
on:
push:
tags:
- '*'
concurrency:
group: ${{ github.workflow }}-${{ github.ref }}
cancel-in-progress: true
permissions:
contents: write
jobs:
build:
uses: pezkuwichain/pezkuwi-wallet-android/.github/workflows/android_build.yml@main
with:
branch: master
gradlew-command: assembleReleaseGithub
keystore-file-name: github_key.jks
secrets: inherit
create-release:
runs-on: ubuntu-latest
needs: build
steps:
- uses: actions/checkout@v4
- name: Download built artifact
uses: actions/download-artifact@v4
with:
name: apk
path: app
- name: Rename artifacts
run: mv app/releaseGithub/app-releaseGithub.apk app/releaseGithub/pezkuwi-wallet-android-${{ github.ref_name }}-github.apk
- name: Create Release
id: create_release
uses: softprops/action-gh-release@v1
with:
name: Release ${{ github.ref_name }}
tag_name: ${{ github.ref_name }}
generate_release_notes: true
draft: true
files: app/releaseGithub/pezkuwi-wallet-android-${{ github.ref_name }}-github.apk
deploy-to-vps:
runs-on: ubuntu-latest
needs: build
steps:
- name: Download built artifact
uses: actions/download-artifact@v4
with:
name: apk
path: deploy
- name: Prepare APK for VPS
run: |
mkdir -p ./vps-deploy
mv deploy/releaseGithub/app-releaseGithub.apk ./vps-deploy/pezkuwi-wallet.apk
- name: Create version.json
run: |
TAG="${{ github.ref_name }}"
VERSION="${TAG#v}"
cat > ./vps-deploy/version.json << EOF
{
"version": "$VERSION",
"tag": "$TAG",
"apk": "pezkuwi-wallet.apk",
"updated_at": "$(date -u +%Y-%m-%dT%H:%M:%SZ)"
}
EOF
- name: Deploy to VPS
uses: appleboy/scp-action@v1.0.0
with:
host: ${{ secrets.VPS_HOST }}
username: ${{ secrets.VPS_USER }}
key: ${{ secrets.VPS_SSH_KEY }}
source: 'vps-deploy/*'
target: '/var/www/wallet.pezkuwichain.io'
strip_components: 1
overwrite: true
+14
View File
@@ -0,0 +1,14 @@
name: Pull request
on:
pull_request:
jobs:
test:
uses: pezkuwichain/pezkuwi-wallet-android/.github/workflows/android_build.yml@main
with:
branch: ${{github.head_ref}}
gradlew-command: assembleDevelop
build-debug-tests: false # TODO: Enable this, when debug build will be fixed for tests
secrets: inherit
+78
View File
@@ -0,0 +1,78 @@
name: Build test and deploy debug apk
on:
push:
branches: [main]
workflow_dispatch:
jobs:
build:
uses: pezkuwichain/pezkuwi-wallet-android/.github/workflows/android_build.yml@main
with:
branch: main
gradlew-command: assembleDebug
secrets: inherit
upload-to-firebase:
runs-on: ubuntu-latest
needs: build
steps:
- uses: actions/checkout@v4
- name: Download built artifact
uses: actions/download-artifact@v4
with:
name: apk
path: app
- name: 🗳 Upload to Firebase
uses: ./.github/workflows/upload-to-firebase
with:
appId: ${{ secrets.ANDROID_DEBUG_FIREBASE_APP_ID }}
firebase-token: ${{ secrets.CREDENTIAL_FILE_CONTENT }}
releaseNotes: ${{ github.event.head_commit.message }}
test-groups: dev-team
upload-file: app/debug/app-debug.apk
upload-to-s3:
runs-on: ubuntu-latest
needs: build
outputs:
s3_url: ${{ steps.s3_upload.outputs.s3_url }}
env:
S3_BUCKET: s3://pezkuwi-wallet-android-app
S3_REGION: nl-ams
steps:
- uses: actions/checkout@v4
- name: Download built artifact
uses: actions/download-artifact@v4
with:
name: apk
path: app
- name: ⚙️ Upload to S3
id: s3_upload
uses: ./.github/workflows/upload-to-s3
with:
s3_region: ${{ env.S3_REGION }}
s3_access_key: ${{ secrets.SCW_ACCESS_KEY }}
s3_secret_key: ${{ secrets.SCW_SECRET_KEY }}
s3_bucket: ${{ env.S3_BUCKET }}
upload_file: app/debug/app-debug.apk
- name: Show S3 URL
run: |
echo "App uploaded to: ${{ steps.s3_upload.outputs.s3_url }}"
appium-mobile-tests:
needs: [upload-to-s3]
if: ${{ always() && needs.upload-to-s3.result == 'success' }}
uses: ./.github/workflows/appium-mobile-tests.yml
with:
app_url: ${{ needs.upload-to-s3.outputs.s3_url }}
test_grep: "android"
allure_job_run_id: ""
secrets:
WORKFLOW_TOKEN: ${{ secrets.PAT_TOKEN }}
ALLURE_TOKEN: ${{ secrets.ALLURE_TOKEN }}
+40
View File
@@ -0,0 +1,40 @@
name: Publish report to gh-pages
description: That workflow will publish report to the github-pages
inputs:
keep-reports-history:
description: "History storage depth, integer"
required: true
token:
description: "Github PAT"
required: true
runs:
using: "composite"
steps:
- name: Get Allure history
uses: actions/checkout@v4
if: always()
continue-on-error: true
with:
repository: pezkuwichain/balances_test_result
ref: gh-pages
path: gh-pages
- name: Allure Report action
uses: simple-elf/allure-report-action@master
if: always()
with:
allure_results: allure-results
allure_history: allure-history
keep_reports: ${{ inputs.keep-reports-history }}
github_repo: balances_test_result
github_repo_owner: pezkuwichain
- name: Deploy report to Github Pages
if: always()
uses: peaceiris/actions-gh-pages@v4
with:
deploy_key: ${{ inputs.token }}
publish_branch: gh-pages
publish_dir: allure-history
external_repository: pezkuwichain/balances_test_result
+34
View File
@@ -0,0 +1,34 @@
name: Sync main and master branches
on:
push:
branches:
- main
- master
jobs:
sync:
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v4
with:
fetch-depth: 0
token: ${{ secrets.PAT_TOKEN }}
- name: Sync branches
run: |
git config user.name "github-actions[bot]"
git config user.email "github-actions[bot]@users.noreply.github.com"
if [ "${{ github.ref }}" = "refs/heads/main" ]; then
echo "main was updated, syncing master..."
git checkout master
git reset --hard origin/main
git push origin master --force
elif [ "${{ github.ref }}" = "refs/heads/master" ]; then
echo "master was updated, syncing main..."
git checkout main
git reset --hard origin/master
git push origin main --force
fi
+43
View File
@@ -0,0 +1,43 @@
name: Bump app version
on:
push:
branches:
['master']
permissions:
contents: write
jobs:
update-tag:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
with:
fetch-depth: 0
- name: Version in build.gradle
run: |
versionName=$(grep "versionName" build.gradle | grep -o "'.*'")
versionName=${versionName//\'/}
echo Version in gradle file: $versionName
echo "GRADLE_APP_VERSION=$versionName" >> "$GITHUB_ENV"
- name: Check if tag exists
id: version
run: |
if git rev-parse "v${{ env.GRADLE_APP_VERSION }}" >/dev/null 2>&1; then
echo "Tag already exists"
echo "changed=false" >> $GITHUB_OUTPUT
else
echo "Tag does not exist"
echo "changed=true" >> $GITHUB_OUTPUT
fi
- uses: rickstaa/action-create-tag@v1
if: steps.version.outputs.changed == 'true'
with:
tag: 'v${{ env.GRADLE_APP_VERSION }}'
message: Release v${{ env.GRADLE_APP_VERSION }}
github_token: ${{ secrets.GITHUB_TOKEN }}
@@ -0,0 +1,49 @@
name: Deploy to Firebase
description: Deploy artifacts by provided path to firebase for provided groups
inputs:
appId:
description: 'Firebase AppID'
required: true
firebase-token:
description: 'Token from firebase CLI'
required: true
releaseNotes:
description: 'Notes which will attach to version'
required: true
default: 'update'
test-groups:
description: 'Groups which will receive the version'
required: true
upload-file:
description: 'File to uploading'
required: true
runs:
using: "composite"
steps:
- name: Upload artifact to Firebase App Distribution
id: upload
continue-on-error: true
uses: wzieba/Firebase-Distribution-Github-Action@v1.7.0
with:
appId: ${{ inputs.appId }}
serviceCredentialsFileContent: ${{ inputs.firebase-token }}
releaseNotes: ${{ inputs.releaseNotes }}
groups: ${{ inputs.test-groups }}
file: ${{ inputs.upload-file }}
- name: Sleep for 60 seconds
uses: whatnick/wait-action@master
if: steps.upload.outcome=='failure'
with:
time: '60s'
- name: Retry upload artifacts
if: steps.upload.outcome=='failure'
uses: wzieba/Firebase-Distribution-Github-Action@v1.7.0
with:
appId: ${{ inputs.appId }}
serviceCredentialsFileContent: ${{ inputs.firebase-token }}
releaseNotes: ${{ inputs.releaseNotes }}
groups: ${{ inputs.test-groups }}
file: ${{ inputs.upload-file }}
+52
View File
@@ -0,0 +1,52 @@
name: Upload to s3
description: Upload artifacts to s3
inputs:
s3_region:
description: 'S3 region'
required: true
s3_access_key:
description: 'S3 access key'
required: true
s3_secret_key:
description: 'S3 secret key'
required: true
s3_bucket:
description: 'S3 bucket'
required: true
upload_file:
description: 'File to uploading'
required: true
outputs:
s3_url:
description: 'URL of the uploaded file'
value: ${{ steps.interact_with_storage.outputs.s3_url }}
runs:
using: "composite"
steps:
- name: Set up S3cmd cli tool
uses: s3-actions/s3cmd@v1.6.1
with:
provider: scaleway
region: ${{ inputs.s3_region }}
secret_key: ${{ inputs.s3_secret_key }}
access_key: ${{ inputs.s3_access_key }}
- name: List available S3 buckets
run: s3cmd ls
shell: bash
- name: Interact with object storage
id: interact_with_storage
run: |
file="${{ inputs.upload_file }}"
destination_s3="${{ inputs.s3_bucket }}"
filename=$(basename "$file")
s3cmd sync "$file" "${destination_s3}/${filename}" --acl-public
bucket_name=$(echo "${{ inputs.s3_bucket }}" | sed 's|s3://||')
s3_url="https://${bucket_name}.s3.${{ inputs.s3_region }}.scw.cloud/${filename}"
echo "s3_url=${s3_url}" >> $GITHUB_OUTPUT
echo "Uploaded file URL: ${s3_url}"
shell: bash
+3
View File
@@ -0,0 +1,3 @@
# Android Build Variables for Pezkuwi Wallet
APP_NAME=Pezkuwi Wallet
PACKAGE_NAME=io.pezkuwichain.wallet