summaryrefslogtreecommitdiffstats
path: root/scripts
diff options
context:
space:
mode:
Diffstat (limited to 'scripts')
-rwxr-xr-xscripts/deploy.sh9
-rw-r--r--scripts/installers/linux.sh.in78
-rw-r--r--scripts/installers/windows.ps1.in75
-rwxr-xr-xscripts/publish.sh66
-rw-r--r--scripts/render-installer.py49
5 files changed, 271 insertions, 6 deletions
diff --git a/scripts/deploy.sh b/scripts/deploy.sh
index 99129ad..b18d462 100755
--- a/scripts/deploy.sh
+++ b/scripts/deploy.sh
@@ -1,5 +1,6 @@
1#!/usr/bin/env bash 1#!/usr/bin/env bash
2# Upload web/ and verify every published artifact over the public URL. 2# Upload web/ and verify every published artifact (GUI app and installers,
3# terminal client and server) over the public URL.
3# 4#
4# Usage: 5# Usage:
5# ./scripts/deploy.sh user@host:/srv/zagan.bet https://zagan.bet/ 6# ./scripts/deploy.sh user@host:/srv/zagan.bet https://zagan.bet/
@@ -50,7 +51,11 @@ done
50[ -f web/index.php ] || die "web/index.php is missing" 51[ -f web/index.php ] || die "web/index.php is missing"
51 52
52shopt -s nullglob 53shopt -s nullglob
53artifacts=(web/bin/client/zagan-bet-* web/bin/server/zagan-bet-*) 54artifacts=(
55 web/bin/gui/zagan-bet-*
56 web/bin/client/zagan-bet-*
57 web/bin/server/zagan-bet-*
58)
54shopt -u nullglob 59shopt -u nullglob
55[ ${#artifacts[@]} -gt 0 ] || die "no artifacts in web/bin; run scripts/publish.sh first" 60[ ${#artifacts[@]} -gt 0 ] || die "no artifacts in web/bin; run scripts/publish.sh first"
56 61
diff --git a/scripts/installers/linux.sh.in b/scripts/installers/linux.sh.in
new file mode 100644
index 0000000..5b53c02
--- /dev/null
+++ b/scripts/installers/linux.sh.in
@@ -0,0 +1,78 @@
1#!/usr/bin/env bash
2# Zagan-Bet desktop installer for Linux.
3#
4# Verifies the app against its published SHA-256, installs it for the current
5# user and adds a launcher. Put the downloaded app next to this script, or run
6# the script on its own and it will fetch the matching binary.
7set -euo pipefail
8
9VERSION="__VERSION__"
10BINARY="__BINARY__"
11SHA256="__SHA256__"
12URL="__URL__"
13
14say() { printf '%s\n' "$*"; }
15die() { printf 'install: %s\n' "$*" >&2; exit 1; }
16
17command -v sha256sum >/dev/null 2>&1 || die "sha256sum is required"
18
19here=$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)
20source_file=""
21for candidate in "$here/$BINARY" "./$BINARY" "$HOME/Downloads/$BINARY"; do
22 if [ -f "$candidate" ]; then
23 source_file=$candidate
24 break
25 fi
26done
27
28cleanup=""
29if [ -z "$source_file" ]; then
30 command -v curl >/dev/null 2>&1 || die "put $BINARY next to this installer, or install curl"
31 cleanup=$(mktemp) || die "cannot create a temporary file"
32 say "Downloading $BINARY..."
33 curl -fsSL --max-time 300 "$URL" -o "$cleanup" || die "download failed"
34 source_file=$cleanup
35fi
36trap 'if [ -n "$cleanup" ]; then rm -f "$cleanup"; fi' EXIT
37
38actual=$(sha256sum "$source_file" | awk '{print $1}')
39[ "$actual" = "$SHA256" ] || die "checksum mismatch: expected $SHA256, got $actual"
40
41bin_dir="${XDG_BIN_HOME:-$HOME/.local/bin}"
42data_dir="${XDG_DATA_HOME:-$HOME/.local/share}"
43app="$bin_dir/zagan-bet-gui"
44desktop_dir="$data_dir/applications"
45icon_dir="$data_dir/icons/hicolor/scalable/apps"
46
47mkdir -p "$bin_dir" "$desktop_dir" "$icon_dir"
48install -m 0755 "$source_file" "$app"
49
50cat > "$icon_dir/zagan-bet.svg" <<'ICON'
51__ICON__
52ICON
53
54cat > "$desktop_dir/zagan-bet.desktop" <<DESKTOP
55[Desktop Entry]
56Type=Application
57Name=Zagan-Bet
58Comment=Slots, roulette, blackjack and crash
59Exec=$app
60Icon=zagan-bet
61Terminal=false
62Categories=Game;
63DESKTOP
64chmod 0644 "$desktop_dir/zagan-bet.desktop" "$icon_dir/zagan-bet.svg"
65
66if command -v update-desktop-database >/dev/null 2>&1; then
67 update-desktop-database "$desktop_dir" >/dev/null 2>&1 || true
68fi
69if command -v gtk-update-icon-cache >/dev/null 2>&1; then
70 gtk-update-icon-cache -q "$data_dir/icons/hicolor" >/dev/null 2>&1 || true
71fi
72
73say "Installed Zagan-Bet $VERSION to $app"
74say "A launcher was added to your applications menu."
75case ":$PATH:" in
76 *":$bin_dir:"*) ;;
77 *) say "Add $bin_dir to PATH to start it as 'zagan-bet-gui'." ;;
78esac
diff --git a/scripts/installers/windows.ps1.in b/scripts/installers/windows.ps1.in
new file mode 100644
index 0000000..fd2b8b3
--- /dev/null
+++ b/scripts/installers/windows.ps1.in
@@ -0,0 +1,75 @@
1# Zagan-Bet desktop installer for Windows.
2#
3# Verifies the app against its published SHA-256, installs it for the current
4# user and adds a Start-menu shortcut. Put the downloaded app next to this
5# script, or run the script on its own and it will fetch the matching binary.
6#
7# Run it with:
8# powershell -ExecutionPolicy Bypass -File .\zagan-bet-gui-*-install.ps1
9$ErrorActionPreference = "Stop"
10
11$Version = "__VERSION__"
12$Binary = "__BINARY__"
13$Sha256 = "__SHA256__"
14$Url = "__URL__"
15
16function Stop-Install([string]$Message) {
17 Write-Host "install: $Message" -ForegroundColor Red
18 exit 1
19}
20
21$here = Split-Path -Parent $MyInvocation.MyCommand.Path
22$candidates = @(
23 (Join-Path $here $Binary),
24 (Join-Path (Get-Location) $Binary),
25 (Join-Path $env:USERPROFILE "Downloads\$Binary")
26)
27$sourceFile = $null
28foreach ($candidate in $candidates) {
29 if (Test-Path -LiteralPath $candidate) {
30 $sourceFile = $candidate
31 break
32 }
33}
34
35$temporary = $null
36if (-not $sourceFile) {
37 $temporary = Join-Path ([System.IO.Path]::GetTempPath()) $Binary
38 Write-Host "Downloading $Binary..."
39 try {
40 Invoke-WebRequest -Uri $Url -OutFile $temporary -UseBasicParsing
41 } catch {
42 Stop-Install "download failed: $_"
43 }
44 $sourceFile = $temporary
45}
46
47try {
48 $actual = (Get-FileHash -LiteralPath $sourceFile -Algorithm SHA256).Hash.ToLowerInvariant()
49 if ($actual -ne $Sha256.ToLowerInvariant()) {
50 Stop-Install "checksum mismatch: expected $Sha256, got $actual"
51 }
52
53 $installDir = Join-Path $env:LOCALAPPDATA "Programs\Zagan-Bet"
54 New-Item -ItemType Directory -Force -Path $installDir | Out-Null
55 $target = Join-Path $installDir "zagan-bet-gui.exe"
56 Copy-Item -LiteralPath $sourceFile -Destination $target -Force
57
58 $startMenu = Join-Path $env:APPDATA "Microsoft\Windows\Start Menu\Programs"
59 New-Item -ItemType Directory -Force -Path $startMenu | Out-Null
60 $shortcutPath = Join-Path $startMenu "Zagan-Bet.lnk"
61 $shell = New-Object -ComObject WScript.Shell
62 $shortcut = $shell.CreateShortcut($shortcutPath)
63 $shortcut.TargetPath = $target
64 $shortcut.WorkingDirectory = $installDir
65 $shortcut.IconLocation = "$target,0"
66 $shortcut.Description = "Slots, roulette, blackjack and crash"
67 $shortcut.Save()
68
69 Write-Host "Installed Zagan-Bet $Version to $target"
70 Write-Host "A Start-menu shortcut was created."
71} finally {
72 if ($temporary -and (Test-Path -LiteralPath $temporary)) {
73 Remove-Item -LiteralPath $temporary -Force
74 }
75}
diff --git a/scripts/publish.sh b/scripts/publish.sh
index 8240859..3a04631 100755
--- a/scripts/publish.sh
+++ b/scripts/publish.sh
@@ -3,14 +3,22 @@
3# download page expects: 3# download page expects:
4# 4#
5# zagan-bet-client-<version>-<platform>-<arch>[.exe] 5# zagan-bet-client-<version>-<platform>-<arch>[.exe]
6# zagan-bet-server-<version>-<platform>-<arch> 6# zagan-bet-server-<version>-<platform>-<arch>[.exe]
7# zagan-bet-gui-<version>-<platform>-<arch>[.exe]
8# zagan-bet-gui-<version>-<platform>-<arch>-install.sh
9# zagan-bet-gui-<version>-<platform>-<arch>-install.ps1
7# 10#
8# Cargo always writes target/<triple>/release/<bin>, so the rename happens here 11# Cargo always writes target/<triple>/<profile>/<bin>, so the rename happens
9# after each build. The version comes from Cargo.toml. 12# here after each build. The version comes from Cargo.toml.
13#
14# The GUI uses the workspace's `gui-release` profile (small, stripped, LTO).
15# The public git repository is the GPL source for these binaries, so publish
16# from a clean tree (or set ALLOW_DIRTY=1 for a preview).
10# 17#
11# Usage: 18# Usage:
12# ./scripts/publish.sh 19# ./scripts/publish.sh
13# DEST=/srv/www/bin ./scripts/publish.sh 20# DEST=/srv/www/bin ./scripts/publish.sh
21# URL_BASE=https://staging.example/ ./scripts/publish.sh
14set -euo pipefail 22set -euo pipefail
15 23
16ROOT=$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd) 24ROOT=$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)
@@ -23,6 +31,21 @@ if [ -z "${VERSION:-}" ]; then
23fi 31fi
24 32
25DEST=${DEST:-web/bin} 33DEST=${DEST:-web/bin}
34URL_BASE=${URL_BASE:-https://zagan.2137697.xyz}
35
36if [ "${ALLOW_DIRTY:-0}" != "1" ] && [ -n "$(git status --porcelain)" ]; then
37 echo "the working tree is dirty; commit the release first (or ALLOW_DIRTY=1)" >&2
38 exit 1
39fi
40
41command -v python3 >/dev/null || {
42 echo "python3 is required to render the installers" >&2
43 exit 1
44}
45
46hash_of() {
47 sha256sum "$1" | awk '{print $1}'
48}
26 49
27publish() { 50publish() {
28 local triple=$1 kind=$2 platform=$3 arch=$4 51 local triple=$1 kind=$2 platform=$3 arch=$4
@@ -47,11 +70,46 @@ publish() {
47 echo " $DEST/$kind/$artifact ($(du -h "$DEST/$kind/$artifact" | cut -f1))" 70 echo " $DEST/$kind/$artifact ($(du -h "$DEST/$kind/$artifact" | cut -f1))"
48} 71}
49 72
73publish_gui() {
74 local triple=$1 platform=$2 arch=$3
75 local ext=""
76 if [ "$platform" = windows ]; then
77 ext=".exe"
78 fi
79
80 local artifact="zagan-bet-gui-$VERSION-$platform-$arch$ext"
81 local source="target/$triple/gui-release/zagan-bet-gui$ext"
82
83 echo "==> $artifact"
84 cargo build -p zagan-bet-gui --profile gui-release --quiet --target "$triple"
85
86 mkdir -p "$DEST/gui"
87 install -m 0755 "$source" "$DEST/gui/$artifact"
88 local sha
89 sha=$(hash_of "$DEST/gui/$artifact")
90 echo " $DEST/gui/$artifact ($(du -h "$DEST/gui/$artifact" | cut -f1))"
91
92 if [ "$platform" = linux ]; then
93 python3 scripts/render-installer.py \
94 scripts/installers/linux.sh.in \
95 "$DEST/gui/zagan-bet-gui-$VERSION-linux-$arch-install.sh" \
96 "$VERSION" "$artifact" "$sha" "$URL_BASE/bin/gui/$artifact"
97 elif [ "$platform" = windows ]; then
98 python3 scripts/render-installer.py \
99 scripts/installers/windows.ps1.in \
100 "$DEST/gui/zagan-bet-gui-$VERSION-windows-$arch-install.ps1" \
101 "$VERSION" "$artifact" "$sha" "$URL_BASE/bin/gui/$artifact"
102 fi
103}
104
105publish_gui x86_64-unknown-linux-gnu linux x86_64
106publish_gui x86_64-pc-windows-gnu windows x86_64
107
50publish x86_64-unknown-linux-gnu client linux x86_64 108publish x86_64-unknown-linux-gnu client linux x86_64
51publish i686-unknown-linux-gnu client linux i686 109publish i686-unknown-linux-gnu client linux i686
52publish x86_64-pc-windows-gnu client windows x86_64 110publish x86_64-pc-windows-gnu client windows x86_64
53publish x86_64-unknown-linux-gnu server linux x86_64 111publish x86_64-unknown-linux-gnu server linux x86_64
54publish x86_64-pc-windows-gnu server windows x86_64 112publish x86_64-pc-windows-gnu server windows x86_64
55 113
56echo 114echo
57echo "published $VERSION to $DEST" 115echo "published $VERSION to $DEST"
diff --git a/scripts/render-installer.py b/scripts/render-installer.py
new file mode 100644
index 0000000..1a00551
--- /dev/null
+++ b/scripts/render-installer.py
@@ -0,0 +1,49 @@
1#!/usr/bin/env python3
2"""Fill an installer template with one published artifact's details.
3
4Usage: render-installer.py <template> <output> <version> <binary> <sha256> <url>
5
6The Linux template embeds the application icon so the installer works offline.
7"""
8
9from __future__ import annotations
10
11import pathlib
12import sys
13
14
15def main(argv: list[str]) -> int:
16 if len(argv) != 7:
17 print(
18 "usage: render-installer.py <template> <output> <version> "
19 "<binary> <sha256> <url>",
20 file=sys.stderr,
21 )
22 return 2
23
24 template, output, version, binary, sha256, url = argv[1:]
25 root = pathlib.Path(__file__).resolve().parent.parent
26 icon = (root / "gui" / "assets" / "zagan-bet.svg").read_text()
27
28 text = pathlib.Path(template).read_text()
29 replacements = {
30 "__VERSION__": version,
31 "__BINARY__": binary,
32 "__SHA256__": sha256,
33 "__URL__": url,
34 "__ICON__": icon.rstrip("\n"),
35 }
36 for placeholder, value in replacements.items():
37 text = text.replace(placeholder, value)
38
39 destination = pathlib.Path(output)
40 destination.write_text(text)
41 if destination.suffix == ".sh":
42 destination.chmod(0o755)
43
44 print(f" {destination} ({destination.stat().st_size} bytes)")
45 return 0
46
47
48if __name__ == "__main__":
49 raise SystemExit(main(sys.argv))