e3f823045c
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>
243 lines
6.8 KiB
Bash
Executable File
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}! ==="
|