| 1 | #!/usr/bin/env bash |
| 2 | # Build a local production-style Reasonix desktop package from this checkout. |
| 3 | # |
| 4 | # This is intentionally worktree-relative: run ./prod_test from any clone or |
| 5 | # git worktree and it will package that exact tree, including dirty local changes. |
| 6 | set -euo pipefail |
| 7 | |
| 8 | usage() { |
| 9 | cat <<'EOF' |
| 10 | Usage: |
| 11 | ./prod_test [platform] [version] [channel] |
| 12 | |
| 13 | Examples: |
| 14 | ./prod_test |
| 15 | ./prod_test darwin/arm64 |
| 16 | ./prod_test darwin/universal v1.5.0-prodtest stable |
| 17 | ./prod_fast_test |
| 18 | |
| 19 | Defaults: |
| 20 | platform auto-detected from this machine; macOS defaults to darwin/universal |
| 21 | version v0.0.0-prodtest.<branch-or-detached>.<short-sha>[.dirty] |
| 22 | channel stable |
| 23 | |
| 24 | Environment overrides: |
| 25 | PROD_TEST_PLATFORM Same as first argument. |
| 26 | PROD_TEST_VERSION Same as second argument. |
| 27 | PROD_TEST_CHANNEL Same as third argument. |
| 28 | PROD_TEST_FAST 1 enables a faster local iteration build: |
| 29 | native host architecture on macOS, no Wails clean, |
| 30 | and no DMG packaging unless overridden below. |
| 31 | PROD_TEST_INSTALL_TOOLS 1 installs missing Wails/create-dmg/nfpm when possible. |
| 32 | Set to 0 to only check and print missing deps. |
| 33 | PROD_TEST_OPEN_DIST 1 opens dist/ after a successful build when supported. |
| 34 | DESKTOP_BUILD_CLEAN 0 skips Wails -clean. |
| 35 | DESKTOP_BUILD_SKIP_DMG 1 skips macOS DMG creation. |
| 36 | EOF |
| 37 | } |
| 38 | |
| 39 | if [[ "${1:-}" == "-h" || "${1:-}" == "--help" ]]; then |
| 40 | usage |
| 41 | exit 0 |
| 42 | fi |
| 43 | |
| 44 | SCRIPT_DIR="$(cd -- "$(dirname -- "${BASH_SOURCE[0]}")" && pwd)" |
| 45 | ROOT="$SCRIPT_DIR" |
| 46 | cd "$ROOT" |
| 47 | |
| 48 | fail() { |
| 49 | echo "prod_test: $*" >&2 |
| 50 | exit 1 |
| 51 | } |
| 52 | |
| 53 | need_cmd() { |
| 54 | command -v "$1" >/dev/null 2>&1 || fail "missing required command: $1" |
| 55 | } |
| 56 | |
| 57 | go_bin_dir() { |
| 58 | local gobin |
| 59 | gobin="$(go env GOBIN)" |
| 60 | if [[ -z "$gobin" ]]; then |
| 61 | gobin="$(go env GOPATH)/bin" |
| 62 | fi |
| 63 | printf '%s' "$gobin" |
| 64 | } |
| 65 | |
| 66 | sanitize_semver_id() { |
| 67 | local value="$1" |
| 68 | value="$(printf '%s' "$value" | tr '[:upper:]' '[:lower:]' | sed -E 's/[^0-9A-Za-z]+/-/g; s/^-+//; s/-+$//; s/-+/-/g')" |
| 69 | printf '%s' "${value:-local}" |
| 70 | } |
| 71 | |
| 72 | detect_platform() { |
| 73 | local os arch machine |
| 74 | case "$(uname -s)" in |
| 75 | Darwin) os="darwin" ;; |
| 76 | Linux) os="linux" ;; |
| 77 | MINGW*|MSYS*|CYGWIN*) os="windows" ;; |
| 78 | *) fail "unsupported host OS: $(uname -s)" ;; |
| 79 | esac |
| 80 | |
| 81 | machine="$(uname -m)" |
| 82 | case "$machine" in |
| 83 | arm64|aarch64) arch="arm64" ;; |
| 84 | x86_64|amd64) arch="amd64" ;; |
| 85 | *) fail "unsupported host architecture: $machine" ;; |
| 86 | esac |
| 87 | |
| 88 | if [[ "$os" == "darwin" ]]; then |
| 89 | # Match the release workflow's single macOS artifact by default. |
| 90 | printf 'darwin/universal' |
| 91 | else |
| 92 | printf '%s/%s' "$os" "$arch" |
| 93 | fi |
| 94 | } |
| 95 | |
| 96 | detect_native_platform() { |
| 97 | local os arch machine |
| 98 | case "$(uname -s)" in |
| 99 | Darwin) os="darwin" ;; |
| 100 | Linux) os="linux" ;; |
| 101 | MINGW*|MSYS*|CYGWIN*) os="windows" ;; |
| 102 | *) fail "unsupported host OS: $(uname -s)" ;; |
| 103 | esac |
| 104 | |
| 105 | machine="$(uname -m)" |
| 106 | case "$machine" in |
| 107 | arm64|aarch64) arch="arm64" ;; |
| 108 | x86_64|amd64) arch="amd64" ;; |
| 109 | *) fail "unsupported host architecture: $machine" ;; |
| 110 | esac |
| 111 | printf '%s/%s' "$os" "$arch" |
| 112 | } |
| 113 | |
| 114 | default_version() { |
| 115 | local ref sha dirty |
| 116 | ref="$(git branch --show-current 2>/dev/null || true)" |
| 117 | if [[ -z "$ref" ]]; then |
| 118 | ref="$(git describe --tags --exact-match 2>/dev/null || true)" |
| 119 | fi |
| 120 | ref="${ref:-detached}" |
| 121 | sha="$(git rev-parse --short=8 HEAD 2>/dev/null || echo unknown)" |
| 122 | dirty="" |
| 123 | if [[ -n "$(git status --porcelain 2>/dev/null)" ]]; then |
| 124 | dirty=".dirty" |
| 125 | fi |
| 126 | printf 'v0.0.0-prodtest.%s.%s%s' "$(sanitize_semver_id "$ref")" "$sha" "$dirty" |
| 127 | } |
| 128 | |
| 129 | install_or_check_tools() { |
| 130 | local os install_tools |
| 131 | os="${1%/*}" |
| 132 | install_tools="${PROD_TEST_INSTALL_TOOLS:-1}" |
| 133 | |
| 134 | need_cmd git |
| 135 | need_cmd go |
| 136 | need_cmd node |
| 137 | export PATH="$(go_bin_dir):$PATH" |
| 138 | |
| 139 | if ! command -v pnpm >/dev/null 2>&1; then |
| 140 | if [[ "$install_tools" == "1" ]]; then |
| 141 | echo "==> installing pnpm" |
| 142 | if command -v corepack >/dev/null 2>&1; then |
| 143 | corepack enable |
| 144 | corepack prepare pnpm@10 --activate |
| 145 | else |
| 146 | need_cmd npm |
| 147 | npm install -g pnpm@10 |
| 148 | fi |
| 149 | else |
| 150 | fail "pnpm not found. Install with: corepack enable && corepack prepare pnpm@10 --activate" |
| 151 | fi |
| 152 | fi |
| 153 | |
| 154 | if ! command -v wails >/dev/null 2>&1; then |
| 155 | if [[ "$install_tools" == "1" ]]; then |
| 156 | echo "==> installing Wails CLI" |
| 157 | go install github.com/wailsapp/wails/v2/cmd/wails@v2.12.0 |
| 158 | else |
| 159 | fail "wails not found. Install with: go install github.com/wailsapp/wails/v2/cmd/wails@v2.12.0" |
| 160 | fi |
| 161 | fi |
| 162 | |
| 163 | case "$os" in |
| 164 | darwin) |
| 165 | if [[ "${DESKTOP_BUILD_SKIP_DMG:-0}" == "1" ]]; then |
| 166 | return |
| 167 | fi |
| 168 | if ! command -v create-dmg >/dev/null 2>&1; then |
| 169 | if [[ "$install_tools" == "1" && -x "$(command -v brew 2>/dev/null || true)" ]]; then |
| 170 | echo "==> installing create-dmg" |
| 171 | brew install create-dmg |
| 172 | else |
| 173 | fail "create-dmg not found. Install with: brew install create-dmg" |
| 174 | fi |
| 175 | fi |
| 176 | ;; |
| 177 | linux) |
| 178 | if ! command -v nfpm >/dev/null 2>&1; then |
| 179 | if [[ "$install_tools" == "1" ]]; then |
| 180 | echo "==> installing nfpm" |
| 181 | go install github.com/goreleaser/nfpm/v2/cmd/nfpm@v2.46.3 |
| 182 | else |
| 183 | fail "nfpm not found. Install with: go install github.com/goreleaser/nfpm/v2/cmd/nfpm@v2.46.3" |
| 184 | fi |
| 185 | fi |
| 186 | ;; |
| 187 | windows) |
| 188 | command -v makensis >/dev/null 2>&1 || fail "makensis not found. Install NSIS first." |
| 189 | ;; |
| 190 | esac |
| 191 | } |
| 192 | |
| 193 | FAST="${PROD_TEST_FAST:-0}" |
| 194 | if [[ "$FAST" == "1" ]]; then |
| 195 | export DESKTOP_BUILD_CLEAN="${DESKTOP_BUILD_CLEAN:-0}" |
| 196 | export DESKTOP_BUILD_SKIP_DMG="${DESKTOP_BUILD_SKIP_DMG:-1}" |
| 197 | fi |
| 198 | |
| 199 | if [[ -n "${1:-}" ]]; then |
| 200 | PLATFORM="$1" |
| 201 | elif [[ -n "${PROD_TEST_PLATFORM:-}" ]]; then |
| 202 | PLATFORM="$PROD_TEST_PLATFORM" |
| 203 | elif [[ "$FAST" == "1" ]]; then |
| 204 | PLATFORM="$(detect_native_platform)" |
| 205 | else |
| 206 | PLATFORM="$(detect_platform)" |
| 207 | fi |
| 208 | VERSION="${2:-${PROD_TEST_VERSION:-$(default_version)}}" |
| 209 | CHANNEL="${3:-${PROD_TEST_CHANNEL:-stable}}" |
| 210 | OPEN_DIST="${PROD_TEST_OPEN_DIST:-1}" |
| 211 | |
| 212 | case "$PLATFORM" in |
| 213 | darwin/*|linux/*|windows/*) ;; |
| 214 | *) fail "platform must look like os/arch, got: $PLATFORM" ;; |
| 215 | esac |
| 216 | |
| 217 | case "$CHANNEL" in |
| 218 | stable|canary) ;; |
| 219 | *) fail "channel must be stable or canary, got: $CHANNEL" ;; |
| 220 | esac |
| 221 | |
| 222 | install_or_check_tools "$PLATFORM" |
| 223 | |
| 224 | if [[ -n "$(git status --porcelain)" ]]; then |
| 225 | echo "==> worktree has uncommitted changes; this build will include them" |
| 226 | git status --short |
| 227 | fi |
| 228 | |
| 229 | wails_json="desktop/wails.json" |
| 230 | tmp_wails="$(mktemp)" |
| 231 | cp "$wails_json" "$tmp_wails" |
| 232 | restore_wails() { |
| 233 | if [[ -f "$tmp_wails" ]]; then |
| 234 | cp "$tmp_wails" "$wails_json" |
| 235 | rm -f "$tmp_wails" |
| 236 | fi |
| 237 | } |
| 238 | trap restore_wails EXIT |
| 239 | |
| 240 | echo "==> root: ." |
| 241 | echo "==> branch: $(git branch --show-current 2>/dev/null || echo detached)" |
| 242 | echo "==> commit: $(git rev-parse --short=8 HEAD 2>/dev/null || echo unknown)" |
| 243 | echo "==> platform: $PLATFORM" |
| 244 | echo "==> version: $VERSION" |
| 245 | echo "==> channel: $CHANNEL" |
| 246 | if [[ "$FAST" == "1" ]]; then |
| 247 | echo "==> fast mode: DESKTOP_BUILD_CLEAN=$DESKTOP_BUILD_CLEAN DESKTOP_BUILD_SKIP_DMG=$DESKTOP_BUILD_SKIP_DMG" |
| 248 | fi |
| 249 | |
| 250 | bash scripts/desktop-build.sh "$PLATFORM" "$VERSION" "$CHANNEL" |
| 251 | |
| 252 | echo "==> restored desktop/wails.json" |
| 253 | echo "==> production-style artifacts are in dist/" |
| 254 | |
| 255 | if [[ "$OPEN_DIST" == "1" ]]; then |
| 256 | case "$(uname -s)" in |
| 257 | Darwin) open dist ;; |
| 258 | Linux) command -v xdg-open >/dev/null 2>&1 && xdg-open dist >/dev/null 2>&1 || true ;; |
| 259 | MINGW*|MSYS*|CYGWIN*) explorer.exe dist 2>/dev/null || true ;; |
| 260 | esac |
| 261 | fi |
| 262 |