#!/bin/bash
set -e
cd "$(dirname "$0")/.."
ROOT="$(pwd)"
if [ -x "$HOME/.cargo/bin/rustc" ]; then
export PATH="$HOME/.cargo/bin:$PATH"
fi
VERSION="${ATOMCODE_VERSION:-}"
if [ -z "$VERSION" ]; then
CARGO_VERSION=$(awk -F'"' '
/^\[workspace\.package\]/ { in_section = 1; next }
/^\[/ { in_section = 0 }
in_section && /^version *=/ { print $2; exit }
' Cargo.toml)
if [ -n "$CARGO_VERSION" ]; then
VERSION="v${CARGO_VERSION}"
fi
fi
if [ -z "$VERSION" ]; then
echo "Could not determine version. Set [workspace.package].version in Cargo.toml,"
echo "or override with ATOMCODE_VERSION=v1.0.0."
exit 1
fi
case "$VERSION" in
v[0-9]*) ;;
*)
echo "Refusing to release with non-vX.Y.Z version: '$VERSION'"
echo "Set ATOMCODE_VERSION=v1.2.3 if you really mean to."
exit 1
;;
esac
echo "Using VERSION=${VERSION}"
DIST="dist/${VERSION}"
mkdir -p "$DIST"
echo "=== AtomCode Release ${VERSION} ==="
echo ""
if [ -d webui ] && command -v npm >/dev/null 2>&1; then
echo "Building webui frontend..."
(cd webui && npm ci && npm run build)
else
echo "warning: skipping webui build (npm not found or webui/ missing); using committed webui/dist" >&2
fi
echo ""
INCLUDE_DAEMON="${ATOMCODE_INCLUDE_DAEMON:-0}"
CARGO_PKG_ARGS=(-p atomcode)
if [ "$INCLUDE_DAEMON" = "1" ]; then
CARGO_PKG_ARGS+=(-p atomcode-daemon)
fi
copy_daemon() {
[ "$INCLUDE_DAEMON" = "1" ] || return 0
local src="$1$3" dst="$2$3"
cp "$src" "$dst"
echo " -> $dst"
}
TARGET_ARM="aarch64-apple-darwin"
echo "[1/6] Building ${TARGET_ARM}..."
rustup target add "$TARGET_ARM" 2>/dev/null || true
cargo build --release --target "$TARGET_ARM" "${CARGO_PKG_ARGS[@]}"
cp "target/${TARGET_ARM}/release/atomcode" "${DIST}/atomcode-${VERSION}-darwin-arm64"
echo " -> ${DIST}/atomcode-${VERSION}-darwin-arm64"
copy_daemon "target/${TARGET_ARM}/release/atomcode-daemon" "${DIST}/atomcode-daemon-${VERSION}-darwin-arm64" ""
TARGET_X86="x86_64-apple-darwin"
echo "[2/6] Building ${TARGET_X86}..."
rustup target add "$TARGET_X86" 2>/dev/null || true
cargo build --release --target "$TARGET_X86" "${CARGO_PKG_ARGS[@]}"
cp "target/${TARGET_X86}/release/atomcode" "${DIST}/atomcode-${VERSION}-darwin-x64"
echo " -> ${DIST}/atomcode-${VERSION}-darwin-x64"
copy_daemon "target/${TARGET_X86}/release/atomcode-daemon" "${DIST}/atomcode-daemon-${VERSION}-darwin-x64" ""
TARGET_LINUX="x86_64-unknown-linux-musl"
echo "[3/6] Building ${TARGET_LINUX}..."
rustup target add "$TARGET_LINUX" 2>/dev/null || true
if command -v x86_64-linux-musl-gcc &>/dev/null; then
export CC_x86_64_unknown_linux_musl=x86_64-linux-musl-gcc
export CFLAGS_x86_64_unknown_linux_musl="-fPIC"
cargo build --release --target "$TARGET_LINUX" "${CARGO_PKG_ARGS[@]}"
cp "target/${TARGET_LINUX}/release/atomcode" "${DIST}/atomcode-${VERSION}-linux-x64"
echo " -> ${DIST}/atomcode-${VERSION}-linux-x64"
copy_daemon "target/${TARGET_LINUX}/release/atomcode-daemon" "${DIST}/atomcode-daemon-${VERSION}-linux-x64" ""
else
echo " !! Skipped: musl-cross not installed (brew install FiloSottile/musl-cross/musl-cross)"
fi
TARGET_LINUX_ARM="aarch64-unknown-linux-musl"
echo "[4/6] Building ${TARGET_LINUX_ARM}..."
rustup target add "$TARGET_LINUX_ARM" 2>/dev/null || true
if command -v aarch64-linux-musl-gcc &>/dev/null; then
export CC_aarch64_unknown_linux_musl=aarch64-linux-musl-gcc
export CFLAGS_aarch64_unknown_linux_musl="-fPIC"
cargo build --release --target "$TARGET_LINUX_ARM" "${CARGO_PKG_ARGS[@]}"
cp "target/${TARGET_LINUX_ARM}/release/atomcode" "${DIST}/atomcode-${VERSION}-linux-arm64"
echo " -> ${DIST}/atomcode-${VERSION}-linux-arm64"
copy_daemon "target/${TARGET_LINUX_ARM}/release/atomcode-daemon" "${DIST}/atomcode-daemon-${VERSION}-linux-arm64" ""
else
echo " !! Skipped: aarch64 musl-cross not installed (brew reinstall FiloSottile/musl-cross/musl-cross — aarch64 ships by default; do NOT pass --with-aarch64, it gets fuzzy-matched to --without-aarch64)"
fi
TARGET_WIN="x86_64-pc-windows-gnu"
echo "[5/6] Building ${TARGET_WIN}..."
rustup target add "$TARGET_WIN" 2>/dev/null || true
if command -v x86_64-w64-mingw32-gcc &>/dev/null; then
cargo build --release --target "$TARGET_WIN" "${CARGO_PKG_ARGS[@]}"
cp "target/${TARGET_WIN}/release/atomcode.exe" "${DIST}/atomcode-${VERSION}-windows-x64.exe"
echo " -> ${DIST}/atomcode-${VERSION}-windows-x64.exe"
copy_daemon "target/${TARGET_WIN}/release/atomcode-daemon" "${DIST}/atomcode-daemon-${VERSION}-windows-x64" ".exe"
else
echo " !! Skipped: mingw-w64 not installed (brew install mingw-w64)"
fi
TARGET_WIN_ARM="aarch64-pc-windows-gnullvm"
echo "[6/6] Building ${TARGET_WIN_ARM}..."
rustup target add "$TARGET_WIN_ARM" 2>/dev/null || true
if command -v aarch64-w64-mingw32-gcc &>/dev/null; then
cargo build --release --target "$TARGET_WIN_ARM" "${CARGO_PKG_ARGS[@]}"
cp "target/${TARGET_WIN_ARM}/release/atomcode.exe" "${DIST}/atomcode-${VERSION}-windows-arm64.exe"
echo " -> ${DIST}/atomcode-${VERSION}-windows-arm64.exe"
copy_daemon "target/${TARGET_WIN_ARM}/release/atomcode-daemon" "${DIST}/atomcode-daemon-${VERSION}-windows-arm64" ".exe"
else
echo " !! Skipped: llvm-mingw not installed (brew install llvm-mingw or see https://github.com/mstorsjo/llvm-mingw)"
fi
if [ "${ATOMCODE_SKIP_SIGN:-0}" != "1" ]; then
echo ""
echo "=== Signing macOS atomcode binaries ==="
"$(dirname "$0")/sign-macos.sh" "$DIST"
else
echo ""
echo "=== Skipping macOS signing (ATOMCODE_SKIP_SIGN=1) ==="
fi
echo ""
echo "=== SHA256 ==="
cd "$DIST"
shasum -a 256 atomcode-* 2>/dev/null | tee checksums.txt
echo ""
echo "=== Generating latest.json ==="
MANIFEST="$ROOT/latest.json"
RELEASED_AT=$(date -u +"%Y-%m-%dT%H:%M:%SZ")
emit_entry() {
local target="$1"
local file="$2"
if [ ! -f "$file" ]; then
return 1
fi
local sha
if command -v sha256sum >/dev/null 2>&1; then
sha=$(sha256sum "$file" | awk '{print $1}')
elif command -v shasum >/dev/null 2>&1; then
sha=$(shasum -a 256 "$file" | awk '{print $1}')
else
echo "ERROR: sha256sum or shasum not found" >&2
return 1
fi
local size
if stat -f%z "$file" >/dev/null 2>&1; then
size=$(stat -f%z "$file")
else
size=$(stat -c%s "$file")
fi
printf ' "%s": { "sha256": "%s", "size": %s }' "$target" "$sha" "$size"
}
{
printf '{\n'
printf ' "version": "%s",\n' "$VERSION"
printf ' "released_at": "%s",\n' "$RELEASED_AT"
printf ' "binaries": {\n'
first=1
for pair in \
"darwin-arm64:atomcode-${VERSION}-darwin-arm64" \
"darwin-x64:atomcode-${VERSION}-darwin-x64" \
"linux-x64:atomcode-${VERSION}-linux-x64" \
"linux-arm64:atomcode-${VERSION}-linux-arm64" \
"ohos-arm64:atomcode-${VERSION}-ohos-arm64" \
"windows-x64:atomcode-${VERSION}-windows-x64.exe" \
"windows-arm64:atomcode-${VERSION}-windows-arm64.exe"
do
target="${pair%%:*}"
file="${pair#*:}"
if [ -f "$file" ]; then
if [ "$first" -eq 0 ]; then printf ',\n'; fi
emit_entry "$target" "$file"
first=0
fi
done
printf '\n }\n'
printf '}\n'
} > "$MANIFEST"
echo " -> ${MANIFEST}"
cat "$MANIFEST"
echo ""
echo "Done. Release artifacts in ${DIST}/"
echo " * Copy ${MANIFEST} to the docs repo root (next to latest.txt) so /upgrade can find it."
ls -lh atomcode-* 2>/dev/null