Compare commits

...

6 Commits

Author SHA1 Message Date
f1d6f165ea Merge pull request 'dev' (#14) from github-such-software/hash-wallet:dev into dev
Some checks failed
Hash Wallet iOS Simulator build / build (push) Failing after 3s
Hash Wallet Android build / build (push) Has been cancelled
Hash Wallet Linux build / build (push) Has been cancelled
Reviewed-on: #14
2026-05-18 21:26:28 -04:00
jwinterm
c7f64f8273 CI: iOS TestFlight build workflow (signed + upload via altool)
Some checks failed
Hash Wallet iOS Simulator build / build (pull_request) Failing after 16s
Hash Wallet Android build / build (pull_request) Failing after 2m50s
Hash Wallet Linux build / build (pull_request) Has been cancelled
Phase 2 of iOS CI. Full signed pipeline that:

1) Decodes secrets:
   - APPLE_DIST_CERT_P12_BASE64 + password → temp keychain
   - APPLE_PROVISIONING_PROFILE_BASE64 → ~/Library/MobileDevice/...
   - APPLE_API_KEY_P8_BASE64 → ~/.appstoreconnect/private_keys/
2) Parses the .mobileprovision for its UUID + Name (used in ExportOptions).
3) Generates ios/ExportOptions.plist dynamically with the right Team ID +
   profile name + 'app-store' method.
4) Runs flutter build ipa --release --export-options-plist=...
5) Uploads the .ipa to TestFlight via xcrun altool with the API key.
6) Always cleans up keychain + temp credentials, even on failure.

Trigger: workflow_dispatch ONLY (manual). TestFlight uploads consume a
build number with every push — we don't want auto-trigger spamming
TestFlight. Bump the build number in scripts/ios/app_env.sh before
each TestFlight push (or wire a workflow_dispatch input later).

All Apple secrets (and the Trocador ones reused from Linux/Android) are
already configured in Gitea Actions.

If signing fails on first run: most likely the profile name extracted
from the .mobileprovision doesn't match what Xcode expects, OR the
distribution cert in the .p12 doesn't match what the profile pins. The
keychain identity sanity-check + ExportOptions.plist echo in the logs
will tell us which.
2026-05-18 21:23:54 -04:00
jwinterm
6304521e92 CI: iOS Simulator build workflow (no signing)
Phase 1 of iOS CI. Validates the Mac runner end-to-end with the
simplest possible target: an unsigned simulator .app.

Runs on the mac-mini-m2 self-hosted runner (label: macos-latest).
Mirrors the Linux/Android workflows for the shared prep steps
(torch_dart, reown_flutter, bitbox_flutter, monero_c prebuilt bundle,
configure, codegen, Trocador secrets), then does flutter build ios
--simulator --no-codesign and zips Runner.app.

Artifact: hash-wallet-ios-sim-<sha>. Unzip on any Apple Silicon Mac,
drag Runner.app into Simulator to run.

Phase 2 (TestFlight, separate file) follows after Phase 1 is green —
adds keychain setup, .p12 + provisioning profile install, ipa build,
xcrun altool upload using the App Store Connect API key. All Apple
secrets are already configured in Gitea.

The 'Inspect iOS targets in monero_c bundle' debug step will reveal
which Apple targets the prebuilt bundle ships — we'll need
aarch64-apple-ios-sim (or similar) for arm64 simulator builds on
M-series Macs. If missing, follow-up will either fetch from a different
source or build the iOS .a archives from source via Cake's scripts/ios/
build_*.sh chain (slow but reliable).
2026-05-18 21:22:07 -04:00
jwinterm
083f8b7f2a CI: add Android build workflow (signed APK + AAB)
Mirrors the Linux workflow's approach — uses the Cake-Labs prebuilt
Docker image (Flutter 3.32.0 + NDK r28 + Go + Android SDK + JDK17 +
all native build deps) and the prebuilt monero_c .so bundle, so we
skip the multi-hour native cross-compile.

Stages monero + wownero .so files into android/app/src/main/jniLibs/
for all 4 Android ABIs (arm64-v8a, armeabi-v7a, x86, x86_64). Zano
libs in the bundle are ignored.

Decodes the upload keystore from secret ANDROID_KEYSTORE_BASE64,
writes android/key.properties (which build.gradle's signingConfigs
already reads), builds a universal release APK for sideload + an
AAB for Play Console. Both signed with the upload key; Play App
Signing re-signs the AAB server-side with the production key on
upload.

Required Gitea Actions secrets:
  ANDROID_KEYSTORE_BASE64
  ANDROID_KEYSTORE_PASSWORD
  ANDROID_KEY_ALIAS
  ANDROID_KEY_PASSWORD
  TROCADOR_API_KEY (already set, reused from Linux)
  TROCADOR_MONERO_API_KEY (same)
  TROCADOR_EXCHANGE_MARKUP (same)

mwebd (Litecoin MWEB privacy) is NOT built. LTC works without it,
just no MWEB feature. Add the build step + go build pipeline if we
want MWEB on Android — separate effort.

Artifacts:
  - hash-wallet-android-apk-<sha>  (single universal APK, 14-day retention)
  - hash-wallet-android-aab-<sha>  (Play Console upload, 30-day retention)
2026-05-18 20:48:44 -04:00
jwinterm
360556b8f7 Top-bar: use onSurfaceVariant for the gear icon (same fix as navbar)
Same root cause as the bottom navbar invisibility: iconColor was set to
theme.colorScheme.primary, which on the light theme is our dark forest
green (#1A5C38) and goes invisible on dark surfaces. onSurfaceVariant
is Material's de-emphasized-foreground token and has guaranteed contrast
against the surface across all our themes.
2026-05-18 14:12:36 -04:00
jwinterm
1098a1a017 Skip Lightning username onboarding + restore navbar inactive icon contrast
Two fixes:

1) lib/src/screens/seed/seed_verification/seed_verification_success_view.dart
   After verifying a BTC wallet's seed, the original flow pushed the user to
   the lightning_username_page which suggests an @cake.cash Lightning
   address. We disabled Lightning entirely (no Greenlight infra) and the
   page is meaningless without it. Collapse the conditional so the button
   always pops back to the wallet list. Restore the conditional if/when
   Hash Wallet ever runs its own Lightning backend.

2) lib/src/screens/dashboard/widgets/new_main_navbar_widget.dart
   inactiveColor was set to theme.colorScheme.primary, which is our brand
   dark forest green (#1A5C38) post-retheme. That renders nearly invisible
   against the navbar's semi-transparent dark surface backdrop, so the
   three non-active tab icons (Wallets / Contacts / Apps) disappear and
   the user just sees three blob shapes. Material's onSurfaceVariant is
   the standard token for de-emphasized icons and is visible across our
   light/dark/black themes.
2026-05-18 13:58:25 -04:00
6 changed files with 676 additions and 8 deletions

228
.github/workflows/build-android.yml vendored Normal file
View File

@@ -0,0 +1,228 @@
name: Hash Wallet Android build
# Triggers:
# - push to dev/main (validate every commit)
# - PRs targeting dev/main (gate merges)
# - manual via workflow_dispatch
on:
push:
branches: [dev, main]
pull_request:
branches: [dev, main]
workflow_dispatch:
defaults:
run:
shell: bash
jobs:
build:
runs-on: ubuntu-latest
container:
# Same Cake-Labs prebuilt image used for Linux. Bundles Flutter 3.32.0,
# Android NDK r28, Go 1.24.1, Android SDK, JDK 17, gradle, and the
# native build deps (boost, openssl, libsodium, libzmq, unbound, icu).
image: ghcr.io/cake-tech/cake_wallet:debian13-flutter3.32.0-ndkr28-go1.24.1-ruststablenightly
env:
BRANCH_NAME: ${{ github.head_ref || github.ref_name }}
APP_ANDROID_TYPE: cakewallet
steps:
- name: Fix Actions HOME (writeable for ~/.cache + gradle)
run: echo "HOME=/root" >> $GITHUB_ENV
- uses: actions/checkout@v4
with:
fetch-depth: 1
- name: Configure git inside the container
run: |
git config --global --add safe.directory '*'
git config --global user.email "ci@suchsoftware.com"
git config --global user.name "Hash Wallet CI"
# ---- External prebuilt deps (same approach as Linux workflow) --------
- name: Fetch prebuilt torch_dart
run: |
set -x -e
pushd scripts
rm -rf torch_dart torch_dart.tar.gz
wget -q https://github.com/MrCyjaneK/torch_dart/releases/download/v1.0.17/torch_dart-v1.0.17.tar.gz -O torch_dart.tar.gz
mkdir torch_dart
tar -xzf torch_dart.tar.gz -C torch_dart
rm torch_dart.tar.gz
popd
- name: Fetch prebuilt reown_flutter
run: |
set -x -e
pushd scripts
rm -rf reown_flutter reown_flutter.tar.gz
wget -q https://github.com/cake-tech/reown_flutter/releases/download/v0.0.4/reown_flutter-v0.0.4.tar.gz -O reown_flutter.tar.gz
mkdir reown_flutter
tar -xzf reown_flutter.tar.gz -C reown_flutter
rm reown_flutter.tar.gz
popd
- name: Clone BitBox Flutter
run: |
set -x -e
pushd scripts
./build_bitbox_flutter.sh
popd
# ---- Native crypto cores (monero_c prebuilt bundle) ------------------
# Same release-bundle.zip as Linux. Contains pre-cross-compiled .so
# files for monero/wownero/zano across all 4 Android ABIs. We only ship
# monero + wownero — zano libs in the bundle are ignored.
- name: Fetch prebuilt monero_c .so bundle
run: |
set -x -e
./scripts/prepare_moneroc.sh
MONERO_C_TAG=$(cd scripts/monero_c && git describe --tags)
echo "monero_c TAG: $MONERO_C_TAG"
mkdir -p "scripts/monero_c/release/$MONERO_C_TAG"
pushd "scripts/monero_c/release/$MONERO_C_TAG"
wget -q https://github.com/MrCyjaneK/monero_c/releases/download/v0.18.4.6-RC1/release-bundle.zip
unzip -q release-bundle.zip
rm release-bundle.zip
ls
popd
# ---- Stage native libs into android/app/src/main/jniLibs/<ABI>/ -----
# Flutter Android packaging auto-includes everything under jniLibs.
- name: Place Android .so files into jniLibs
run: |
set -x -e
MONERO_C_TAG=$(cd scripts/monero_c && git describe --tags)
BUNDLE_DIR="scripts/monero_c/release/$MONERO_C_TAG"
# monero_c target dir → Android ABI dir
declare -A ABI_MAP=(
[aarch64-linux-android]=arm64-v8a
[armv7a-linux-androideabi]=armeabi-v7a
[i686-linux-android]=x86
[x86_64-linux-android]=x86_64
)
for target in "${!ABI_MAP[@]}"; do
abi="${ABI_MAP[$target]}"
mkdir -p "android/app/src/main/jniLibs/$abi"
for coin in monero wownero; do
src="$BUNDLE_DIR/$target/lib${coin}_wallet2_api_c.so"
if [[ -f "$src" ]]; then
cp -v "$src" "android/app/src/main/jniLibs/$abi/"
else
echo "MISSING: $src — fail loud, the bundle layout may have changed"
exit 1
fi
done
done
echo "=== jniLibs contents ==="
find android/app/src/main/jniLibs/ -type f -name '*.so' | sort
# mwebd (Litecoin MWEB) is NOT built here. LTC works without MWEB —
# users just don't get the privacy MWEB feature. Add a build_mwebd
# step if/when we want MWEB on Android.
# ---- Configure: pubspec.yaml, AndroidManifest.xml, app_properties ----
- name: Run Android configure (cakewallet profile)
run: |
set -x -e
pushd scripts/android
source ./app_env.sh cakewallet
./app_config.sh
popd
# ---- Secrets ---------------------------------------------------------
- name: Generate per-module secrets.g.dart files (all empty defaults)
run: dart run tool/generate_new_secrets.dart
- name: Inject Trocador affiliate secrets into lib/.secrets.g.dart
env:
TROCADOR_API_KEY: ${{ secrets.TROCADOR_API_KEY }}
TROCADOR_MONERO_API_KEY: ${{ secrets.TROCADOR_MONERO_API_KEY }}
TROCADOR_EXCHANGE_MARKUP: ${{ secrets.TROCADOR_EXCHANGE_MARKUP }}
run: |
sed -i \
-e "s|const trocadorApiKey = '';|const trocadorApiKey = '${TROCADOR_API_KEY}';|" \
-e "s|const trocadorMoneroApiKey = '';|const trocadorMoneroApiKey = '${TROCADOR_MONERO_API_KEY}';|" \
-e "s|const trocadorExchangeMarkup = '';|const trocadorExchangeMarkup = '${TROCADOR_EXCHANGE_MARKUP:-1}';|" \
lib/.secrets.g.dart
grep '^const trocador' lib/.secrets.g.dart
# ---- Signing: decode upload keystore + write key.properties ---------
- name: Decode Android upload keystore + write key.properties
env:
ANDROID_KEYSTORE_BASE64: ${{ secrets.ANDROID_KEYSTORE_BASE64 }}
ANDROID_KEYSTORE_PASSWORD: ${{ secrets.ANDROID_KEYSTORE_PASSWORD }}
ANDROID_KEY_ALIAS: ${{ secrets.ANDROID_KEY_ALIAS }}
ANDROID_KEY_PASSWORD: ${{ secrets.ANDROID_KEY_PASSWORD }}
run: |
set -e
if [[ -z "$ANDROID_KEYSTORE_BASE64" ]]; then
echo "FATAL: ANDROID_KEYSTORE_BASE64 not set — configure Gitea Actions secrets first"
exit 1
fi
# Write decoded keystore next to build.gradle (storeFile path
# in key.properties is resolved relative to android/app/).
echo "$ANDROID_KEYSTORE_BASE64" | base64 -d > android/app/upload-keystore.jks
ls -la android/app/upload-keystore.jks
# key.properties consumed by build.gradle's signingConfigs.release.
cat > android/key.properties <<EOF
storePassword=$ANDROID_KEYSTORE_PASSWORD
keyPassword=$ANDROID_KEY_PASSWORD
keyAlias=$ANDROID_KEY_ALIAS
storeFile=upload-keystore.jks
EOF
chmod 600 android/key.properties
# ---- Flutter SDK init + codegen --------------------------------------
- name: Initialize Flutter SDK (Android precache)
run: |
flutter --version
flutter precache --android --no-linux --no-ios --no-macos --no-windows --no-fuchsia --no-web || true
- name: Build generated code (mobx + hive adapters)
run: bash model_generator.sh
- name: Generate localization
run: dart run tool/generate_localization.dart
# ---- Compile + sign --------------------------------------------------
# Universal APK (single binary covers all 4 ABIs, easy to sideload).
- name: Build release APK (universal, all ABIs)
run: flutter build apk --dart-define-from-file=env.json --release
# AAB for Play Console upload (Google generates per-ABI APKs server-side
# via Play App Signing).
- name: Build release AAB
run: flutter build appbundle --dart-define-from-file=env.json --release
- name: Sanity-check signature on APK
run: |
set -x
ls -la build/app/outputs/flutter-apk/
# apksigner is in build-tools; locate dynamically.
APKSIGNER=$(find $ANDROID_HOME/build-tools -name apksigner -type f 2>/dev/null | sort -V | tail -1 || which apksigner)
if [[ -n "$APKSIGNER" ]]; then
"$APKSIGNER" verify --print-certs build/app/outputs/flutter-apk/app-release.apk || true
else
echo "apksigner not on PATH; skipping sig print"
fi
# actions/upload-artifact@v4 doesn't work on Gitea; pin to v3.
- name: Upload APK artifact (sideload-able)
uses: actions/upload-artifact@v3
with:
name: hash-wallet-android-apk-${{ github.sha }}
path: build/app/outputs/flutter-apk/app-release.apk
retention-days: 14
- name: Upload AAB artifact (for Play Console)
uses: actions/upload-artifact@v3
with:
name: hash-wallet-android-aab-${{ github.sha }}
path: build/app/outputs/bundle/release/app-release.aab
retention-days: 30

164
.github/workflows/build-ios-sim.yml vendored Normal file
View File

@@ -0,0 +1,164 @@
name: Hash Wallet iOS Simulator build
# Phase 1 of iOS CI: validate the Mac runner end-to-end by producing an
# unsigned simulator .app. Drop the resulting Runner.app into the iOS
# Simulator on any Apple Silicon Mac to verify the app actually compiles
# and launches.
#
# Phase 2 (separate workflow): full TestFlight pipeline with signing.
on:
push:
branches: [dev, main]
pull_request:
branches: [dev, main]
workflow_dispatch:
defaults:
run:
shell: bash
jobs:
build:
runs-on: macos-latest
env:
APP_IOS_TYPE: cakewallet
BRANCH_NAME: ${{ github.head_ref || github.ref_name }}
steps:
- uses: actions/checkout@v4
with:
fetch-depth: 1
- name: Show toolchain
run: |
set -x
flutter --version
xcodebuild -version
pod --version
which wget unzip
uname -m
# ---- External prebuilt deps (same as android/linux) ------------------
- name: Fetch prebuilt torch_dart
run: |
set -x -e
pushd scripts
rm -rf torch_dart torch_dart.tar.gz
wget -q https://github.com/MrCyjaneK/torch_dart/releases/download/v1.0.17/torch_dart-v1.0.17.tar.gz -O torch_dart.tar.gz
mkdir torch_dart
tar -xzf torch_dart.tar.gz -C torch_dart
rm torch_dart.tar.gz
popd
- name: Fetch prebuilt reown_flutter
run: |
set -x -e
pushd scripts
rm -rf reown_flutter reown_flutter.tar.gz
wget -q https://github.com/cake-tech/reown_flutter/releases/download/v0.0.4/reown_flutter-v0.0.4.tar.gz -O reown_flutter.tar.gz
mkdir reown_flutter
tar -xzf reown_flutter.tar.gz -C reown_flutter
rm reown_flutter.tar.gz
popd
- name: Clone BitBox Flutter
run: |
set -x -e
pushd scripts
./build_bitbox_flutter.sh
popd
# ---- Native crypto cores (monero_c prebuilt bundle) ------------------
- name: Fetch prebuilt monero_c bundle
run: |
set -x -e
./scripts/prepare_moneroc.sh
MONERO_C_TAG=$(cd scripts/monero_c && git describe --tags)
mkdir -p "scripts/monero_c/release/$MONERO_C_TAG"
pushd "scripts/monero_c/release/$MONERO_C_TAG"
wget -q https://github.com/MrCyjaneK/monero_c/releases/download/v0.18.4.6-RC1/release-bundle.zip
unzip -q release-bundle.zip
rm release-bundle.zip
echo "=== bundle contents (one level) ==="
ls
popd
# iOS native lib staging: Cake's scripts/ios/setup.sh + gen_framework.sh
# expect pre-built .a archives in specific paths. The first run will
# likely surface what's missing; we iterate from there.
- name: Inspect iOS targets available in monero_c bundle
run: |
set -x
MONERO_C_TAG=$(cd scripts/monero_c && git describe --tags)
BUNDLE_DIR="scripts/monero_c/release/$MONERO_C_TAG"
# List anything iOS-y
find "$BUNDLE_DIR" -maxdepth 1 -name '*ios*' -o -name '*apple*' 2>/dev/null || true
ls "$BUNDLE_DIR" || true
# ---- Configure: pubspec.yaml, Info.plist, GeneratedPluginRegistrant ---
- name: Run iOS configure (cakewallet profile)
run: |
set -x -e
pushd scripts/ios
source ./app_env.sh cakewallet
./app_config.sh
popd
# ---- Secrets ---------------------------------------------------------
- name: Generate per-module secrets.g.dart files (all empty defaults)
run: dart run tool/generate_new_secrets.dart
- name: Inject Trocador affiliate secrets
env:
TROCADOR_API_KEY: ${{ secrets.TROCADOR_API_KEY }}
TROCADOR_MONERO_API_KEY: ${{ secrets.TROCADOR_MONERO_API_KEY }}
TROCADOR_EXCHANGE_MARKUP: ${{ secrets.TROCADOR_EXCHANGE_MARKUP }}
run: |
# macOS sed needs '' after -i; Linux sed doesn't accept that. This
# workflow runs on macos only.
sed -i '' \
-e "s|const trocadorApiKey = '';|const trocadorApiKey = '${TROCADOR_API_KEY}';|" \
-e "s|const trocadorMoneroApiKey = '';|const trocadorMoneroApiKey = '${TROCADOR_MONERO_API_KEY}';|" \
-e "s|const trocadorExchangeMarkup = '';|const trocadorExchangeMarkup = '${TROCADOR_EXCHANGE_MARKUP:-1}';|" \
lib/.secrets.g.dart
grep '^const trocador' lib/.secrets.g.dart
# ---- Flutter init + codegen -----------------------------------------
- name: Initialize Flutter SDK (iOS precache)
run: |
flutter --version
flutter precache --ios --no-linux --no-android --no-macos --no-windows --no-fuchsia --no-web || true
- name: Build generated code (mobx + hive adapters)
run: bash model_generator.sh
- name: Generate localization
run: dart run tool/generate_localization.dart
# CocoaPods: the iOS Podfile installs Flutter plugin pods. Required
# before flutter build can link them.
- name: pod install
run: |
cd ios
pod install --repo-update
# ---- Build iOS Simulator app (no codesign) ---------------------------
- name: Build iOS Simulator app
run: |
set -x -e
flutter build ios --simulator --no-codesign \
--dart-define-from-file=env.json
ls -la build/ios/iphonesimulator/
- name: Zip Runner.app
run: |
cd build/ios/iphonesimulator
zip -r hash_wallet_ios_sim_${{ github.sha }}.zip Runner.app
- name: Upload .app artifact
uses: actions/upload-artifact@v3
with:
name: hash-wallet-ios-sim-${{ github.sha }}
path: build/ios/iphonesimulator/hash_wallet_ios_sim_*.zip
retention-days: 14

View File

@@ -0,0 +1,266 @@
name: Hash Wallet iOS TestFlight build
# Phase 2 of iOS CI: full signed build that uploads to TestFlight.
# Phase 1 (build-ios-sim.yml) validates the Mac runner with no signing —
# run that first if TestFlight blows up unexpectedly.
#
# Required Gitea Actions secrets (all account/app credentials):
# APPLE_TEAM_ID — 10-char (D8PL9F7X33)
# APPLE_KEY_ID — 10-char (CHK6B69G58)
# APPLE_ISSUER_ID — UUID
# APPLE_API_KEY_P8_BASE64 — base64 of AuthKey_<KeyID>.p8
# APPLE_DIST_CERT_P12_BASE64 — base64 of distribution.p12
# APPLE_DIST_CERT_P12_PASSWORD — password set during openssl pkcs12 export
# APPLE_PROVISIONING_PROFILE_BASE64 — base64 of Hash_Wallet.mobileprovision
# APPLE_KEYCHAIN_PASSWORD — any string, used to create the temp keychain
# TROCADOR_* — reused from Linux/Android
on:
# Don't auto-trigger on every push — TestFlight uploads are slow and use
# build numbers. Manual trigger only; later we can add a workflow_dispatch
# input for build number or auto-bump.
workflow_dispatch:
defaults:
run:
shell: bash
jobs:
build:
runs-on: macos-latest
env:
APP_IOS_TYPE: cakewallet
BRANCH_NAME: ${{ github.head_ref || github.ref_name }}
steps:
- uses: actions/checkout@v4
with:
fetch-depth: 1
- name: Show toolchain
run: |
set -x
flutter --version
xcodebuild -version
pod --version
uname -m
# ---- External prebuilt deps -----------------------------------------
- name: Fetch prebuilt torch_dart
run: |
set -x -e
pushd scripts
rm -rf torch_dart torch_dart.tar.gz
wget -q https://github.com/MrCyjaneK/torch_dart/releases/download/v1.0.17/torch_dart-v1.0.17.tar.gz -O torch_dart.tar.gz
mkdir torch_dart
tar -xzf torch_dart.tar.gz -C torch_dart
rm torch_dart.tar.gz
popd
- name: Fetch prebuilt reown_flutter
run: |
set -x -e
pushd scripts
rm -rf reown_flutter reown_flutter.tar.gz
wget -q https://github.com/cake-tech/reown_flutter/releases/download/v0.0.4/reown_flutter-v0.0.4.tar.gz -O reown_flutter.tar.gz
mkdir reown_flutter
tar -xzf reown_flutter.tar.gz -C reown_flutter
rm reown_flutter.tar.gz
popd
- name: Clone BitBox Flutter
run: |
set -x -e
pushd scripts
./build_bitbox_flutter.sh
popd
- name: Fetch prebuilt monero_c bundle
run: |
set -x -e
./scripts/prepare_moneroc.sh
MONERO_C_TAG=$(cd scripts/monero_c && git describe --tags)
mkdir -p "scripts/monero_c/release/$MONERO_C_TAG"
pushd "scripts/monero_c/release/$MONERO_C_TAG"
wget -q https://github.com/MrCyjaneK/monero_c/releases/download/v0.18.4.6-RC1/release-bundle.zip
unzip -q release-bundle.zip
rm release-bundle.zip
popd
# ---- Configure: pubspec.yaml, Info.plist, etc. ----------------------
- name: Run iOS configure (cakewallet profile)
run: |
set -x -e
pushd scripts/ios
source ./app_env.sh cakewallet
./app_config.sh
popd
# ---- Secrets ---------------------------------------------------------
- name: Generate per-module secrets.g.dart files (empty defaults)
run: dart run tool/generate_new_secrets.dart
- name: Inject Trocador affiliate secrets
env:
TROCADOR_API_KEY: ${{ secrets.TROCADOR_API_KEY }}
TROCADOR_MONERO_API_KEY: ${{ secrets.TROCADOR_MONERO_API_KEY }}
TROCADOR_EXCHANGE_MARKUP: ${{ secrets.TROCADOR_EXCHANGE_MARKUP }}
run: |
sed -i '' \
-e "s|const trocadorApiKey = '';|const trocadorApiKey = '${TROCADOR_API_KEY}';|" \
-e "s|const trocadorMoneroApiKey = '';|const trocadorMoneroApiKey = '${TROCADOR_MONERO_API_KEY}';|" \
-e "s|const trocadorExchangeMarkup = '';|const trocadorExchangeMarkup = '${TROCADOR_EXCHANGE_MARKUP:-1}';|" \
lib/.secrets.g.dart
# ---- Apple signing setup --------------------------------------------
- name: Create temp keychain + import distribution cert
env:
APPLE_DIST_CERT_P12_BASE64: ${{ secrets.APPLE_DIST_CERT_P12_BASE64 }}
APPLE_DIST_CERT_P12_PASSWORD: ${{ secrets.APPLE_DIST_CERT_P12_PASSWORD }}
APPLE_KEYCHAIN_PASSWORD: ${{ secrets.APPLE_KEYCHAIN_PASSWORD }}
run: |
set -e
KEYCHAIN_PATH="$RUNNER_TEMP/build.keychain-db"
# Create + unlock dedicated keychain so we don't touch the user's login keychain
security create-keychain -p "$APPLE_KEYCHAIN_PASSWORD" "$KEYCHAIN_PATH"
security set-keychain-settings -lut 21600 "$KEYCHAIN_PATH" # 6 hours
security unlock-keychain -p "$APPLE_KEYCHAIN_PASSWORD" "$KEYCHAIN_PATH"
# Add to user search list so codesign can find it
security list-keychains -d user -s "$KEYCHAIN_PATH" $(security list-keychains -d user | sed 's/"//g')
# Import the .p12
P12="$RUNNER_TEMP/dist.p12"
echo "$APPLE_DIST_CERT_P12_BASE64" | base64 -d > "$P12"
security import "$P12" -k "$KEYCHAIN_PATH" -P "$APPLE_DIST_CERT_P12_PASSWORD" \
-T /usr/bin/codesign -T /usr/bin/security
security set-key-partition-list -S apple-tool:,apple:,codesign: -s -k "$APPLE_KEYCHAIN_PASSWORD" "$KEYCHAIN_PATH"
# Sanity-check: list identities the keychain offers
security find-identity -v -p codesigning "$KEYCHAIN_PATH"
rm -f "$P12"
- name: Install provisioning profile + parse name/UUID for ExportOptions
env:
APPLE_PROVISIONING_PROFILE_BASE64: ${{ secrets.APPLE_PROVISIONING_PROFILE_BASE64 }}
run: |
set -e
PROFILES_DIR="$HOME/Library/MobileDevice/Provisioning Profiles"
mkdir -p "$PROFILES_DIR"
PROFILE="$RUNNER_TEMP/Hash_Wallet.mobileprovision"
echo "$APPLE_PROVISIONING_PROFILE_BASE64" | base64 -d > "$PROFILE"
# Parse the .mobileprovision to get its UUID + Name (CMS-decoded plist).
PROFILE_UUID=$(security cms -D -i "$PROFILE" | plutil -extract UUID raw -o - -)
PROFILE_NAME=$(security cms -D -i "$PROFILE" | plutil -extract Name raw -o - -)
echo "Profile UUID: $PROFILE_UUID"
echo "Profile name: $PROFILE_NAME"
echo "PROFILE_UUID=$PROFILE_UUID" >> "$GITHUB_ENV"
echo "PROFILE_NAME=$PROFILE_NAME" >> "$GITHUB_ENV"
# Xcode looks profiles up by UUID filename.
cp "$PROFILE" "$PROFILES_DIR/$PROFILE_UUID.mobileprovision"
ls -la "$PROFILES_DIR" | head -10
- name: Install App Store Connect API key for altool
env:
APPLE_API_KEY_P8_BASE64: ${{ secrets.APPLE_API_KEY_P8_BASE64 }}
APPLE_KEY_ID: ${{ secrets.APPLE_KEY_ID }}
run: |
set -e
mkdir -p "$HOME/.appstoreconnect/private_keys"
echo "$APPLE_API_KEY_P8_BASE64" | base64 -d > "$HOME/.appstoreconnect/private_keys/AuthKey_${APPLE_KEY_ID}.p8"
ls -la "$HOME/.appstoreconnect/private_keys/"
# ---- Flutter init + codegen + pod install ---------------------------
- name: Initialize Flutter SDK (iOS precache)
run: |
flutter --version
flutter precache --ios --no-linux --no-android --no-macos --no-windows --no-fuchsia --no-web || true
- name: Build generated code
run: bash model_generator.sh
- name: Generate localization
run: dart run tool/generate_localization.dart
- name: pod install
run: |
cd ios
pod install --repo-update
# ---- Generate ExportOptions.plist + build IPA -----------------------
- name: Write ExportOptions.plist
env:
APPLE_TEAM_ID: ${{ secrets.APPLE_TEAM_ID }}
run: |
cat > ios/ExportOptions.plist <<EOF
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>method</key>
<string>app-store</string>
<key>teamID</key>
<string>${APPLE_TEAM_ID}</string>
<key>signingStyle</key>
<string>manual</string>
<key>signingCertificate</key>
<string>Apple Distribution</string>
<key>provisioningProfiles</key>
<dict>
<key>com.suchsoftware.hashwallet</key>
<string>${PROFILE_NAME}</string>
</dict>
<key>uploadBitcode</key>
<false/>
<key>uploadSymbols</key>
<true/>
<key>destination</key>
<string>export</string>
<key>stripSwiftSymbols</key>
<true/>
</dict>
</plist>
EOF
cat ios/ExportOptions.plist
- name: Build IPA
run: |
set -x -e
flutter build ipa --release \
--dart-define-from-file=env.json \
--export-options-plist=ios/ExportOptions.plist
ls -la build/ios/ipa/
- name: Upload IPA artifact
uses: actions/upload-artifact@v3
with:
name: hash-wallet-ios-${{ github.sha }}
path: build/ios/ipa/*.ipa
retention-days: 30
# ---- Upload to TestFlight -------------------------------------------
- name: Upload to TestFlight via altool
env:
APPLE_KEY_ID: ${{ secrets.APPLE_KEY_ID }}
APPLE_ISSUER_ID: ${{ secrets.APPLE_ISSUER_ID }}
run: |
set -e
IPA=$(ls build/ios/ipa/*.ipa | head -1)
echo "Uploading $IPA to TestFlight..."
xcrun altool --upload-app \
--type ios \
--file "$IPA" \
--apiKey "$APPLE_KEY_ID" \
--apiIssuer "$APPLE_ISSUER_ID"
# ---- Cleanup keychain so we don't leak it across builds -------------
- name: Cleanup
if: always()
env:
APPLE_KEYCHAIN_PASSWORD: ${{ secrets.APPLE_KEYCHAIN_PASSWORD }}
run: |
set +e
KEYCHAIN_PATH="$RUNNER_TEMP/build.keychain-db"
security delete-keychain "$KEYCHAIN_PATH" 2>/dev/null
rm -rf "$HOME/.appstoreconnect/private_keys"
rm -f "$HOME/Library/MobileDevice/Provisioning Profiles"/*.mobileprovision
true

View File

@@ -45,7 +45,11 @@ class TopBar extends StatelessWidget {
isSyncHeavy: dashboardViewModel.isSyncHeavy,
),
ModernButton.svg(
iconColor: Theme.of(context).colorScheme.primary,
// Hash Wallet: was theme.colorScheme.primary (dark forest green)
// which rendered the gear icon nearly invisible on the dark
// dashboard backdrop. onSurfaceVariant is the de-emphasized-
// foreground Material token.
iconColor: Theme.of(context).colorScheme.onSurfaceVariant,
size: 36,
onPressed: () {
HapticFeedback.mediumImpact();

View File

@@ -122,7 +122,11 @@ class _NEWNewMainNavBarState extends State<NewMainNavBar> {
final backgroundColor = theme.colorScheme.surfaceContainer.withAlpha(127);
final pillColor = theme.colorScheme.onSurface.withAlpha(25);
final activeColor = theme.colorScheme.onSurface;
final inactiveColor = theme.colorScheme.primary;
// Hash Wallet: was theme.colorScheme.primary — that's our brand forest
// green (#1A5C38), which renders dark-on-dark against the navbar's
// semi-transparent surface backdrop. Material's onSurfaceVariant is the
// standard token for de-emphasized icons and is visible across themes.
final inactiveColor = theme.colorScheme.onSurfaceVariant;
return Observer(
builder: (_) {

View File

@@ -66,13 +66,15 @@ class SeedVerificationSuccessView extends StatelessWidget {
PrimaryButton(
key: ValueKey('wallet_seed_page_open_wallet_button_key'),
onPressed: () {
if (walletType == WalletType.bitcoin) {
Navigator.of(context).pushNamed(Routes.lightningUsernamePage, arguments: true);
} else {
Navigator.of(context).popUntil((route) => route.isFirst);
}
// Hash Wallet: Lightning Username onboarding skipped. The original
// Cake flow pushed BTC wallets to the lightning_username_page,
// which suggests an @cake.cash address. We disabled Lightning
// entirely (no Greenlight infra) and the screen is meaningless
// without it. Re-enable here if we ever stand up our own LN
// backend + domain.
Navigator.of(context).popUntil((route) => route.isFirst);
},
text: (walletType == WalletType.bitcoin) ? S.current.continue_text : S.current.open_wallet,
text: S.current.open_wallet,
color: Theme.of(context).colorScheme.primary,
textColor: Theme.of(context).colorScheme.onPrimary,
),