Files
steve e3f823045c Auto-publish in non-interactive mode in release script
When stdin is not a terminal (e.g. run from a tool or CI), skip the
confirmation prompt and proceed with publishing.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-13 09:23:28 +01:00

243 lines
6.8 KiB
Bash
Executable File

#!/usr/bin/env bash
set -euo pipefail
# Ensure cargo is on PATH
export PATH="${HOME}/.cargo/bin:${PATH}"
# ============================================================
# Release configuration
# ============================================================
REPO="https://gitea.dcglab.co.uk/steve/mouth"
FORGE="gitea" # "gitea" (uses tea CLI) or "github" (uses gh CLI)
# ============================================================
# Derived variables
# ============================================================
VERSION=$(grep '^version' Cargo.toml | head -1 | sed 's/.*"\(.*\)"/\1/')
RELEASE_DIR="release/v${VERSION}"
BINARY_NAME="mouth"
TAG="v${VERSION}"
echo "=== Mouth Release Build ${TAG} ==="
echo "Forge: ${FORGE} (${REPO})"
echo ""
# Ensure we're in the project root
if [ ! -f Cargo.toml ]; then
echo "ERROR: Must be run from the project root (where Cargo.toml is)"
exit 1
fi
# Check CLI tools
if [ "${FORGE}" = "gitea" ]; then
if ! command -v tea &>/dev/null; then
echo "ERROR: 'tea' CLI not found. Install: https://gitea.com/gitea/tea"
exit 1
fi
elif [ "${FORGE}" = "github" ]; then
if ! command -v gh &>/dev/null; then
echo "ERROR: 'gh' CLI not found. Install: https://cli.github.com"
exit 1
fi
else
echo "ERROR: Unknown forge '${FORGE}'. Must be 'gitea' or 'github'."
exit 1
fi
# Clean previous release artifacts for this version
rm -rf "${RELEASE_DIR}"
mkdir -p "${RELEASE_DIR}"
# Track build results
BUILT=()
FAILED=()
build_target() {
local target="$1"
local label="$2"
local friendly_name="$3"
local ext="${4:-}"
echo "--- Building ${label} (${target}) ---"
if cargo build --release --target "${target}" 2>&1; then
local binary="target/${target}/release/${BINARY_NAME}${ext}"
if [ -f "${binary}" ]; then
local archive="${RELEASE_DIR}/${BINARY_NAME}-${TAG}-${friendly_name}"
if [ -n "${ext}" ]; then
# Windows: ship the exe directly
cp "${binary}" "${archive}${ext}"
echo " -> ${archive}${ext}"
BUILT+=("${archive}${ext}")
else
# Linux/macOS: tar.gz
local tar_name="${archive}.tar.gz"
tar -czf "${tar_name}" -C "target/${target}/release" "${BINARY_NAME}"
echo " -> ${tar_name}"
BUILT+=("${tar_name}")
fi
else
echo " WARN: Binary not found at ${binary}"
FAILED+=("${label}")
fi
else
echo " FAILED: ${label}"
FAILED+=("${label}")
fi
echo ""
}
# ============================================================
# Build targets
# ============================================================
# Linux x86_64 (native — always available)
build_target "x86_64-unknown-linux-gnu" "Linux x86_64" "linux-x86_64"
# Windows x86_64 (MSVC target via cargo-xwin)
if command -v cargo-xwin &>/dev/null && rustup target list --installed | grep -q x86_64-pc-windows-msvc; then
echo "--- Building Windows x86_64 (x86_64-pc-windows-msvc via cargo-xwin) ---"
if cargo xwin build --release --target x86_64-pc-windows-msvc 2>&1; then
local_binary="target/x86_64-pc-windows-msvc/release/${BINARY_NAME}.exe"
if [ -f "${local_binary}" ]; then
archive="${RELEASE_DIR}/${BINARY_NAME}-${TAG}-windows-x86_64.exe"
cp "${local_binary}" "${archive}"
echo " -> ${archive}"
BUILT+=("${archive}")
else
echo " WARN: Binary not found"
FAILED+=("Windows x86_64 (MSVC)")
fi
else
echo " FAILED: Windows x86_64 (MSVC)"
FAILED+=("Windows x86_64 (MSVC)")
fi
echo ""
else
echo "--- Skipping Windows x86_64 ---"
echo " cargo-xwin not installed or x86_64-pc-windows-msvc target missing."
echo " To enable Windows builds from Linux:"
echo " cargo install cargo-xwin"
echo " rustup target add x86_64-pc-windows-msvc"
echo ""
FAILED+=("Windows x86_64 (not configured)")
fi
# ============================================================
# Summary
# ============================================================
echo "=== Build Summary ==="
echo ""
if [ ${#BUILT[@]} -gt 0 ]; then
echo "Successful builds:"
for b in "${BUILT[@]}"; do
local_size=$(du -h "${b}" | cut -f1)
echo " ${b} (${local_size})"
done
fi
if [ ${#FAILED[@]} -gt 0 ]; then
echo ""
echo "Failed / skipped builds:"
for f in "${FAILED[@]}"; do
echo " ${f}"
done
fi
echo ""
echo "Release artifacts in: ${RELEASE_DIR}/"
echo ""
# Generate checksums
if [ ${#BUILT[@]} -gt 0 ]; then
echo "--- Checksums (SHA256) ---"
cd "${RELEASE_DIR}"
sha256sum * > checksums-sha256.txt 2>/dev/null || shasum -a 256 * > checksums-sha256.txt
cat checksums-sha256.txt
cd - > /dev/null
fi
# ============================================================
# Publish release
# ============================================================
if [ ${#BUILT[@]} -eq 0 ]; then
echo ""
echo "No successful builds — skipping release publish."
exit 1
fi
echo ""
if [ -t 0 ]; then
read -rp "Publish release ${TAG} to ${FORGE}? [y/N] " confirm
else
# Non-interactive (piped/scripted) — default to yes
confirm="y"
echo "Non-interactive mode: auto-publishing release ${TAG} to ${FORGE}"
fi
if [[ ! "${confirm}" =~ ^[Yy]$ ]]; then
echo "Skipped. Artifacts are in ${RELEASE_DIR}/"
exit 0
fi
# Ensure the git tag exists
if ! git rev-parse "${TAG}" &>/dev/null; then
echo "Creating git tag ${TAG}..."
git tag -a "${TAG}" -m "Release ${TAG}"
git push origin "${TAG}"
fi
# Collect all release files (artifacts + checksums)
RELEASE_FILES=()
for b in "${BUILT[@]}"; do
RELEASE_FILES+=("${b}")
done
RELEASE_FILES+=("${RELEASE_DIR}/checksums-sha256.txt")
RELEASE_TITLE="Mouth ${TAG}"
RELEASE_BODY="## Mouth ${TAG}
### Downloads
$(for b in "${BUILT[@]}"; do echo "- $(basename "${b}")"; done)
### Checksums (SHA256)
\`\`\`
$(cat "${RELEASE_DIR}/checksums-sha256.txt")
\`\`\`
"
if [ "${FORGE}" = "gitea" ]; then
echo "Publishing to Gitea via tea..."
# Extract host and owner/repo from REPO URL
REPO_OWNER_NAME=$(echo "${REPO}" | sed 's|.*://[^/]*/||')
# Create the release
tea release create \
--repo "${REPO_OWNER_NAME}" \
--tag "${TAG}" \
--title "${RELEASE_TITLE}" \
--note "${RELEASE_BODY}"
# Upload assets
echo " Uploading assets..."
tea release asset create \
--repo "${REPO_OWNER_NAME}" \
"${TAG}" \
"${RELEASE_FILES[@]}"
elif [ "${FORGE}" = "github" ]; then
echo "Publishing to GitHub via gh..."
gh release create "${TAG}" \
--repo "${REPO}" \
--title "${RELEASE_TITLE}" \
--notes "${RELEASE_BODY}" \
"${RELEASE_FILES[@]}"
fi
echo ""
echo "=== Release ${TAG} published to ${FORGE}! ==="