From 8ad7fe2748185d55e05a8d77017592c5084cf3bb Mon Sep 17 00:00:00 2001 From: Michal Rydlikowski Date: Fri, 13 Mar 2026 23:01:51 +0000 Subject: [PATCH] feat: add ARM64 (aarch64) architecture support for builds and packages 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) --- .gitea/workflows/ci.yml | 44 +++++++++++++++++++-------- installlocal.sh | 66 ++++++++++++++++++++++++++++++++++------- nfpm.yaml | 2 +- package.json | 5 ++++ scripts/arch-helper.sh | 62 ++++++++++++++++++++++++++++++++++++++ scripts/build-deb.sh | 33 ++++++++++++++++----- scripts/build-mcpd.sh | 66 +++++++++++++++++++++++++++++++++++------ scripts/build-rpm.sh | 38 +++++++++++++++++------- scripts/publish-deb.sh | 11 ++++++- scripts/publish-rpm.sh | 11 ++++++- scripts/release.sh | 52 ++++++++++++++++++++++++-------- 11 files changed, 326 insertions(+), 64 deletions(-) create mode 100644 scripts/arch-helper.sh diff --git a/.gitea/workflows/ci.yml b/.gitea/workflows/ci.yml index d1ef473..d0956e1 100644 --- a/.gitea/workflows/ci.yml +++ b/.gitea/workflows/ci.yml @@ -250,11 +250,14 @@ jobs: if: failure() run: cat /tmp/mcplocal.log || true - # ── Build & package RPM ─────────────────────────────────── + # ── Build & package (both amd64 and arm64) ────────────── build: runs-on: ubuntu-latest needs: [lint, typecheck, test] + strategy: + matrix: + arch: [amd64, arm64] steps: - uses: actions/checkout@v4 @@ -283,11 +286,18 @@ jobs: - name: Install nfpm 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" 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: | + source scripts/arch-helper.sh + resolve_arch "$MCPCTL_TARGET_ARCH" + mkdir -p dist # Stub for optional dep that Ink tries to import (only used when DEV=true) # Copy instead of symlink — bun can't read directory symlinks @@ -295,26 +305,30 @@ jobs: rm -rf node_modules/react-devtools-core cp -r 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 + 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/ - - name: Package DEB + - name: Package DEB (${{ matrix.arch }}) + env: + NFPM_ARCH: ${{ matrix.arch }} run: nfpm pkg --packager deb --target dist/ - name: Upload RPM artifact uses: actions/upload-artifact@v3 with: - name: rpm-package + name: rpm-package-${{ matrix.arch }} path: dist/mcpctl-*.rpm retention-days: 7 - name: Upload DEB artifact uses: actions/upload-artifact@v3 with: - name: deb-package + name: deb-package-${{ matrix.arch }} path: dist/mcpctl*.deb retention-days: 7 @@ -328,19 +342,22 @@ jobs: runs-on: ubuntu-latest needs: [build] if: github.ref == 'refs/heads/main' && github.event_name == 'push' + strategy: + matrix: + arch: [amd64, arm64] steps: - uses: actions/checkout@v4 - name: Download RPM artifact uses: actions/download-artifact@v3 with: - name: rpm-package + name: rpm-package-${{ matrix.arch }} path: dist/ - name: Install rpm tools run: sudo apt-get update && sudo apt-get install -y rpm - - name: Publish RPM to Gitea + - name: Publish RPM (${{ matrix.arch }}) to Gitea env: GITEA_TOKEN: ${{ secrets.PACKAGES_TOKEN }} GITEA_URL: http://${{ env.GITEA_REGISTRY }} @@ -379,16 +396,19 @@ jobs: runs-on: ubuntu-latest needs: [build] if: github.ref == 'refs/heads/main' && github.event_name == 'push' + strategy: + matrix: + arch: [amd64, arm64] steps: - uses: actions/checkout@v4 - name: Download DEB artifact uses: actions/download-artifact@v3 with: - name: deb-package + name: deb-package-${{ matrix.arch }} path: dist/ - - name: Publish DEB to Gitea + - name: Publish DEB (${{ matrix.arch }}) to Gitea env: GITEA_TOKEN: ${{ secrets.PACKAGES_TOKEN }} GITEA_URL: http://${{ env.GITEA_REGISTRY }} diff --git a/installlocal.sh b/installlocal.sh index 3c07dd7..d956e2a 100755 --- a/installlocal.sh +++ b/installlocal.sh @@ -1,23 +1,69 @@ #!/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 SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" 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 -if [[ -z "$RPM_FILE" ]] || [[ $(find src/ -name '*.ts' -newer "$RPM_FILE" 2>/dev/null | head -1) ]]; then - echo "==> Building RPM..." - bash scripts/build-rpm.sh - RPM_FILE=$(ls dist/mcpctl-*.rpm 2>/dev/null | head -1) +# Detect package format +if command -v rpm &>/dev/null && command -v dnf &>/dev/null; then + PKG_FORMAT="rpm" +elif command -v dpkg &>/dev/null && command -v apt &>/dev/null; then + PKG_FORMAT="deb" +elif command -v rpm &>/dev/null; then + PKG_FORMAT="rpm" else - echo "==> RPM is up to date: $RPM_FILE" + echo "Error: Neither rpm/dnf nor dpkg/apt found. Unsupported system." + exit 1 fi -echo "==> Installing $RPM_FILE..." -sudo rpm -Uvh --force "$RPM_FILE" +echo "==> Detected package format: $PKG_FORMAT (arch: $NFPM_ARCH)" + +# 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..." systemctl --user daemon-reload diff --git a/nfpm.yaml b/nfpm.yaml index e27f048..34ae5fd 100644 --- a/nfpm.yaml +++ b/nfpm.yaml @@ -1,5 +1,5 @@ name: mcpctl -arch: amd64 +arch: ${NFPM_ARCH:-amd64} version: 0.0.1 release: "1" maintainer: michal diff --git a/package.json b/package.json index 47e84ab..a238b35 100644 --- a/package.json +++ b/package.json @@ -20,10 +20,15 @@ "completions:generate": "tsx scripts/generate-completions.ts --write", "completions:check": "tsx scripts/generate-completions.ts --check", "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", "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", "release": "bash scripts/release.sh", + "release:both": "bash scripts/release.sh --both-arches", "mcpd:build": "bash scripts/build-mcpd.sh", "mcpd:deploy": "bash deploy.sh", "mcpd:deploy-dry": "bash deploy.sh --dry-run", diff --git a/scripts/arch-helper.sh b/scripts/arch-helper.sh new file mode 100644 index 0000000..74b02e3 --- /dev/null +++ b/scripts/arch-helper.sh @@ -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})" +} diff --git a/scripts/build-deb.sh b/scripts/build-deb.sh index d045312..812171b 100755 --- a/scripts/build-deb.sh +++ b/scripts/build-deb.sh @@ -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 diff --git a/scripts/build-mcpd.sh b/scripts/build-mcpd.sh index ae1b313..08a7f3a 100755 --- a/scripts/build-mcpd.sh +++ b/scripts/build-mcpd.sh @@ -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" diff --git a/scripts/build-rpm.sh b/scripts/build-rpm.sh index 0bfc3c0..b5d9a45 100755 --- a/scripts/build-rpm.sh +++ b/scripts/build-rpm.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)" diff --git a/scripts/publish-deb.sh b/scripts/publish-deb.sh index fa448d4..ec7f9e2 100755 --- a/scripts/publish-deb.sh +++ b/scripts/publish-deb.sh @@ -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 diff --git a/scripts/publish-rpm.sh b/scripts/publish-rpm.sh index e61fb5b..0a15196 100755 --- a/scripts/publish-rpm.sh +++ b/scripts/publish-rpm.sh @@ -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 diff --git a/scripts/release.sh b/scripts/release.sh index a1b3ccb..2e5c8ff 100755 --- a/scripts/release.sh +++ b/scripts/release.sh @@ -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:"