feat: add ARM64 (aarch64) architecture support for builds and packages
Some checks failed
CI/CD / lint (push) Successful in 46s
CI/CD / test (push) Successful in 1m3s
CI/CD / typecheck (push) Has started running
CI/CD / smoke (push) Has been cancelled
CI/CD / build (amd64) (push) Has been cancelled
CI/CD / build (arm64) (push) Has been cancelled
CI/CD / publish-rpm (amd64) (push) Has been cancelled
CI/CD / publish-rpm (arm64) (push) Has been cancelled
CI/CD / publish-deb (amd64) (push) Has been cancelled
CI/CD / publish-deb (arm64) (push) Has been cancelled
Some checks failed
CI/CD / lint (push) Successful in 46s
CI/CD / test (push) Successful in 1m3s
CI/CD / typecheck (push) Has started running
CI/CD / smoke (push) Has been cancelled
CI/CD / build (amd64) (push) Has been cancelled
CI/CD / build (arm64) (push) Has been cancelled
CI/CD / publish-rpm (amd64) (push) Has been cancelled
CI/CD / publish-rpm (arm64) (push) Has been cancelled
CI/CD / publish-deb (amd64) (push) Has been cancelled
CI/CD / publish-deb (arm64) (push) Has been cancelled
Add cross-architecture build support so the project can be developed on ARM64 (Fedora aarch64 laptop) while still producing amd64 packages for production. All build, package, publish, and install scripts are now architecture-aware via shared arch-helper.sh detection. - Add scripts/arch-helper.sh for shared architecture detection - CI builds both amd64 and arm64 in matrix strategy - nfpm.yaml uses NFPM_ARCH env var instead of hardcoded amd64 - Build scripts support MCPCTL_TARGET_ARCH for cross-compilation - installlocal.sh auto-detects RPM/DEB and filters by architecture - release.sh gains --both-arches flag for dual-arch releases - Package cleanup is arch-scoped (won't clobber other arch's packages) - build-mcpd.sh supports --platform and --multi-arch flags - Add pnpm scripts: rpm:build:amd64, deb:build:arm64, release:both - Conditional rpm/dpkg-deb checks for cross-distro compatibility Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
62
scripts/arch-helper.sh
Normal file
62
scripts/arch-helper.sh
Normal file
@@ -0,0 +1,62 @@
|
||||
#!/bin/bash
|
||||
# Shared architecture detection for build scripts.
|
||||
# Source this file, then call: resolve_arch [target_arch]
|
||||
#
|
||||
# Outputs (exported):
|
||||
# NFPM_ARCH — nfpm/deb/rpm arch name: "amd64" or "arm64"
|
||||
# BUN_TARGET — bun cross-compile target (empty if native build)
|
||||
# ARCH_SUFFIX — filename suffix for cross-compiled binaries (empty if native)
|
||||
|
||||
_detect_native_arch() {
|
||||
case "$(uname -m)" in
|
||||
x86_64) echo "amd64" ;;
|
||||
aarch64) echo "arm64" ;;
|
||||
arm64) echo "arm64" ;; # macOS reports arm64
|
||||
*) echo "amd64" ;; # fallback
|
||||
esac
|
||||
}
|
||||
|
||||
_bun_target_for() {
|
||||
local arch="$1"
|
||||
case "$arch" in
|
||||
amd64) echo "bun-linux-x64" ;;
|
||||
arm64) echo "bun-linux-arm64" ;;
|
||||
esac
|
||||
}
|
||||
|
||||
_nfpm_download_arch() {
|
||||
local arch="$1"
|
||||
case "$arch" in
|
||||
amd64) echo "x86_64" ;;
|
||||
arm64) echo "arm64" ;;
|
||||
esac
|
||||
}
|
||||
|
||||
# resolve_arch [override]
|
||||
# override: "amd64" or "arm64" (optional, auto-detects if empty)
|
||||
resolve_arch() {
|
||||
local requested="${1:-}"
|
||||
local native
|
||||
native="$(_detect_native_arch)"
|
||||
|
||||
if [ -z "$requested" ]; then
|
||||
# Native build
|
||||
NFPM_ARCH="$native"
|
||||
BUN_TARGET=""
|
||||
ARCH_SUFFIX=""
|
||||
else
|
||||
NFPM_ARCH="$requested"
|
||||
if [ "$requested" = "$native" ]; then
|
||||
# Requesting our own arch — native build
|
||||
BUN_TARGET=""
|
||||
ARCH_SUFFIX=""
|
||||
else
|
||||
# Cross-compilation
|
||||
BUN_TARGET="$(_bun_target_for "$requested")"
|
||||
ARCH_SUFFIX="-${requested}"
|
||||
fi
|
||||
fi
|
||||
|
||||
export NFPM_ARCH BUN_TARGET ARCH_SUFFIX
|
||||
echo " Architecture: ${NFPM_ARCH} (native: ${native}${BUN_TARGET:+, cross-compiling via $BUN_TARGET})"
|
||||
}
|
||||
@@ -13,8 +13,14 @@ fi
|
||||
# Ensure tools are on PATH
|
||||
export PATH="$HOME/.npm-global/bin:$HOME/.bun/bin:$HOME/.local/bin:$PATH"
|
||||
|
||||
# Architecture detection / cross-compilation support
|
||||
# MCPCTL_TARGET_ARCH overrides native detection (e.g. "amd64" or "arm64")
|
||||
source "$SCRIPT_DIR/arch-helper.sh"
|
||||
resolve_arch "${MCPCTL_TARGET_ARCH:-}"
|
||||
# Sets: NFPM_ARCH, BUN_TARGET, ARCH_SUFFIX
|
||||
|
||||
# Check if binaries already exist (build-rpm.sh may have been run first)
|
||||
if [ ! -f dist/mcpctl ] || [ ! -f dist/mcpctl-local ]; then
|
||||
if [ ! -f "dist/mcpctl${ARCH_SUFFIX}" ] || [ ! -f "dist/mcpctl-local${ARCH_SUFFIX}" ]; then
|
||||
echo "==> Binaries not found, building from scratch..."
|
||||
echo ""
|
||||
|
||||
@@ -28,7 +34,7 @@ if [ ! -f dist/mcpctl ] || [ ! -f dist/mcpctl-local ]; then
|
||||
echo "==> Generating shell completions..."
|
||||
pnpm completions:generate
|
||||
|
||||
echo "==> Bundling standalone binaries..."
|
||||
echo "==> Bundling standalone binaries (target: ${NFPM_ARCH})..."
|
||||
mkdir -p dist
|
||||
|
||||
# Ink optionally imports react-devtools-core which isn't installed.
|
||||
@@ -37,17 +43,28 @@ if [ ! -f dist/mcpctl ] || [ ! -f dist/mcpctl-local ]; then
|
||||
ln -s ../src/cli/stubs/react-devtools-core node_modules/react-devtools-core
|
||||
fi
|
||||
|
||||
bun build src/cli/src/index.ts --compile --outfile dist/mcpctl
|
||||
bun build src/mcplocal/src/main.ts --compile --outfile dist/mcpctl-local
|
||||
bun build src/cli/src/index.ts --compile ${BUN_TARGET:+--target "$BUN_TARGET"} --outfile "dist/mcpctl${ARCH_SUFFIX}"
|
||||
bun build src/mcplocal/src/main.ts --compile ${BUN_TARGET:+--target "$BUN_TARGET"} --outfile "dist/mcpctl-local${ARCH_SUFFIX}"
|
||||
else
|
||||
echo "==> Using existing binaries in dist/"
|
||||
fi
|
||||
|
||||
echo "==> Packaging DEB..."
|
||||
rm -f dist/mcpctl-*.deb dist/mcpctl_*.deb
|
||||
# If cross-compiling, copy arch-suffixed binaries to the names nfpm expects
|
||||
if [ -n "$ARCH_SUFFIX" ]; then
|
||||
cp "dist/mcpctl${ARCH_SUFFIX}" dist/mcpctl
|
||||
cp "dist/mcpctl-local${ARCH_SUFFIX}" dist/mcpctl-local
|
||||
fi
|
||||
|
||||
echo "==> Packaging DEB (arch: ${NFPM_ARCH})..."
|
||||
# Only remove DEBs for the target arch (preserve cross-compiled packages)
|
||||
ls dist/mcpctl*_${NFPM_ARCH}.deb 2>/dev/null | xargs -r rm -f
|
||||
export NFPM_ARCH
|
||||
nfpm pkg --packager deb --target dist/
|
||||
|
||||
DEB_FILE=$(ls dist/mcpctl*.deb 2>/dev/null | head -1)
|
||||
DEB_FILE=$(ls dist/mcpctl*.deb 2>/dev/null | grep -E "[._]${NFPM_ARCH}[._]" | head -1)
|
||||
echo "==> Built: $DEB_FILE"
|
||||
echo " Size: $(du -h "$DEB_FILE" | cut -f1)"
|
||||
dpkg-deb --info "$DEB_FILE" 2>/dev/null || true
|
||||
# dpkg-deb may not be available on RPM-based systems (Fedora)
|
||||
if command -v dpkg-deb &>/dev/null; then
|
||||
dpkg-deb --info "$DEB_FILE" 2>/dev/null || true
|
||||
fi
|
||||
|
||||
@@ -1,5 +1,10 @@
|
||||
#!/bin/bash
|
||||
# Build mcpd Docker image and push to Gitea container registry
|
||||
# Build mcpd Docker image and push to Gitea container registry.
|
||||
#
|
||||
# Usage:
|
||||
# ./build-mcpd.sh [tag] # Build for native arch
|
||||
# ./build-mcpd.sh [tag] --platform linux/amd64 # Build for specific platform
|
||||
# ./build-mcpd.sh [tag] --multi-arch # Build for both amd64 and arm64
|
||||
set -e
|
||||
|
||||
SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)"
|
||||
@@ -16,17 +21,60 @@ REGISTRY="10.0.0.194:3012"
|
||||
IMAGE="mcpd"
|
||||
TAG="${1:-latest}"
|
||||
|
||||
echo "==> Building mcpd image..."
|
||||
podman build -t "$IMAGE:$TAG" -f deploy/Dockerfile.mcpd .
|
||||
# Parse optional flags
|
||||
PLATFORM=""
|
||||
MULTI_ARCH=false
|
||||
shift 2>/dev/null || true
|
||||
while [[ $# -gt 0 ]]; do
|
||||
case "$1" in
|
||||
--platform)
|
||||
PLATFORM="$2"
|
||||
shift 2
|
||||
;;
|
||||
--multi-arch)
|
||||
MULTI_ARCH=true
|
||||
shift
|
||||
;;
|
||||
*)
|
||||
shift
|
||||
;;
|
||||
esac
|
||||
done
|
||||
|
||||
echo "==> Tagging as $REGISTRY/michal/$IMAGE:$TAG..."
|
||||
podman tag "$IMAGE:$TAG" "$REGISTRY/michal/$IMAGE:$TAG"
|
||||
if [ "$MULTI_ARCH" = true ]; then
|
||||
echo "==> Building multi-arch mcpd image (linux/amd64 + linux/arm64)..."
|
||||
podman build --platform linux/amd64,linux/arm64 \
|
||||
--manifest "$IMAGE:$TAG" -f deploy/Dockerfile.mcpd .
|
||||
|
||||
echo "==> Logging in to $REGISTRY..."
|
||||
podman login --tls-verify=false -u michal -p "$GITEA_TOKEN" "$REGISTRY"
|
||||
echo "==> Tagging manifest as $REGISTRY/michal/$IMAGE:$TAG..."
|
||||
podman tag "$IMAGE:$TAG" "$REGISTRY/michal/$IMAGE:$TAG"
|
||||
|
||||
echo "==> Pushing to $REGISTRY/michal/$IMAGE:$TAG..."
|
||||
podman push --tls-verify=false "$REGISTRY/michal/$IMAGE:$TAG"
|
||||
echo "==> Logging in to $REGISTRY..."
|
||||
podman login --tls-verify=false -u michal -p "$GITEA_TOKEN" "$REGISTRY"
|
||||
|
||||
echo "==> Pushing manifest to $REGISTRY/michal/$IMAGE:$TAG..."
|
||||
podman manifest push --tls-verify=false --all \
|
||||
"$REGISTRY/michal/$IMAGE:$TAG" "docker://$REGISTRY/michal/$IMAGE:$TAG"
|
||||
else
|
||||
PLATFORM_FLAG=""
|
||||
if [ -n "$PLATFORM" ]; then
|
||||
PLATFORM_FLAG="--platform $PLATFORM"
|
||||
echo "==> Building mcpd image for $PLATFORM..."
|
||||
else
|
||||
echo "==> Building mcpd image (native arch)..."
|
||||
fi
|
||||
|
||||
podman build $PLATFORM_FLAG -t "$IMAGE:$TAG" -f deploy/Dockerfile.mcpd .
|
||||
|
||||
echo "==> Tagging as $REGISTRY/michal/$IMAGE:$TAG..."
|
||||
podman tag "$IMAGE:$TAG" "$REGISTRY/michal/$IMAGE:$TAG"
|
||||
|
||||
echo "==> Logging in to $REGISTRY..."
|
||||
podman login --tls-verify=false -u michal -p "$GITEA_TOKEN" "$REGISTRY"
|
||||
|
||||
echo "==> Pushing to $REGISTRY/michal/$IMAGE:$TAG..."
|
||||
podman push --tls-verify=false "$REGISTRY/michal/$IMAGE:$TAG"
|
||||
fi
|
||||
|
||||
# Ensure package is linked to the repository
|
||||
source "$SCRIPT_DIR/link-package.sh"
|
||||
|
||||
@@ -13,6 +13,12 @@ fi
|
||||
# Ensure tools are on PATH
|
||||
export PATH="$HOME/.npm-global/bin:$HOME/.bun/bin:$HOME/.local/bin:$PATH"
|
||||
|
||||
# Architecture detection / cross-compilation support
|
||||
# MCPCTL_TARGET_ARCH overrides native detection (e.g. "amd64" or "arm64")
|
||||
source "$SCRIPT_DIR/arch-helper.sh"
|
||||
resolve_arch "${MCPCTL_TARGET_ARCH:-}"
|
||||
# Sets: NFPM_ARCH, BUN_TARGET, ARCH_SUFFIX
|
||||
|
||||
echo "==> Running unit tests..."
|
||||
pnpm test:run
|
||||
echo ""
|
||||
@@ -23,9 +29,11 @@ pnpm build
|
||||
echo "==> Generating shell completions..."
|
||||
pnpm completions:generate
|
||||
|
||||
echo "==> Bundling standalone binaries..."
|
||||
echo "==> Bundling standalone binaries (target: ${NFPM_ARCH})..."
|
||||
mkdir -p dist
|
||||
rm -f dist/mcpctl dist/mcpctl-local dist/mcpctl-*.rpm
|
||||
rm -f "dist/mcpctl${ARCH_SUFFIX}" "dist/mcpctl-local${ARCH_SUFFIX}"
|
||||
# Only remove RPMs for the target arch (preserve cross-compiled packages)
|
||||
ls dist/mcpctl-*.${NFPM_ARCH}.rpm 2>/dev/null | xargs -r rm -f
|
||||
|
||||
# Ink optionally imports react-devtools-core which isn't installed.
|
||||
# Provide a no-op stub so bun can bundle it (it's only invoked when DEV=true).
|
||||
@@ -33,22 +41,32 @@ if [ ! -e node_modules/react-devtools-core ]; then
|
||||
ln -s ../src/cli/stubs/react-devtools-core node_modules/react-devtools-core
|
||||
fi
|
||||
|
||||
bun build src/cli/src/index.ts --compile --outfile dist/mcpctl
|
||||
bun build src/mcplocal/src/main.ts --compile --outfile dist/mcpctl-local
|
||||
bun build src/cli/src/index.ts --compile ${BUN_TARGET:+--target "$BUN_TARGET"} --outfile "dist/mcpctl${ARCH_SUFFIX}"
|
||||
bun build src/mcplocal/src/main.ts --compile ${BUN_TARGET:+--target "$BUN_TARGET"} --outfile "dist/mcpctl-local${ARCH_SUFFIX}"
|
||||
|
||||
echo "==> Packaging RPM..."
|
||||
# If cross-compiling, copy arch-suffixed binaries to the names nfpm expects
|
||||
if [ -n "$ARCH_SUFFIX" ]; then
|
||||
cp "dist/mcpctl${ARCH_SUFFIX}" dist/mcpctl
|
||||
cp "dist/mcpctl-local${ARCH_SUFFIX}" dist/mcpctl-local
|
||||
fi
|
||||
|
||||
echo "==> Packaging RPM (arch: ${NFPM_ARCH})..."
|
||||
export NFPM_ARCH
|
||||
nfpm pkg --packager rpm --target dist/
|
||||
|
||||
RPM_FILE=$(ls dist/mcpctl-*.rpm 2>/dev/null | head -1)
|
||||
RPM_FILE=$(ls dist/mcpctl-*.${NFPM_ARCH}.rpm 2>/dev/null | head -1)
|
||||
echo "==> Built: $RPM_FILE"
|
||||
echo " Size: $(du -h "$RPM_FILE" | cut -f1)"
|
||||
rpm -qpi "$RPM_FILE"
|
||||
if command -v rpm &>/dev/null; then
|
||||
rpm -qpi "$RPM_FILE"
|
||||
fi
|
||||
|
||||
echo ""
|
||||
echo "==> Packaging DEB..."
|
||||
rm -f dist/mcpctl*.deb
|
||||
echo "==> Packaging DEB (arch: ${NFPM_ARCH})..."
|
||||
# Only remove DEBs for the target arch
|
||||
ls dist/mcpctl*_${NFPM_ARCH}.deb 2>/dev/null | xargs -r rm -f
|
||||
nfpm pkg --packager deb --target dist/
|
||||
|
||||
DEB_FILE=$(ls dist/mcpctl*.deb 2>/dev/null | head -1)
|
||||
DEB_FILE=$(ls dist/mcpctl*_${NFPM_ARCH}.deb 2>/dev/null | head -1)
|
||||
echo "==> Built: $DEB_FILE"
|
||||
echo " Size: $(du -h "$DEB_FILE" | cut -f1)"
|
||||
|
||||
@@ -20,7 +20,16 @@ if [ -z "$GITEA_TOKEN" ]; then
|
||||
exit 1
|
||||
fi
|
||||
|
||||
DEB_FILE=$(ls dist/mcpctl*.deb 2>/dev/null | head -1)
|
||||
# Architecture detection (respects MCPCTL_TARGET_ARCH)
|
||||
source "$SCRIPT_DIR/arch-helper.sh"
|
||||
resolve_arch "${MCPCTL_TARGET_ARCH:-}"
|
||||
|
||||
# Find DEB matching target architecture
|
||||
DEB_FILE=$(ls dist/mcpctl*.deb 2>/dev/null | grep -E "[._]${NFPM_ARCH}[._]" | head -1)
|
||||
if [ -z "$DEB_FILE" ]; then
|
||||
# Fallback: try any deb file
|
||||
DEB_FILE=$(ls dist/mcpctl*.deb 2>/dev/null | head -1)
|
||||
fi
|
||||
if [ -z "$DEB_FILE" ]; then
|
||||
echo "Error: No DEB found in dist/. Run scripts/build-deb.sh first."
|
||||
exit 1
|
||||
|
||||
@@ -20,7 +20,16 @@ if [ -z "$GITEA_TOKEN" ]; then
|
||||
exit 1
|
||||
fi
|
||||
|
||||
RPM_FILE=$(ls dist/mcpctl-*.rpm 2>/dev/null | head -1)
|
||||
# Architecture detection (respects MCPCTL_TARGET_ARCH)
|
||||
source "$SCRIPT_DIR/arch-helper.sh"
|
||||
resolve_arch "${MCPCTL_TARGET_ARCH:-}"
|
||||
|
||||
# Find RPM matching target architecture
|
||||
RPM_FILE=$(ls dist/mcpctl-*.rpm 2>/dev/null | grep -E "[._]${NFPM_ARCH}[._]" | head -1)
|
||||
if [ -z "$RPM_FILE" ]; then
|
||||
# Fallback: try any rpm file
|
||||
RPM_FILE=$(ls dist/mcpctl-*.rpm 2>/dev/null | head -1)
|
||||
fi
|
||||
if [ -z "$RPM_FILE" ]; then
|
||||
echo "Error: No RPM found in dist/. Run scripts/build-rpm.sh first."
|
||||
exit 1
|
||||
|
||||
@@ -1,4 +1,9 @@
|
||||
#!/bin/bash
|
||||
# Build, publish, and install mcpctl packages.
|
||||
#
|
||||
# Usage:
|
||||
# ./release.sh # Build + publish for native arch only
|
||||
# ./release.sh --both-arches # Build + publish for both amd64 and arm64
|
||||
set -e
|
||||
|
||||
SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)"
|
||||
@@ -10,24 +15,47 @@ if [ -f .env ]; then
|
||||
set -a; source .env; set +a
|
||||
fi
|
||||
|
||||
source "$SCRIPT_DIR/arch-helper.sh"
|
||||
resolve_arch "${MCPCTL_TARGET_ARCH:-}"
|
||||
NATIVE_ARCH="$NFPM_ARCH"
|
||||
|
||||
BOTH_ARCHES=false
|
||||
if [[ "${1:-}" == "--both-arches" ]]; then
|
||||
BOTH_ARCHES=true
|
||||
fi
|
||||
|
||||
echo "=== mcpctl release ==="
|
||||
echo " Native arch: $NATIVE_ARCH"
|
||||
echo ""
|
||||
|
||||
# Build
|
||||
bash scripts/build-rpm.sh
|
||||
build_and_publish() {
|
||||
local arch="$1"
|
||||
echo ""
|
||||
echo "=== Building for $arch ==="
|
||||
MCPCTL_TARGET_ARCH="$arch" bash scripts/build-rpm.sh
|
||||
echo ""
|
||||
MCPCTL_TARGET_ARCH="$arch" bash scripts/publish-rpm.sh
|
||||
MCPCTL_TARGET_ARCH="$arch" bash scripts/publish-deb.sh
|
||||
}
|
||||
|
||||
if [ "$BOTH_ARCHES" = true ]; then
|
||||
build_and_publish "amd64"
|
||||
build_and_publish "arm64"
|
||||
else
|
||||
build_and_publish "$NATIVE_ARCH"
|
||||
fi
|
||||
|
||||
echo ""
|
||||
|
||||
# Publish
|
||||
bash scripts/publish-rpm.sh
|
||||
bash scripts/publish-deb.sh
|
||||
|
||||
echo ""
|
||||
|
||||
# Install locally
|
||||
echo "==> Installing locally..."
|
||||
RPM_FILE=$(ls dist/mcpctl-*.rpm 2>/dev/null | head -1)
|
||||
sudo rpm -U --force "$RPM_FILE"
|
||||
# Install locally for native arch (auto-detect RPM or DEB)
|
||||
echo "==> Installing locally (${NATIVE_ARCH})..."
|
||||
if command -v dpkg &>/dev/null && ! command -v dnf &>/dev/null; then
|
||||
DEB_FILE=$(ls dist/mcpctl*.deb 2>/dev/null | grep -E "[._]${NATIVE_ARCH}[._]" | head -1)
|
||||
sudo dpkg -i "$DEB_FILE" || sudo apt-get install -f -y
|
||||
else
|
||||
RPM_FILE=$(ls dist/mcpctl-*.rpm 2>/dev/null | grep -E "[._]${NATIVE_ARCH}[._]" | head -1)
|
||||
sudo rpm -U --force "$RPM_FILE"
|
||||
fi
|
||||
|
||||
echo ""
|
||||
echo "==> Installed:"
|
||||
|
||||
Reference in New Issue
Block a user