ci: publish windows-arm64 desktop + windows amy/geode release assets

Follow-up to the linux-arm64 CI leg (feat/release-linux-arm64). Extends the
release matrix to Windows in three places, all on free public-repo hosted
GitHub runners:

* build-desktop: adds windows-11-arm (arm64) alongside the existing
  windows-latest (x64). jpackage/jlink on Windows arm64 produce arm64 MSIs
  natively; the same packageReleaseMsi + createReleaseDistributable task
  list is used unchanged and the portable-archive step already parameterises
  on ${{ matrix.arch }}.

* build-cli: adds windows-latest (x64) and windows-11-arm (arm64) legs
  running :cli:amyImage. Windows has no jpackageDeb/Rpm and MSI-for-CLI is
  deferred (portable zip is the documented Windows install path); the
  headless-lib assertion runs unchanged under git-bash. amyImage now emits
  both a POSIX `bin/amy` shell launcher AND a Windows `bin/amy.bat`
  launcher into the flat image so the tree layout is uniform regardless of
  build host. The .bat pins UTF-8 (chcp 65001) for sun.jnu.encoding, same
  reason the installDist .bat was already patched.

* build-geode: adds windows-latest + windows-11-arm legs running
  :geode:geodeImage. The existing --port smoke test is generalised to pick
  bin/geode.bat on Windows; NIP-11 fetch via curl works unchanged under
  git-bash on GH windows runners. Same dual-launcher pattern as amy.

scripts/asset-name.sh: collect_cli_assets and collect_geode_assets now
package the flat image as .zip on Windows (7z when available, falling
back to `zip`, then a portable python3 zipfile.ZipFile invocation). Every
other OS continues to use tar.gz. Adds the expected Windows examples to
the header block.

BUILDING.md: mentions the windows-11-arm runner and updates the asset
count in the Release runbook. No asset-naming contract changes — the
existing amethyst-desktop-<v>-windows-<arch>.<ext>, amy-<v>-windows-<arch>.zip,
and geode-<v>-windows-<arch>.zip shapes were already in scope, they just
weren't produced by any CI leg before.

Local validation on macOS arm64 (build host: JDK 21, gradle 9.5.0):
  ./gradlew :cli:amyImage     -> bin/amy + bin/amy.bat both present
  ./gradlew :geode:geodeImage -> bin/geode + bin/geode.bat both present
  ./bin/amy --help            -> parses (unix launcher unbroken)
  ./bin/geode --port 17447    -> NIP-11 served, "supported_nips" present
  collect_cli_assets windows arm64 ...   -> valid .zip with bin/amy.bat
  collect_geode_assets windows x64 ...   -> valid .zip with bin/geode.bat
  actionlint .github/workflows/create-release.yml   -> no new findings
  Cross-compile is impossible for jlink/jpackage, so end-to-end
  Windows-runtime validation still happens on GH CI on the first PR
  build; nothing in this change can be verified any harder locally.
This commit is contained in:
mstrofnone
2026-08-05 17:31:52 -04:00
committed by Vitor Pamplona
parent 79d7d27198
commit cd2ce05ee8
5 changed files with 185 additions and 39 deletions
+75 -14
View File
@@ -24,6 +24,8 @@
# amethyst-desktop-1.08.0-macos-arm64.dmg
# amethyst-desktop-1.08.0-windows-x64.msi
# amethyst-desktop-1.08.0-windows-x64.zip
# amethyst-desktop-1.08.0-windows-arm64.msi
# amethyst-desktop-1.08.0-windows-arm64.zip
# amethyst-desktop-1.08.0-linux-x64.deb
# amethyst-desktop-1.08.0-linux-x64.rpm
# amethyst-desktop-1.08.0-linux-x64.AppImage
@@ -42,6 +44,8 @@
# amy-1.08.0-linux-arm64.tar.gz
# amy-1.08.0-linux-arm64.deb
# amy-1.08.0-linux-arm64.rpm
# amy-1.08.0-windows-x64.zip
# amy-1.08.0-windows-arm64.zip
# geode-1.08.0-macos-arm64.tar.gz
# geode-1.08.0-linux-x64.tar.gz
# geode-1.08.0-linux-x64.deb
@@ -49,6 +53,8 @@
# geode-1.08.0-linux-arm64.tar.gz
# geode-1.08.0-linux-arm64.deb
# geode-1.08.0-linux-arm64.rpm
# geode-1.08.0-windows-x64.zip
# geode-1.08.0-windows-arm64.zip
#
# Two assets break the family/arch shape on purpose: the no-JRE jar bundles for
# Homebrew-core are pure JVM bytecode (no bundled runtime), so a single
@@ -121,10 +127,14 @@ collect_assets() {
#
# Expected inputs:
# cli/build/amy-image/amy/ flat app-image built by :cli:amyImage
# (bin/amy + lib/*.jar + runtime/)
# (bin/amy + bin/amy.bat + lib/*.jar + runtime/)
# cli/build/jpackage/*.deb from :cli:jpackageDeb (Linux only)
# cli/build/jpackage/*.rpm from :cli:jpackageRpm (Linux only)
#
# On Windows the flat image is packaged as .zip (native archive format,
# preserves file layout without requiring a tar tool at install time). Every
# other OS uses tar.gz.
#
# Usage: collect_cli_assets <family> <arch> <version> <dest_dir>
collect_cli_assets() {
local family="$1" arch="$2" version="$3" dest="$4"
@@ -133,14 +143,39 @@ collect_cli_assets() {
abs_dest="$(cd "$dest" && pwd)"
shopt -s nullglob
# 1. Tar the flat app-image into amy-<version>-<family>-<arch>.tar.gz.
# This is the portable-across-OS asset — macOS runners produce only
# this one.
# 1. Archive the flat app-image into amy-<version>-<family>-<arch>.<ext>.
# macOS runners produce only this one. Windows uses .zip; every other
# OS uses tar.gz.
local app_image="cli/build/amy-image/amy"
if [ -d "$app_image" ]; then
local tarball="$abs_dest/$(cli_asset_name "$family" "$arch" "$version" tar.gz)"
( cd "$(dirname "$app_image")" && tar czf "$tarball" "$(basename "$app_image")" )
echo "Collected: $tarball"
if [ "$family" = "windows" ]; then
local zipfile="$abs_dest/$(cli_asset_name "$family" "$arch" "$version" zip)"
# Prefer 7z when available (bash+7zip is standard on GH windows runners),
# else fall back to a portable python3 zipfile. `zip` itself is not always
# present on GH windows runners.
if command -v 7z >/dev/null 2>&1; then
( cd "$(dirname "$app_image")" && 7z a -tzip "$zipfile" "$(basename "$app_image")/" >/dev/null )
elif command -v zip >/dev/null 2>&1; then
( cd "$(dirname "$app_image")" && zip -qr "$zipfile" "$(basename "$app_image")" )
else
python3 - "$app_image" "$zipfile" <<'PY'
import os, sys, zipfile
src, dst = sys.argv[1], sys.argv[2]
root = os.path.dirname(src)
base = os.path.basename(src)
with zipfile.ZipFile(dst, "w", zipfile.ZIP_DEFLATED) as zf:
for dirpath, _dirs, files in os.walk(src):
for f in files:
p = os.path.join(dirpath, f)
zf.write(p, os.path.relpath(p, root))
PY
fi
echo "Collected: $zipfile"
else
local tarball="$abs_dest/$(cli_asset_name "$family" "$arch" "$version" tar.gz)"
( cd "$(dirname "$app_image")" && tar czf "$tarball" "$(basename "$app_image")" )
echo "Collected: $tarball"
fi
fi
# 2. Linux native installers (.deb, .rpm). jpackage writes them directly
@@ -166,10 +201,14 @@ collect_cli_assets() {
#
# Expected inputs:
# geode/build/geode-image/geode/ flat app-image built by :geode:geodeImage
# (bin/geode + lib/*.jar + runtime/ + share/)
# (bin/geode + bin/geode.bat + lib/*.jar
# + runtime/ + share/)
# geode/build/jpackage/*.deb from :geode:jpackageDeb (Linux only)
# geode/build/jpackage/*.rpm from :geode:jpackageRpm (Linux only)
#
# On Windows the flat image is packaged as .zip; every other OS uses tar.gz.
# See the matching comment in collect_cli_assets for the tool-selection order.
#
# Usage: collect_geode_assets <family> <arch> <version> <dest_dir>
collect_geode_assets() {
local family="$1" arch="$2" version="$3" dest="$4"
@@ -178,14 +217,36 @@ collect_geode_assets() {
abs_dest="$(cd "$dest" && pwd)"
shopt -s nullglob
# 1. Tar the flat app-image into geode-<version>-<family>-<arch>.tar.gz.
# This is the portable-across-OS asset — macOS runners produce only
# this one.
# 1. Archive the flat app-image into geode-<version>-<family>-<arch>.<ext>.
# macOS runners produce only this one. Windows uses .zip; every other
# OS uses tar.gz.
local app_image="geode/build/geode-image/geode"
if [ -d "$app_image" ]; then
local tarball="$abs_dest/$(geode_asset_name "$family" "$arch" "$version" tar.gz)"
( cd "$(dirname "$app_image")" && tar czf "$tarball" "$(basename "$app_image")" )
echo "Collected: $tarball"
if [ "$family" = "windows" ]; then
local zipfile="$abs_dest/$(geode_asset_name "$family" "$arch" "$version" zip)"
if command -v 7z >/dev/null 2>&1; then
( cd "$(dirname "$app_image")" && 7z a -tzip "$zipfile" "$(basename "$app_image")/" >/dev/null )
elif command -v zip >/dev/null 2>&1; then
( cd "$(dirname "$app_image")" && zip -qr "$zipfile" "$(basename "$app_image")" )
else
python3 - "$app_image" "$zipfile" <<'PY'
import os, sys, zipfile
src, dst = sys.argv[1], sys.argv[2]
root = os.path.dirname(src)
base = os.path.basename(src)
with zipfile.ZipFile(dst, "w", zipfile.ZIP_DEFLATED) as zf:
for dirpath, _dirs, files in os.walk(src):
for f in files:
p = os.path.join(dirpath, f)
zf.write(p, os.path.relpath(p, root))
PY
fi
echo "Collected: $zipfile"
else
local tarball="$abs_dest/$(geode_asset_name "$family" "$arch" "$version" tar.gz)"
( cd "$(dirname "$app_image")" && tar czf "$tarball" "$(basename "$app_image")" )
echo "Collected: $tarball"
fi
fi
# 2. Linux native installers (.deb, .rpm). jpackage writes them directly