#!/usr/bin/env bash # # deb-publish.sh — build the desktop app's .deb and add it to a signed apt repo. # # Usage: # DEB_REPO_URL=https://deb.example.com/mtgc \ # ./scripts/deb-publish.sh --init-key ~/deb-mtgc # first time # ./scripts/deb-publish.sh ~/deb-mtgc # every release after # # Or through npm: `npm run deb:publish -- ~/deb-mtgc`. # # Options: # --init-key generate the package signing key (once, ever; skip it if # rpm-publish.sh already made one, the two share it) # --allow-dirty publish uncommitted changes; the version gets a ~dirty suffix # # Environment: # MTGC_DEB_DIR repo directory, if not given as an argument # DEB_REPO_URL public address of the repo directory; needed the first time, # and changes the address in mtgc.sources if set later # MTGC_DEB_GNUPGHOME where the key lives (default ~/.local/share/mtgc/gnupg) # # The whole repo directory is public — the signing key lives elsewhere — so # serve it as is. Needs reprepro and dpkg (`sudo dnf install reprepro dpkg`) # and the desktop setup from ./scripts/devel-setup.sh. # set -euo pipefail usage() { sed -n '3,26p' "$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" SUITE="stable" GNUPGHOME="${MTGC_DEB_GNUPGHOME:-$HOME/.local/share/mtgc/gnupg}" KEEP=3 # versions kept in the repo, so a bad release can be downgraded from init_key=false allow_dirty=false dir="${MTGC_DEB_DIR:-}" while [ $# -gt 0 ]; do case "$1" in --init-key) init_key=true ;; --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 reprepro dpkg-deb gpg; do if ! command -v "$tool" >/dev/null 2>&1; then err "$tool not found. Install them: sudo dnf install reprepro dpkg" exit 1 fi done # --- 2. Package signing key ------------------------------------------------- # # The same key and keyring as rpm-publish.sh, with no passphrase so publishing # runs unattended. Every machine trusts it once, when it adds the repo; lose it # and each one has to take a new key before it takes another update. gpg_() { gpg --homedir "$GNUPGHOME" --batch "$@"; } key_fpr() { gpg_ --list-secret-keys --with-colons 2>/dev/null | awk -F: '/^fpr/ { print $10; exit }'; } if $init_key; then if [ -n "$(key_fpr)" ]; then err "a signing key already exists in $GNUPGHOME; not making another" exit 1 fi info "generating the package signing key in $GNUPGHOME" mkdir -p "$GNUPGHOME" chmod 700 "$GNUPGHOME" gpg_ --pinentry-mode loopback --passphrase '' \ --quick-gen-key "mtgc packages" rsa4096 sign never 2>/dev/null warn "Back up $GNUPGHOME." warn "Without it, every machine has to trust a new key before it takes another update." fi fpr="$(key_fpr)" if [ -z "$fpr" ]; then err "no package signing key in $GNUPGHOME. Run once with --init-key." exit 1 fi # --- 3. Version ------------------------------------------------------------- source scripts/lib/release-version.sh deb_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 # ~ sorts before everything, so the clean build of this version still wins. deb_version="$release_version~dirty" fi # --- 4. The repo's address -------------------------------------------------- # # mtgc.sources carries the public key inline, so a machine adds the repo with # one download and no separate keyring step. repo_url="${DEB_REPO_URL:-}" repo_url="${repo_url%/}" case "$repo_url" in "" | http://* | https://*) ;; *) err "DEB_REPO_URL must be an http(s):// address"; exit 1 ;; esac if [ -z "$repo_url" ] && [ ! -f "$dir/$PACKAGE.sources" ]; then err "no repo in $dir yet; set DEB_REPO_URL to the address $dir will be served at" exit 1 fi gpg_ --armor --export "$fpr" >"$dir/$PACKAGE.asc" if [ -z "$repo_url" ]; then repo_url="$(sed -n 's/^URIs: //p' "$dir/$PACKAGE.sources")" fi { cat <"$dir/$PACKAGE.sources" # A landing page for whoever opens the repo's address in a browser. cat >"$dir/README.html" < mtgc apt repo

mtgc for Debian & Ubuntu

An apt repository for the mtgc desktop app (amd64).

Install

Add the repo once. The file includes the signing key, so you don't need a separate keyring step.

sudo curl -fsSLo /etc/apt/sources.list.d/$PACKAGE.sources $repo_url/$PACKAGE.sources
sudo apt update && sudo apt install $PACKAGE

From then on, sudo apt upgrade picks up new releases.

Requirements

A 64-bit x86 system with glibc 2.43 or newer (check with ldd --version). Older releases, such as Ubuntu 24.04 and Debian 13, can't install it.

Uninstall

sudo apt remove $PACKAGE
sudo rm /etc/apt/sources.list.d/$PACKAGE.sources

Signing key: $PACKAGE.asc, fingerprint $fpr

EOF mkdir -p "$dir/conf" cat >"$dir/conf/distributions" </dev/null | awk '{ print $NF }') if [ "$release_count" -le "$newest" ]; then err "version $release_version is not newer than the repo's x.y.$newest; commit something first" exit 1 fi # --- 6. Build --------------------------------------------------------------- info "building $deb_version" outputs="target/release/bundle/deb" rm -rf "$outputs" # reprepro files packages by section, which Tauri leaves out unless told. cargo tauri build --bundles deb --ci \ --config "{\"version\":\"$release_version\",\"bundle\":{\"linux\":{\"deb\":{\"section\":\"games\",\"priority\":\"optional\"}}}}" built="$(find "$outputs" -maxdepth 1 -name '*.deb' | head -1)" if [ -z "$built" ]; then err "the build produced no .deb under $outputs" exit 1 fi # Tauri's deb config has no release field, so mark a dirty build by repacking. if [ "$deb_version" != "$release_version" ]; then unpacked="$outputs/unpacked" dpkg-deb -R "$built" "$unpacked" sed -i "s/^Version: .*/Version: $deb_version/" "$unpacked/DEBIAN/control" repacked="$outputs/${PACKAGE}_${deb_version}_amd64.deb" dpkg-deb --root-owner-group -b "$unpacked" "$repacked" >/dev/null rm -rf "$unpacked" "$built" built="$repacked" fi # --- 7. Publish ------------------------------------------------------------- # # reprepro writes the pool, the indexes and the signed Release in one go, and # drops all but the newest $KEEP versions; if it fails, nothing changed. info "adding it to the repo" if ! repo_ includedeb "$SUITE" "$built"; then err "reprepro failed; nothing was published" exit 1 fi # --- 8. How to use it ------------------------------------------------------- info "published $deb_version" cat <