diff --git a/.github/workflows/build-android.yml b/.github/workflows/build-android.yml index 6a6f259c4..97de57745 100644 --- a/.github/workflows/build-android.yml +++ b/.github/workflows/build-android.yml @@ -228,7 +228,7 @@ jobs: -e "s|const moonPayApiKey = '';|const moonPayApiKey = '${MOONPAY_API_KEY}';|" \ -e "s|const exchangeHelperApiKey = '';|const exchangeHelperApiKey = '${EXCHANGE_HELPER_API_KEY}';|" \ lib/.secrets.g.dart - grep -E "^const (trocador|walletConnectProjectId|moonPayApiKey|exchangeHelperApiKey)" lib/.secrets.g.dart + awk -F"'" '/^const (trocador[A-Za-z]*|walletConnectProjectId|moonPayApiKey|exchangeHelperApiKey) = / { split($1, a, " "); v = (length($2) ? "set (" length($2) " chars)" : "EMPTY"); print a[2] ": " v }' lib/.secrets.g.dart # lengths only; values never reach the log # ---- Signing: decode upload keystore + write key.properties --------- - name: Decode Android upload keystore + write key.properties diff --git a/.github/workflows/build-ios-sim.yml b/.github/workflows/build-ios-sim.yml index 9a54442bf..8ab88501e 100644 --- a/.github/workflows/build-ios-sim.yml +++ b/.github/workflows/build-ios-sim.yml @@ -270,7 +270,7 @@ jobs: -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 + awk -F"'" '/^const (trocador[A-Za-z]*) = / { split($1, a, " "); v = (length($2) ? "set (" length($2) " chars)" : "EMPTY"); print a[2] ": " v }' lib/.secrets.g.dart # lengths only; values never reach the log # ---- Flutter init + codegen ----------------------------------------- - name: Initialize Flutter SDK (iOS precache) diff --git a/.github/workflows/build-linux.yml b/.github/workflows/build-linux.yml index e15354051..f7c5bfc24 100644 --- a/.github/workflows/build-linux.yml +++ b/.github/workflows/build-linux.yml @@ -202,7 +202,7 @@ jobs: -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 # verify the substitutions + awk -F"'" '/^const (trocador[A-Za-z]*) = / { split($1, a, " "); v = (length($2) ? "set (" length($2) " chars)" : "EMPTY"); print a[2] ": " v }' lib/.secrets.g.dart # lengths only; values never reach the log # Initialize Flutter SDK once before parallel-ish work happens. flutter # precache for Flutter 3.32.0 hits a 404 on flutter_gpu.zip (asset not diff --git a/.github/workflows/build-macos.yml b/.github/workflows/build-macos.yml index 948c389e0..0520d9c1e 100644 --- a/.github/workflows/build-macos.yml +++ b/.github/workflows/build-macos.yml @@ -130,12 +130,58 @@ jobs: gem install --user-install cocoapods fi + # ---- Prerequisites ------------------------------------------------------ + # Ported step for step from build-linux.yml, which is the desktop build + # known to pass, rather than written fresh. The first version of this + # workflow was written fresh: it skipped the clone that creates + # scripts/monero_c and died there, and behind that it had also skipped the + # step that pins the crypto salts. curl instead of wget, and BSD sed's + # -i.bak form, because this runner is a Mac. + - name: Fetch prebuilt torch_dart + run: | + set -euo pipefail + pushd scripts + rm -rf torch_dart torch_dart.tar.gz + curl -fsSL 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 -euo pipefail + pushd scripts + rm -rf reown_flutter reown_flutter.tar.gz + curl -fsSL 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 + + # The iOS variant, not build_bitbox_flutter.sh: that script also builds + # the Android .aar through gomobile, which an Apple target does not use. + - name: Clone BitBox Flutter (Apple — skip Android bindings) + run: | + set -euo pipefail + pushd scripts + if [[ ! -d bitbox_flutter ]]; then + git clone https://github.com/konstantinullrich/bitbox_flutter + fi + cd bitbox_flutter + git fetch -a + git reset --hard + git checkout 5a6e6dd388ef64003f86094af80d5453518b601d + git reset --hard + popd + # ---- Native crypto cores ---------------------------------------------- - # Same prebuilt bundle the iOS and Android legs use, so all three ship the - # same wallet core rather than three builds of it. + # prepare_moneroc.sh clones scripts/monero_c at the pinned revision. It + # is not a submodule, so checkout alone leaves the directory absent. - name: Fetch prebuilt monero_c bundle run: | set -euo pipefail + ./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" @@ -161,8 +207,7 @@ jobs: [ -f "$arm" ] || { echo "missing $arm"; exit 1; } # Universal, so one download serves Apple Silicon and Intel. A # single-arch build looks fine on the machine that made it and - # fails on half the users' machines, which is the worst way to - # find out. + # fails on half the users' machines. if [ -f "$x86" ]; then lipo -create "$arm" "$x86" -output "macos/$lib.dylib" else @@ -172,18 +217,90 @@ jobs: lipo -info "macos/$lib.dylib" done - - name: Configure the app (bundle id, version, icons) + # ---- Configure -------------------------------------------------------- + # app_env.sh sets the name, bundle id and version that app_config.sh + # writes into Info.plist; without it PlistBuddy writes empty strings. It + # has to be sourced in the same shell, and app_config.sh has to start in + # scripts/macos because it walks up with relative cds. + - name: Run configure (cakewallet build profile) run: | set -euo pipefail - # Copies pubspec_description.yaml over pubspec.yaml, so the version - # comes from the one file every platform reads. pushd scripts/macos - bash ./app_config.sh + source ./app_env.sh cakewallet + ./app_config.sh popd - grep -m1 '^version:' pubspec.yaml + /usr/libexec/PlistBuddy -c "Print :CFBundleName" macos/Runner/Info.plist + /usr/libexec/PlistBuddy -c "Print :CFBundleShortVersionString" macos/Runner/Info.plist + /usr/libexec/PlistBuddy -c "Print :CFBundleVersion" macos/Runner/Info.plist - - name: flutter pub get - run: flutter pub get + - name: Generate per-module secrets.g.dart files (all empty defaults) + run: | + set -euo pipefail + flutter pub get + dart run tool/generate_new_secrets.dart + + - name: Pin crypto secrets (never ship random salts) + env: + CRYPTO_SALT: ${{ secrets.CRYPTO_SALT }} + CRYPTO_KEYCHAIN_SALT: ${{ secrets.CRYPTO_KEYCHAIN_SALT }} + CRYPTO_KEY: ${{ secrets.CRYPTO_KEY }} + CRYPTO_WALLET_SALT: ${{ secrets.CRYPTO_WALLET_SALT }} + CRYPTO_SHORT_KEY: ${{ secrets.CRYPTO_SHORT_KEY }} + CRYPTO_BACKUP_SALT: ${{ secrets.CRYPTO_BACKUP_SALT }} + CRYPTO_BACKUP_KEYCHAIN_SALT: ${{ secrets.CRYPTO_BACKUP_KEYCHAIN_SALT }} + CRYPTO_WALLET_GROUP_SALT: ${{ secrets.CRYPTO_WALLET_GROUP_SALT }} + run: | + set -euo pipefail + # generate_new_secrets.dart mints RANDOM salts each run. Shipping + # those breaks decryption of everything the previous build stored + # (2026-08-29 "Corrupted seeds" incident). The same pinned values as + # every other platform, so a wallet moves between them intact. + for v in CRYPTO_SALT CRYPTO_KEYCHAIN_SALT CRYPTO_KEY CRYPTO_WALLET_SALT CRYPTO_SHORT_KEY CRYPTO_BACKUP_SALT CRYPTO_BACKUP_KEYCHAIN_SALT CRYPTO_WALLET_GROUP_SALT; do + eval "val=\${$v:-}" + [ -n "$val" ] || { echo "FATAL: $v is empty — refusing to ship random crypto secrets"; exit 1; } + done + sed -i.bak \ + -e "s|const salt = '.*';|const salt = '${CRYPTO_SALT}';|" \ + -e "s|const keychainSalt = '.*';|const keychainSalt = '${CRYPTO_KEYCHAIN_SALT}';|" \ + -e "s|const key = '.*';|const key = '${CRYPTO_KEY}';|" \ + -e "s|const walletSalt = '.*';|const walletSalt = '${CRYPTO_WALLET_SALT}';|" \ + -e "s|const shortKey = '.*';|const shortKey = '${CRYPTO_SHORT_KEY}';|" \ + -e "s|const backupSalt = '.*';|const backupSalt = '${CRYPTO_BACKUP_SALT}';|" \ + -e "s|const backupKeychainSalt = '.*';|const backupKeychainSalt = '${CRYPTO_BACKUP_KEYCHAIN_SALT}';|" \ + -e "s|const walletGroupSalt = '.*';|const walletGroupSalt = '${CRYPTO_WALLET_GROUP_SALT}';|" \ + lib/.secrets.g.dart + rm -f lib/.secrets.g.dart.bak + for c in salt keychainSalt key walletSalt shortKey backupSalt backupKeychainSalt walletGroupSalt; do + grep -qE "^const $c = '[0-9a-f]{8,}';" lib/.secrets.g.dart || { echo "FATAL: $c not pinned"; exit 1; } + done + echo "crypto secrets pinned (values not printed)" + + - 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: | + set -euo pipefail + sed -i.bak \ + -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 + rm -f lib/.secrets.g.dart.bak + awk -F"'" '/^const (trocador[A-Za-z]*) = / { split($1, a, " "); v = (length($2) ? "set (" length($2) " chars)" : "EMPTY"); print a[2] ": " v }' lib/.secrets.g.dart # lengths only; values never reach the log + + - name: Build generated code (mobx + hive adapters) + run: bash model_generator.sh + + - name: Generate localization + run: dart run tool/generate_localization.dart + + # res/pictures is the source for every compiled brand asset. Skipping this + # would fall back to raw SVGs, and it is the step whose inputs shipped + # Cake's artwork in 1.0.3, so it runs here exactly as it does elsewhere. + - name: Compile SVG assets (res/pictures/*.svg → assets/new-ui/*.svg.vec) + run: ./compile_graphics.sh - name: Build macOS release run: | diff --git a/.github/workflows/build-windows.yml b/.github/workflows/build-windows.yml index 6ffdfd0d2..477c527d6 100644 --- a/.github/workflows/build-windows.yml +++ b/.github/workflows/build-windows.yml @@ -444,7 +444,7 @@ jobs: -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 + awk -F"'" '/^const (trocador[A-Za-z]*) = / { split($1, a, " "); v = (length($2) ? "set (" length($2) " chars)" : "EMPTY"); print a[2] ": " v }' lib/.secrets.g.dart # lengths only; values never reach the log # ---- Codegen --------------------------------------------------------- - name: Build generated code (mobx + hive adapters) diff --git a/scripts/linux/app_env.fish b/scripts/linux/app_env.fish index 8dec90ce3..621971692 100644 --- a/scripts/linux/app_env.fish +++ b/scripts/linux/app_env.fish @@ -14,8 +14,10 @@ if test -n "$argv[1]" end set -g CAKEWALLET_NAME "Cake Wallet" -set -g CAKEWALLET_VERSION "1.9.0" -set -g CAKEWALLET_BUILD_NUMBER 29 +# From pubspec_description.yaml, like app_env.sh. These were Cake's 1.9.0 (29). +set -l _pubspec_version (awk -F': ' '/^version:/ {print $2; exit}' (dirname (status --current-filename))/../../pubspec_description.yaml) +set -g CAKEWALLET_VERSION (string split -f1 '+' $_pubspec_version) +set -g CAKEWALLET_BUILD_NUMBER (string split -f2 '+' $_pubspec_version) if not contains -- $APP_LINUX_TYPE $TYPES echo "Wrong app type." diff --git a/scripts/linux/app_env.sh b/scripts/linux/app_env.sh index c00168dcb..c8d876a5d 100755 --- a/scripts/linux/app_env.sh +++ b/scripts/linux/app_env.sh @@ -13,9 +13,14 @@ if [ -n "$1" ]; then APP_LINUX_TYPE=$1 fi +# Version and build come from pubspec_description.yaml, the one file every +# platform reads. These said 6.1.0 (79), Cake Wallet's own numbers, which only +# never reached a binary because app_config.sh's substitution looks for a +# version: 0.0.0 placeholder that pubspec_description.yaml does not contain. +_PUBSPEC_VERSION=$(awk -F': ' '/^version:/ {print $2; exit}' "$(dirname "${BASH_SOURCE[0]:-$0}")/../../pubspec_description.yaml") CAKEWALLET_NAME="Hash Bags" -CAKEWALLET_VERSION="6.1.0" -CAKEWALLET_BUILD_NUMBER=79 +CAKEWALLET_VERSION="${_PUBSPEC_VERSION%+*}" +CAKEWALLET_BUILD_NUMBER="${_PUBSPEC_VERSION#*+}" if ! [[ " ${TYPES[*]} " =~ " ${APP_LINUX_TYPE} " ]]; then echo "Wrong app type." diff --git a/scripts/macos/app_env.sh b/scripts/macos/app_env.sh index c1a677fbb..46128fefc 100755 --- a/scripts/macos/app_env.sh +++ b/scripts/macos/app_env.sh @@ -20,9 +20,13 @@ MONERO_COM_VERSION="6.1.0" MONERO_COM_BUILD_NUMBER=79 MONERO_COM_BUNDLE_ID="com.cakewallet.monero" +# Version and build come from pubspec_description.yaml, the one file every +# platform reads. A hardcoded value here overrides it silently: Android shipped +# versionCode 1 that way once, and this file said 1.0.0 (1) until 1.0.4. +_PUBSPEC_VERSION=$(awk -F': ' '/^version:/ {print $2; exit}' "$(dirname "${BASH_SOURCE[0]:-$0}")/../../pubspec_description.yaml") CAKEWALLET_NAME="Hash Bags" -CAKEWALLET_VERSION="1.0.0" -CAKEWALLET_BUILD_NUMBER=1 +CAKEWALLET_VERSION="${_PUBSPEC_VERSION%+*}" +CAKEWALLET_BUILD_NUMBER="${_PUBSPEC_VERSION#*+}" CAKEWALLET_BUNDLE_ID="com.suchsoftware.hashwallet" if ! [[ " ${TYPES[*]} " =~ " ${APP_MACOS_TYPE} " ]]; then