From 3cebb89b9419d6c0961854901702e09ffc82c2f2 Mon Sep 17 00:00:00 2001 From: Kurt Landman Date: Tue, 28 Jul 2026 16:52:56 +0800 Subject: [PATCH 1/7] Prepare BTC09 Wallet v0.1.35 release --- appveyor.yml | 2 +- cmd/btc09/main.go | 2 +- cmd/btc09/main_test.go | 4 +- cmd/btc09/main_wallet.go | 2 +- docs/RELEASE-v0.1.35.md | 47 +++++ tools/desktop/native-smoke.mjs | 2 +- tools/mobile/run-ios-simulator.sh | 2 +- tools/mobile/verify-artifact.mjs | 18 +- tools/mobile/verify-artifact.test.mjs | 12 ++ tools/mobile/verify-release-outputs.mjs | 66 ++++++ tools/mobile/verify-release-outputs.test.mjs | 34 ++++ tools/release/build_android_signed_local.ps1 | 200 +++++++++++++++++++ tools/release/build_windows_appveyor.ps1 | 4 +- tools/release/package_windows_store.ps1 | 2 +- tools/release/test_direct_distribution.py | 57 +++++- tools/release/test_package_windows_store.py | 4 +- walletapp/package-lock.json | 4 +- walletapp/package.json | 4 +- walletapp/src-tauri/Cargo.lock | 2 +- walletapp/src-tauri/Cargo.toml | 2 +- walletapp/src-tauri/src/lib.rs | 2 +- walletapp/src-tauri/tauri.conf.json | 2 +- walletapp/src/index.html | 2 +- walletapp/src/mobile.html | 2 +- 24 files changed, 451 insertions(+), 27 deletions(-) create mode 100644 docs/RELEASE-v0.1.35.md create mode 100644 tools/mobile/verify-release-outputs.mjs create mode 100644 tools/mobile/verify-release-outputs.test.mjs create mode 100644 tools/release/build_android_signed_local.ps1 diff --git a/appveyor.yml b/appveyor.yml index 0b2a6b5..c70b1bc 100644 --- a/appveyor.yml +++ b/appveyor.yml @@ -1,4 +1,4 @@ -version: 0.1.34.{build} +version: 0.1.35.{build} image: - macos-sonoma diff --git a/cmd/btc09/main.go b/cmd/btc09/main.go index 417435b..82ea738 100644 --- a/cmd/btc09/main.go +++ b/cmd/btc09/main.go @@ -40,7 +40,7 @@ import ( ) // nodeVersion is the release version; bump alongside git tags. -const nodeVersion = "v0.1.34" +const nodeVersion = "v0.1.35" func defaultDataDir() string { home, _ := os.UserHomeDir() diff --git a/cmd/btc09/main_test.go b/cmd/btc09/main_test.go index 54b6491..aab588b 100644 --- a/cmd/btc09/main_test.go +++ b/cmd/btc09/main_test.go @@ -763,8 +763,8 @@ func TestReleaseNewer(t *testing.T) { } func TestNodeVersionMatchesV034Release(t *testing.T) { - if nodeVersion != "v0.1.34" { - t.Fatalf("nodeVersion = %q, want v0.1.34", nodeVersion) + if nodeVersion != "v0.1.35" { + t.Fatalf("nodeVersion = %q, want v0.1.35", nodeVersion) } } diff --git a/cmd/btc09/main_wallet.go b/cmd/btc09/main_wallet.go index cb1d768..6285e90 100644 --- a/cmd/btc09/main_wallet.go +++ b/cmd/btc09/main_wallet.go @@ -14,7 +14,7 @@ import ( ) // nodeVersion is the release version; bump alongside git tags. -const nodeVersion = "v0.1.34" +const nodeVersion = "v0.1.35" func main() { log.SetFlags(log.Ltime) diff --git a/docs/RELEASE-v0.1.35.md b/docs/RELEASE-v0.1.35.md new file mode 100644 index 0000000..115431e --- /dev/null +++ b/docs/RELEASE-v0.1.35.md @@ -0,0 +1,47 @@ +# Bitcoin 09 v0.1.35 + +v0.1.35 is an application and usability release. It does not change consensus, +proof of work, supply, rewards, addresses, wallet files, or the ASERT rules +activated at height 12,096. + +## Wallet + +- Rebuilt the Windows, macOS, Linux, Android, and iPhone wallet interface around + the same clear receive, send, activity, backup, and cleanup flow. +- Uses the full desktop window on computers instead of presenting a phone-sized + page inside it. +- Keeps the balance, network state, and next safe action visible. +- Shortens setup and payment copy while preserving recovery-word confirmation + and transaction review. +- Keeps private keys, recovery words, passwords, and signing on the device. + +## Desktop client + +- The full desktop client keeps its CPU miner and node tools. +- The wallet-only edition cannot start a miner and does not contain mining + commands. +- Native package checks now exercise the actual embedded desktop interface, + including onboarding, receive, send, activity, and the optional full-client + miner. + +## Upgrade notes + +- Existing wallet files and addresses continue to work. +- Back up the wallet file or recovery words before upgrading. +- This is not a mandatory network fork. Nodes running v0.1.34 remain + consensus-compatible, although v0.1.35 is recommended for the app fixes. +- Install only files attached to the official GitHub release and verify + `SHA256SUMS`. + +## Planned release files + +- `btc09-wallet-windows-x64-setup.exe` +- `btc09-wallet-macos-universal.zip` +- `btc09-wallet-linux-x64.AppImage` +- `btc09-wallet-android-arm64.apk` +- `btc09-windows-amd64.exe` +- `btc09-linux-amd64` +- `btc09-linux-arm64` +- `btc09-macos-apple.zip` +- `btc09-macos-intel.zip` +- `SHA256SUMS` diff --git a/tools/desktop/native-smoke.mjs b/tools/desktop/native-smoke.mjs index 494c57b..c899735 100644 --- a/tools/desktop/native-smoke.mjs +++ b/tools/desktop/native-smoke.mjs @@ -94,7 +94,7 @@ try { }); const launch = JSON.parse(await firstLine(backend.stdout, 15000)); assert.equal(launch.schema_version, 1); - assert.equal(launch.version, "v0.1.34"); + assert.equal(launch.version, "v0.1.35"); assert.match(launch.launch_url, /^http:\/\/127\.0\.0\.1:\d+\?token=[a-f0-9]{64}$/); browser = await chromium.launch({ headless: true }); diff --git a/tools/mobile/run-ios-simulator.sh b/tools/mobile/run-ios-simulator.sh index 3504574..e382f2f 100644 --- a/tools/mobile/run-ios-simulator.sh +++ b/tools/mobile/run-ios-simulator.sh @@ -50,7 +50,7 @@ case " $actual_archs " in *) echo "Simulator app is missing $expected_arch: $actual_archs" >&2; exit 1 ;; esac -test "$(plutil -extract CFBundleShortVersionString raw "$APP/Info.plist")" = "0.1.34" +test "$(plutil -extract CFBundleShortVersionString raw "$APP/Info.plist")" = "0.1.35" symbols="$APPLE_DIR/build/app-symbols.txt" nm -gU "$APP_BINARY" > "$symbols" grep -q ' _MobilewalletNewEngine$' "$symbols" diff --git a/tools/mobile/verify-artifact.mjs b/tools/mobile/verify-artifact.mjs index 53eb14c..c55738d 100644 --- a/tools/mobile/verify-artifact.mjs +++ b/tools/mobile/verify-artifact.mjs @@ -46,9 +46,25 @@ function jarCommand() { return "jar"; } +export function resolveAndroidSdkRoot(options = {}) { + const platform = options.platform || process.platform; + const env = options.env || process.env; + const directoryExists = options.directoryExists || existsSync; + const paths = platform === "win32" ? path.win32 : path; + const candidates = [ + env.ANDROID_HOME, + env.ANDROID_SDK_ROOT, + platform === "win32" && env.LOCALAPPDATA + ? paths.join(env.LOCALAPPDATA, "Android", "Sdk") + : undefined, + platform !== "win32" ? paths.join(os.homedir(), "Android", "Sdk") : undefined, + ].filter(Boolean); + return candidates.find((candidate) => directoryExists(candidate)); +} + function apksignerCommand() { const executable = process.platform === "win32" ? "apksigner.bat" : "apksigner"; - const sdkRoot = process.env.ANDROID_HOME || process.env.ANDROID_SDK_ROOT; + const sdkRoot = resolveAndroidSdkRoot(); if (!sdkRoot) return executable; const buildToolsRoot = path.join(sdkRoot, "build-tools"); if (!existsSync(buildToolsRoot)) return executable; diff --git a/tools/mobile/verify-artifact.test.mjs b/tools/mobile/verify-artifact.test.mjs index 33f65f0..2d62320 100644 --- a/tools/mobile/verify-artifact.test.mjs +++ b/tools/mobile/verify-artifact.test.mjs @@ -19,6 +19,18 @@ test("Windows runs the Android signer jar directly instead of spawning a batch f }); }); +test("Windows discovers the default Android SDK for release verification", () => { + const expected = "C:\\Users\\builder\\AppData\\Local\\Android\\Sdk"; + assert.equal( + artifactGate.resolveAndroidSdkRoot({ + platform: "win32", + env: { LOCALAPPDATA: "C:\\Users\\builder\\AppData\\Local" }, + directoryExists: (candidate) => candidate === expected, + }), + expected, + ); +}); + test("mobile artifact gate accepts ordinary packaged wallet files", () => { assert.doesNotThrow(() => assertMobileArtifactEntries([ { name: "assets/mobile.html", bytes: Buffer.from("BTC09 Wallet Receive Send") }, diff --git a/tools/mobile/verify-release-outputs.mjs b/tools/mobile/verify-release-outputs.mjs new file mode 100644 index 0000000..12b754a --- /dev/null +++ b/tools/mobile/verify-release-outputs.mjs @@ -0,0 +1,66 @@ +import { existsSync } from "node:fs"; +import path from "node:path"; +import { fileURLToPath, pathToFileURL } from "node:url"; +import { + verifyAndroidApkSignature, + verifyMobileArtifact, +} from "./verify-artifact.mjs"; + +const root = path.resolve(path.dirname(fileURLToPath(import.meta.url)), "..", ".."); + +export function selectReleaseApk(directory, fileExists = existsSync) { + const signed = path.join(directory, "app-universal-release.apk"); + const unsigned = path.join(directory, "app-universal-release-unsigned.apk"); + const existing = [signed, unsigned].filter((candidate) => fileExists(candidate)); + if (existing.length !== 1) { + throw new Error( + "Android verification requires exactly one fresh signed or unsigned release APK.", + ); + } + return { + path: existing[0], + signed: existing[0] === signed, + }; +} + +const apkDirectory = path.join( + root, + "walletapp", + "src-tauri", + "gen", + "android", + "app", + "build", + "outputs", + "apk", + "universal", + "release", +); +const aab = path.join( + root, + "walletapp", + "src-tauri", + "gen", + "android", + "app", + "build", + "outputs", + "bundle", + "universalRelease", + "app-universal-release.aab", +); + +const invokedPath = process.argv[1] ? pathToFileURL(path.resolve(process.argv[1])).href : ""; +if (import.meta.url === invokedPath) { + const selected = selectReleaseApk(apkDirectory); + verifyMobileArtifact(selected.path); + verifyMobileArtifact(aab); + process.stdout.write(`Verified Android release APK: ${selected.path}\n`); + process.stdout.write(`Verified Android release bundle: ${aab}\n`); + if (selected.signed) { + const fingerprint = verifyAndroidApkSignature(selected.path); + process.stdout.write(`Verified Android release signer: ${fingerprint}\n`); + } else { + process.stdout.write("Verified unsigned Android CI preflight boundary.\n"); + } +} diff --git a/tools/mobile/verify-release-outputs.test.mjs b/tools/mobile/verify-release-outputs.test.mjs new file mode 100644 index 0000000..f0f6f1a --- /dev/null +++ b/tools/mobile/verify-release-outputs.test.mjs @@ -0,0 +1,34 @@ +import assert from "node:assert/strict"; +import test from "node:test"; +import path from "node:path"; +import { selectReleaseApk } from "./verify-release-outputs.mjs"; + +test("Android release verification accepts one signed output", () => { + const directory = path.resolve("android-output"); + const signed = path.join(directory, "app-universal-release.apk"); + assert.deepEqual( + selectReleaseApk(directory, (candidate) => candidate === signed), + { path: signed, signed: true }, + ); +}); + +test("Android release verification accepts one unsigned CI output", () => { + const directory = path.resolve("android-output"); + const unsigned = path.join(directory, "app-universal-release-unsigned.apk"); + assert.deepEqual( + selectReleaseApk(directory, (candidate) => candidate === unsigned), + { path: unsigned, signed: false }, + ); +}); + +test("Android release verification rejects missing or stale ambiguous outputs", () => { + const directory = path.resolve("android-output"); + assert.throws( + () => selectReleaseApk(directory, () => false), + /exactly one fresh signed or unsigned/, + ); + assert.throws( + () => selectReleaseApk(directory, () => true), + /exactly one fresh signed or unsigned/, + ); +}); diff --git a/tools/release/build_android_signed_local.ps1 b/tools/release/build_android_signed_local.ps1 new file mode 100644 index 0000000..4d70cef --- /dev/null +++ b/tools/release/build_android_signed_local.ps1 @@ -0,0 +1,200 @@ +[CmdletBinding()] +param( + [Parameter(Mandatory = $true)] + [string]$VaultPath, + + [Parameter(Mandatory = $true)] + [string]$CertificateFingerprintPath, + + [string]$OutputDirectory = 'walletapp/src-tauri/target/direct' +) + +Set-StrictMode -Version Latest +$ErrorActionPreference = 'Stop' + +$repoRoot = [System.IO.Path]::GetFullPath((Join-Path $PSScriptRoot '..\..')) +$resolvedVault = [System.IO.Path]::GetFullPath($VaultPath) +$resolvedFingerprint = [System.IO.Path]::GetFullPath($CertificateFingerprintPath) +$resolvedOutput = [System.IO.Path]::GetFullPath((Join-Path $repoRoot $OutputDirectory)) +$originalAndroidHome = $env:ANDROID_HOME +$originalAndroidSDKRoot = $env:ANDROID_SDK_ROOT + +if (-not (Test-Path -LiteralPath $resolvedVault -PathType Leaf)) { + throw 'The encrypted Android signing vault was not found.' +} +if (-not (Test-Path -LiteralPath $resolvedFingerprint -PathType Leaf)) { + throw 'The pinned Android certificate fingerprint was not found.' +} + +$androidHome = $env:ANDROID_HOME +if ([string]::IsNullOrWhiteSpace($androidHome)) { + $androidHome = $env:ANDROID_SDK_ROOT +} +if ([string]::IsNullOrWhiteSpace($androidHome)) { + $androidHome = Join-Path $env:LOCALAPPDATA 'Android\Sdk' +} +$androidHome = [System.IO.Path]::GetFullPath($androidHome) +if (-not (Test-Path -LiteralPath $androidHome -PathType Container)) { + throw 'The Android SDK was not found.' +} +$env:ANDROID_HOME = $androidHome +$env:ANDROID_SDK_ROOT = $androidHome + +$temporaryRoot = [System.IO.Path]::GetFullPath([System.IO.Path]::GetTempPath()) +$temporaryPrefix = $temporaryRoot.TrimEnd( + [System.IO.Path]::DirectorySeparatorChar +) + [System.IO.Path]::DirectorySeparatorChar +$temporaryKey = [System.IO.Path]::GetFullPath( + (Join-Path $temporaryRoot ("btc09-android-{0}.jks" -f [Guid]::NewGuid().ToString('N'))) +) +if (-not $temporaryKey.StartsWith($temporaryPrefix, [StringComparison]::OrdinalIgnoreCase)) { + throw 'The temporary Android keystore escaped the operating-system temp directory.' +} + +$cipherText = Get-Content -LiteralPath $resolvedVault -Raw +$secureVault = ConvertTo-SecureString $cipherText +$vaultPointer = [Runtime.InteropServices.Marshal]::SecureStringToBSTR($secureVault) +$plainText = $null +$releaseIdentity = $null + +try { + $plainText = [Runtime.InteropServices.Marshal]::PtrToStringBSTR($vaultPointer) + $releaseIdentity = $plainText | ConvertFrom-Json + foreach ($field in @( + 'keystore_base64', + 'store_password', + 'alias', + 'key_password', + 'certificate_sha256' + )) { + if ([string]::IsNullOrWhiteSpace([string]$releaseIdentity.$field)) { + throw "The encrypted Android signing vault is missing $field." + } + } + + [System.IO.File]::WriteAllBytes( + $temporaryKey, + [Convert]::FromBase64String([string]$releaseIdentity.keystore_base64) + ) + + $env:BTC09_ANDROID_KEYSTORE = $temporaryKey + $env:BTC09_ANDROID_KEYSTORE_PASSWORD = [string]$releaseIdentity.store_password + $env:BTC09_ANDROID_KEY_ALIAS = [string]$releaseIdentity.alias + $env:BTC09_ANDROID_KEY_PASSWORD = [string]$releaseIdentity.key_password + + foreach ($relativeOutput in @( + 'walletapp\src-tauri\gen\android\app\build\outputs\apk\universal\release', + 'walletapp\src-tauri\gen\android\app\build\outputs\bundle\universalRelease' + )) { + $staleOutput = [System.IO.Path]::GetFullPath((Join-Path $repoRoot $relativeOutput)) + $repoPrefix = $repoRoot.TrimEnd( + [System.IO.Path]::DirectorySeparatorChar + ) + [System.IO.Path]::DirectorySeparatorChar + if (-not $staleOutput.StartsWith($repoPrefix, [StringComparison]::OrdinalIgnoreCase)) { + throw 'The Android output cleanup path escaped the repository.' + } + if (Test-Path -LiteralPath $staleOutput -PathType Container) { + Remove-Item -LiteralPath $staleOutput -Recurse -Force + } + } + + Push-Location $repoRoot + try { + & npm --prefix walletapp run mobile:android:build + if ($LASTEXITCODE -ne 0) { + throw 'The signed Android release build failed.' + } + } + finally { + Pop-Location + } + + $apk = Join-Path $repoRoot 'walletapp\src-tauri\gen\android\app\build\outputs\apk\universal\release\app-universal-release.apk' + $aab = Join-Path $repoRoot 'walletapp\src-tauri\gen\android\app\build\outputs\bundle\universalRelease\app-universal-release.aab' + foreach ($artifact in @($apk, $aab)) { + if (-not (Test-Path -LiteralPath $artifact -PathType Leaf)) { + throw "The Android release artifact was not produced: $artifact" + } + } + + $buildTools = Get-ChildItem -LiteralPath (Join-Path $env:ANDROID_HOME 'build-tools') -Directory | + Sort-Object { [version]$_.Name } -Descending | + Select-Object -First 1 + if ($null -eq $buildTools) { + throw 'Android build-tools are unavailable for certificate verification.' + } + $apksignerJar = Join-Path $buildTools.FullName 'lib\apksigner.jar' + if (-not (Test-Path -LiteralPath $apksignerJar -PathType Leaf)) { + throw 'The Android APK signer is unavailable for certificate verification.' + } + $javaExecutable = $null + if (-not [string]::IsNullOrWhiteSpace($env:JAVA_HOME)) { + $configuredJava = Join-Path $env:JAVA_HOME 'bin\java.exe' + if (Test-Path -LiteralPath $configuredJava -PathType Leaf) { + $javaExecutable = $configuredJava + } + } + if ($null -eq $javaExecutable) { + $javaCommand = Get-Command java -ErrorAction SilentlyContinue + if ($null -ne $javaCommand) { + $javaExecutable = $javaCommand.Source + } + } + if ([string]::IsNullOrWhiteSpace($javaExecutable)) { + throw 'Java is unavailable for Android certificate verification.' + } + + $signerOutput = & $javaExecutable -jar $apksignerJar ` + verify --verbose --print-certs $apk 2>&1 + if ($LASTEXITCODE -ne 0) { + throw 'The Android APK signature is invalid.' + } + $signerLine = $signerOutput | + Select-String 'Signer #1 certificate SHA-256 digest:' | + Select-Object -First 1 + if ($null -eq $signerLine) { + throw 'The Android APK certificate fingerprint was not reported.' + } + + $actualFingerprint = (($signerLine.Line -split ':', 2)[1] -replace '[^A-Fa-f0-9]', '').ToUpperInvariant() + $vaultFingerprint = ([string]$releaseIdentity.certificate_sha256 -replace '[^A-Fa-f0-9]', '').ToUpperInvariant() + $pinnedFingerprint = ( + (Get-Content -LiteralPath $resolvedFingerprint -Raw) -replace '[^A-Fa-f0-9]', '' + ).ToUpperInvariant() + if ($actualFingerprint -ne $vaultFingerprint -or $actualFingerprint -ne $pinnedFingerprint) { + throw 'The Android APK does not use the preserved BTC09 release certificate.' + } + + New-Item -ItemType Directory -Path $resolvedOutput -Force | Out-Null + $publishedApk = Join-Path $resolvedOutput 'btc09-wallet-android-arm64.apk' + $publishedAab = Join-Path $resolvedOutput 'btc09-wallet-android-arm64.aab' + Copy-Item -LiteralPath $apk -Destination $publishedApk -Force + Copy-Item -LiteralPath $aab -Destination $publishedAab -Force + + [pscustomobject]@{ + ApkPath = $publishedApk + ApkSize = (Get-Item -LiteralPath $publishedApk).Length + ApkSHA256 = (Get-FileHash -LiteralPath $publishedApk -Algorithm SHA256).Hash + AabPath = $publishedAab + AabSize = (Get-Item -LiteralPath $publishedAab).Length + AabSHA256 = (Get-FileHash -LiteralPath $publishedAab -Algorithm SHA256).Hash + CertificateMatch = $true + } +} +finally { + Remove-Item Env:BTC09_ANDROID_KEYSTORE -ErrorAction SilentlyContinue + Remove-Item Env:BTC09_ANDROID_KEYSTORE_PASSWORD -ErrorAction SilentlyContinue + Remove-Item Env:BTC09_ANDROID_KEY_ALIAS -ErrorAction SilentlyContinue + Remove-Item Env:BTC09_ANDROID_KEY_PASSWORD -ErrorAction SilentlyContinue + if (Test-Path -LiteralPath $temporaryKey -PathType Leaf) { + Remove-Item -LiteralPath $temporaryKey -Force + } + if ($null -ne $plainText) { + $plainText = $null + } + if ($null -ne $vaultPointer -and $vaultPointer -ne [IntPtr]::Zero) { + [Runtime.InteropServices.Marshal]::ZeroFreeBSTR($vaultPointer) + } + $env:ANDROID_HOME = $originalAndroidHome + $env:ANDROID_SDK_ROOT = $originalAndroidSDKRoot +} diff --git a/tools/release/build_windows_appveyor.ps1 b/tools/release/build_windows_appveyor.ps1 index 7b318a5..2cf7fd8 100644 --- a/tools/release/build_windows_appveyor.ps1 +++ b/tools/release/build_windows_appveyor.ps1 @@ -94,8 +94,8 @@ if (-not (Test-Path -LiteralPath $installer -PathType Leaf)) { } $item = Get-Item -LiteralPath $installer if ($item.VersionInfo.ProductName -ne 'BTC09 Wallet' -or - $item.VersionInfo.ProductVersion -ne '0.1.34' -or - $item.VersionInfo.FileVersion -ne '0.1.34') { + $item.VersionInfo.ProductVersion -ne '0.1.35' -or + $item.VersionInfo.FileVersion -ne '0.1.35') { throw 'The Windows installer metadata is not safe for the signing policy.' } $signature = Get-AuthenticodeSignature -LiteralPath $installer diff --git a/tools/release/package_windows_store.ps1 b/tools/release/package_windows_store.ps1 index 66d6a13..190412d 100644 --- a/tools/release/package_windows_store.ps1 +++ b/tools/release/package_windows_store.ps1 @@ -123,7 +123,7 @@ $patch = [int]$versionMatch.Groups[2].Value if ($minor -lt 1 -or $minor -gt 65535 -or $patch -gt 65535) { throw 'The wallet version does not fit the four-part Microsoft Store version format.' } -# Example: BTC09 Wallet 0.1.34 maps monotonically to Store version 1.34.0.0. +# Example: BTC09 Wallet 0.1.35 maps monotonically to Store version 1.35.0.0. $storeVersion = "$minor.$patch.0.0" $stageRoot = Join-Path $targetRoot 'store-msix\stage' diff --git a/tools/release/test_direct_distribution.py b/tools/release/test_direct_distribution.py index ebb9899..f49ffd9 100644 --- a/tools/release/test_direct_distribution.py +++ b/tools/release/test_direct_distribution.py @@ -88,6 +88,30 @@ def test_windows_direct_packager_is_wallet_only_and_fails_closed_on_signing(self self.assertNotIn("Start mining", script) + def test_local_android_signing_keeps_credentials_out_of_git_and_logs(self): + script_path = pathlib.Path("tools/release/build_android_signed_local.ps1") + self.assertTrue(script_path.exists(), "local Android release helper is missing") + script = script_path.read_text(encoding="utf-8") if script_path.exists() else "" + for token in ( + "ConvertTo-SecureString", + "SecureStringToBSTR", + "BTC09_ANDROID_KEYSTORE", + "mobile:android:build", + "Signer #1 certificate SHA-256 digest:", + "CertificateMatch", + "ZeroFreeBSTR", + ): + with self.subTest(token=token): + self.assertIn(token, script) + for forbidden in ( + "store_password =", + "key_password =", + "keystore_base64 =", + "Write-Host $plainText", + ): + with self.subTest(forbidden=forbidden): + self.assertNotIn(forbidden, script) + def test_homepage_links_the_released_native_wallet_beta(self): homepage = pathlib.Path("docs/index.html").read_text(encoding="utf-8") for token in ( @@ -124,17 +148,25 @@ def test_homepage_and_download_guide_disclose_the_signing_policy(self): self.assertIn(token, surface) def test_asert_activation_status_is_current_and_plain(self): - surfaces = { + release_surfaces = { "node": pathlib.Path("cmd/btc09/main.go").read_text(encoding="utf-8"), "wallet node": pathlib.Path("cmd/btc09/main_wallet.go").read_text(encoding="utf-8"), "tauri": pathlib.Path("walletapp/src-tauri/tauri.conf.json").read_text(encoding="utf-8"), "wallet package": pathlib.Path("walletapp/package.json").read_text(encoding="utf-8"), "appveyor": pathlib.Path("appveyor.yml").read_text(encoding="utf-8"), - "exchange integration": pathlib.Path("docs/EXCHANGE-INTEGRATION.md").read_text(encoding="utf-8"), } - for name, surface in surfaces.items(): + for name, surface in release_surfaces.items(): with self.subTest(surface=name): - self.assertIn("0.1.34", surface) + self.assertIn("0.1.35", surface) + + exchange_integration = pathlib.Path("docs/EXCHANGE-INTEGRATION.md").read_text( + encoding="utf-8" + ) + self.assertIn( + "0.1.34", + exchange_integration, + "exchange operators must keep the mandatory ASERT baseline", + ) homepage = pathlib.Path("docs/index.html").read_text(encoding="utf-8") whitepaper = pathlib.Path("WHITEPAPER.md").read_text(encoding="utf-8") @@ -174,6 +206,23 @@ def test_v0134_release_notes_cover_the_consensus_upgrade_and_files(self): with self.subTest(token=token): self.assertIn(token, notes) + def test_v0135_release_notes_cover_the_native_wallet_without_claiming_a_fork(self): + notes_path = pathlib.Path("docs/RELEASE-v0.1.35.md") + self.assertTrue(notes_path.exists(), "v0.1.35 release notes are missing") + notes = notes_path.read_text(encoding="utf-8") if notes_path.exists() else "" + for token in ( + "application and usability release", + "does not change consensus", + "Windows, macOS, Linux, Android, and iPhone", + "actual embedded desktop interface", + "wallet-only edition cannot start a miner", + "Existing wallet files and addresses continue to work", + "not a mandatory network fork", + "SHA256SUMS", + ): + with self.subTest(token=token): + self.assertIn(token, notes) + def test_current_social_copy_does_not_repeat_the_retired_difficulty_rule(self): posts = pathlib.Path("POSTS.md").read_text(encoding="utf-8") self.assertIn("per-block ASERT from height 12,096", posts) diff --git a/tools/release/test_package_windows_store.py b/tools/release/test_package_windows_store.py index 8d60e41..d6861ed 100644 --- a/tools/release/test_package_windows_store.py +++ b/tools/release/test_package_windows_store.py @@ -48,8 +48,8 @@ def test_packages_only_the_wallet_shell_core_and_store_artwork(self): def test_store_version_is_valid_and_derived_from_the_wallet_version(self): for token in ( "Cargo.toml", - "0.1.34", - "1.34.0.0", + "0.1.35", + "1.35.0.0", "Version must use 0.minor.patch", ): with self.subTest(token=token): diff --git a/walletapp/package-lock.json b/walletapp/package-lock.json index 8905236..6012143 100644 --- a/walletapp/package-lock.json +++ b/walletapp/package-lock.json @@ -1,12 +1,12 @@ { "name": "btc09-wallet", - "version": "0.1.34", + "version": "0.1.35", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "btc09-wallet", - "version": "0.1.34", + "version": "0.1.35", "devDependencies": { "@tauri-apps/cli": "2.11.4", "playwright": "1.61.1" diff --git a/walletapp/package.json b/walletapp/package.json index c3816f7..f2c1bd7 100644 --- a/walletapp/package.json +++ b/walletapp/package.json @@ -1,7 +1,7 @@ { "name": "btc09-wallet", "private": true, - "version": "0.1.34", + "version": "0.1.35", "description": "Native BTC09 wallet", "type": "module", "scripts": { @@ -15,7 +15,7 @@ "mobile:core:android": "node ../tools/mobile/prepare-core.mjs android", "mobile:core:ios": "node ../tools/mobile/prepare-core.mjs ios", "mobile:android:build": "tauri android build --apk --aab --target aarch64 --ci && npm run mobile:android:verify", - "mobile:android:verify": "node ../tools/mobile/verify-artifact.mjs src-tauri/gen/android/app/build/outputs/apk/universal/release/app-universal-release-unsigned.apk src-tauri/gen/android/app/build/outputs/bundle/universalRelease/app-universal-release.aab", + "mobile:android:verify": "node ../tools/mobile/verify-release-outputs.mjs", "mobile:ios:simulator": "bash ../tools/mobile/run-ios-simulator.sh" }, "devDependencies": { diff --git a/walletapp/src-tauri/Cargo.lock b/walletapp/src-tauri/Cargo.lock index 0d4d055..c708812 100644 --- a/walletapp/src-tauri/Cargo.lock +++ b/walletapp/src-tauri/Cargo.lock @@ -174,7 +174,7 @@ dependencies = [ [[package]] name = "btc09-wallet" -version = "0.1.34" +version = "0.1.35" dependencies = [ "serde", "serde_json", diff --git a/walletapp/src-tauri/Cargo.toml b/walletapp/src-tauri/Cargo.toml index 8570a31..33d8f98 100644 --- a/walletapp/src-tauri/Cargo.toml +++ b/walletapp/src-tauri/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "btc09-wallet" -version = "0.1.34" +version = "0.1.35" description = "Native BTC09 wallet" authors = ["Bitcoin 09 contributors"] edition = "2021" diff --git a/walletapp/src-tauri/src/lib.rs b/walletapp/src-tauri/src/lib.rs index ca665af..a291348 100644 --- a/walletapp/src-tauri/src/lib.rs +++ b/walletapp/src-tauri/src/lib.rs @@ -119,7 +119,7 @@ mod tests { }; use std::{path::Path, process::Command, time::Duration}; - const VERSION: &str = "v0.1.34"; + const VERSION: &str = "v0.1.35"; const TOKEN: &str = "0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef"; fn launch_json(version: &str, url: &str) -> String { diff --git a/walletapp/src-tauri/tauri.conf.json b/walletapp/src-tauri/tauri.conf.json index 434e66d..346da3e 100644 --- a/walletapp/src-tauri/tauri.conf.json +++ b/walletapp/src-tauri/tauri.conf.json @@ -1,7 +1,7 @@ { "$schema": "https://schema.tauri.app/config/2", "productName": "BTC09 Wallet", - "version": "0.1.34", + "version": "0.1.35", "identifier": "org.bitcoin09.wallet", "build": { "beforeBuildCommand": "node ../tools/desktop/prepare-sidecar.mjs", diff --git a/walletapp/src/index.html b/walletapp/src/index.html index 5c09543..0ef860d 100644 --- a/walletapp/src/index.html +++ b/walletapp/src/index.html @@ -25,7 +25,7 @@

Opening your wallet

diff --git a/walletapp/src/mobile.html b/walletapp/src/mobile.html index 5814b95..4333280 100644 --- a/walletapp/src/mobile.html +++ b/walletapp/src/mobile.html @@ -247,7 +247,7 @@

Settings

-
BTC09 WalletVersion 0.1.34 · Mainnet
+
BTC09 WalletVersion 0.1.35 · Mainnet
From 2c86bca52f8d1ecb55d1d87b35c5e60749e2d334 Mon Sep 17 00:00:00 2001 From: Kurt Landman Date: Tue, 28 Jul 2026 17:01:38 +0800 Subject: [PATCH 2/7] Harden release artifact preparation --- docs/RELEASE-v0.1.35.md | 2 +- tools/release/build_android_signed_local.ps1 | 26 ++++++++++++++------ 2 files changed, 20 insertions(+), 8 deletions(-) diff --git a/docs/RELEASE-v0.1.35.md b/docs/RELEASE-v0.1.35.md index 115431e..1a94e27 100644 --- a/docs/RELEASE-v0.1.35.md +++ b/docs/RELEASE-v0.1.35.md @@ -36,7 +36,7 @@ activated at height 12,096. ## Planned release files - `btc09-wallet-windows-x64-setup.exe` -- `btc09-wallet-macos-universal.zip` +- `btc09-wallet-macos-universal-preview.zip` - `btc09-wallet-linux-x64.AppImage` - `btc09-wallet-android-arm64.apk` - `btc09-windows-amd64.exe` diff --git a/tools/release/build_android_signed_local.ps1 b/tools/release/build_android_signed_local.ps1 index 4d70cef..6480478 100644 --- a/tools/release/build_android_signed_local.ps1 +++ b/tools/release/build_android_signed_local.ps1 @@ -117,16 +117,28 @@ try { } } - $buildTools = Get-ChildItem -LiteralPath (Join-Path $env:ANDROID_HOME 'build-tools') -Directory | - Sort-Object { [version]$_.Name } -Descending | + $apksignerCandidate = Get-ChildItem ` + -LiteralPath (Join-Path $env:ANDROID_HOME 'build-tools') ` + -Directory | + ForEach-Object { + $apksignerJar = Join-Path $_.FullName 'lib\apksigner.jar' + if (Test-Path -LiteralPath $apksignerJar -PathType Leaf) { + $versionMatch = [regex]::Match($_.Name, '^\d+(?:\.\d+){1,3}') + if ($versionMatch.Success) { + [pscustomobject]@{ + JarPath = $apksignerJar + Version = [version]$versionMatch.Value + IsStable = [regex]::IsMatch($_.Name, '^\d+(?:\.\d+){1,3}$') + } + } + } + } | + Sort-Object Version, IsStable -Descending | Select-Object -First 1 - if ($null -eq $buildTools) { - throw 'Android build-tools are unavailable for certificate verification.' - } - $apksignerJar = Join-Path $buildTools.FullName 'lib\apksigner.jar' - if (-not (Test-Path -LiteralPath $apksignerJar -PathType Leaf)) { + if ($null -eq $apksignerCandidate) { throw 'The Android APK signer is unavailable for certificate verification.' } + $apksignerJar = $apksignerCandidate.JarPath $javaExecutable = $null if (-not [string]::IsNullOrWhiteSpace($env:JAVA_HOME)) { $configuredJava = Join-Path $env:JAVA_HOME 'bin\java.exe' From a4f9bd173db2a33956ec480118d2a6b31059e199 Mon Sep 17 00:00:00 2001 From: Kurt Landman Date: Tue, 28 Jul 2026 17:07:14 +0800 Subject: [PATCH 3/7] Add independent Linux wallet release build --- appveyor.yml | 12 +++ tools/release/run_linux_appveyor.sh | 124 ++++++++++++++++++++++ tools/release/test_direct_distribution.py | 31 ++++++ 3 files changed, 167 insertions(+) create mode 100644 tools/release/run_linux_appveyor.sh diff --git a/appveyor.yml b/appveyor.yml index c70b1bc..148bb8f 100644 --- a/appveyor.yml +++ b/appveyor.yml @@ -3,6 +3,7 @@ version: 0.1.35.{build} image: - macos-sonoma - Visual Studio 2022 + - Ubuntu2404 skip_branch_with_pr: true @@ -35,3 +36,14 @@ for: - path: walletapp/src-tauri/target/direct/btc09-wallet-macos-universal-preview.zip name: btc09-wallet-macos-universal-preview.zip type: File + + - + matrix: + only: + - image: Ubuntu2404 + build_script: + - sh: bash tools/release/run_linux_appveyor.sh + artifacts: + - path: walletapp/src-tauri/target/direct/btc09-wallet-linux-x64.AppImage + name: btc09-wallet-linux-x64.AppImage + type: File diff --git a/tools/release/run_linux_appveyor.sh b/tools/release/run_linux_appveyor.sh new file mode 100644 index 0000000..c76a631 --- /dev/null +++ b/tools/release/run_linux_appveyor.sh @@ -0,0 +1,124 @@ +#!/usr/bin/env bash +set -euo pipefail + +repo_root="$(cd "$(dirname "${BASH_SOURCE[0]}")/../.." && pwd)" +cd "$repo_root" + +export CI=true +export GOTOOLCHAIN=auto + +sudo apt-get update +sudo apt-get install -y --no-install-recommends \ + build-essential \ + ca-certificates \ + curl \ + dbus-x11 \ + file \ + libasound2t64 \ + libayatana-appindicator3-dev \ + libgtk-3-0 \ + librsvg2-dev \ + libssl-dev \ + libwebkit2gtk-4.1-dev \ + libxdo-dev \ + patchelf \ + x11-utils \ + xvfb \ + xz-utils +sudo rm -rf /var/lib/apt/lists/* + +curl --fail --location --silent --show-error \ + https://go.dev/dl/go1.25.12.linux-amd64.tar.gz \ + --output /tmp/go.tgz +echo "234828b7a89e0e303d2556310ee549fbcf253d28de937bac3da13d6294262ac1 /tmp/go.tgz" | + sha256sum --check +sudo mkdir -p /opt/go1.25.12 +sudo tar -C /opt/go1.25.12 --strip-components=1 -xzf /tmp/go.tgz + +curl --fail --location --silent --show-error \ + https://nodejs.org/dist/v24.11.1/node-v24.11.1-linux-x64.tar.xz \ + --output /tmp/node.tar.xz +echo "60e3b0a8500819514aca603487c254298cd776de0698d3cd08f11dba5b8289a8 /tmp/node.tar.xz" | + sha256sum --check +sudo mkdir -p /opt/node24 +sudo tar -C /opt/node24 --strip-components=1 -xf /tmp/node.tar.xz + +export PATH="/opt/go1.25.12/bin:/opt/node24/bin:$HOME/.cargo/bin:$PATH" +rustup toolchain install 1.95.0 --profile minimal --no-self-update +rustup default 1.95.0 +rustup component add rustfmt + +go version +node --version +rustc --version + +npm ci --prefix walletapp +go vet ./... +go test ./... +cargo fmt --manifest-path walletapp/src-tauri/Cargo.toml -- --check +node tools/desktop/prepare-sidecar.mjs wallet +cargo test --manifest-path walletapp/src-tauri/Cargo.toml + +npm --prefix walletapp run store:build -- --bundles appimage +node tools/desktop/verify-wallet-edition.mjs \ + walletapp/src-tauri/target/release/btc09-core + +bundle_dir="walletapp/src-tauri/target/release/bundle/appimage" +source_appimage="$(find "$bundle_dir" -maxdepth 1 -type f -name '*.AppImage' -print -quit)" +test -n "$source_appimage" +test -s "$source_appimage" + +direct_dir="walletapp/src-tauri/target/direct" +release_appimage="$direct_dir/btc09-wallet-linux-x64.AppImage" +mkdir -p "$direct_dir" +cp "$source_appimage" "$release_appimage" +chmod +x "$release_appimage" +sha256sum "$release_appimage" + +runtime_dir="$(mktemp -d)" +chmod 700 "$runtime_dir" +xvfb_pid="" +wallet_pid="" +cleanup() { + if [[ -n "$wallet_pid" ]]; then + kill "$wallet_pid" 2>/dev/null || true + fi + if [[ -n "$xvfb_pid" ]]; then + kill "$xvfb_pid" 2>/dev/null || true + fi + rm -rf "$runtime_dir" +} +trap cleanup EXIT + +Xvfb :99 -screen 0 1280x800x24 >/tmp/btc09-xvfb.log 2>&1 & +xvfb_pid=$! +display_ready=0 +for _ in $(seq 1 30); do + if DISPLAY=:99 xdpyinfo >/dev/null 2>&1; then + display_ready=1 + break + fi + sleep 0.5 +done +test "$display_ready" = 1 + +DISPLAY=:99 XDG_RUNTIME_DIR="$runtime_dir" \ + dbus-run-session -- env APPIMAGE_EXTRACT_AND_RUN=1 "$release_appimage" \ + >/tmp/btc09-wallet.log 2>&1 & +wallet_pid=$! + +window_found=0 +for _ in $(seq 1 40); do + if ! kill -0 "$wallet_pid" 2>/dev/null; then + cat /tmp/btc09-wallet.log >&2 + exit 1 + fi + if DISPLAY=:99 xwininfo -root -tree >/tmp/btc09-windows.txt 2>&1 && + grep -F "BTC09 Wallet" /tmp/btc09-windows.txt; then + window_found=1 + break + fi + sleep 0.5 +done +test "$window_found" = 1 +echo "Verified clean Linux launch: BTC09 Wallet" diff --git a/tools/release/test_direct_distribution.py b/tools/release/test_direct_distribution.py index f49ffd9..5522f51 100644 --- a/tools/release/test_direct_distribution.py +++ b/tools/release/test_direct_distribution.py @@ -307,6 +307,37 @@ def test_appveyor_verifies_macos_and_iphone_and_keeps_macos_preview(self): "the unproven iPhone gate should fail before the expensive macOS release build", ) + def test_appveyor_builds_and_launch_checks_the_linux_wallet(self): + pipeline = pathlib.Path("appveyor.yml").read_text(encoding="utf-8") + runner = pathlib.Path("tools/release/run_linux_appveyor.sh").read_text( + encoding="utf-8" + ) + + for token in ( + "Ubuntu2404", + "bash tools/release/run_linux_appveyor.sh", + "btc09-wallet-linux-x64.AppImage", + ): + with self.subTest(pipeline_token=token): + self.assertIn(token, pipeline) + + for token in ( + "go1.25.12.linux-amd64.tar.gz", + "node-v24.11.1-linux-x64.tar.xz", + "rustup toolchain install 1.95.0", + "go vet ./...", + "go test ./...", + "cargo fmt --manifest-path walletapp/src-tauri/Cargo.toml -- --check", + "node tools/desktop/prepare-sidecar.mjs wallet", + "cargo test --manifest-path walletapp/src-tauri/Cargo.toml", + "npm --prefix walletapp run store:build -- --bundles appimage", + "node tools/desktop/verify-wallet-edition.mjs", + "APPIMAGE_EXTRACT_AND_RUN=1", + 'grep -F "BTC09 Wallet"', + ): + with self.subTest(runner_token=token): + self.assertIn(token, runner) + def test_appveyor_reuses_preinstalled_rustup_before_bootstrap(self): script = pathlib.Path( "tools/release/install_appveyor_toolchain.ps1" From ccea5ac34701a16a3d99537733620673b414e910 Mon Sep 17 00:00:00 2001 From: Kurt Landman Date: Tue, 28 Jul 2026 18:45:18 +0800 Subject: [PATCH 4/7] Bootstrap Rust on Linux release workers --- appveyor.yml | 2 +- tools/release/run_linux_appveyor.sh | 9 +++++++++ tools/release/test_direct_distribution.py | 2 ++ 3 files changed, 12 insertions(+), 1 deletion(-) diff --git a/appveyor.yml b/appveyor.yml index 148bb8f..2cb3932 100644 --- a/appveyor.yml +++ b/appveyor.yml @@ -1,9 +1,9 @@ version: 0.1.35.{build} image: + - Ubuntu2404 - macos-sonoma - Visual Studio 2022 - - Ubuntu2404 skip_branch_with_pr: true diff --git a/tools/release/run_linux_appveyor.sh b/tools/release/run_linux_appveyor.sh index c76a631..99c5db1 100644 --- a/tools/release/run_linux_appveyor.sh +++ b/tools/release/run_linux_appveyor.sh @@ -44,6 +44,15 @@ sudo mkdir -p /opt/node24 sudo tar -C /opt/node24 --strip-components=1 -xf /tmp/node.tar.xz export PATH="/opt/go1.25.12/bin:/opt/node24/bin:$HOME/.cargo/bin:$PATH" +if ! command -v rustup >/dev/null 2>&1; then + curl --proto '=https' --tlsv1.2 --silent --show-error --fail \ + https://sh.rustup.rs \ + --output /tmp/rustup-init.sh + sh /tmp/rustup-init.sh \ + -y \ + --profile minimal \ + --default-toolchain none +fi rustup toolchain install 1.95.0 --profile minimal --no-self-update rustup default 1.95.0 rustup component add rustfmt diff --git a/tools/release/test_direct_distribution.py b/tools/release/test_direct_distribution.py index 5522f51..087c975 100644 --- a/tools/release/test_direct_distribution.py +++ b/tools/release/test_direct_distribution.py @@ -324,6 +324,8 @@ def test_appveyor_builds_and_launch_checks_the_linux_wallet(self): for token in ( "go1.25.12.linux-amd64.tar.gz", "node-v24.11.1-linux-x64.tar.xz", + "https://sh.rustup.rs", + "--default-toolchain none", "rustup toolchain install 1.95.0", "go vet ./...", "go test ./...", From e6aade83825ae8c2239222222ff6f7e92eda6651 Mon Sep 17 00:00:00 2001 From: Kurt Landman Date: Tue, 28 Jul 2026 18:50:42 +0800 Subject: [PATCH 5/7] Pin Linux release GOROOT --- tools/release/run_linux_appveyor.sh | 6 ++++-- tools/release/test_direct_distribution.py | 3 +++ 2 files changed, 7 insertions(+), 2 deletions(-) diff --git a/tools/release/run_linux_appveyor.sh b/tools/release/run_linux_appveyor.sh index 99c5db1..2567994 100644 --- a/tools/release/run_linux_appveyor.sh +++ b/tools/release/run_linux_appveyor.sh @@ -5,7 +5,6 @@ repo_root="$(cd "$(dirname "${BASH_SOURCE[0]}")/../.." && pwd)" cd "$repo_root" export CI=true -export GOTOOLCHAIN=auto sudo apt-get update sudo apt-get install -y --no-install-recommends \ @@ -43,7 +42,9 @@ echo "60e3b0a8500819514aca603487c254298cd776de0698d3cd08f11dba5b8289a8 /tmp/nod sudo mkdir -p /opt/node24 sudo tar -C /opt/node24 --strip-components=1 -xf /tmp/node.tar.xz -export PATH="/opt/go1.25.12/bin:/opt/node24/bin:$HOME/.cargo/bin:$PATH" +export GOROOT="/opt/go1.25.12" +export GOTOOLCHAIN=local +export PATH="$GOROOT/bin:/opt/node24/bin:$HOME/.cargo/bin:$PATH" if ! command -v rustup >/dev/null 2>&1; then curl --proto '=https' --tlsv1.2 --silent --show-error --fail \ https://sh.rustup.rs \ @@ -58,6 +59,7 @@ rustup default 1.95.0 rustup component add rustfmt go version +test "$(go env GOROOT)" = "$GOROOT" node --version rustc --version diff --git a/tools/release/test_direct_distribution.py b/tools/release/test_direct_distribution.py index 087c975..4abb13e 100644 --- a/tools/release/test_direct_distribution.py +++ b/tools/release/test_direct_distribution.py @@ -326,6 +326,9 @@ def test_appveyor_builds_and_launch_checks_the_linux_wallet(self): "node-v24.11.1-linux-x64.tar.xz", "https://sh.rustup.rs", "--default-toolchain none", + 'export GOROOT="/opt/go1.25.12"', + "export GOTOOLCHAIN=local", + 'test "$(go env GOROOT)" = "$GOROOT"', "rustup toolchain install 1.95.0", "go vet ./...", "go test ./...", From f783ecbf443c1fbada7b3c02034deb841ea7d61b Mon Sep 17 00:00:00 2001 From: Kurt Landman Date: Tue, 28 Jul 2026 19:18:39 +0800 Subject: [PATCH 6/7] Strip Linux release build paths --- tools/release/run_linux_appveyor.sh | 14 ++++++++++++++ tools/release/test_direct_distribution.py | 4 ++++ 2 files changed, 18 insertions(+) diff --git a/tools/release/run_linux_appveyor.sh b/tools/release/run_linux_appveyor.sh index 2567994..a2f9840 100644 --- a/tools/release/run_linux_appveyor.sh +++ b/tools/release/run_linux_appveyor.sh @@ -45,6 +45,7 @@ sudo tar -C /opt/node24 --strip-components=1 -xf /tmp/node.tar.xz export GOROOT="/opt/go1.25.12" export GOTOOLCHAIN=local export PATH="$GOROOT/bin:/opt/node24/bin:$HOME/.cargo/bin:$PATH" +export RUSTFLAGS="--remap-path-prefix=$HOME=/home/build" if ! command -v rustup >/dev/null 2>&1; then curl --proto '=https' --tlsv1.2 --silent --show-error --fail \ https://sh.rustup.rs \ @@ -86,6 +87,19 @@ cp "$source_appimage" "$release_appimage" chmod +x "$release_appimage" sha256sum "$release_appimage" +extract_dir="$(mktemp -d)" +( + cd "$extract_dir" + "$release_appimage" --appimage-extract >/dev/null +) +if grep -R -a -F -l "$HOME" "$extract_dir/squashfs-root" >/tmp/btc09-local-paths.txt 2>/dev/null; then + echo "Linux AppImage contains local build paths:" >&2 + cat /tmp/btc09-local-paths.txt >&2 + exit 1 +fi +rm -rf "$extract_dir" +echo "Verified Linux AppImage contains no local build paths" + runtime_dir="$(mktemp -d)" chmod 700 "$runtime_dir" xvfb_pid="" diff --git a/tools/release/test_direct_distribution.py b/tools/release/test_direct_distribution.py index 4abb13e..d8e0426 100644 --- a/tools/release/test_direct_distribution.py +++ b/tools/release/test_direct_distribution.py @@ -328,6 +328,7 @@ def test_appveyor_builds_and_launch_checks_the_linux_wallet(self): "--default-toolchain none", 'export GOROOT="/opt/go1.25.12"', "export GOTOOLCHAIN=local", + 'export RUSTFLAGS="--remap-path-prefix=$HOME=/home/build"', 'test "$(go env GOROOT)" = "$GOROOT"', "rustup toolchain install 1.95.0", "go vet ./...", @@ -337,6 +338,9 @@ def test_appveyor_builds_and_launch_checks_the_linux_wallet(self): "cargo test --manifest-path walletapp/src-tauri/Cargo.toml", "npm --prefix walletapp run store:build -- --bundles appimage", "node tools/desktop/verify-wallet-edition.mjs", + '"$release_appimage" --appimage-extract', + 'grep -R -a -F -l "$HOME"', + "Verified Linux AppImage contains no local build paths", "APPIMAGE_EXTRACT_AND_RUN=1", 'grep -F "BTC09 Wallet"', ): From 04e676852eb8aa32229e968c0c552dcc368ebe42 Mon Sep 17 00:00:00 2001 From: Kurt Landman Date: Tue, 28 Jul 2026 19:41:19 +0800 Subject: [PATCH 7/7] Fix Linux artifact extraction path --- tools/release/run_linux_appveyor.sh | 2 +- tools/release/test_direct_distribution.py | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/tools/release/run_linux_appveyor.sh b/tools/release/run_linux_appveyor.sh index a2f9840..06ff596 100644 --- a/tools/release/run_linux_appveyor.sh +++ b/tools/release/run_linux_appveyor.sh @@ -81,7 +81,7 @@ test -n "$source_appimage" test -s "$source_appimage" direct_dir="walletapp/src-tauri/target/direct" -release_appimage="$direct_dir/btc09-wallet-linux-x64.AppImage" +release_appimage="$repo_root/$direct_dir/btc09-wallet-linux-x64.AppImage" mkdir -p "$direct_dir" cp "$source_appimage" "$release_appimage" chmod +x "$release_appimage" diff --git a/tools/release/test_direct_distribution.py b/tools/release/test_direct_distribution.py index d8e0426..5bfaf86 100644 --- a/tools/release/test_direct_distribution.py +++ b/tools/release/test_direct_distribution.py @@ -338,6 +338,7 @@ def test_appveyor_builds_and_launch_checks_the_linux_wallet(self): "cargo test --manifest-path walletapp/src-tauri/Cargo.toml", "npm --prefix walletapp run store:build -- --bundles appimage", "node tools/desktop/verify-wallet-edition.mjs", + 'release_appimage="$repo_root/$direct_dir/btc09-wallet-linux-x64.AppImage"', '"$release_appimage" --appimage-extract', 'grep -R -a -F -l "$HOME"', "Verified Linux AppImage contains no local build paths",