mirror of
https://github.com/Such-Software/hash-wallet.git
synced 2026-10-17 05:41:29 +02:00
Windows took whatever Flutter the runner happened to have. On 2026-09-19 that
was 3.38.5 with Dart 3.10.4, and Dart 3.10 removed the _macros package, so pub
could not solve hive_generator against build_resolvers:
macros >=0.1.3-main.0 depends on _macros from sdk which doesn't exist
So, because hash_wallet depends on both build_resolvers ^2.4.4 and
hive_generator ^2.0.1, version solving failed.
The build died at exit 69 before compiling anything, on the same commit that
Android and Linux built successfully minutes earlier. They differ only in
that both run inside the cake_wallet debian13-flutter3.32.0 image, and iOS
installs 3.32.0 explicitly. Windows was the only platform whose dependency
resolution was decided by a machine somebody had updated.
It now checks for 3.32.0 and installs it when absent, the same two steps iOS
already uses. No app code changes; hash-bags-ios-1.0.3 stays at 0697f41c7.
576 lines
29 KiB
YAML
576 lines
29 KiB
YAML
name: Hash Bags Windows build
|
|
|
|
# Phase 1 of Windows CI: produce an unsigned Windows release bundle (.zip of
|
|
# the Flutter Release output + MSVC runtime DLLs). Drop the zip on any Win
|
|
# 10/11 box, extract, run hash_wallet.exe.
|
|
#
|
|
# Phase 2 (separate iteration): wire up Inno Setup installer once the
|
|
# scripts/windows/build_exe_installer.iss file is rebranded from Cake Wallet
|
|
# → Hash Bags.
|
|
|
|
on:
|
|
# Manual-only. Trigger via Actions → "Hash Bags Windows build" → Run workflow.
|
|
# workflow_dispatch runs as the triggering user, so secrets are always
|
|
# available (PR triggers from forks would strip them — not relevant here
|
|
# since the runner is self-hosted and listening on a self-hosted Gitea).
|
|
workflow_dispatch:
|
|
|
|
# Cancel in-flight runs when a newer commit lands on the same branch.
|
|
concurrency:
|
|
group: windows-${{ github.ref }}
|
|
cancel-in-progress: true
|
|
|
|
defaults:
|
|
run:
|
|
# Explicit Git Bash path (not `shell: bash`) because Windows resolves
|
|
# bare `bash` to C:\Windows\System32\bash.exe (WSL), which refuses to
|
|
# run when the act_runner service is hosted under LocalSystem:
|
|
# "Running WSL as local system is not supported."
|
|
# Git Bash works fine under LocalSystem and matches how GitHub's
|
|
# hosted windows-latest runner invokes bash.
|
|
shell: '"C:\Program Files\Git\bin\bash.exe" --noprofile --norc -eo pipefail {0}'
|
|
|
|
jobs:
|
|
build:
|
|
# Matches the label our act_runner.exe advertised: "windows:host" → executor=host.
|
|
runs-on: windows
|
|
env:
|
|
BRANCH_NAME: ${{ github.head_ref || github.ref_name }}
|
|
# Belt-and-suspenders fallback that bypasses Node TLS verification for
|
|
# actions/* against the self-hosted runner's internal git host. Same
|
|
# fallback as build-ios-sim.yml.
|
|
NODE_TLS_REJECT_UNAUTHORIZED: '0'
|
|
|
|
steps:
|
|
- uses: actions/checkout@v4
|
|
with:
|
|
fetch-depth: 1
|
|
|
|
# ---- Git config for LocalSystem ---------------------------------------
|
|
# The runner service runs as LocalSystem, which has no global git
|
|
# identity. scripts/monero_c/apply_patches.sh (and a few other
|
|
# submodule operations) do `git commit`-equivalents that need
|
|
# user.email + user.name set. Also pre-empt git's "safe.directory"
|
|
# check, which fires when the checkout dir owner != runner identity.
|
|
- name: Configure git identity + safe.directory
|
|
run: |
|
|
git config --global --add safe.directory '*'
|
|
git config --global user.email "ci@suchsoftware.com"
|
|
git config --global user.name "Hash Bags CI"
|
|
git config --global --list
|
|
|
|
# ---- Rust toolchain ---------------------------------------------------
|
|
# The dnssec_proof plugin builds its native lib through cargokit
|
|
# (windows/CMakeLists.txt -> apply_cargokit), so `flutter build windows`
|
|
# genuinely needs cargo/rustc — the prebuilt monero_c bundle doesn't
|
|
# cover it.
|
|
#
|
|
# setup-windows-runner.ps1 installs Rust to C:\Rust and puts
|
|
# C:\Rust\cargo\bin on the *Machine* PATH, but the act_runner service
|
|
# only picks up Machine PATH changes when it restarts, and a rebuilt or
|
|
# re-imaged box loses it entirely. Either way the job died at exit 127
|
|
# ("rustc: command not found"). Locate it ourselves, and install it if
|
|
# it really isn't there, so CI doesn't depend on the box's env being
|
|
# right. No-op when rustc is already on PATH.
|
|
- name: Ensure Rust toolchain is on PATH
|
|
run: |
|
|
set -e
|
|
if command -v rustc >/dev/null 2>&1 && command -v cargo >/dev/null 2>&1; then
|
|
echo "Rust already on PATH: $(command -v rustc)"
|
|
exit 0
|
|
fi
|
|
|
|
# Known install locations, in preference order. First is where
|
|
# setup-windows-runner.ps1 puts it; the rest cover a rustup-init
|
|
# run under a normal user account.
|
|
CANDIDATES=(
|
|
"/c/Rust/cargo/bin"
|
|
"$USERPROFILE/.cargo/bin"
|
|
"/c/Users/Administrator/.cargo/bin"
|
|
"/c/Windows/System32/config/systemprofile/.cargo/bin"
|
|
)
|
|
for d in "${CANDIDATES[@]}"; do
|
|
if [[ -x "$d/rustc.exe" ]]; then
|
|
echo "Found Rust at $d — adding to PATH for later steps"
|
|
export PATH="$d:$PATH"
|
|
# $GITHUB_PATH wants a Windows-style path; MSYS converts the
|
|
# resulting semicolon PATH back to POSIX form on shell start.
|
|
cygpath -w "$d" >> "$GITHUB_PATH"
|
|
# rustup's shims resolve toolchains via RUSTUP_HOME; without it
|
|
# they look in %USERPROFILE%\.rustup, which is empty for the
|
|
# LocalSystem service account.
|
|
if [[ -d "$(dirname "$(dirname "$d")")/rustup" ]]; then
|
|
RUSTUP_DIR="$(dirname "$(dirname "$d")")/rustup"
|
|
# Export for THIS step as well as writing to $GITHUB_ENV.
|
|
# $GITHUB_ENV only reaches later steps, and the verification on
|
|
# the next line is itself a rustup shim: without the export it
|
|
# falls back to %USERPROFILE%\.rustup, which on this runner is
|
|
# a file rather than a directory, so it fails with os error 183
|
|
# having ignored the toolchain we just found.
|
|
RUSTUP_HOME="$(cygpath -w "$RUSTUP_DIR")"
|
|
CARGO_HOME="$(cygpath -w "$(dirname "$d")")"
|
|
export RUSTUP_HOME CARGO_HOME
|
|
echo "RUSTUP_HOME=$RUSTUP_HOME" >> "$GITHUB_ENV"
|
|
echo "CARGO_HOME=$CARGO_HOME" >> "$GITHUB_ENV"
|
|
fi
|
|
rustc --version
|
|
exit 0
|
|
fi
|
|
done
|
|
|
|
echo "Rust not found in any known location — installing to C:\\Rust"
|
|
CARGO_HOME="C:\\Rust\\cargo"
|
|
RUSTUP_HOME="C:\\Rust\\rustup"
|
|
export CARGO_HOME RUSTUP_HOME
|
|
mkdir -p /c/Rust/cargo /c/Rust/rustup
|
|
# act_runner sets RUNNER_TEMP, but don't hard-depend on it.
|
|
DL_DIR="${RUNNER_TEMP:-/c/Rust}"
|
|
curl -fsSL -o "$DL_DIR/rustup-init.exe" https://win.rustup.rs/x86_64
|
|
"$DL_DIR/rustup-init.exe" -y \
|
|
--default-toolchain stable \
|
|
--profile minimal \
|
|
--target x86_64-pc-windows-msvc \
|
|
--no-modify-path
|
|
echo "C:\\Rust\\cargo\\bin" >> "$GITHUB_PATH"
|
|
echo "CARGO_HOME=$CARGO_HOME" >> "$GITHUB_ENV"
|
|
echo "RUSTUP_HOME=$RUSTUP_HOME" >> "$GITHUB_ENV"
|
|
/c/Rust/cargo/bin/rustc --version
|
|
|
|
# ---- Windows Developer Mode (symlink support) ------------------------
|
|
# `flutter build windows` materialises each plugin under
|
|
# windows/flutter/ephemeral/.plugin_symlinks/ as a real NTFS symlink, and
|
|
# creating one needs SeCreateSymbolicLinkPrivilege. Flutter doesn't test
|
|
# the privilege directly — it reads the AppModelUnlock registry key and
|
|
# bails with:
|
|
# "Building with plugins requires symlink support.
|
|
# Please enable Developer Mode in your system settings."
|
|
# Setting the key is exactly what the Settings toggle does; HKLM is
|
|
# writable by the LocalSystem service account, so no interactive login
|
|
# is involved. Idempotent — a no-op once the value is 1.
|
|
- name: Enable Developer Mode (symlink support for plugin builds)
|
|
run: |
|
|
set -e
|
|
powershell -NoProfile -Command '
|
|
$key = "HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\AppModelUnlock"
|
|
if (-not (Test-Path $key)) { New-Item -Path $key -Force | Out-Null }
|
|
$cur = (Get-ItemProperty -Path $key -Name AllowDevelopmentWithoutDevLicense -ErrorAction SilentlyContinue).AllowDevelopmentWithoutDevLicense
|
|
if ($cur -eq 1) {
|
|
Write-Host "Developer Mode already enabled"
|
|
} else {
|
|
Set-ItemProperty -Path $key -Name AllowDevelopmentWithoutDevLicense -Value 1 -Type DWord
|
|
Write-Host "Developer Mode enabled (was: $cur)"
|
|
}
|
|
'
|
|
|
|
# ---- Flutter, pinned -------------------------------------------------
|
|
# Every other platform builds on 3.32.0: Linux and Android inside the
|
|
# cake_wallet debian13-flutter3.32.0 image, iOS by installing it
|
|
# explicitly. Windows was the one that took whatever the runner happened
|
|
# to have, and on 2026-09-19 that was 3.38.5 / Dart 3.10.4. Dart 3.10
|
|
# removed the _macros package, so `flutter pub get` could not solve
|
|
# hive_generator against build_resolvers and the build died at exit 69
|
|
# long before it compiled anything. A build machine's drifting toolchain
|
|
# must not decide which dependencies resolve.
|
|
- name: Check Flutter 3.32.0
|
|
id: flutter_check
|
|
run: |
|
|
if command -v flutter >/dev/null && flutter --version 2>/dev/null | grep -q "3\.32\.0"; then
|
|
echo "installed=true" >> "$GITHUB_OUTPUT"
|
|
flutter --version
|
|
else
|
|
echo "installed=false" >> "$GITHUB_OUTPUT"
|
|
fi
|
|
|
|
- name: Install Flutter 3.32.0 (if missing)
|
|
if: steps.flutter_check.outputs.installed != 'true'
|
|
uses: subosito/flutter-action@v2
|
|
with:
|
|
flutter-version: '3.32.0'
|
|
channel: stable
|
|
cache: true
|
|
|
|
# ---- Toolchain sanity ------------------------------------------------
|
|
# Our self-hosted runner has Flutter / Rust / VS Build Tools / Inno
|
|
# Setup pre-installed (see scripts/windows/setup-windows-runner.ps1).
|
|
# This step just prints versions so failures are easy to diagnose.
|
|
- name: Show installed toolchain versions
|
|
run: |
|
|
set -x
|
|
# PATH may need refreshing after install — refresh from the registry.
|
|
# Git Bash inherits Windows PATH at shell start, so just printing
|
|
# `which` is enough.
|
|
which flutter dart cargo rustc git 2>/dev/null || true
|
|
flutter --version
|
|
dart --version
|
|
rustc --version
|
|
cargo --version
|
|
|
|
# ---- External path-dep plugins ----------------------------------------
|
|
# Several plugins are path-deps in pubspec.yaml. The path directories
|
|
# must exist BEFORE `flutter pub get` runs against the regenerated
|
|
# pubspec, or pub fails with "could not find package X at <path>".
|
|
#
|
|
# The Android + iOS + Linux workflows use upstream-published .tar.gz
|
|
# tarballs. We don't, because those tarballs contain example/*
|
|
# symlinks pointing to absolute /home/runner/.pub-cache/ paths
|
|
# (artifacts from the upstream build host) — Linux tar tolerates
|
|
# dangling symlinks, Windows tar bails the whole extraction. Clone
|
|
# the source repos at the matching tagged versions instead. Same
|
|
# pattern as bitbox_flutter further down.
|
|
|
|
- name: Clone torch_dart at v1.0.17
|
|
run: |
|
|
set -x -e
|
|
pushd scripts
|
|
rm -rf torch_dart
|
|
git clone --depth 1 --branch v1.0.17 https://github.com/MrCyjaneK/torch_dart
|
|
popd
|
|
|
|
- name: Clone reown_flutter at v0.0.4
|
|
run: |
|
|
set -x -e
|
|
pushd scripts
|
|
rm -rf reown_flutter
|
|
git clone --depth 1 --branch v0.0.4 https://github.com/cake-tech/reown_flutter
|
|
popd
|
|
|
|
- name: Clone BitBox Flutter
|
|
run: |
|
|
# Pubspec has bitbox_flutter as a path dep — the directory must
|
|
# exist for pub get even if BitBox Windows support isn't
|
|
# functional. Same approach as the iOS workflow: clone the repo
|
|
# at a pinned commit, don't run any Android-binding build step.
|
|
set -x -e
|
|
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 (monero_c prebuilt bundle) ------------------
|
|
# The same release-bundle.zip that Android + iOS use. Contains
|
|
# pre-cross-compiled native libs for many target triples. We'll need
|
|
# the x86_64-w64-mingw32 (Windows MinGW) or x86_64-pc-windows-msvc
|
|
# entries — inspection step below logs what's actually shipped.
|
|
- name: Fetch prebuilt monero_c 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"
|
|
curl -fsSL -O 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 (top level) ==="
|
|
ls
|
|
popd
|
|
|
|
- name: Inspect Windows targets 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"
|
|
echo "=== full mingw32 dir contents ==="
|
|
ls -la "$BUNDLE_DIR/x86_64-w64-mingw32" 2>/dev/null || true
|
|
|
|
# ---- Stage monero_c DLLs into the layout windows/CMakeLists.txt expects
|
|
# Upstream Cake's Windows build cross-compiles monero_c on Linux via
|
|
# scripts/windows/build_all.sh, which lays files out as:
|
|
# scripts/monero_c/release/<coin>/<triple>_<basename>.dll
|
|
# The CMakeLists.txt at windows/CMakeLists.txt:84-94 hardcodes those
|
|
# paths in install(FILES ...) rules — it copies + RENAMEs them to the
|
|
# final names (monero_libwallet2_api_c.dll etc.) in the Release dir.
|
|
#
|
|
# We're using the prebuilt bundle from MrCyjaneK's release, which has
|
|
# a different layout:
|
|
# scripts/monero_c/release/<TAG>/<triple>/lib<coin>_wallet2_api_c.dll
|
|
# So we restage the bundle's files into the upstream-build-all-style
|
|
# layout before flutter build windows runs.
|
|
- name: Stage monero_c bundle into upstream cross-compile layout
|
|
run: |
|
|
set -x -e
|
|
MONERO_C_TAG=$(cd scripts/monero_c && git describe --tags)
|
|
SRC="scripts/monero_c/release/$MONERO_C_TAG/x86_64-w64-mingw32"
|
|
MONERO_DST="scripts/monero_c/release/monero"
|
|
WOWNERO_DST="scripts/monero_c/release/wownero"
|
|
mkdir -p "$MONERO_DST" "$WOWNERO_DST"
|
|
|
|
# The wallet C-API DLLs, one per coin
|
|
cp -v "$SRC/libmonero_wallet2_api_c.dll" "$MONERO_DST/x86_64-w64-mingw32_libwallet2_api_c.dll"
|
|
cp -v "$SRC/libwownero_wallet2_api_c.dll" "$WOWNERO_DST/x86_64-w64-mingw32_libwallet2_api_c.dll"
|
|
|
|
# MinGW runtime DLLs (libssp-0, libwinpthread-1) — the wallet libs
|
|
# are dynamically linked against these. MrCyjaneK's prebuilt
|
|
# release-bundle.zip does NOT ship them (only the wallet *.dll +
|
|
# *.dll.a import libs), so we source them from Git Bash's bundled
|
|
# MinGW-w64 distribution, which ships the same x86_64-w64-mingw32
|
|
# ABI used to build the wallet libs.
|
|
# ORDERING MATTERS: monero_c's prebuilt wallet DLLs are built against
|
|
# recent MinGW-w64 (post-2022 winpthreads ABI with pthread_cond_timedwait64).
|
|
# Flutter's bundled mingit ships a 2017-vintage libwinpthread that's
|
|
# missing newer symbols and crashes wallet creation at runtime. Prefer
|
|
# Git for Windows' mingw64/bin which is updated frequently.
|
|
RUNTIME_PATHS=(
|
|
"/c/Program Files/Git/mingw64/bin" # Git for Windows — modern MinGW runtime (preferred)
|
|
"$SRC" # monero_c bundle (in case it grows DLLs)
|
|
"/c/ProgramData/chocolatey/lib/mingw/tools/install/mingw64/bin" # choco install -y mingw
|
|
"/c/flutter/bin/mingit/mingw64/bin" # Flutter's bundled mingit — old (2017), use only as last resort
|
|
"/mingw64/bin" # MSYS2 fallback
|
|
)
|
|
for runtime in libssp-0.dll libwinpthread-1.dll; do
|
|
src=""
|
|
for d in "${RUNTIME_PATHS[@]}"; do
|
|
if [[ -f "$d/$runtime" ]]; then
|
|
src="$d/$runtime"
|
|
break
|
|
fi
|
|
done
|
|
if [[ -z "$src" ]]; then
|
|
echo "FATAL: $runtime not found in any of:"
|
|
printf ' %s\n' "${RUNTIME_PATHS[@]}"
|
|
for d in "${RUNTIME_PATHS[@]}"; do
|
|
echo "--- contents of $d (if exists) ---"
|
|
ls "$d" 2>/dev/null | head -20 || echo "(missing)"
|
|
done
|
|
exit 1
|
|
fi
|
|
cp -v "$src" "$MONERO_DST/x86_64-w64-mingw32_${runtime}"
|
|
done
|
|
|
|
echo "=== final staged layout ==="
|
|
ls -la "$MONERO_DST/" "$WOWNERO_DST/"
|
|
|
|
# ---- Configure: pubspec.yaml + per-coin enablement -------------------
|
|
# Mirror of hashwallet.bat — drives tool/configure.dart with the same
|
|
# set of coin flags that upstream Cake's Windows build uses.
|
|
- name: Configure pubspec + Wallet types
|
|
run: |
|
|
set -x -e
|
|
cp -f pubspec_description.yaml pubspec.yaml
|
|
flutter pub get
|
|
dart run tool/generate_pubspec.dart
|
|
flutter pub get
|
|
dart run tool/configure.dart \
|
|
--monero --bitcoin --ethereum --polygon --nano --bitcoinCash \
|
|
--wownero --dogecoin --base --arbitrum --bsc
|
|
|
|
# ---- Secrets ---------------------------------------------------------
|
|
- name: Generate per-module secrets.g.dart files (empty defaults)
|
|
run: 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 -e
|
|
# 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). Overwrite with the pinned
|
|
# values and refuse to build if any is missing.
|
|
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 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: |
|
|
# Length-only debug visibility — never prints actual secrets.
|
|
echo "TROCADOR_API_KEY length: ${#TROCADOR_API_KEY}"
|
|
echo "TROCADOR_MONERO_API_KEY length: ${#TROCADOR_MONERO_API_KEY}"
|
|
echo "TROCADOR_EXCHANGE_MARKUP length: ${#TROCADOR_EXCHANGE_MARKUP}"
|
|
if [[ -z "$TROCADOR_API_KEY" ]]; then
|
|
echo "WARN: TROCADOR_API_KEY not reaching runner — build will proceed"
|
|
echo " but Trocador exchange features won't work in this binary."
|
|
fi
|
|
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
|
|
|
|
# ---- Codegen ---------------------------------------------------------
|
|
- name: Build generated code (mobx + hive adapters)
|
|
run: bash model_generator.sh
|
|
|
|
- name: Generate localization
|
|
run: dart run tool/generate_localization.dart
|
|
|
|
- name: Compile SVG assets (res/pictures/*.svg → assets/new-ui/*.svg.vec)
|
|
run: ./compile_graphics.sh
|
|
|
|
# ---- Build the Windows .exe ------------------------------------------
|
|
- name: Build Windows release
|
|
run: |
|
|
set -x -e
|
|
flutter config --enable-windows-desktop
|
|
flutter build windows --dart-define-from-file=env.json --release --verbose
|
|
|
|
# ---- Reconcile monero/wownero DLL naming for Dart FFI ----------------
|
|
# windows/CMakeLists.txt's install(FILES ... RENAME ...) rules produce
|
|
# <coin>_libwallet2_api_c.dll, but package:monero's Dart FFI loader
|
|
# (mrcyjanek's package) opens lib<coin>_wallet2_api_c.dll — different
|
|
# filename layout, runtime error "Failed to load dynamic library".
|
|
# Create the lib-prefixed copies so both name conventions resolve.
|
|
- name: Mirror monero/wownero DLLs under lib-prefixed names
|
|
run: |
|
|
set -x -e
|
|
DST="build/windows/x64/runner/Release"
|
|
for coin in monero wownero; do
|
|
src="$DST/${coin}_libwallet2_api_c.dll"
|
|
dst="$DST/lib${coin}_wallet2_api_c.dll"
|
|
if [[ -f "$src" ]]; then
|
|
cp -v "$src" "$dst"
|
|
else
|
|
echo "WARN: $src not in Release dir — CMake install may have skipped it"
|
|
ls "$DST" | head -20
|
|
fi
|
|
done
|
|
|
|
# ---- Bundle MSVC runtime DLLs with the .exe --------------------------
|
|
# Standalone Windows builds need msvcp140.dll + vcruntime140.dll +
|
|
# vcruntime140_1.dll next to the .exe (or installed via vc_redist on
|
|
# the user's machine). Bundling avoids the "VCRUNTIME140.dll not found"
|
|
# error on machines without VC++ redistributable installed.
|
|
- name: Copy MSVC runtime DLLs next to the .exe
|
|
run: |
|
|
set -x -e
|
|
# Find the redist dir for the currently-installed MSVC version (the
|
|
# version number rolls forward with VS updates, so do NOT hardcode).
|
|
REDIST_BASE="/c/Program Files (x86)/Microsoft Visual Studio/2022/BuildTools/VC/Redist/MSVC"
|
|
REDIST_DIR=$(find "$REDIST_BASE" -type d -name 'Microsoft.VC*.CRT' -path '*/x64/*' 2>/dev/null | sort | tail -1)
|
|
if [[ -z "$REDIST_DIR" ]]; then
|
|
echo "FATAL: could not find VC redist dir under $REDIST_BASE"
|
|
ls "$REDIST_BASE" 2>/dev/null || echo "(redist base dir missing entirely)"
|
|
exit 1
|
|
fi
|
|
echo "Using redist dir: $REDIST_DIR"
|
|
cp -v "$REDIST_DIR/msvcp140.dll" "$REDIST_DIR/vcruntime140.dll" "$REDIST_DIR/vcruntime140_1.dll" \
|
|
build/windows/x64/runner/Release/
|
|
|
|
# ---- Authenticode sign the launcher ----------------------------------
|
|
# This workflow already runs on the machine with the EV code-signing
|
|
# token in it, but the runner (NT SERVICE\act_runner) cannot use the
|
|
# token: SafeNet holds the login per user and an operator establishes it
|
|
# interactively. So the exe is queued for the Fleet-installed signing
|
|
# broker (such-fleet roles/windows_code_signing), which signs it in the
|
|
# operator's session. CI never holds the key or the token password, so it
|
|
# cannot spend one of the token's three password attempts.
|
|
#
|
|
# Only HashWallet.exe is signed. The bundled MSVC runtime DLLs are
|
|
# already Microsoft-signed and must not be re-signed, and the plugin DLLs
|
|
# are loaded by an already-trusted process -- it is the launcher the user
|
|
# double-clicks that SmartScreen judges.
|
|
- name: Sign HashWallet.exe with the hardware token
|
|
shell: powershell
|
|
run: |
|
|
$ErrorActionPreference = 'Stop'
|
|
$release = "build\windows\x64\runner\Release"
|
|
$exe = Join-Path $release 'HashWallet.exe'
|
|
if (-not (Test-Path $exe)) { throw "no HashWallet.exe at $exe" }
|
|
|
|
if (Test-Path 'C:\signing\SIGNING_LOCKED') {
|
|
Get-Content 'C:\signing\SIGNING_LOCKED'
|
|
throw "The signing broker's circuit breaker is tripped. An operator must clear it."
|
|
}
|
|
|
|
$queued = "hashbags-$env:GITHUB_RUN_ID.exe"
|
|
$inPath = "C:\signing\in\$queued"
|
|
$outPath = "C:\signing\out\$queued"
|
|
Copy-Item $exe $inPath -Force
|
|
Set-Content -LiteralPath "$inPath.ready" -Value $env:GITHUB_RUN_ID
|
|
Write-Host "queued $queued"
|
|
|
|
$deadline = (Get-Date).AddMinutes(10)
|
|
while ((Get-Date) -lt $deadline) {
|
|
if (Test-Path "$outPath.done") { break }
|
|
if (Test-Path 'C:\signing\SIGNING_LOCKED') {
|
|
Get-Content 'C:\signing\SIGNING_LOCKED'
|
|
throw "The broker tripped its circuit breaker while signing this artifact."
|
|
}
|
|
Start-Sleep -Seconds 5
|
|
}
|
|
if (-not (Test-Path "$outPath.done")) { throw "Timed out waiting for the signing broker." }
|
|
|
|
Move-Item $outPath $exe -Force
|
|
Remove-Item "$outPath.done" -Force -ErrorAction SilentlyContinue
|
|
|
|
# Verify on the binary that actually ships. A bad signature or a
|
|
# missing timestamp blocks the release.
|
|
$sig = Get-AuthenticodeSignature $exe
|
|
$sig | Format-List Status, StatusMessage,
|
|
@{n='Signer';e={$_.SignerCertificate.Subject}},
|
|
@{n='TimeStamper';e={$_.TimeStamperCertificate.Subject}}
|
|
if ($sig.Status -ne 'Valid') { throw "Authenticode status is $($sig.Status)" }
|
|
if (-not $sig.TimeStamperCertificate) { throw "Signature carries no RFC 3161 timestamp" }
|
|
if ($sig.SignerCertificate.Subject -notmatch 'Such Software LLC') {
|
|
throw "Unexpected signer: $($sig.SignerCertificate.Subject)"
|
|
}
|
|
Write-Host "SHA256 $((Get-FileHash $exe -Algorithm SHA256).Hash)"
|
|
|
|
# ---- Package + upload ------------------------------------------------
|
|
- name: List built artifacts (for debug visibility)
|
|
run: |
|
|
ls -la build/windows/x64/runner/Release/
|
|
|
|
# ---- Package + upload as zip -----------------------------------------
|
|
# The Flutter Windows build produces a directory tree (~500 files —
|
|
# HashWallet.exe + plugin DLLs + data/ + locales/ + bundled monero_c
|
|
# + MinGW runtime DLLs). Ship as a zip; users extract and run.
|
|
#
|
|
# Tried wrapping with 7-Zip SFX for a single-file "portable" .exe
|
|
# earlier but path_provider_windows computes the wallet storage dir
|
|
# from the .exe location, and SFX extracts to a different %TEMP%
|
|
# subdir on every launch — wallet keys couldn't be found across
|
|
# runs. Multi-file zip is what Firefox Portable / VS Code Portable
|
|
# / every other Flutter Windows desktop app does too.
|
|
- name: Create Hash Bags Windows release zip
|
|
run: |
|
|
set -x -e
|
|
cd build/windows/x64/runner/Release
|
|
# PowerShell's Compress-Archive is in PATH; no extra deps needed.
|
|
powershell -NoProfile -Command "Compress-Archive -Path .\* -DestinationPath ..\..\..\..\..\HashBags-windows-${{ github.sha }}.zip -Force"
|
|
cd "$GITHUB_WORKSPACE"
|
|
ls -la HashBags-windows-*.zip
|
|
echo "Size: $(du -h HashBags-windows-${{ github.sha }}.zip | cut -f1)"
|
|
|
|
- name: Upload Hash Bags Windows zip
|
|
uses: actions/upload-artifact@v3
|
|
with:
|
|
name: hash-bags-windows-${{ github.sha }}
|
|
path: HashBags-windows-${{ github.sha }}.zip
|
|
retention-days: 14
|