diff options
| author | Bitflash <btf01@proton.me> | 2026-07-27 17:53:00 -0300 |
|---|---|---|
| committer | GitHub <noreply@github.com> | 2026-07-27 17:53:00 -0300 |
| commit | 8261c39527032485b0e14b4e4a230768218e9b21 (patch) | |
| tree | 83ce382fc8fa3c58881fb17ba960dbaec2778895 | |
| parent | 6ca1a1d6799474f8ca977b93ab630af7ace879c2 (diff) | |
Update install-bitflash-wine.sh
| -rw-r--r-- | install-bitflash-wine.sh | 192 |
1 files changed, 124 insertions, 68 deletions
diff --git a/install-bitflash-wine.sh b/install-bitflash-wine.sh index 3fa18a1..91fef59 100644 --- a/install-bitflash-wine.sh +++ b/install-bitflash-wine.sh | |||
| @@ -1,5 +1,6 @@ | |||
| 1 | #!/usr/bin/env bash | 1 | #!/usr/bin/env bash |
| 2 | # Bitflash GUI installer for Linux (via Wine) | 2 | # Bitflash GUI installer for Linux (via Wine) — hardened |
| 3 | set -euo pipefail | ||
| 3 | 4 | ||
| 4 | INSTALL_DIR="$HOME/.btf-wine" | 5 | INSTALL_DIR="$HOME/.btf-wine" |
| 5 | WINE_DATA="$HOME/.wine/drive_c/users/$USER/AppData/Roaming/Bitflash" | 6 | WINE_DATA="$HOME/.wine/drive_c/users/$USER/AppData/Roaming/Bitflash" |
| @@ -10,146 +11,201 @@ REPO="Bitflash-sh/bitflash" | |||
| 10 | echo "==> Bitflash GUI installer" | 11 | echo "==> Bitflash GUI installer" |
| 11 | echo | 12 | echo |
| 12 | 13 | ||
| 14 | # ---- Safety: don't run as root (breaks Wine + pollutes /root) | ||
| 15 | if [ "$(id -u)" -eq 0 ]; then | ||
| 16 | echo "ERROR: do not run this as root. Run as your normal user (it will call sudo only when needed)." >&2 | ||
| 17 | exit 1 | ||
| 18 | fi | ||
| 19 | |||
| 20 | need() { command -v "$1" >/dev/null 2>&1; } | ||
| 21 | |||
| 13 | # ---- Wine | 22 | # ---- Wine |
| 14 | echo "==> Checking Wine..." | 23 | echo "==> Checking Wine..." |
| 15 | if ! command -v wine &>/dev/null; then | 24 | if ! need wine; then |
| 16 | echo " Installing Wine..." | 25 | echo " Installing Wine..." |
| 17 | sudo apt-get update -qq && sudo apt-get install -y wine | 26 | if ! need apt-get; then |
| 27 | echo "ERROR: apt-get not found. Install 'wine' manually, then re-run." >&2 | ||
| 28 | exit 1 | ||
| 29 | fi | ||
| 30 | sudo apt-get update -qq | ||
| 31 | sudo apt-get install -y wine | ||
| 18 | fi | 32 | fi |
| 19 | echo " Wine: $(wine --version)" | 33 | echo " Wine: $(wine --version)" |
| 20 | 34 | ||
| 21 | # ---- db-util | 35 | # ---- db-util (db_dump / db_load for wallet conversion) |
| 22 | echo "==> Checking db-util..." | 36 | echo "==> Checking db-util..." |
| 23 | if ! command -v db_dump &>/dev/null; then | 37 | if ! need db_dump; then |
| 24 | echo " Installing db-util..." | 38 | echo " Installing db-util..." |
| 25 | sudo apt-get install -y db-util | 39 | sudo apt-get install -y db-util |
| 26 | fi | 40 | fi |
| 27 | echo " db_dump: $(which db_dump)" | 41 | echo " db_dump: $(command -v db_dump)" |
| 28 | 42 | ||
| 29 | # ---- Download | 43 | # ---- unzip + sha256sum must exist |
| 30 | echo "==> Fetching latest release from GitHub..." | 44 | for tool in unzip sha256sum curl; do |
| 31 | RELEASE_URL=$(curl -s "https://api.github.com/repos/$REPO/releases/latest" \ | 45 | if ! need "$tool"; then |
| 32 | | grep "browser_download_url" \ | 46 | echo " Installing $tool..." |
| 33 | | grep -i "win\|windows\|gui" \ | 47 | sudo apt-get install -y "$tool" |
| 34 | | head -1 | cut -d '"' -f 4) | 48 | fi |
| 49 | done | ||
| 35 | 50 | ||
| 36 | if [ -z "$RELEASE_URL" ]; then | 51 | # ---- Resolve latest release (single API call) |
| 37 | RELEASE_URL=$(curl -s "https://api.github.com/repos/$REPO/releases/latest" \ | 52 | echo "==> Fetching latest release metadata from GitHub..." |
| 38 | | grep "browser_download_url" | head -1 | cut -d '"' -f 4) | 53 | API_JSON=$(curl -fsSL "https://api.github.com/repos/$REPO/releases/latest") |
| 39 | fi | 54 | |
| 55 | RELEASE_URL=$(printf '%s\n' "$API_JSON" | grep '"browser_download_url"' \ | ||
| 56 | | grep -iE 'win|windows|gui' | head -1 | cut -d '"' -f 4 || true) | ||
| 57 | [ -z "$RELEASE_URL" ] && RELEASE_URL=$(printf '%s\n' "$API_JSON" | grep '"browser_download_url"' \ | ||
| 58 | | grep -iE '\.zip"?$' | head -1 | cut -d '"' -f 4 || true) | ||
| 40 | 59 | ||
| 41 | if [ -z "$RELEASE_URL" ]; then | 60 | if [ -z "$RELEASE_URL" ]; then |
| 42 | echo "ERROR: No release found at https://github.com/$REPO/releases" | 61 | echo "ERROR: no downloadable release asset found at https://github.com/$REPO/releases" >&2 |
| 43 | exit 1 | 62 | exit 1 |
| 44 | fi | 63 | fi |
| 64 | ASSET_NAME=$(basename "$RELEASE_URL") | ||
| 65 | echo " Asset: $ASSET_NAME" | ||
| 45 | 66 | ||
| 46 | echo " URL: $RELEASE_URL" | 67 | # checksum manifest published alongside the release (SHA256SUMS) |
| 68 | SUMS_URL=$(printf '%s\n' "$API_JSON" | grep '"browser_download_url"' \ | ||
| 69 | | grep -iE 'sha256sums|checksums|\.sha256"?$' | head -1 | cut -d '"' -f 4 || true) | ||
| 70 | |||
| 71 | # ---- Download the release | ||
| 47 | TMPZIP=$(mktemp /tmp/bitflash-XXXXXX.zip) | 72 | TMPZIP=$(mktemp /tmp/bitflash-XXXXXX.zip) |
| 48 | echo " Downloading..." | 73 | cleanup() { rm -f "$TMPZIP" "${TMPSUMS:-}"; } |
| 49 | curl -L -o "$TMPZIP" "$RELEASE_URL" | 74 | trap cleanup EXIT |
| 75 | echo "==> Downloading release..." | ||
| 76 | curl -fL --retry 3 --retry-delay 2 -o "$TMPZIP" "$RELEASE_URL" | ||
| 50 | echo " Downloaded: $(du -h "$TMPZIP" | cut -f1)" | 77 | echo " Downloaded: $(du -h "$TMPZIP" | cut -f1)" |
| 51 | 78 | ||
| 79 | # ---- MANDATORY integrity check | ||
| 80 | echo "==> Verifying integrity..." | ||
| 81 | GOT=$(sha256sum "$TMPZIP" | awk '{print $1}') | ||
| 82 | if [ -n "${EXPECTED_SHA256:-}" ]; then | ||
| 83 | # Strongest: user pinned a known-good hash out-of-band. | ||
| 84 | if [ "$GOT" != "$EXPECTED_SHA256" ]; then | ||
| 85 | echo "ERROR: checksum mismatch. expected $EXPECTED_SHA256, got $GOT" >&2 | ||
| 86 | exit 1 | ||
| 87 | fi | ||
| 88 | echo " OK (matches pinned EXPECTED_SHA256)" | ||
| 89 | elif [ -n "$SUMS_URL" ]; then | ||
| 90 | TMPSUMS=$(mktemp /tmp/bitflash-sums-XXXXXX) | ||
| 91 | curl -fsSL --retry 3 -o "$TMPSUMS" "$SUMS_URL" | ||
| 92 | WANT=$(grep -iF "$ASSET_NAME" "$TMPSUMS" | awk '{print $1}' | head -1 || true) | ||
| 93 | if [ -z "$WANT" ]; then | ||
| 94 | echo "ERROR: no checksum for $ASSET_NAME in published SHA256SUMS." >&2 | ||
| 95 | exit 1 | ||
| 96 | fi | ||
| 97 | if [ "$GOT" != "$WANT" ]; then | ||
| 98 | echo "ERROR: checksum mismatch. published $WANT, got $GOT" >&2 | ||
| 99 | exit 1 | ||
| 100 | fi | ||
| 101 | echo " OK (matches published SHA256SUMS)" | ||
| 102 | else | ||
| 103 | echo "ERROR: no checksum available for this release." >&2 | ||
| 104 | echo " Publish a SHA256SUMS asset with the release, or re-run with" >&2 | ||
| 105 | echo " EXPECTED_SHA256=<hash> to verify against a known-good value." >&2 | ||
| 106 | echo " (To bypass at your own risk: ALLOW_UNVERIFIED=1)" >&2 | ||
| 107 | if [ "${ALLOW_UNVERIFIED:-0}" != "1" ]; then | ||
| 108 | exit 1 | ||
| 109 | fi | ||
| 110 | echo " WARNING: proceeding UNVERIFIED (ALLOW_UNVERIFIED=1). sha256=$GOT" | ||
| 111 | fi | ||
| 112 | |||
| 52 | # ---- Extract | 113 | # ---- Extract |
| 53 | echo "==> Extracting to $INSTALL_DIR..." | 114 | echo "==> Extracting to $INSTALL_DIR..." |
| 54 | rm -rf "$INSTALL_DIR" | 115 | rm -rf "$INSTALL_DIR" |
| 55 | mkdir -p "$INSTALL_DIR" | 116 | mkdir -p "$INSTALL_DIR" |
| 56 | echo " Running unzip -j..." | 117 | unzip -j -o "$TMPZIP" -d "$INSTALL_DIR" >/dev/null |
| 57 | unzip -j "$TMPZIP" -d "$INSTALL_DIR" 2>&1 | grep -v "^warning" || true | ||
| 58 | echo " Files after extract: $(ls "$INSTALL_DIR" | wc -l)" | ||
| 59 | 118 | ||
| 60 | # ---- Rename backslash files | 119 | # ---- Rename backslash-mangled entries (Windows zip paths) |
| 61 | echo "==> Renaming files..." | ||
| 62 | count=0 | 120 | count=0 |
| 63 | for f in "$INSTALL_DIR"/*; do | 121 | for f in "$INSTALL_DIR"/*; do |
| 64 | [ -f "$f" ] || continue | 122 | [ -f "$f" ] || continue |
| 65 | base=$(basename "$f") | 123 | base=$(basename "$f") |
| 66 | newname=$(echo "$base" | sed 's/.*\\//') | 124 | newname=$(printf '%s' "$base" | sed 's/.*\\//') |
| 67 | if [ "$newname" != "$base" ]; then | 125 | if [ "$newname" != "$base" ]; then |
| 68 | mv "$f" "$INSTALL_DIR/$newname" | 126 | mv "$f" "$INSTALL_DIR/$newname" |
| 69 | count=$((count+1)) | 127 | count=$((count+1)) |
| 70 | fi | 128 | fi |
| 71 | done | 129 | done |
| 72 | echo " Renamed $count files" | 130 | echo " Renamed $count files; total: $(ls "$INSTALL_DIR" | wc -l)" |
| 73 | echo " Files now: $(ls "$INSTALL_DIR" | wc -l)" | ||
| 74 | 131 | ||
| 75 | rm -f "$TMPZIP" | 132 | # Locate the Bitflash exe case-insensitively (zip may ship Bitflash.exe or bitflash.exe) |
| 76 | 133 | EXE=$(find "$INSTALL_DIR" -maxdepth 1 -iname 'bitflash.exe' | head -1) | |
| 77 | # ---- Verify | 134 | if [ -z "$EXE" ]; then |
| 78 | if [ ! -f "$INSTALL_DIR/bitflash.exe" ]; then | 135 | echo "ERROR: bitflash exe not found after extract. Contents:" >&2 |
| 79 | echo "ERROR: bitflash.exe not found. Files present:" | 136 | ls "$INSTALL_DIR" >&2 |
| 80 | ls "$INSTALL_DIR" | ||
| 81 | exit 1 | 137 | exit 1 |
| 82 | fi | 138 | fi |
| 83 | echo " bitflash.exe found ✓" | 139 | echo " Found $(basename "$EXE")" |
| 84 | 140 | ||
| 85 | # ---- Init Wine data dir | 141 | # ---- Init Wine data dir |
| 86 | echo "==> Initializing Wine Bitflash data directory..." | 142 | echo "==> Initializing Wine data directory..." |
| 87 | echo " Starting Wine to create data dir (may take a moment)..." | 143 | WINEDLLOVERRIDES="libdb_cxx-6.2=n" wine "$EXE" & |
| 88 | WINEDLLOVERRIDES="libdb_cxx-6.2=n" wine "$INSTALL_DIR/bitflash.exe" & | ||
| 89 | WINE_PID=$! | 144 | WINE_PID=$! |
| 90 | # Wait until wallet.dat is created, max 30 seconds | 145 | created=0 |
| 91 | for i in $(seq 1 30); do | 146 | for i in $(seq 1 30); do |
| 92 | sleep 1 | 147 | sleep 1 |
| 93 | if [ -f "$WINE_DATA/wallet.dat" ]; then | 148 | if [ -f "$WINE_DATA/wallet.dat" ]; then |
| 94 | echo " wallet.dat created after ${i}s" | 149 | echo " wallet.dat created after ${i}s" |
| 150 | created=1 | ||
| 95 | break | 151 | break |
| 96 | fi | 152 | fi |
| 97 | echo " Waiting for Wine to initialize... ${i}s" | ||
| 98 | done | 153 | done |
| 99 | kill $WINE_PID 2>/dev/null || true | 154 | kill "$WINE_PID" 2>/dev/null || true |
| 100 | wait $WINE_PID 2>/dev/null || true | 155 | wait "$WINE_PID" 2>/dev/null || true |
| 101 | sleep 1 | 156 | sleep 1 |
| 102 | mkdir -p "$WINE_DATA" | 157 | mkdir -p "$WINE_DATA" |
| 103 | echo " Wine data dir ready" | 158 | [ "$created" -eq 1 ] || echo " NOTE: Wine did not create wallet.dat in 30s; continuing." |
| 104 | 159 | ||
| 105 | # ---- Wallet | 160 | # ---- Optional wallet import (with backup) |
| 106 | echo | 161 | echo |
| 107 | if [ -f "$LINUX_DATA/wallet.dat" ]; then | 162 | if [ -f "$LINUX_DATA/wallet.dat" ]; then |
| 108 | echo "==> Found existing Linux wallet at $LINUX_DATA/wallet.dat" | 163 | echo "==> Found existing Linux wallet at $LINUX_DATA/wallet.dat" |
| 109 | read -p " Import it into the GUI wallet? [Y/n] " ans | 164 | read -r -p " Import it into the GUI wallet? [Y/n] " ans |
| 110 | ans=${ans:-Y} | 165 | ans=${ans:-Y} |
| 111 | if [[ "$ans" =~ ^[Yy] ]]; then | 166 | if [[ "$ans" =~ ^[Yy] ]]; then |
| 112 | echo " Dumping Linux wallet..." | 167 | if [ -f "$WINE_DATA/wallet.dat" ]; then |
| 113 | DUMP=$(mktemp) | 168 | BK="$WINE_DATA/wallet.dat.bak.$(date +%Y%m%d%H%M%S)" |
| 114 | CONVERTED=$(mktemp) | 169 | cp "$WINE_DATA/wallet.dat" "$BK" |
| 115 | rm "$CONVERTED" # db_load needs a non-existing file | 170 | echo " Backed up existing GUI wallet -> $BK" |
| 171 | fi | ||
| 172 | DUMP=$(mktemp); CONVERTED=$(mktemp); rm -f "$CONVERTED" | ||
| 173 | echo " Converting Linux wallet format..." | ||
| 116 | db_dump -p "$LINUX_DATA/wallet.dat" > "$DUMP" | 174 | db_dump -p "$LINUX_DATA/wallet.dat" > "$DUMP" |
| 117 | echo " Converting format..." | ||
| 118 | db_load -f "$DUMP" "$CONVERTED" | 175 | db_load -f "$DUMP" "$CONVERTED" |
| 119 | echo " Installing converted wallet..." | ||
| 120 | cp "$CONVERTED" "$WINE_DATA/wallet.dat" | 176 | cp "$CONVERTED" "$WINE_DATA/wallet.dat" |
| 121 | rm "$DUMP" "$CONVERTED" | 177 | rm -f "$DUMP" "$CONVERTED" |
| 122 | echo " Wallet imported ✓" | 178 | echo " Wallet imported." |
| 123 | else | 179 | else |
| 124 | echo " Skipping. Re-run script to import later." | 180 | echo " Skipped. Re-run to import later." |
| 125 | fi | 181 | fi |
| 126 | else | 182 | else |
| 127 | echo "==> No Linux wallet found, starting fresh." | 183 | echo "==> No Linux wallet found, starting fresh." |
| 128 | fi | 184 | fi |
| 129 | 185 | ||
| 130 | # ---- Launcher | 186 | # ---- Launcher |
| 131 | echo "==> Creating bitflash command..." | 187 | echo "==> Creating 'bitflash' command..." |
| 132 | mkdir -p "$BIN_DIR" | 188 | mkdir -p "$BIN_DIR" |
| 133 | LAUNCHER_FILE="$BIN_DIR/bitflash" | 189 | LAUNCHER_FILE="$BIN_DIR/bitflash" |
| 134 | printf '#!/usr/bin/env bash\n' > "$LAUNCHER_FILE" | 190 | cat > "$LAUNCHER_FILE" <<EOF |
| 135 | printf 'RELAY="${BTF_RELAY:-}"\n' >> "$LAUNCHER_FILE" | 191 | #!/usr/bin/env bash |
| 136 | printf 'ANNOUNCE=""\n' >> "$LAUNCHER_FILE" | 192 | RELAY="\${BTF_RELAY:-}" |
| 137 | printf '[ -n "$RELAY" ] && ANNOUNCE="-announcerelay=$RELAY"\n' >> "$LAUNCHER_FILE" | 193 | ANNOUNCE="" |
| 138 | printf 'WINEDLLOVERRIDES="libdb_cxx-6.2=n" wine "%s/bitflash.exe" $ANNOUNCE "$@"\n' "$INSTALL_DIR" >> "$LAUNCHER_FILE" | 194 | [ -n "\$RELAY" ] && ANNOUNCE="-announcerelay=\$RELAY" |
| 195 | WINEDLLOVERRIDES="libdb_cxx-6.2=n" wine "$EXE" \$ANNOUNCE "\$@" | ||
| 196 | EOF | ||
| 139 | chmod +x "$LAUNCHER_FILE" | 197 | chmod +x "$LAUNCHER_FILE" |
| 140 | echo " Launcher created: $(cat "$LAUNCHER_FILE" | wc -l) lines" | ||
| 141 | 198 | ||
| 142 | if [[ ":$PATH:" != *":$HOME/.local/bin:"* ]]; then | 199 | if [[ ":$PATH:" != *":$BIN_DIR:"* ]] && ! grep -qF 'export PATH="$HOME/.local/bin:$PATH"' "$HOME/.bashrc" 2>/dev/null; then |
| 143 | echo "==> Adding ~/.local/bin to PATH..." | ||
| 144 | echo 'export PATH="$HOME/.local/bin:$PATH"' >> "$HOME/.bashrc" | 200 | echo 'export PATH="$HOME/.local/bin:$PATH"' >> "$HOME/.bashrc" |
| 201 | echo " Added ~/.local/bin to PATH (restart shell or: source ~/.bashrc)" | ||
| 145 | fi | 202 | fi |
| 146 | export PATH="$HOME/.local/bin:$PATH" | ||
| 147 | 203 | ||
| 148 | echo | 204 | echo |
| 149 | echo "==================================================================" | 205 | echo "==================================================================" |
| 150 | echo " DONE." | 206 | echo " DONE." |
| 151 | echo " Launch: bitflash" | 207 | echo " Launch: bitflash" |
| 152 | echo " Mine: bitflash -gen" | 208 | echo " Mine: bitflash -gen" |
| 153 | echo " Relay: bitflash -announcerelay=YOUR_HOST:8434" | 209 | echo " Relay: bitflash -announcerelay=YOUR_HOST:8434" |
| 154 | echo " Data: $WINE_DATA" | 210 | echo " Data: $WINE_DATA" |
| 155 | echo "==================================================================" | 211 | echo "==================================================================" |