b9d06d5f77
Replace undefined signed shifts in SPHlib SIMD FFT arithmetic with bounded multiplications, handle empty vectors in base64/base32/base58/hash/script paths, and skip the DoS_checkSig microbenchmark threshold under sanitizer instrumentation. Sanitizer ctest is now green locally, so make the GitHub sanitizer job blocking again.
891 lines
37 KiB
YAML
891 lines
37 KiB
YAML
name: Build All Platforms
|
|
|
|
on:
|
|
push:
|
|
branches: [master, cpp20-modernization]
|
|
tags: ['v*']
|
|
pull_request:
|
|
branches: [master]
|
|
workflow_dispatch:
|
|
|
|
jobs:
|
|
test-linux-unit:
|
|
runs-on: ubuntu-22.04
|
|
continue-on-error: true
|
|
steps:
|
|
- uses: actions/checkout@v4
|
|
with:
|
|
submodules: recursive
|
|
|
|
- name: Install dependencies
|
|
run: |
|
|
sudo apt-get update
|
|
sudo apt-get install -y build-essential cmake ninja-build \
|
|
libboost-all-dev libssl-dev libdb++-dev libleveldb-dev \
|
|
libevent-dev libminiupnpc-dev zlib1g-dev \
|
|
libsnappy-dev liblz4-dev libzstd-dev
|
|
|
|
- name: Build RocksDB from source
|
|
# Ubuntu 22.04's librocksdb-dev is 6.11.4 which CMakeLists.txt now
|
|
# refuses to configure against (need >= 7.4 for XXH3 per-block
|
|
# checksum). Build 8.9.1 from source — same version DNS2 ships —
|
|
# into /usr/local so CMake's find_library picks it up first.
|
|
run: sudo bash scripts/ci/build-rocksdb.sh
|
|
|
|
- name: Configure
|
|
run: |
|
|
cmake -B build -G Ninja \
|
|
-DCMAKE_BUILD_TYPE=Release \
|
|
-DBUILD_QT=OFF \
|
|
-DBUILD_DAEMON=ON \
|
|
-DBUILD_TESTS=ON \
|
|
-DUSE_UPNP=OFF
|
|
|
|
- name: Build libtor (embedded Tor static lib)
|
|
# USE_TOR_EMBEDDED defaults to ON and the daemon/Qt GUI both
|
|
# link -ltor. The Tor source is a git submodule but libtor.a
|
|
# is NOT built by cmake. build-libtor.sh defaults to /mingw64
|
|
# paths which don't exist on the ubuntu-22.04 runner; pass
|
|
# /usr where libevent-dev/libssl-dev/zlib1g-dev install.
|
|
run: |
|
|
sudo apt-get install -y libevent-dev libssl-dev zlib1g-dev
|
|
LIBEVENT_DIR=/usr OPENSSL_DIR=/usr ZLIB_DIR=/usr \
|
|
bash src/tor/build-libtor.sh
|
|
|
|
# CI Layer 2: v3 onion address validation (defense-in-depth against
|
|
# the btb6/gtb6 corruption class — see references/onion-corruption-ci-defense.md).
|
|
# Validates: (a) src/onionseed.h hardcoded seeds, (b) contrib/triangles.conf.example
|
|
# operator-facing example. Runs in --ci mode → exits 1 on any failure,
|
|
# which fails the job and blocks the build.
|
|
- name: Validate .onion addresses (CI gate)
|
|
run: |
|
|
python3 scripts/validate_onion_seeds.py \
|
|
--ci \
|
|
--against src/onionseed.h \
|
|
src/onionseed.h \
|
|
contrib/triangles.conf.example
|
|
|
|
# CI Layer 3: chaindb equivalence test (the "carry every single thing over"
|
|
# guarantee — see references/leveldb-to-rocksdb-migration.md Phase A).
|
|
# Loads a fixture txleveldb/, runs MaybeMigrateLevelDbToRocksDb(true),
|
|
# then re-reads every record from RocksDB and asserts byte-equality.
|
|
# This is the proof that no data is lost in the LevelDB→RocksDB migration.
|
|
- name: Build
|
|
run: cmake --build build -j$(nproc)
|
|
|
|
- name: Run chaindb equivalence test
|
|
# chaindb_equivalence_tests is a SEPARATE binary (test_chaindb_equivalence),
|
|
# not a suite inside test_triangles. Run the right binary.
|
|
run: |
|
|
if [ -x build/bin/test_chaindb_equivalence ]; then
|
|
./build/bin/test_chaindb_equivalence --log_level=test_suite
|
|
else
|
|
echo "test_chaindb_equivalence not built — skipping chaindb equivalence"
|
|
exit 0
|
|
fi
|
|
|
|
- name: Run unit tests
|
|
run: cd build && ctest --output-on-failure || true
|
|
|
|
test-linux-sanitizers:
|
|
# ASan + UBSan build of the daemon + unit tests. This is a blocking
|
|
# signal: sanitizer regressions should fail the PR.
|
|
runs-on: ubuntu-22.04
|
|
env:
|
|
# ASan: leak detection off by default (BDB and OpenSSL produce noise on shutdown).
|
|
# Re-enable once we've quieted the legitimate suspects.
|
|
ASAN_OPTIONS: "detect_leaks=0:halt_on_error=1:abort_on_error=1:print_stacktrace=1:strict_string_checks=1:detect_stack_use_after_return=1"
|
|
# UBSan: print full stack traces on first error and exit non-zero.
|
|
UBSAN_OPTIONS: "halt_on_error=1:abort_on_error=1:print_stacktrace=1"
|
|
# Suppress UB categories that are pervasive in the Hash9 C cascade
|
|
# and BDB until they're fixed file-by-file.
|
|
SAN_FLAGS: "-fsanitize=address,undefined -fno-omit-frame-pointer -fno-sanitize-recover=undefined -fno-sanitize=alignment,signed-integer-overflow,vptr"
|
|
steps:
|
|
- uses: actions/checkout@v4
|
|
with:
|
|
submodules: recursive
|
|
|
|
- name: Install dependencies
|
|
run: |
|
|
sudo apt-get update
|
|
sudo apt-get install -y build-essential cmake ninja-build \
|
|
libboost-all-dev libssl-dev libdb++-dev libleveldb-dev \
|
|
libevent-dev libminiupnpc-dev zlib1g-dev \
|
|
libsnappy-dev liblz4-dev libzstd-dev
|
|
|
|
- name: Build RocksDB from source
|
|
run: sudo bash scripts/ci/build-rocksdb.sh
|
|
|
|
- name: Configure with sanitizers
|
|
run: |
|
|
cmake -B build-san -G Ninja \
|
|
-DCMAKE_BUILD_TYPE=Debug \
|
|
-DCMAKE_C_FLAGS="$SAN_FLAGS" \
|
|
-DCMAKE_CXX_FLAGS="$SAN_FLAGS" \
|
|
-DCMAKE_EXE_LINKER_FLAGS="$SAN_FLAGS" \
|
|
-DCMAKE_SHARED_LINKER_FLAGS="$SAN_FLAGS" \
|
|
-DBUILD_QT=OFF \
|
|
-DBUILD_DAEMON=ON \
|
|
-DBUILD_TESTS=ON \
|
|
-DUSE_UPNP=OFF
|
|
|
|
- name: Build libtor (embedded Tor static lib)
|
|
# USE_TOR_EMBEDDED defaults to ON and the daemon/Qt GUI both
|
|
# link -ltor. The Tor source is a git submodule but libtor.a
|
|
# is NOT built by cmake. build-libtor.sh defaults to /mingw64
|
|
# paths which don't exist on the ubuntu-22.04 runner; pass
|
|
# /usr where libevent-dev/libssl-dev/zlib1g-dev install.
|
|
run: |
|
|
sudo apt-get install -y libevent-dev libssl-dev zlib1g-dev
|
|
LIBEVENT_DIR=/usr OPENSSL_DIR=/usr ZLIB_DIR=/usr \
|
|
bash src/tor/build-libtor.sh
|
|
|
|
- name: Build
|
|
run: cmake --build build-san -j$(nproc)
|
|
|
|
- name: Run unit tests under sanitizers
|
|
run: cd build-san && ctest --output-on-failure
|
|
|
|
build-windows-qt:
|
|
runs-on: windows-latest
|
|
defaults:
|
|
run:
|
|
shell: msys2 {0}
|
|
steps:
|
|
- uses: actions/checkout@v4
|
|
with:
|
|
submodules: recursive
|
|
|
|
- uses: msys2/setup-msys2@v2
|
|
with:
|
|
msystem: MINGW64
|
|
update: true
|
|
install: >-
|
|
mingw-w64-x86_64-gcc
|
|
mingw-w64-x86_64-cmake
|
|
mingw-w64-x86_64-ninja
|
|
mingw-w64-x86_64-qt5-base
|
|
mingw-w64-x86_64-qt5-tools
|
|
mingw-w64-x86_64-boost
|
|
mingw-w64-x86_64-openssl
|
|
mingw-w64-x86_64-db
|
|
mingw-w64-x86_64-libevent
|
|
mingw-w64-x86_64-miniupnpc
|
|
mingw-w64-x86_64-zlib
|
|
mingw-w64-x86_64-rocksdb
|
|
mingw-w64-x86_64-sqlite3
|
|
mingw-w64-x86_64-autotools
|
|
|
|
- name: Set VERSION
|
|
run: |
|
|
if [[ "${GITHUB_REF}" == refs/tags/v* ]]; then
|
|
echo "VERSION=${GITHUB_REF_NAME#v}" >> $GITHUB_ENV
|
|
else
|
|
MAJOR=$(grep 'CLIENT_VERSION_MAJOR' src/clientversion.h | tr -d '\r' | awk '{print $3}')
|
|
MINOR=$(grep 'CLIENT_VERSION_MINOR' src/clientversion.h | tr -d '\r' | awk '{print $3}')
|
|
REV=$(grep 'CLIENT_VERSION_REVISION' src/clientversion.h | tr -d '\r' | awk '{print $3}')
|
|
echo "VERSION=${MAJOR}.${MINOR}.${REV}" >> $GITHUB_ENV
|
|
fi
|
|
|
|
- name: Configure
|
|
run: |
|
|
cmake -B build -G Ninja \
|
|
-DCMAKE_BUILD_TYPE=Release \
|
|
-DBUILD_QT=ON \
|
|
-DBUILD_DAEMON=OFF \
|
|
-DBUILD_TESTS=OFF \
|
|
-DUSE_UPNP=ON \
|
|
-DUSE_QRCODE=OFF \
|
|
-DUSE_I2P_EMBEDDED=ON
|
|
|
|
- name: Build libtor (embedded Tor static lib)
|
|
# Windows Qt GUI also transitively links -ltor via triangles_common.
|
|
# msys2 default install puts everything in /mingw64.
|
|
run: bash src/tor/build-libtor.sh
|
|
|
|
- name: Build libi2pd (embedded I2P static lib)
|
|
run: bash src/i2p/build-libi2pd.sh
|
|
|
|
- name: Build
|
|
run: cmake --build build -j$(nproc)
|
|
|
|
- name: Package
|
|
run: |
|
|
mkdir -p dist
|
|
cp build/bin/triangles-qt.exe dist/
|
|
windeployqt dist/triangles-qt.exe || true
|
|
|
|
# Copy ALL runtime DLLs the binary needs
|
|
# MinGW runtime
|
|
for dll in libgcc_s_seh-1.dll libstdc++-6.dll libwinpthread-1.dll; do
|
|
cp /mingw64/bin/$dll dist/ 2>/dev/null || true
|
|
done
|
|
# Boost
|
|
for dll in /mingw64/bin/libboost_system*.dll /mingw64/bin/libboost_filesystem*.dll \
|
|
/mingw64/bin/libboost_thread*.dll /mingw64/bin/libboost_program_options*.dll \
|
|
/mingw64/bin/libboost_chrono*.dll; do
|
|
cp $dll dist/ 2>/dev/null || true
|
|
done
|
|
# OpenSSL
|
|
for dll in /mingw64/bin/libssl*.dll /mingw64/bin/libcrypto*.dll; do
|
|
cp $dll dist/ 2>/dev/null || true
|
|
done
|
|
# BerkeleyDB, libevent, miniupnpc, zlib
|
|
for dll in /mingw64/bin/libdb*.dll /mingw64/bin/libevent*.dll \
|
|
/mingw64/bin/libminiupnpc*.dll /mingw64/bin/zlib1.dll; do
|
|
cp $dll dist/ 2>/dev/null || true
|
|
done
|
|
|
|
# Catch anything we missed: scan ldd output for /mingw64 deps
|
|
ldd dist/triangles-qt.exe | grep '/mingw64' | awk '{print $3}' | while read dll; do
|
|
cp "$dll" dist/ 2>/dev/null || true
|
|
done
|
|
|
|
# Write qt.conf so the exe finds plugins relative to itself
|
|
printf '[Paths]\nPlugins = .\n' > dist/qt.conf
|
|
|
|
# Ensure Qt platform plugins are present (windeployqt sometimes misses them in MSYS2)
|
|
if [ ! -f dist/platforms/qwindows.dll ]; then
|
|
echo "WARNING: windeployqt did not copy platform plugins, copying manually..."
|
|
mkdir -p dist/platforms
|
|
cp /mingw64/share/qt5/plugins/platforms/qwindows.dll dist/platforms/ 2>/dev/null || \
|
|
cp /mingw64/lib/qt5/plugins/platforms/qwindows.dll dist/platforms/ 2>/dev/null || \
|
|
find /mingw64 -name 'qwindows.dll' -exec cp {} dist/platforms/ \; 2>/dev/null
|
|
fi
|
|
|
|
# Also copy styles and imageformats for good measure
|
|
for plugdir in styles imageformats; do
|
|
if [ ! -d "dist/$plugdir" ]; then
|
|
srcdir=$(find /mingw64 -type d -name "$plugdir" -path "*/plugins/*" 2>/dev/null | head -1)
|
|
if [ -n "$srcdir" ]; then
|
|
cp -r "$srcdir" dist/
|
|
fi
|
|
fi
|
|
done
|
|
|
|
strip --strip-all dist/triangles-qt.exe
|
|
echo "=== dist/ contents ==="
|
|
find dist/ -type f | head -50
|
|
|
|
- name: Upload portable wallet zip
|
|
# Portable Windows GUI wallet ZIP — what users extract to a folder
|
|
# and run triangles-qt.exe directly. This is what the Chocolatey
|
|
# package and most manual downloads expect.
|
|
shell: powershell
|
|
run: |
|
|
Compress-Archive -Path dist/* -DestinationPath "Cryptographic-Triangles-${env:VERSION}-win-x64.zip" -Force
|
|
echo "Created Cryptographic-Triangles-${env:VERSION}-win-x64.zip"
|
|
Get-Item "Cryptographic-Triangles-${env:VERSION}-win-x64.zip"
|
|
|
|
- name: Upload artifact (portable zip)
|
|
uses: actions/upload-artifact@v4
|
|
with:
|
|
name: windows-qt-zip
|
|
path: Cryptographic-Triangles-*-win-x64.zip
|
|
|
|
- name: Download Tor
|
|
# Resilient download: archive.torproject.org occasionally times out
|
|
# from CI egress (observed 2026-07-03: macOS job exit code 6 after
|
|
# exactly 30s of curl hang). Retries cover transient connection drops;
|
|
# size check rejects 0-byte "200 OK" responses from broken mirrors.
|
|
# NOTE: Invoke-WebRequest on PowerShell 5.1 (default on Windows-latest
|
|
# runners) does NOT accept -ConnectionTimeout/-OperationTimeout — those
|
|
# are PowerShell 7+. We rely on the retry loop + size check only.
|
|
shell: powershell
|
|
run: |
|
|
$TOR_VERSION = "15.0.9"
|
|
$TOR_URL = "https://archive.torproject.org/tor-package-archive/torbrowser/${TOR_VERSION}/tor-expert-bundle-windows-x86_64-${TOR_VERSION}.tar.gz"
|
|
$torPath = "tor-bundle.tar.gz"
|
|
$attempts = 0
|
|
$maxAttempts = 3
|
|
$downloaded = $false
|
|
while ($attempts -lt $maxAttempts -and -not $downloaded) {
|
|
$attempts++
|
|
try {
|
|
if (Test-Path $torPath) { Remove-Item $torPath -ErrorAction SilentlyContinue }
|
|
Invoke-WebRequest -Uri $TOR_URL -OutFile $torPath -UseBasicParsing
|
|
$size = (Get-Item $torPath).Length
|
|
if ($size -gt 1MB) {
|
|
Write-Host "Downloaded $size bytes on attempt $attempts"
|
|
$downloaded = $true
|
|
} else {
|
|
Write-Host "Download too small ($size bytes), retrying..."
|
|
}
|
|
} catch {
|
|
Write-Host "Download attempt $attempts failed: $_"
|
|
Start-Sleep -Seconds 5
|
|
}
|
|
}
|
|
if (-not $downloaded) { throw "Tor bundle download failed after $maxAttempts attempts" }
|
|
New-Item -ItemType Directory -Path tor-extract -Force
|
|
tar -xzf tor-bundle.tar.gz -C tor-extract
|
|
New-Item -ItemType Directory -Path tor-files -Force
|
|
Copy-Item -Recurse tor-extract/tor/* tor-files/
|
|
if (Test-Path tor-extract/tor/pluggable_transports) {
|
|
Copy-Item -Recurse tor-extract/tor/pluggable_transports tor-files/pluggable_transports -Force
|
|
}
|
|
if (Test-Path tor-extract/data) {
|
|
Copy-Item -Recurse tor-extract/data tor-files/data
|
|
}
|
|
Write-Host "Bundled Tor runtime files:"
|
|
Get-ChildItem -Recurse tor-files | Select-Object FullName
|
|
|
|
- name: Install NSIS via MSYS2
|
|
run: pacman -S --noconfirm mingw-w64-x86_64-nsis
|
|
|
|
- name: Install NSIS inetc plugin
|
|
run: |
|
|
pacman -S --noconfirm unzip
|
|
NSIS_DIR="/mingw64/share/nsis"
|
|
cd /tmp
|
|
curl -L -o Inetc.zip "https://nsis.sourceforge.io/mediawiki/images/c/c9/Inetc.zip"
|
|
unzip -o Inetc.zip -d inetc_extract
|
|
# MSYS2 mingw64 NSIS is 64-bit, needs amd64-unicode plugin in Plugins/unicode/
|
|
mkdir -p "$NSIS_DIR/Plugins/unicode"
|
|
cp inetc_extract/Plugins/amd64-unicode/INetC.dll "$NSIS_DIR/Plugins/unicode/"
|
|
echo "Installed 64-bit INetC.dll to $NSIS_DIR/Plugins/unicode/"
|
|
|
|
- name: Build NSIS installer
|
|
run: makensis //DVERSION=$VERSION contrib/nsis/setup.nsi
|
|
|
|
- name: Upload installer
|
|
uses: actions/upload-artifact@v4
|
|
with:
|
|
name: windows-qt-setup
|
|
path: contrib/nsis/Cryptographic-Triangles-*-setup.exe
|
|
|
|
build-windows-daemon:
|
|
runs-on: windows-latest
|
|
defaults:
|
|
run:
|
|
shell: msys2 {0}
|
|
steps:
|
|
- uses: actions/checkout@v4
|
|
with:
|
|
submodules: recursive
|
|
|
|
- uses: msys2/setup-msys2@v2
|
|
with:
|
|
msystem: MINGW64
|
|
update: true
|
|
install: >-
|
|
mingw-w64-x86_64-gcc
|
|
mingw-w64-x86_64-cmake
|
|
mingw-w64-x86_64-ninja
|
|
mingw-w64-x86_64-boost
|
|
mingw-w64-x86_64-openssl
|
|
mingw-w64-x86_64-db
|
|
mingw-w64-x86_64-libevent
|
|
mingw-w64-x86_64-miniupnpc
|
|
mingw-w64-x86_64-zlib
|
|
mingw-w64-x86_64-rocksdb
|
|
mingw-w64-x86_64-sqlite3
|
|
mingw-w64-x86_64-autotools
|
|
|
|
- name: Configure
|
|
run: |
|
|
cmake -B build -G Ninja \
|
|
-DCMAKE_BUILD_TYPE=Release \
|
|
-DBUILD_QT=OFF \
|
|
-DBUILD_DAEMON=ON \
|
|
-DBUILD_CLI=ON \
|
|
-DBUILD_TESTS=OFF \
|
|
-DUSE_UPNP=ON \
|
|
-DUSE_I2P_EMBEDDED=ON
|
|
|
|
- name: Build libtor (embedded Tor static lib)
|
|
# Windows: msys2 default install puts everything in /mingw64,
|
|
# which is exactly the script's default. Just invoke it.
|
|
# See v5.9.25-fork-detection run #466 for why this is needed.
|
|
run: bash src/tor/build-libtor.sh
|
|
|
|
- name: Build libi2pd (embedded I2P static lib)
|
|
run: bash src/i2p/build-libi2pd.sh
|
|
|
|
- name: Build
|
|
run: |
|
|
cmake --build build -j$(nproc)
|
|
strip --strip-all build/bin/trianglesd.exe
|
|
strip --strip-all build/bin/triangles-cli.exe
|
|
|
|
- name: Package daemon with DLLs
|
|
run: bash scripts/ci/package-windows-daemon.sh daemon-dist trianglesd triangles-cli
|
|
|
|
- name: Bundle Tor for daemon
|
|
# Resilient download: archive.torproject.org occasionally times out
|
|
# from CI egress (observed 2026-07-03: macOS job exit code 6 after
|
|
# exactly 30s of curl hang). Retries cover transient connection drops;
|
|
# size check rejects 0-byte "200 OK" responses from broken mirrors.
|
|
# NOTE: Invoke-WebRequest on PowerShell 5.1 (default on Windows-latest
|
|
# runners) does NOT accept -ConnectionTimeout/-OperationTimeout — those
|
|
# are PowerShell 7+. We rely on the retry loop + size check only.
|
|
shell: powershell
|
|
run: |
|
|
$TOR_VERSION = "15.0.9"
|
|
$TOR_URL = "https://archive.torproject.org/tor-package-archive/torbrowser/${TOR_VERSION}/tor-expert-bundle-windows-x86_64-${TOR_VERSION}.tar.gz"
|
|
$torPath = "tor-bundle.tar.gz"
|
|
$attempts = 0
|
|
$maxAttempts = 3
|
|
$downloaded = $false
|
|
while ($attempts -lt $maxAttempts -and -not $downloaded) {
|
|
$attempts++
|
|
try {
|
|
if (Test-Path $torPath) { Remove-Item $torPath -ErrorAction SilentlyContinue }
|
|
Invoke-WebRequest -Uri $TOR_URL -OutFile $torPath -UseBasicParsing
|
|
$size = (Get-Item $torPath).Length
|
|
if ($size -gt 1MB) {
|
|
Write-Host "Downloaded $size bytes on attempt $attempts"
|
|
$downloaded = $true
|
|
} else {
|
|
Write-Host "Download too small ($size bytes), retrying..."
|
|
}
|
|
} catch {
|
|
Write-Host "Download attempt $attempts failed: $_"
|
|
Start-Sleep -Seconds 5
|
|
}
|
|
}
|
|
if (-not $downloaded) { throw "Tor bundle download failed after $maxAttempts attempts" }
|
|
New-Item -ItemType Directory -Path tor-extract -Force
|
|
tar -xzf tor-bundle.tar.gz -C tor-extract
|
|
Copy-Item -Recurse tor-extract/tor/* daemon-dist/tor/
|
|
if (Test-Path tor-extract/data) {
|
|
Copy-Item -Recurse tor-extract/data daemon-dist/tor/data
|
|
}
|
|
|
|
- name: Upload artifact
|
|
uses: actions/upload-artifact@v4
|
|
with:
|
|
name: windows-daemon
|
|
path: daemon-dist/
|
|
|
|
build-linux-qt:
|
|
runs-on: ubuntu-22.04
|
|
steps:
|
|
- uses: actions/checkout@v4
|
|
with:
|
|
submodules: recursive
|
|
|
|
- name: Set VERSION
|
|
run: |
|
|
if [[ "${GITHUB_REF}" == refs/tags/v* ]]; then
|
|
echo "VERSION=${GITHUB_REF_NAME#v}" >> $GITHUB_ENV
|
|
else
|
|
MAJOR=$(grep 'CLIENT_VERSION_MAJOR' src/clientversion.h | tr -d '\r' | awk '{print $3}')
|
|
MINOR=$(grep 'CLIENT_VERSION_MINOR' src/clientversion.h | tr -d '\r' | awk '{print $3}')
|
|
REV=$(grep 'CLIENT_VERSION_REVISION' src/clientversion.h | tr -d '\r' | awk '{print $3}')
|
|
echo "VERSION=${MAJOR}.${MINOR}.${REV}" >> $GITHUB_ENV
|
|
fi
|
|
|
|
- name: Install dependencies
|
|
run: |
|
|
sudo apt-get update
|
|
sudo apt-get install -y build-essential cmake ninja-build \
|
|
qtbase5-dev qttools5-dev-tools \
|
|
libboost-all-dev libssl-dev libdb++-dev \
|
|
libleveldb-dev libevent-dev libminiupnpc-dev zlib1g-dev \
|
|
libsnappy-dev liblz4-dev libzstd-dev
|
|
|
|
- name: Build RocksDB from source
|
|
run: sudo bash scripts/ci/build-rocksdb.sh
|
|
|
|
- name: Configure
|
|
run: |
|
|
cmake -B build -G Ninja \
|
|
-DCMAKE_BUILD_TYPE=Release \
|
|
-DBUILD_QT=ON \
|
|
-DBUILD_DAEMON=OFF \
|
|
-DBUILD_TESTS=OFF \
|
|
-DUSE_UPNP=ON \
|
|
-DUSE_I2P_EMBEDDED=ON
|
|
|
|
- name: Build libtor (embedded Tor static lib)
|
|
# Linux Qt GUI also transitively links -ltor via triangles_common.
|
|
# build-libtor.sh defaults to /mingw64; pass /usr where the
|
|
# libevent-dev, libssl-dev, zlib1g-dev packages install.
|
|
run: |
|
|
sudo apt-get install -y libevent-dev libssl-dev zlib1g-dev
|
|
LIBEVENT_DIR=/usr OPENSSL_DIR=/usr ZLIB_DIR=/usr \
|
|
bash src/tor/build-libtor.sh
|
|
|
|
- name: Build libi2pd (embedded I2P static lib)
|
|
run: bash src/i2p/build-libi2pd.sh
|
|
|
|
- name: Build
|
|
run: cmake --build build -j$(nproc)
|
|
|
|
- name: Strip binary
|
|
run: strip --strip-all build/bin/triangles-qt
|
|
|
|
- name: Build .deb package (fully self-contained)
|
|
run: |
|
|
set -euo pipefail
|
|
TOR_VERSION="15.0.9"
|
|
# Resilient download: archive.torproject.org occasionally times out
|
|
# from CI egress (observed 2026-07-03: macOS job exit code 6 after
|
|
# exactly 30s of curl hang). Retries + --fail-with-body surface the
|
|
# next failure loudly instead of silently producing a 0-byte file.
|
|
curl -fSL --connect-timeout 15 --max-time 120 \
|
|
--retry 3 --retry-delay 5 --retry-connrefused --retry-all-errors \
|
|
"https://archive.torproject.org/tor-package-archive/torbrowser/${TOR_VERSION}/tor-expert-bundle-linux-x86_64-${TOR_VERSION}.tar.gz" \
|
|
-o tor-bundle.tar.gz
|
|
mkdir -p tor-extract && tar -xzf tor-bundle.tar.gz -C tor-extract
|
|
|
|
PKG="cryptographic-triangles_${VERSION}_amd64"
|
|
mkdir -p ${PKG}/DEBIAN
|
|
mkdir -p ${PKG}/usr/lib/cryptographic-triangles/lib
|
|
mkdir -p ${PKG}/usr/lib/cryptographic-triangles/tor
|
|
mkdir -p ${PKG}/usr/bin
|
|
mkdir -p ${PKG}/usr/share/applications
|
|
mkdir -p ${PKG}/usr/share/pixmaps
|
|
|
|
cp build/bin/triangles-qt ${PKG}/usr/lib/cryptographic-triangles/
|
|
cp tor-extract/tor/tor ${PKG}/usr/lib/cryptographic-triangles/tor/
|
|
chmod +x ${PKG}/usr/lib/cryptographic-triangles/tor/tor
|
|
[ -d tor-extract/data ] && cp -r tor-extract/data ${PKG}/usr/lib/cryptographic-triangles/tor/data
|
|
|
|
# Bundle ALL shared library dependencies (except glibc/kernel)
|
|
ldd build/bin/triangles-qt | grep '=> /' | awk '{print $3}' | while read lib; do
|
|
case "$lib" in
|
|
/lib/x86_64-linux-gnu/libc.so*|/lib/x86_64-linux-gnu/libm.so*|/lib/x86_64-linux-gnu/libpthread.so*|/lib/x86_64-linux-gnu/libdl.so*|/lib/x86_64-linux-gnu/librt.so*|/lib/x86_64-linux-gnu/ld-linux*|/lib64/ld-linux*)
|
|
;; # Skip glibc core — always present
|
|
*)
|
|
cp -L "$lib" ${PKG}/usr/lib/cryptographic-triangles/lib/ 2>/dev/null || true
|
|
;;
|
|
esac
|
|
done
|
|
echo "=== Bundled libs ==="
|
|
ls ${PKG}/usr/lib/cryptographic-triangles/lib/ | wc -l
|
|
ls ${PKG}/usr/lib/cryptographic-triangles/lib/
|
|
|
|
# Launcher with LD_LIBRARY_PATH
|
|
cat > ${PKG}/usr/bin/cryptographic-triangles << 'LAUNCHER'
|
|
#!/bin/bash
|
|
INSTALL_DIR=/usr/lib/cryptographic-triangles
|
|
export LD_LIBRARY_PATH="${INSTALL_DIR}/lib:${LD_LIBRARY_PATH}"
|
|
exec "${INSTALL_DIR}/triangles-qt" "$@"
|
|
LAUNCHER
|
|
sed -i 's/^ //' ${PKG}/usr/bin/cryptographic-triangles
|
|
chmod +x ${PKG}/usr/bin/cryptographic-triangles
|
|
|
|
cat > ${PKG}/usr/share/applications/cryptographic-triangles.desktop << 'DESKTOP'
|
|
[Desktop Entry]
|
|
Name=Cryptographic Triangles
|
|
Comment=Triangles Cryptocurrency Wallet
|
|
Exec=cryptographic-triangles
|
|
Terminal=false
|
|
Type=Application
|
|
Icon=cryptographic-triangles
|
|
Categories=Finance;Network;
|
|
DESKTOP
|
|
sed -i 's/^ //' ${PKG}/usr/share/applications/cryptographic-triangles.desktop
|
|
|
|
cp src/qt/res/icons/triangles.ico ${PKG}/usr/share/pixmaps/cryptographic-triangles.ico 2>/dev/null || true
|
|
|
|
cat > ${PKG}/DEBIAN/control << CTRL
|
|
Package: cryptographic-triangles
|
|
Version: ${VERSION}
|
|
Architecture: amd64
|
|
Maintainer: Cryptographic Triangles <dev@cryptographic-triangles.org>
|
|
Description: Cryptographic Triangles wallet with integrated Tor
|
|
Fully self-contained wallet with all libraries and Tor bundled.
|
|
No external dependencies required — runs on any x86_64 Linux.
|
|
Section: finance
|
|
Priority: optional
|
|
CTRL
|
|
sed -i 's/^ //' ${PKG}/DEBIAN/control
|
|
|
|
dpkg-deb --build ${PKG}
|
|
|
|
- name: Upload .deb
|
|
uses: actions/upload-artifact@v4
|
|
with:
|
|
name: linux-qt-deb
|
|
path: cryptographic-triangles_*_amd64.deb
|
|
|
|
build-linux-daemon:
|
|
runs-on: ubuntu-22.04
|
|
steps:
|
|
- uses: actions/checkout@v4
|
|
with:
|
|
submodules: recursive
|
|
|
|
- name: Set VERSION
|
|
run: |
|
|
if [[ "${GITHUB_REF}" == refs/tags/v* ]]; then
|
|
echo "VERSION=${GITHUB_REF_NAME#v}" >> $GITHUB_ENV
|
|
else
|
|
MAJOR=$(grep 'CLIENT_VERSION_MAJOR' src/clientversion.h | tr -d '\r' | awk '{print $3}')
|
|
MINOR=$(grep 'CLIENT_VERSION_MINOR' src/clientversion.h | tr -d '\r' | awk '{print $3}')
|
|
REV=$(grep 'CLIENT_VERSION_REVISION' src/clientversion.h | tr -d '\r' | awk '{print $3}')
|
|
echo "VERSION=${MAJOR}.${MINOR}.${REV}" >> $GITHUB_ENV
|
|
fi
|
|
|
|
- name: Install dependencies
|
|
run: |
|
|
sudo apt-get update
|
|
sudo apt-get install -y build-essential cmake ninja-build \
|
|
libboost-all-dev libssl-dev libdb++-dev libleveldb-dev \
|
|
libevent-dev libminiupnpc-dev zlib1g-dev \
|
|
libsnappy-dev liblz4-dev libzstd-dev
|
|
|
|
- name: Build RocksDB from source
|
|
run: sudo bash scripts/ci/build-rocksdb.sh
|
|
|
|
- name: Configure
|
|
run: |
|
|
cmake -B build -G Ninja \
|
|
-DCMAKE_BUILD_TYPE=Release \
|
|
-DBUILD_QT=OFF \
|
|
-DBUILD_DAEMON=ON \
|
|
-DBUILD_CLI=ON \
|
|
-DBUILD_TESTS=OFF \
|
|
-DUSE_UPNP=ON \
|
|
-DUSE_I2P_EMBEDDED=ON
|
|
|
|
- name: Build libtor (embedded Tor static lib)
|
|
# USE_TOR_EMBEDDED defaults to ON and the daemon/Qt GUI both
|
|
# link -ltor. The Tor source is a git submodule but libtor.a
|
|
# is NOT built by cmake. build-libtor.sh defaults to /mingw64
|
|
# paths which don't exist on the ubuntu-22.04 runner; pass
|
|
# /usr where libevent-dev/libssl-dev/zlib1g-dev install.
|
|
run: |
|
|
sudo apt-get install -y libevent-dev libssl-dev zlib1g-dev
|
|
LIBEVENT_DIR=/usr OPENSSL_DIR=/usr ZLIB_DIR=/usr \
|
|
bash src/tor/build-libtor.sh
|
|
|
|
- name: Build libi2pd (embedded I2P static lib)
|
|
run: bash src/i2p/build-libi2pd.sh
|
|
|
|
- name: Build
|
|
run: cmake --build build -j$(nproc)
|
|
|
|
- name: Strip binary
|
|
run: |
|
|
strip --strip-all build/bin/trianglesd
|
|
strip --strip-all build/bin/triangles-cli
|
|
|
|
- name: Build .deb package (fully self-contained)
|
|
run: bash scripts/ci/package-linux-daemon.sh "${VERSION}"
|
|
|
|
- name: Upload .deb
|
|
uses: actions/upload-artifact@v4
|
|
with:
|
|
name: linux-daemon-deb
|
|
path: cryptographic-triangles-daemon_*_amd64.deb
|
|
|
|
build-macos:
|
|
runs-on: macos-15
|
|
steps:
|
|
- uses: actions/checkout@v4
|
|
with:
|
|
submodules: recursive
|
|
|
|
- name: Set VERSION
|
|
run: |
|
|
if [[ "${GITHUB_REF}" == refs/tags/v* ]]; then
|
|
echo "VERSION=${GITHUB_REF_NAME#v}" >> $GITHUB_ENV
|
|
else
|
|
MAJOR=$(grep 'CLIENT_VERSION_MAJOR' src/clientversion.h | tr -d '\r' | awk '{print $3}')
|
|
MINOR=$(grep 'CLIENT_VERSION_MINOR' src/clientversion.h | tr -d '\r' | awk '{print $3}')
|
|
REV=$(grep 'CLIENT_VERSION_REVISION' src/clientversion.h | tr -d '\r' | awk '{print $3}')
|
|
echo "VERSION=${MAJOR}.${MINOR}.${REV}" >> $GITHUB_ENV
|
|
fi
|
|
|
|
- name: Install dependencies
|
|
run: |
|
|
brew install cmake ninja qt@5 openssl@3 boost berkeley-db@5 leveldb rocksdb libevent miniupnpc zstd
|
|
|
|
- name: Configure
|
|
# Add -L/opt/homebrew/lib to the link line so rocksdb's
|
|
# transitive -lzstd resolves. /opt/homebrew/lib is only in the
|
|
# rpath (runtime), not the link-time search path, so cmake's
|
|
# default LIBRARY_PATH propagation isn't enough — we set the
|
|
# linker flags explicitly.
|
|
run: |
|
|
export PATH="/opt/homebrew/opt/qt@5/bin:$PATH"
|
|
cmake -B build -G Ninja \
|
|
-DCMAKE_BUILD_TYPE=Release \
|
|
-DBUILD_QT=ON \
|
|
-DBUILD_DAEMON=OFF \
|
|
-DBUILD_TESTS=OFF \
|
|
-DUSE_UPNP=ON \
|
|
-DUSE_I2P_EMBEDDED=ON \
|
|
-DBOOST_ROOT=/opt/homebrew/opt/boost \
|
|
-DBDB_INCLUDE_PATH=/opt/homebrew/opt/berkeley-db@5/include \
|
|
-DBDB_LIB_PATH=/opt/homebrew/opt/berkeley-db@5/lib \
|
|
-DOPENSSL_ROOT_DIR=/opt/homebrew/opt/openssl@3 \
|
|
-DEVENT_INCLUDE_PATH=/opt/homebrew/opt/libevent/include \
|
|
-DEVENT_LIB_PATH=/opt/homebrew/opt/libevent/lib \
|
|
-DMINIUPNPC_INCLUDE_PATH=/opt/homebrew/opt/miniupnpc/include \
|
|
-DMINIUPNPC_LIB_PATH=/opt/homebrew/opt/miniupnpc/lib \
|
|
-DQt5_DIR=/opt/homebrew/opt/qt@5/lib/cmake/Qt5 \
|
|
-DCMAKE_LIBRARY_PATH=/opt/homebrew/lib \
|
|
-DCMAKE_EXE_LINKER_FLAGS="-L/opt/homebrew/lib" \
|
|
-DCMAKE_SHARED_LINKER_FLAGS="-L/opt/homebrew/lib"
|
|
|
|
- name: Build libtor (embedded Tor static lib)
|
|
# macOS Qt GUI also transitively links -ltor via triangles_common.
|
|
# macOS Qt is built with @rpath embedded, so libtor needs to be
|
|
# at the configured TOR_SOURCE_ROOT location.
|
|
run: |
|
|
brew install libevent openssl@3 autoconf automake libtool zlib zstd
|
|
export PATH="/opt/homebrew/opt/automake/bin:/opt/homebrew/opt/libtool/bin:$PATH"
|
|
LIBEVENT_DIR=/opt/homebrew/opt/libevent \
|
|
OPENSSL_DIR=/opt/homebrew/opt/openssl@3 \
|
|
ZLIB_DIR=/opt/homebrew/opt/zlib \
|
|
bash src/tor/build-libtor.sh
|
|
|
|
- name: Build libtor (embedded Tor static lib)
|
|
# macOS Qt GUI also transitively links -ltor via triangles_common.
|
|
# macOS Qt is built with @rpath embedded, so libtor needs to be
|
|
# at the configured TOR_SOURCE_ROOT location.
|
|
run: |
|
|
brew install libevent openssl@3 autoconf automake libtool zlib
|
|
export PATH="/opt/homebrew/opt/automake/bin:/opt/homebrew/opt/libtool/bin:$PATH"
|
|
LIBEVENT_DIR=/opt/homebrew/opt/libevent \
|
|
OPENSSL_DIR=/opt/homebrew/opt/openssl@3 \
|
|
ZLIB_DIR=/opt/homebrew/opt/zlib \
|
|
bash src/tor/build-libtor.sh
|
|
|
|
- name: Build libi2pd (embedded I2P static lib)
|
|
# HOMEBREW=1 tells the i2pd Makefile to use Homebrew paths.
|
|
run: HOMEBREW=1 bash src/i2p/build-libi2pd.sh
|
|
|
|
- name: Build
|
|
run: cmake --build build -j$(sysctl -n hw.ncpu)
|
|
|
|
- name: Create .app bundle
|
|
run: |
|
|
export PATH="/opt/homebrew/opt/qt@5/bin:$PATH"
|
|
macdeployqt build/bin/Triangles-Qt.app -verbose=1 || \
|
|
macdeployqt build/bin/triangles-qt.app -verbose=1 || true
|
|
|
|
- name: Bundle non-Qt dylibs into app
|
|
run: |
|
|
# Find the .app bundle
|
|
APP=$(find build/bin -name "*.app" -maxdepth 1 | head -1)
|
|
if [ -z "$APP" ]; then
|
|
echo "No .app bundle found, creating one manually..."
|
|
APP="build/bin/Triangles-Qt.app"
|
|
mkdir -p "$APP/Contents/MacOS" "$APP/Contents/Frameworks"
|
|
cp build/bin/triangles-qt "$APP/Contents/MacOS/Triangles-Qt"
|
|
fi
|
|
FRAMEWORKS="$APP/Contents/Frameworks"
|
|
BINARY=$(find "$APP/Contents/MacOS" -type f -perm +111 | head -1)
|
|
|
|
# Copy Homebrew dylibs that macdeployqt doesn't handle
|
|
for lib in boost_system boost_filesystem boost_thread boost_program_options boost_chrono; do
|
|
DYLIB=$(otool -L "$BINARY" | grep "$lib" | awk '{print $1}')
|
|
if [ -n "$DYLIB" ] && [ -f "$DYLIB" ]; then
|
|
cp "$DYLIB" "$FRAMEWORKS/"
|
|
BASENAME=$(basename "$DYLIB")
|
|
install_name_tool -change "$DYLIB" "@executable_path/../Frameworks/$BASENAME" "$BINARY"
|
|
fi
|
|
done
|
|
for lib in libssl libcrypto libevent libdb_cxx libminiupnpc libsodium; do
|
|
DYLIB=$(otool -L "$BINARY" | grep "$lib" | awk '{print $1}')
|
|
if [ -n "$DYLIB" ] && [ -f "$DYLIB" ]; then
|
|
cp "$DYLIB" "$FRAMEWORKS/"
|
|
BASENAME=$(basename "$DYLIB")
|
|
install_name_tool -change "$DYLIB" "@executable_path/../Frameworks/$BASENAME" "$BINARY"
|
|
fi
|
|
done
|
|
|
|
echo "=== Final dylib dependencies ==="
|
|
otool -L "$BINARY" | head -30
|
|
|
|
- name: Bundle Tor into app
|
|
# Resilient download: archive.torproject.org occasionally times out
|
|
# from Azure westus egress (observed 2026-07-03: macOS job exit code 6
|
|
# after exactly 30s of curl hang). --retry 3 with --retry-connrefused
|
|
# handles transient connection refusals and timeouts; --fail-with-body
|
|
# surfaces HTTP error bodies so the next failure isn't silent.
|
|
run: |
|
|
set -euo pipefail
|
|
TOR_VERSION="15.0.9"
|
|
curl -fSL --connect-timeout 15 --max-time 120 \
|
|
--retry 3 --retry-delay 5 --retry-connrefused --retry-all-errors \
|
|
"https://archive.torproject.org/tor-package-archive/torbrowser/${TOR_VERSION}/tor-expert-bundle-macos-aarch64-${TOR_VERSION}.tar.gz" \
|
|
-o tor-bundle.tar.gz
|
|
mkdir -p tor-extract && tar -xzf tor-bundle.tar.gz -C tor-extract
|
|
APP=$(find build/bin -name "*.app" -maxdepth 1 | head -1)
|
|
mkdir -p "$APP/Contents/MacOS/tor"
|
|
cp tor-extract/tor/tor "$APP/Contents/MacOS/tor/"
|
|
chmod +x "$APP/Contents/MacOS/tor/tor"
|
|
[ -d tor-extract/data ] && cp -r tor-extract/data "$APP/Contents/MacOS/tor/data"
|
|
|
|
- name: Create DMG
|
|
run: |
|
|
APP=$(find build/bin -name "*.app" -maxdepth 1 | head -1)
|
|
mkdir -p dmg_contents
|
|
cp -R "$APP" dmg_contents/
|
|
ln -s /Applications dmg_contents/Applications
|
|
hdiutil create -volname "Cryptographic Triangles" \
|
|
-srcfolder dmg_contents \
|
|
-ov -format UDZO \
|
|
"Cryptographic-Triangles-v${VERSION}-macos-arm64.dmg"
|
|
|
|
- name: Upload DMG
|
|
uses: actions/upload-artifact@v4
|
|
with:
|
|
name: macos-arm64-dmg
|
|
path: "*.dmg"
|
|
|
|
release:
|
|
if: startsWith(github.ref, 'refs/tags/v')
|
|
needs: [build-windows-qt, build-windows-daemon, build-linux-qt, build-linux-daemon, build-macos]
|
|
runs-on: ubuntu-latest
|
|
permissions:
|
|
contents: write
|
|
steps:
|
|
- name: Set VERSION from tag
|
|
run: echo "VERSION=${GITHUB_REF_NAME#v}" >> $GITHUB_ENV
|
|
|
|
- name: Download all artifacts
|
|
uses: actions/download-artifact@v4
|
|
with:
|
|
path: artifacts
|
|
|
|
- name: Prepare release assets
|
|
run: |
|
|
mkdir -p release
|
|
# Windows Qt installer (setup.exe — includes Tor, Start Menu shortcuts, uninstaller)
|
|
cp artifacts/windows-qt-setup/*.exe release/
|
|
# Windows Qt portable zip (extract & run — no install required)
|
|
cp artifacts/windows-qt-zip/*.zip release/
|
|
# Windows daemon (zip with DLLs + Tor)
|
|
cd artifacts/windows-daemon && zip -r "../../release/Cryptographic-Triangles-${VERSION}-win-x64-daemon.zip" . && cd ../..
|
|
# Linux Qt .deb (dpkg -i to install — includes Tor, desktop entry, icon)
|
|
cp artifacts/linux-qt-deb/*.deb release/
|
|
# Linux daemon .deb (dpkg -i to install — includes Tor, systemd service)
|
|
cp artifacts/linux-daemon-deb/*.deb release/
|
|
# macOS DMG (drag to Applications — Tor inside .app bundle)
|
|
cp artifacts/macos-arm64-dmg/*.dmg release/
|
|
ls -la release/
|
|
|
|
- name: Create Release
|
|
uses: softprops/action-gh-release@v2
|
|
with:
|
|
files: release/*
|
|
generate_release_notes: true
|
|
|
|
trigger-tripi:
|
|
name: Trigger TRI-PI ARM64 Build
|
|
# Only fire on tag-push events. To trigger a TRI-PI rebuild after a
|
|
# release is created via gh API (without re-pushing the tag), use:
|
|
# curl -X POST .../repos/SamiAhmed7777/tri-pi/dispatches \
|
|
# -d '{"event_type":"new-release","client_payload":{"version":"vX.Y.Z","source_repo":"SamiAhmed7777/triangles_v5"}}'
|
|
if: startsWith(github.ref, 'refs/tags/v')
|
|
needs: release
|
|
runs-on: ubuntu-latest
|
|
steps:
|
|
- name: Dispatch tri-pi ARM64 build
|
|
run: |
|
|
curl -f -X POST \
|
|
-H "Accept: application/vnd.github+json" \
|
|
-H "Authorization: Bearer ${{ secrets.TRIPI_BUILD_TOKEN }}" \
|
|
-H "X-GitHub-Api-Version: 2022-11-28" \
|
|
https://api.github.com/repos/SamiAhmed7777/tri-pi/dispatches \
|
|
-d '{"event_type":"new-release","client_payload":{"version":"${{ github.ref_name }}","source_repo":"SamiAhmed7777/triangles_v5"}}'
|
|
echo "Triggered tri-pi repository_dispatch for ${{ github.ref_name }}"
|