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

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:
Michal Rydlikowski
2026-03-13 23:01:51 +00:00
parent 588b2a9e65
commit 8ad7fe2748
11 changed files with 326 additions and 64 deletions

View File

@@ -250,11 +250,14 @@ jobs:
if: failure() if: failure()
run: cat /tmp/mcplocal.log || true run: cat /tmp/mcplocal.log || true
# ── Build & package RPM ─────────────────────────────────── # ── Build & package (both amd64 and arm64) ──────────────
build: build:
runs-on: ubuntu-latest runs-on: ubuntu-latest
needs: [lint, typecheck, test] needs: [lint, typecheck, test]
strategy:
matrix:
arch: [amd64, arm64]
steps: steps:
- uses: actions/checkout@v4 - uses: actions/checkout@v4
@@ -283,11 +286,18 @@ jobs:
- name: Install nfpm - name: Install nfpm
run: | run: |
# nfpm itself runs on the CI runner (always x86_64); it cross-packages
# for the target arch via NFPM_ARCH env var — no ARM nfpm binary needed.
curl -sL -o /tmp/nfpm.tar.gz "https://github.com/goreleaser/nfpm/releases/download/v2.45.0/nfpm_2.45.0_Linux_x86_64.tar.gz" curl -sL -o /tmp/nfpm.tar.gz "https://github.com/goreleaser/nfpm/releases/download/v2.45.0/nfpm_2.45.0_Linux_x86_64.tar.gz"
tar xzf /tmp/nfpm.tar.gz -C /usr/local/bin nfpm tar xzf /tmp/nfpm.tar.gz -C /usr/local/bin nfpm
- name: Bundle standalone binaries - name: Bundle standalone binaries (${{ matrix.arch }})
env:
MCPCTL_TARGET_ARCH: ${{ matrix.arch }}
run: | run: |
source scripts/arch-helper.sh
resolve_arch "$MCPCTL_TARGET_ARCH"
mkdir -p dist mkdir -p dist
# Stub for optional dep that Ink tries to import (only used when DEV=true) # Stub for optional dep that Ink tries to import (only used when DEV=true)
# Copy instead of symlink — bun can't read directory symlinks # Copy instead of symlink — bun can't read directory symlinks
@@ -295,26 +305,30 @@ jobs:
rm -rf node_modules/react-devtools-core rm -rf node_modules/react-devtools-core
cp -r src/cli/stubs/react-devtools-core node_modules/react-devtools-core cp -r src/cli/stubs/react-devtools-core node_modules/react-devtools-core
fi fi
bun build src/cli/src/index.ts --compile --outfile dist/mcpctl bun build src/cli/src/index.ts --compile ${BUN_TARGET:+--target "$BUN_TARGET"} --outfile dist/mcpctl
bun build src/mcplocal/src/main.ts --compile --outfile dist/mcpctl-local bun build src/mcplocal/src/main.ts --compile ${BUN_TARGET:+--target "$BUN_TARGET"} --outfile dist/mcpctl-local
- name: Package RPM - name: Package RPM (${{ matrix.arch }})
env:
NFPM_ARCH: ${{ matrix.arch }}
run: nfpm pkg --packager rpm --target dist/ run: nfpm pkg --packager rpm --target dist/
- name: Package DEB - name: Package DEB (${{ matrix.arch }})
env:
NFPM_ARCH: ${{ matrix.arch }}
run: nfpm pkg --packager deb --target dist/ run: nfpm pkg --packager deb --target dist/
- name: Upload RPM artifact - name: Upload RPM artifact
uses: actions/upload-artifact@v3 uses: actions/upload-artifact@v3
with: with:
name: rpm-package name: rpm-package-${{ matrix.arch }}
path: dist/mcpctl-*.rpm path: dist/mcpctl-*.rpm
retention-days: 7 retention-days: 7
- name: Upload DEB artifact - name: Upload DEB artifact
uses: actions/upload-artifact@v3 uses: actions/upload-artifact@v3
with: with:
name: deb-package name: deb-package-${{ matrix.arch }}
path: dist/mcpctl*.deb path: dist/mcpctl*.deb
retention-days: 7 retention-days: 7
@@ -328,19 +342,22 @@ jobs:
runs-on: ubuntu-latest runs-on: ubuntu-latest
needs: [build] needs: [build]
if: github.ref == 'refs/heads/main' && github.event_name == 'push' if: github.ref == 'refs/heads/main' && github.event_name == 'push'
strategy:
matrix:
arch: [amd64, arm64]
steps: steps:
- uses: actions/checkout@v4 - uses: actions/checkout@v4
- name: Download RPM artifact - name: Download RPM artifact
uses: actions/download-artifact@v3 uses: actions/download-artifact@v3
with: with:
name: rpm-package name: rpm-package-${{ matrix.arch }}
path: dist/ path: dist/
- name: Install rpm tools - name: Install rpm tools
run: sudo apt-get update && sudo apt-get install -y rpm run: sudo apt-get update && sudo apt-get install -y rpm
- name: Publish RPM to Gitea - name: Publish RPM (${{ matrix.arch }}) to Gitea
env: env:
GITEA_TOKEN: ${{ secrets.PACKAGES_TOKEN }} GITEA_TOKEN: ${{ secrets.PACKAGES_TOKEN }}
GITEA_URL: http://${{ env.GITEA_REGISTRY }} GITEA_URL: http://${{ env.GITEA_REGISTRY }}
@@ -379,16 +396,19 @@ jobs:
runs-on: ubuntu-latest runs-on: ubuntu-latest
needs: [build] needs: [build]
if: github.ref == 'refs/heads/main' && github.event_name == 'push' if: github.ref == 'refs/heads/main' && github.event_name == 'push'
strategy:
matrix:
arch: [amd64, arm64]
steps: steps:
- uses: actions/checkout@v4 - uses: actions/checkout@v4
- name: Download DEB artifact - name: Download DEB artifact
uses: actions/download-artifact@v3 uses: actions/download-artifact@v3
with: with:
name: deb-package name: deb-package-${{ matrix.arch }}
path: dist/ path: dist/
- name: Publish DEB to Gitea - name: Publish DEB (${{ matrix.arch }}) to Gitea
env: env:
GITEA_TOKEN: ${{ secrets.PACKAGES_TOKEN }} GITEA_TOKEN: ${{ secrets.PACKAGES_TOKEN }}
GITEA_URL: http://${{ env.GITEA_REGISTRY }} GITEA_URL: http://${{ env.GITEA_REGISTRY }}

View File

@@ -1,23 +1,69 @@
#!/bin/bash #!/bin/bash
# Build (if needed) and install mcpctl RPM locally # Build (if needed) and install mcpctl locally.
# Auto-detects package format: RPM for Fedora/RHEL, DEB for Debian/Ubuntu.
#
# Usage:
# ./installlocal.sh # Build and install for native arch
# MCPCTL_TARGET_ARCH=amd64 ./installlocal.sh # Cross-compile for amd64
set -e set -e
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
cd "$SCRIPT_DIR" cd "$SCRIPT_DIR"
RPM_FILE=$(ls dist/mcpctl-*.rpm 2>/dev/null | head -1) # Resolve target architecture
source scripts/arch-helper.sh
resolve_arch "${MCPCTL_TARGET_ARCH:-}"
# Build if no RPM exists or if source is newer than the RPM # Detect package format
if [[ -z "$RPM_FILE" ]] || [[ $(find src/ -name '*.ts' -newer "$RPM_FILE" 2>/dev/null | head -1) ]]; then if command -v rpm &>/dev/null && command -v dnf &>/dev/null; then
echo "==> Building RPM..." PKG_FORMAT="rpm"
bash scripts/build-rpm.sh elif command -v dpkg &>/dev/null && command -v apt &>/dev/null; then
RPM_FILE=$(ls dist/mcpctl-*.rpm 2>/dev/null | head -1) PKG_FORMAT="deb"
elif command -v rpm &>/dev/null; then
PKG_FORMAT="rpm"
else else
echo "==> RPM is up to date: $RPM_FILE" echo "Error: Neither rpm/dnf nor dpkg/apt found. Unsupported system."
exit 1
fi fi
echo "==> Installing $RPM_FILE..." echo "==> Detected package format: $PKG_FORMAT (arch: $NFPM_ARCH)"
sudo rpm -Uvh --force "$RPM_FILE"
# Find package matching the target architecture
find_pkg() {
local pattern="$1"
# Filter by architecture name in filename
ls $pattern 2>/dev/null | grep -E "[._]${NFPM_ARCH}[._]" | head -1
}
if [ "$PKG_FORMAT" = "rpm" ]; then
PKG_FILE=$(find_pkg "dist/mcpctl-*.rpm")
# Build if no package exists or if source is newer
if [[ -z "$PKG_FILE" ]] || [[ $(find src/ -name '*.ts' -newer "$PKG_FILE" 2>/dev/null | head -1) ]]; then
echo "==> Building RPM..."
bash scripts/build-rpm.sh
PKG_FILE=$(find_pkg "dist/mcpctl-*.rpm")
else
echo "==> RPM is up to date: $PKG_FILE"
fi
echo "==> Installing $PKG_FILE..."
sudo rpm -Uvh --force "$PKG_FILE"
else
PKG_FILE=$(find_pkg "dist/mcpctl*.deb")
# Build if no package exists or if source is newer
if [[ -z "$PKG_FILE" ]] || [[ $(find src/ -name '*.ts' -newer "$PKG_FILE" 2>/dev/null | head -1) ]]; then
echo "==> Building DEB..."
bash scripts/build-deb.sh
PKG_FILE=$(find_pkg "dist/mcpctl*.deb")
else
echo "==> DEB is up to date: $PKG_FILE"
fi
echo "==> Installing $PKG_FILE..."
sudo dpkg -i "$PKG_FILE" || sudo apt-get install -f -y
fi
echo "==> Reloading systemd user units..." echo "==> Reloading systemd user units..."
systemctl --user daemon-reload systemctl --user daemon-reload

View File

@@ -1,5 +1,5 @@
name: mcpctl name: mcpctl
arch: amd64 arch: ${NFPM_ARCH:-amd64}
version: 0.0.1 version: 0.0.1
release: "1" release: "1"
maintainer: michal maintainer: michal

View File

@@ -20,10 +20,15 @@
"completions:generate": "tsx scripts/generate-completions.ts --write", "completions:generate": "tsx scripts/generate-completions.ts --write",
"completions:check": "tsx scripts/generate-completions.ts --check", "completions:check": "tsx scripts/generate-completions.ts --check",
"rpm:build": "bash scripts/build-rpm.sh", "rpm:build": "bash scripts/build-rpm.sh",
"rpm:build:amd64": "MCPCTL_TARGET_ARCH=amd64 bash scripts/build-rpm.sh",
"rpm:build:arm64": "MCPCTL_TARGET_ARCH=arm64 bash scripts/build-rpm.sh",
"rpm:publish": "bash scripts/publish-rpm.sh", "rpm:publish": "bash scripts/publish-rpm.sh",
"deb:build": "bash scripts/build-deb.sh", "deb:build": "bash scripts/build-deb.sh",
"deb:build:amd64": "MCPCTL_TARGET_ARCH=amd64 bash scripts/build-deb.sh",
"deb:build:arm64": "MCPCTL_TARGET_ARCH=arm64 bash scripts/build-deb.sh",
"deb:publish": "bash scripts/publish-deb.sh", "deb:publish": "bash scripts/publish-deb.sh",
"release": "bash scripts/release.sh", "release": "bash scripts/release.sh",
"release:both": "bash scripts/release.sh --both-arches",
"mcpd:build": "bash scripts/build-mcpd.sh", "mcpd:build": "bash scripts/build-mcpd.sh",
"mcpd:deploy": "bash deploy.sh", "mcpd:deploy": "bash deploy.sh",
"mcpd:deploy-dry": "bash deploy.sh --dry-run", "mcpd:deploy-dry": "bash deploy.sh --dry-run",

62
scripts/arch-helper.sh Normal file
View 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})"
}

View File

@@ -13,8 +13,14 @@ fi
# Ensure tools are on PATH # Ensure tools are on PATH
export PATH="$HOME/.npm-global/bin:$HOME/.bun/bin:$HOME/.local/bin:$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) # 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 "==> Binaries not found, building from scratch..."
echo "" echo ""
@@ -28,7 +34,7 @@ if [ ! -f dist/mcpctl ] || [ ! -f dist/mcpctl-local ]; then
echo "==> Generating shell completions..." echo "==> Generating shell completions..."
pnpm completions:generate pnpm completions:generate
echo "==> Bundling standalone binaries..." echo "==> Bundling standalone binaries (target: ${NFPM_ARCH})..."
mkdir -p dist mkdir -p dist
# Ink optionally imports react-devtools-core which isn't installed. # 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 ln -s ../src/cli/stubs/react-devtools-core node_modules/react-devtools-core
fi fi
bun build src/cli/src/index.ts --compile --outfile dist/mcpctl 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 --outfile dist/mcpctl-local bun build src/mcplocal/src/main.ts --compile ${BUN_TARGET:+--target "$BUN_TARGET"} --outfile "dist/mcpctl-local${ARCH_SUFFIX}"
else else
echo "==> Using existing binaries in dist/" echo "==> Using existing binaries in dist/"
fi fi
echo "==> Packaging DEB..." # If cross-compiling, copy arch-suffixed binaries to the names nfpm expects
rm -f dist/mcpctl-*.deb dist/mcpctl_*.deb 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/ 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 "==> Built: $DEB_FILE"
echo " Size: $(du -h "$DEB_FILE" | cut -f1)" echo " Size: $(du -h "$DEB_FILE" | cut -f1)"
# 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 dpkg-deb --info "$DEB_FILE" 2>/dev/null || true
fi

View File

@@ -1,5 +1,10 @@
#!/bin/bash #!/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 set -e
SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)" SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)"
@@ -16,8 +21,50 @@ REGISTRY="10.0.0.194:3012"
IMAGE="mcpd" IMAGE="mcpd"
TAG="${1:-latest}" TAG="${1:-latest}"
echo "==> Building mcpd image..." # Parse optional flags
podman build -t "$IMAGE:$TAG" -f deploy/Dockerfile.mcpd . 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
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 "==> Tagging manifest 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 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..." echo "==> Tagging as $REGISTRY/michal/$IMAGE:$TAG..."
podman tag "$IMAGE:$TAG" "$REGISTRY/michal/$IMAGE:$TAG" podman tag "$IMAGE:$TAG" "$REGISTRY/michal/$IMAGE:$TAG"
@@ -27,6 +74,7 @@ podman login --tls-verify=false -u michal -p "$GITEA_TOKEN" "$REGISTRY"
echo "==> Pushing to $REGISTRY/michal/$IMAGE:$TAG..." echo "==> Pushing to $REGISTRY/michal/$IMAGE:$TAG..."
podman push --tls-verify=false "$REGISTRY/michal/$IMAGE:$TAG" podman push --tls-verify=false "$REGISTRY/michal/$IMAGE:$TAG"
fi
# Ensure package is linked to the repository # Ensure package is linked to the repository
source "$SCRIPT_DIR/link-package.sh" source "$SCRIPT_DIR/link-package.sh"

View File

@@ -13,6 +13,12 @@ fi
# Ensure tools are on PATH # Ensure tools are on PATH
export PATH="$HOME/.npm-global/bin:$HOME/.bun/bin:$HOME/.local/bin:$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..." echo "==> Running unit tests..."
pnpm test:run pnpm test:run
echo "" echo ""
@@ -23,9 +29,11 @@ pnpm build
echo "==> Generating shell completions..." echo "==> Generating shell completions..."
pnpm completions:generate pnpm completions:generate
echo "==> Bundling standalone binaries..." echo "==> Bundling standalone binaries (target: ${NFPM_ARCH})..."
mkdir -p dist 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. # 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). # 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 ln -s ../src/cli/stubs/react-devtools-core node_modules/react-devtools-core
fi fi
bun build src/cli/src/index.ts --compile --outfile dist/mcpctl 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 --outfile dist/mcpctl-local 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/ 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 "==> Built: $RPM_FILE"
echo " Size: $(du -h "$RPM_FILE" | cut -f1)" echo " Size: $(du -h "$RPM_FILE" | cut -f1)"
if command -v rpm &>/dev/null; then
rpm -qpi "$RPM_FILE" rpm -qpi "$RPM_FILE"
fi
echo "" echo ""
echo "==> Packaging DEB..." echo "==> Packaging DEB (arch: ${NFPM_ARCH})..."
rm -f dist/mcpctl*.deb # 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/ 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 "==> Built: $DEB_FILE"
echo " Size: $(du -h "$DEB_FILE" | cut -f1)" echo " Size: $(du -h "$DEB_FILE" | cut -f1)"

View File

@@ -20,7 +20,16 @@ if [ -z "$GITEA_TOKEN" ]; then
exit 1 exit 1
fi fi
# 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) DEB_FILE=$(ls dist/mcpctl*.deb 2>/dev/null | head -1)
fi
if [ -z "$DEB_FILE" ]; then if [ -z "$DEB_FILE" ]; then
echo "Error: No DEB found in dist/. Run scripts/build-deb.sh first." echo "Error: No DEB found in dist/. Run scripts/build-deb.sh first."
exit 1 exit 1

View File

@@ -20,7 +20,16 @@ if [ -z "$GITEA_TOKEN" ]; then
exit 1 exit 1
fi fi
# 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) RPM_FILE=$(ls dist/mcpctl-*.rpm 2>/dev/null | head -1)
fi
if [ -z "$RPM_FILE" ]; then if [ -z "$RPM_FILE" ]; then
echo "Error: No RPM found in dist/. Run scripts/build-rpm.sh first." echo "Error: No RPM found in dist/. Run scripts/build-rpm.sh first."
exit 1 exit 1

View File

@@ -1,4 +1,9 @@
#!/bin/bash #!/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 set -e
SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)" SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)"
@@ -10,24 +15,47 @@ if [ -f .env ]; then
set -a; source .env; set +a set -a; source .env; set +a
fi 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 "=== mcpctl release ==="
echo " Native arch: $NATIVE_ARCH"
echo "" echo ""
# Build build_and_publish() {
bash scripts/build-rpm.sh 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 "" echo ""
# Publish # Install locally for native arch (auto-detect RPM or DEB)
bash scripts/publish-rpm.sh echo "==> Installing locally (${NATIVE_ARCH})..."
bash scripts/publish-deb.sh 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)
echo "" sudo dpkg -i "$DEB_FILE" || sudo apt-get install -f -y
else
# Install locally RPM_FILE=$(ls dist/mcpctl-*.rpm 2>/dev/null | grep -E "[._]${NATIVE_ARCH}[._]" | head -1)
echo "==> Installing locally..."
RPM_FILE=$(ls dist/mcpctl-*.rpm 2>/dev/null | head -1)
sudo rpm -U --force "$RPM_FILE" sudo rpm -U --force "$RPM_FILE"
fi
echo "" echo ""
echo "==> Installed:" echo "==> Installed:"