Files
mtgc/scripts/windows-publish.sh
Garrett Mills 5d8549af2e Windows installer: cross-build on Linux and publish to a downloads folder
scripts/windows-publish.sh builds the NSIS setup.exe for
x86_64-pc-windows-msvc through cargo-xwin, versioned like the RPM and
APK, and copies it into a folder to serve: the newest three installers,
a stable mtgc-latest-x64-setup.exe, and SHA256SUMS.
2026-10-16 20:11:02 -05:00

150 lines
4.7 KiB
Bash
Executable File

#!/usr/bin/env bash
#
# windows-publish.sh — cross-build the Windows installer and add it to a downloads folder.
#
# Usage:
# ./scripts/windows-publish.sh ~/win-mtgc
#
# Or through npm: `npm run windows:publish -- ~/win-mtgc`.
#
# Options:
# --allow-dirty publish uncommitted changes; the file name gets a -dirty suffix
#
# Environment:
# MTGC_WINDOWS_DIR downloads directory, if not given as an argument
#
# Builds on Linux for x86_64-pc-windows-msvc through cargo-xwin, which fetches
# the MSVC CRT and Windows SDK into ~/.cache/cargo-xwin the first time. Only
# the NSIS installer can be made off Windows (MSI needs WiX). The directory is
# public — serve it as is. Needs `sudo dnf install mingw-nsis-base llvm lld
# clang`, `cargo install --locked cargo-xwin`, `rustup target add
# x86_64-pc-windows-msvc`, and the desktop setup from ./scripts/devel-setup.sh.
#
set -euo pipefail
usage() {
sed -n '3,22p' "$0" | sed 's/^# \{0,1\}//'
exit "${1:-0}"
}
info() { printf '\033[1;34m==>\033[0m %s\n' "$*"; }
warn() { printf '\033[1;33m[warn]\033[0m %s\n' "$*"; }
err() { printf '\033[1;31m[err]\033[0m %s\n' "$*" >&2; }
PACKAGE="mtgc"
TARGET="x86_64-pc-windows-msvc"
LATEST="$PACKAGE-latest-x64-setup.exe"
KEEP=3 # versions kept in the folder, so a bad release can be rolled back
allow_dirty=false
dir="${MTGC_WINDOWS_DIR:-}"
while [ $# -gt 0 ]; do
case "$1" in
--allow-dirty) allow_dirty=true ;;
-h | --help) usage ;;
-*) usage 1 ;;
*) dir="$1" ;;
esac
shift
done
[ -n "$dir" ] || usage 1
mkdir -p "$dir"
dir="$(cd "$dir" && pwd)"
repo_root="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
cd "$repo_root"
# --- 1. Tools ---------------------------------------------------------------
for tool in makensis llvm-rc lld-link clang; do
if ! command -v "$tool" >/dev/null 2>&1; then
err "$tool not found. Install them: sudo dnf install mingw-nsis-base llvm lld clang"
exit 1
fi
done
if ! command -v cargo-xwin >/dev/null 2>&1; then
err "cargo-xwin not found. Install it: cargo install --locked cargo-xwin"
exit 1
fi
if command -v rustup >/dev/null 2>&1 && ! rustup target list --installed | grep -qxF "$TARGET"; then
err "the $TARGET Rust target is missing. Add it: rustup target add $TARGET"
exit 1
fi
# --- 2. Version -------------------------------------------------------------
#
# The installer's own version stays numeric (NSIS insists); a dirty build is
# only marked in the file name.
source scripts/lib/release-version.sh
file_version="$release_version"
if $release_dirty; then
if ! $allow_dirty; then
err "the working tree has uncommitted changes; commit them or pass --allow-dirty"
exit 1
fi
file_version="$release_version-dirty"
fi
# --- 3. Only ever move forward ----------------------------------------------
newest=0
for exe in "$dir/${PACKAGE}"_*_x64-setup.exe; do
[ -e "$exe" ] || continue
patch="${exe##*/${PACKAGE}_}"
patch="${patch%_x64-setup.exe}"
patch="${patch%-dirty}"
patch="${patch##*.}"
[ "$patch" -gt "$newest" ] && newest="$patch"
done
if [ "$release_count" -le "$newest" ]; then
err "version $release_version is not newer than the folder's x.y.$newest; commit something first"
exit 1
fi
# --- 4. Build ---------------------------------------------------------------
info "building $file_version for $TARGET"
outputs="target/$TARGET/release/bundle/nsis"
rm -rf "$outputs"
cargo tauri build --runner cargo-xwin --target "$TARGET" --bundles nsis --ci \
--config "{\"version\":\"$release_version\"}"
built="$(find "$outputs" -name '*-setup.exe' | head -1)"
if [ -z "$built" ]; then
err "the build produced no installer under $outputs"
exit 1
fi
# --- 5. Publish -------------------------------------------------------------
name="${PACKAGE}_${file_version}_x64-setup.exe"
cp "$built" "$dir/$name"
# A copy, not a symlink, so the stable link survives rsync and any static host.
cp "$built" "$dir/.$LATEST.tmp"
mv -f "$dir/.$LATEST.tmp" "$dir/$LATEST"
# Drop all but the newest few versions.
mapfile -t old < <(find "$dir" -maxdepth 1 -name "${PACKAGE}_*_x64-setup.exe" | sort -V | head -n -"$KEEP")
if [ "${#old[@]}" -gt 0 ]; then
rm -f "${old[@]}"
fi
(cd "$dir" && sha256sum "$LATEST" "${PACKAGE}"_*_x64-setup.exe >SHA256SUMS)
# --- 6. How to use it -------------------------------------------------------
info "published $file_version"
cat <<EOF
Serve $dir as is. It holds:
$name
$LATEST (always the newest)
SHA256SUMS
On Windows, download and run the setup.exe; running a newer one upgrades the
installed app in place. The installer is unsigned, so SmartScreen warns about
an unknown publisher the first time: More info → Run anyway.
EOF