Skip to content

fix(shellwrap): capture login-shell PATH for docker credential helpers (#381) #766

fix(shellwrap): capture login-shell PATH for docker credential helpers (#381)

fix(shellwrap): capture login-shell PATH for docker credential helpers (#381) #766

Workflow file for this run

name: PR Build
on:
pull_request:
branches:
- "*"
workflow_dispatch:
jobs:
prepare:
name: Prepare Version
runs-on: ubuntu-latest
outputs:
version: ${{ steps.version.outputs.version }}
clean_version: ${{ steps.version.outputs.clean_version }}
latest_tag: ${{ steps.version.outputs.latest_tag }}
steps:
- name: Checkout
uses: actions/checkout@v4
with:
fetch-depth: 0
- name: Generate PR version
id: version
shell: bash
env:
PR_NUMBER: ${{ github.event.number }}
run: |
set -eo pipefail
LATEST_TAG=$(git describe --tags --abbrev=0 2>/dev/null || echo "v0.0.0")
COMMIT_HASH=$(git rev-parse --short HEAD)
if [ -n "${PR_NUMBER}" ]; then
VERSION="${LATEST_TAG}-dev-${COMMIT_HASH}-pr${PR_NUMBER}"
else
VERSION="${LATEST_TAG}-dev-${COMMIT_HASH}"
fi
echo "version=${VERSION}" >> "$GITHUB_OUTPUT"
echo "clean_version=${VERSION#v}" >> "$GITHUB_OUTPUT"
echo "latest_tag=${LATEST_TAG}" >> "$GITHUB_OUTPUT"
echo "Generated version: ${VERSION}"
verify-oas:
name: Verify OpenAPI Artifacts
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v4
- name: Set up Go
uses: actions/setup-go@v5
with:
go-version: "1.25"
- name: Install swag
run: go install github.com/swaggo/swag/v2/cmd/swag@v2.0.0-rc4
- name: Regenerate and verify OpenAPI spec
run: scripts/verify-oas.sh
# Build frontend once and share across all jobs
build-frontend:
name: Build Frontend
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v4
- name: Setup Node.js
uses: actions/setup-node@v4
with:
node-version: '20'
cache: 'npm'
cache-dependency-path: frontend/package-lock.json
- name: Install frontend dependencies
run: cd frontend && npm ci
- name: Build frontend
run: cd frontend && npm run build
- name: Upload frontend artifact
uses: actions/upload-artifact@v4
with:
name: frontend-dist-pr
path: frontend/dist/
retention-days: 1
build:
name: Build Binaries
needs: [prepare, build-frontend, verify-oas]
runs-on: ${{ matrix.os }}
env:
VERSION: ${{ needs.prepare.outputs.version }}
strategy:
matrix:
include:
- os: ubuntu-latest
goos: linux
goarch: amd64
cgo: "0"
name: mcpproxy-linux-amd64
archive_format: tar.gz
- os: ubuntu-latest
goos: linux
goarch: arm64
cgo: "0"
name: mcpproxy-linux-arm64
archive_format: tar.gz
- os: windows-latest
goos: windows
goarch: amd64
cgo: "1"
name: mcpproxy-windows-amd64.exe
archive_format: zip
- os: windows-latest
goos: windows
goarch: arm64
cgo: "1"
name: mcpproxy-windows-arm64.exe
archive_format: zip
- os: macos-15
goos: darwin
goarch: amd64
cgo: "1"
name: mcpproxy-darwin-amd64
archive_format: tar.gz
- os: macos-15
goos: darwin
goarch: arm64
cgo: "1"
name: mcpproxy-darwin-arm64
archive_format: tar.gz
steps:
- name: Checkout
uses: actions/checkout@v4
with:
fetch-depth: 0
- name: Set up Go
uses: actions/setup-go@v5
with:
go-version: "1.25"
cache: false # Disable built-in cache to use explicit cache step below
- name: Clean Go module cache (macOS workaround)
if: runner.os == 'macOS'
run: |
# Remove potentially corrupted toolchain files
rm -rf ~/go/pkg/mod/golang.org/toolchain* || true
- name: Cache Go modules and build
uses: actions/cache@v4
with:
path: |
~/.cache/go-build
~/go/pkg/mod
~/AppData/Local/go-build
key: ${{ runner.os }}-go-1.25-${{ hashFiles('**/go.sum') }}
restore-keys: |
${{ runner.os }}-go-1.25-
- name: Download dependencies
run: go mod download
- name: Download frontend artifact
uses: actions/download-artifact@v4
with:
name: frontend-dist-pr
path: frontend/dist
- name: Copy frontend dist for embedding
shell: bash
run: |
rm -rf web/frontend
mkdir -p web/frontend
cp -r frontend/dist web/frontend/
- name: Run tests (skip binary E2E tests - not compatible with cross-compilation)
# Skip tests for Windows ARM64 (cross-compilation - can't run ARM64 binaries on AMD64 runner)
if: '!(matrix.goos == ''windows'' && matrix.goarch == ''arm64'')'
shell: bash
run: |
# On Windows, skip internal/server tests (E2E tests timeout after 11 minutes)
if [ "${{ matrix.goos }}" = "windows" ]; then
go test -tags nogui -v -skip "E2E|Binary|MCPProtocol" $(go list ./... | grep -v 'internal/server$')
else
go test -tags nogui -v -skip "E2E|Binary|MCPProtocol" ./...
fi
- name: Build binary and create archives
shell: bash
env:
CGO_ENABLED: ${{ matrix.cgo }}
GOOS: ${{ matrix.goos }}
GOARCH: ${{ matrix.goarch }}
VERSION: ${{ env.VERSION }}
# ✅ Force minimum supported macOS version for compatibility (13.0 for Swift tray app)
MACOSX_DEPLOYMENT_TARGET: "13.0"
# Defensive CGO flags to ensure proper deployment target
CGO_CFLAGS: "-mmacosx-version-min=13.0"
CGO_LDFLAGS: "-mmacosx-version-min=13.0"
run: |
LDFLAGS="-s -w -X mcpproxy-go/cmd/mcpproxy.version=${VERSION} -X main.version=${VERSION}"
# Determine clean binary name
if [ "${{ matrix.goos }}" = "windows" ]; then
CLEAN_BINARY="mcpproxy.exe"
else
CLEAN_BINARY="mcpproxy"
fi
# Create clean core binary for archive
go build -ldflags "${LDFLAGS}" -o ${CLEAN_BINARY} ./cmd/mcpproxy
# Build tray binary for platforms with GUI support (macOS and Windows)
if [ "${{ matrix.goos }}" = "darwin" ] || [ "${{ matrix.goos }}" = "windows" ]; then
echo "Building mcpproxy-tray for ${{ matrix.goos }}..."
# Determine tray binary name
if [ "${{ matrix.goos }}" = "windows" ]; then
TRAY_BINARY="mcpproxy-tray.exe"
else
TRAY_BINARY="mcpproxy-tray"
fi
go build -ldflags "${LDFLAGS}" -o ${TRAY_BINARY} ./cmd/mcpproxy-tray
fi
# Ad-hoc sign macOS binaries (development only)
if [ "${{ matrix.goos }}" = "darwin" ]; then
echo "Ad-hoc signing macOS binaries for development..."
codesign --force --deep --sign - --identifier "com.smartmcpproxy.mcpproxy.dev" ${CLEAN_BINARY}
codesign --force --deep --sign - --identifier "com.smartmcpproxy.mcpproxy-tray.dev" mcpproxy-tray
# Verify signing
codesign --verify --verbose ${CLEAN_BINARY}
codesign --verify --verbose mcpproxy-tray
echo "Binaries signed successfully (development)"
fi
# Create archive with version info
ARCHIVE_BASE="mcpproxy-${VERSION#v}-${{ matrix.goos }}-${{ matrix.goarch }}"
# Determine files to include in archive
FILES_TO_ARCHIVE="${CLEAN_BINARY}"
# Add tray binary if it exists (Windows and macOS)
if [ "${{ matrix.goos }}" = "windows" ] && [ -f "mcpproxy-tray.exe" ]; then
FILES_TO_ARCHIVE="${FILES_TO_ARCHIVE} mcpproxy-tray.exe"
echo "Including mcpproxy-tray.exe in archive"
elif [ "${{ matrix.goos }}" = "darwin" ] && [ -f "mcpproxy-tray" ]; then
FILES_TO_ARCHIVE="${FILES_TO_ARCHIVE} mcpproxy-tray"
echo "Including mcpproxy-tray in archive"
fi
if [ "${{ matrix.archive_format }}" = "zip" ]; then
# Create ZIP archive (Windows)
# Use PowerShell Compress-Archive on Windows since zip command isn't available
if [ "${{ matrix.goos }}" = "windows" ]; then
# Convert space-separated list to comma-separated for PowerShell
PS_FILES=$(echo ${FILES_TO_ARCHIVE} | sed 's/ /,/g')
powershell -Command "Compress-Archive -Path ${PS_FILES} -DestinationPath '${ARCHIVE_BASE}.zip'"
else
zip "${ARCHIVE_BASE}.zip" ${FILES_TO_ARCHIVE}
fi
else
# Create tar.gz archive (Unix)
tar -czf "${ARCHIVE_BASE}.tar.gz" ${FILES_TO_ARCHIVE}
fi
# Build Swift tray app (macOS only)
- name: Build Swift tray app
if: matrix.goos == 'darwin'
run: |
chmod +x scripts/build-swift-app.sh
OUTPUT_DIR="$(pwd)/swift-build"
mkdir -p "$OUTPUT_DIR"
./scripts/build-swift-app.sh "${{ env.VERSION }}" "${{ matrix.goarch }}" "$OUTPUT_DIR"
SWIFT_APP="$OUTPUT_DIR/MCPProxy.app"
if [ -d "$SWIFT_APP" ]; then
# Ad-hoc sign for development
codesign --force --deep --sign - --identifier "com.smartmcpproxy.mcpproxy.dev" "$SWIFT_APP"
echo "SWIFT_APP_PATH=$SWIFT_APP" >> $GITHUB_ENV
else
echo "❌ Swift app build failed"
exit 1
fi
- name: Create .icns icon (macOS)
if: matrix.goos == 'darwin'
run: |
chmod +x scripts/create-icns.sh
./scripts/create-icns.sh
- name: Create app bundle and DMG installer (macOS)
if: matrix.goos == 'darwin'
env:
VERSION: ${{ env.VERSION }}
# Ensure DMG creation also uses correct deployment target
MACOSX_DEPLOYMENT_TARGET: "13.0"
CGO_CFLAGS: "-mmacosx-version-min=13.0"
CGO_LDFLAGS: "-mmacosx-version-min=13.0"
run: |
chmod +x scripts/create-pkg.sh
chmod +x scripts/create-app-dmg.sh
# Set up ad-hoc signing for PR builds
echo "=== Creating app bundle and DMG for PR build (ad-hoc signed) ==="
# Determine binary names
TRAY_BINARY="mcpproxy-tray"
CORE_BINARY="mcpproxy"
# Create app bundle using create-pkg.sh in adhoc mode
# This creates the app bundle but skips PKG creation
export APP_CERT_IDENTITY="" # Empty = ad-hoc signing for app bundle
export PKG_CERT_IDENTITY="adhoc" # Skip PKG creation, just create app bundle
# Create app bundle (prefer Swift app bundle over Go tray binary)
if [ -n "${SWIFT_APP_PATH:-}" ] && [ -d "${SWIFT_APP_PATH:-}" ]; then
echo "Using Swift app bundle: $SWIFT_APP_PATH"
./scripts/create-pkg.sh "$SWIFT_APP_PATH" ${CORE_BINARY} ${VERSION} ${{ matrix.goarch }}
else
echo "Using Go tray binary: ${TRAY_BINARY}"
./scripts/create-pkg.sh ${TRAY_BINARY} ${CORE_BINARY} ${VERSION} ${{ matrix.goarch }}
fi
# App bundle is created at pkg_temp/pkg_root/Applications/mcpproxy.app
APP_BUNDLE_PATH="pkg_temp/pkg_root/Applications/mcpproxy.app"
if [ -d "$APP_BUNDLE_PATH" ]; then
echo "✅ App bundle created successfully"
# Create DMG containing the app bundle (with Applications symlink for drag-and-drop)
./scripts/create-app-dmg.sh "$APP_BUNDLE_PATH" ${VERSION} ${{ matrix.goarch }}
# Clean up temp directory now that DMG is created
rm -rf pkg_temp
echo "✅ Installer DMG created successfully (ad-hoc signed, for testing)"
else
echo "❌ App bundle creation failed"
exit 1
fi
- name: Skip notarization (PR Build)
if: matrix.goos == 'darwin'
run: |
echo "Skipping notarization for PR build - this is a development build"
echo "Production builds go through full notarization in release workflow"
- name: Upload archive artifact
uses: actions/upload-artifact@v4
with:
name: archive-${{ matrix.goos }}-${{ matrix.goarch }}
path: mcpproxy-*-${{ matrix.goos }}-${{ matrix.goarch }}.${{ matrix.archive_format }}
retention-days: 14
- name: Upload macOS installer DMG
if: matrix.goos == 'darwin'
uses: actions/upload-artifact@v4
with:
name: installer-dmg-${{ matrix.goos }}-${{ matrix.goarch }}
path: mcpproxy-*-darwin-${{ matrix.goarch }}-installer.dmg
retention-days: 14
if-no-files-found: warn