From a4efc7988e5adc8945214ecdeea56e724df1ade7 Mon Sep 17 00:00:00 2001 From: Louis Knight-Webb Date: Sat, 1 Nov 2025 10:45:13 +0000 Subject: [PATCH] Implement CI and build releases (vibe-kanban 2ef3e91a) Implement CI and build releases Please copy the pre-release and logic that allows us to use JS + NPX to distribute the rust binary, with code signing on mac. You can view some examples in a different repo at /Users/lkw/Documents/repos/vibe-kanban - .github/workflows/pre-release.yml - .github/workflows/publish.yml - npx-cli/bin/cli.js - npx-cli/package.json --- .github/workflows/pre-release.yml | 36 +++++----- Cargo.lock | 32 ++++----- Cargo.toml | 4 +- README.md | 20 +++--- npx-cli/bin/cli.js | 12 ++-- npx-cli/package-lock.json | 31 +++++++++ npx-cli/package.json | 4 +- scripts/README.md | 52 ++++++++++++++ scripts/manual-release.sh | 111 ++++++++++++++++++++++++++++++ src/main.rs | 6 +- 10 files changed, 251 insertions(+), 57 deletions(-) create mode 100644 npx-cli/package-lock.json create mode 100644 scripts/README.md create mode 100755 scripts/manual-release.sh diff --git a/.github/workflows/pre-release.yml b/.github/workflows/pre-release.yml index ec40453..012dc9f 100644 --- a/.github/workflows/pre-release.yml +++ b/.github/workflows/pre-release.yml @@ -66,7 +66,7 @@ jobs: - name: Determine and update versions id: version run: | - latest_npm_version=$(npm view mcp-dev-manager version 2>/dev/null || echo "0.0.0") + latest_npm_version=$(npm view dev-manager-mcp version 2>/dev/null || echo "0.0.0") echo "Latest npm version: $latest_npm_version" timestamp=$(date +%Y%m%d%H%M%S) @@ -168,9 +168,9 @@ jobs: run: | mkdir -p dist if [[ "${{ matrix.os }}" == "windows-latest" ]]; then - cp target/${{ matrix.target }}/release/mcp-dev-manager.exe dist/mcp-dev-manager-${{ matrix.name }}.exe + cp target/${{ matrix.target }}/release/dev-manager-mcp.exe dist/dev-manager-mcp-${{ matrix.name }}.exe else - cp target/${{ matrix.target }}/release/mcp-dev-manager dist/mcp-dev-manager-${{ matrix.name }} + cp target/${{ matrix.target }}/release/dev-manager-mcp dist/dev-manager-mcp-${{ matrix.name }} fi - name: Prepare Apple certificate (macOS) @@ -182,8 +182,8 @@ jobs: if: runner.os == 'macOS' uses: indygreg/apple-code-sign-action@v1 with: - input_path: target/${{ matrix.target }}/release/mcp-dev-manager - output_path: mcp-dev-manager + input_path: target/${{ matrix.target }}/release/dev-manager-mcp + output_path: dev-manager-mcp p12_file: certificate.p12 p12_password: ${{ secrets.APPLE_CERTIFICATE_PASSWORD }} sign: true @@ -191,13 +191,13 @@ jobs: - name: Package binary (macOS) if: runner.os == 'macOS' - run: zip mcp-dev-manager.zip mcp-dev-manager + run: zip dev-manager-mcp.zip dev-manager-mcp - name: Prepare signed binaries (macOS) if: runner.os == 'macOS' run: | mkdir -p dist - cp mcp-dev-manager.zip dist/mcp-dev-manager-${{ matrix.name }}.zip + cp dev-manager-mcp.zip dist/dev-manager-mcp-${{ matrix.name }}.zip - name: Clean up certificates (macOS) if: runner.os == 'macOS' @@ -218,22 +218,22 @@ jobs: include: - target: x86_64-unknown-linux-musl name: linux-x64 - binary: mcp-dev-manager + binary: dev-manager-mcp - target: x86_64-pc-windows-msvc name: windows-x64 - binary: mcp-dev-manager.exe + binary: dev-manager-mcp.exe - target: x86_64-apple-darwin name: macos-x64 - binary: mcp-dev-manager + binary: dev-manager-mcp - target: aarch64-apple-darwin name: macos-arm64 - binary: mcp-dev-manager + binary: dev-manager-mcp - target: aarch64-pc-windows-msvc name: windows-arm64 - binary: mcp-dev-manager.exe + binary: dev-manager-mcp.exe - target: aarch64-unknown-linux-musl name: linux-arm64 - binary: mcp-dev-manager + binary: dev-manager-mcp steps: - uses: actions/checkout@v4 with: @@ -249,17 +249,17 @@ jobs: if: matrix.name != 'macos-arm64' && matrix.name != 'macos-x64' run: | mkdir -p npx-cli/dist/${{ matrix.name }} - mkdir mcp-dev-manager-${{ matrix.name }} + mkdir dev-manager-mcp-${{ matrix.name }} - cp dist/mcp-dev-manager-${{ matrix.name }}* mcp-dev-manager-${{ matrix.name }}/${{ matrix.binary }} + cp dist/dev-manager-mcp-${{ matrix.name }}* dev-manager-mcp-${{ matrix.name }}/${{ matrix.binary }} - zip -j npx-cli/dist/${{ matrix.name }}/mcp-dev-manager.zip mcp-dev-manager-${{ matrix.name }}/${{ matrix.binary }} + zip -j npx-cli/dist/${{ matrix.name }}/dev-manager-mcp.zip dev-manager-mcp-${{ matrix.name }}/${{ matrix.binary }} - name: Create platform package (macOS) if: matrix.name == 'macos-arm64' || matrix.name == 'macos-x64' run: | mkdir -p npx-cli/dist/${{ matrix.name }} - cp dist/mcp-dev-manager-${{ matrix.name }}* npx-cli/dist/${{ matrix.name }}/mcp-dev-manager.zip + cp dist/dev-manager-mcp-${{ matrix.name }}* npx-cli/dist/${{ matrix.name }}/dev-manager-mcp.zip - name: Upload platform package artifact uses: actions/upload-artifact@v4 @@ -301,4 +301,4 @@ jobs: prerelease: true generate_release_notes: true files: | - npx-cli/mcp-dev-manager-*.tgz + npx-cli/dev-manager-mcp-*.tgz diff --git a/Cargo.lock b/Cargo.lock index f2b3e4d..5b8fcc5 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -289,6 +289,22 @@ dependencies = [ "syn", ] +[[package]] +name = "dev-manager-mcp" +version = "0.1.0" +dependencies = [ + "anyhow", + "async-trait", + "clap", + "libc", + "rand 0.8.5", + "rmcp", + "schemars", + "serde", + "serde_json", + "tokio", +] + [[package]] name = "displaydoc" version = "0.2.5" @@ -767,22 +783,6 @@ version = "0.8.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "47e1ffaa40ddd1f3ed91f717a33c8c0ee23fff369e3aa8772b9605cc1d22f4c3" -[[package]] -name = "mcp-dev-manager" -version = "0.1.0" -dependencies = [ - "anyhow", - "async-trait", - "clap", - "libc", - "rand 0.8.5", - "rmcp", - "schemars", - "serde", - "serde_json", - "tokio", -] - [[package]] name = "memchr" version = "2.7.6" diff --git a/Cargo.toml b/Cargo.toml index b25004c..ffc6bcc 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,10 +1,10 @@ [package] -name = "mcp-dev-manager" +name = "dev-manager-mcp" version = "0.1.0" edition = "2021" [[bin]] -name = "mcp-dev-manager" +name = "dev-manager-mcp" path = "src/main.rs" [dependencies] diff --git a/README.md b/README.md index 890c122..068415e 100644 --- a/README.md +++ b/README.md @@ -17,7 +17,7 @@ A daemon process that runs continuously and accepts multiple concurrent MCP clie cargo build --release ``` -The binary will be at `target/release/mcp-dev-manager`. +The binary will be at `target/release/dev-manager-mcp`. ## Running @@ -25,17 +25,17 @@ The binary will be at `target/release/mcp-dev-manager`. ```bash # Foreground (default mode) -./target/release/mcp-dev-manager +./target/release/dev-manager-mcp # or explicitly: -./target/release/mcp-dev-manager daemon +./target/release/dev-manager-mcp daemon # Background -./target/release/mcp-dev-manager daemon & +./target/release/dev-manager-mcp daemon & # Custom port -./target/release/mcp-dev-manager daemon --port 3010 +./target/release/dev-manager-mcp daemon --port 3010 # or via environment variable: -PORT=3010 ./target/release/mcp-dev-manager daemon +PORT=3010 ./target/release/dev-manager-mcp daemon ``` The daemon will listen on `http://127.0.0.1:3009` by default. @@ -50,7 +50,7 @@ For Claude Desktop (`claude_desktop_config.json`): { "mcpServers": { "dev-manager": { - "command": "mcp-dev-manager", + "command": "dev-manager-mcp", "args": ["stdio"] } } @@ -63,7 +63,7 @@ The STDIO proxy connects to the daemon at `http://127.0.0.1:3009/sse` by default { "mcpServers": { "dev-manager": { - "command": "mcp-dev-manager", + "command": "dev-manager-mcp", "args": ["stdio", "--daemon-url", "http://127.0.0.1:3010/sse"] } } @@ -76,7 +76,7 @@ Or use the environment variable: { "mcpServers": { "dev-manager": { - "command": "mcp-dev-manager", + "command": "dev-manager-mcp", "args": ["stdio"], "env": { "MCP_DAEMON_URL": "http://127.0.0.1:3009/sse" @@ -207,7 +207,7 @@ Get stdout/stderr logs for a development server session. ## Testing Multi-Client Behavior -1. Start daemon: `./target/release/mcp-dev-manager` +1. Start daemon: `./target/release/dev-manager-mcp` 2. Connect Client A and start a server session 3. Connect Client B and query status - should see Client A's session 4. Client B can stop Client A's session diff --git a/npx-cli/bin/cli.js b/npx-cli/bin/cli.js index 98818b5..5356938 100644 --- a/npx-cli/bin/cli.js +++ b/npx-cli/bin/cli.js @@ -56,7 +56,7 @@ function getPlatformDir() { } function getBinaryName() { - return platform === "win32" ? "mcp-dev-manager.exe" : "mcp-dev-manager"; + return platform === "win32" ? "dev-manager-mcp.exe" : "dev-manager-mcp"; } const platformDir = getPlatformDir(); @@ -67,7 +67,7 @@ fs.mkdirSync(extractDir, { recursive: true }); function extractAndRun() { const binName = getBinaryName(); const binPath = path.join(extractDir, binName); - const zipName = "mcp-dev-manager.zip"; + const zipName = "dev-manager-mcp.zip"; const zipPath = path.join(extractDir, zipName); if (fs.existsSync(binPath)) fs.unlinkSync(binPath); @@ -82,8 +82,8 @@ function extractAndRun() { const zip = new AdmZip(zipPath); zip.extractAllTo(extractDir, true); } catch (err) { - console.error("❌ Failed to extract mcp-dev-manager archive:", err.message); - if (process.env.MCP_DEV_MANAGER_DEBUG) { + console.error("❌ Failed to extract dev-manager-mcp archive:", err.message); + if (process.env.DEV_MANAGER_MCP_DEBUG) { console.error(err.stack); } process.exit(1); @@ -101,14 +101,14 @@ function extractAndRun() { } catch { } } - console.log(`🚀 Launching mcp-dev-manager...`); + console.log(`🚀 Launching dev-manager-mcp...`); const args = process.argv.slice(2); const proc = spawn(binPath, args, { stdio: "inherit" }); proc.on("exit", (code) => process.exit(code || 0)); proc.on("error", (err) => { - console.error("❌ Failed to start mcp-dev-manager:", err.message); + console.error("❌ Failed to start dev-manager-mcp:", err.message); process.exit(1); }); diff --git a/npx-cli/package-lock.json b/npx-cli/package-lock.json new file mode 100644 index 0000000..0798089 --- /dev/null +++ b/npx-cli/package-lock.json @@ -0,0 +1,31 @@ +{ + "name": "dev-manager-mcp", + "version": "0.1.0", + "lockfileVersion": 3, + "requires": true, + "packages": { + "": { + "name": "dev-manager-mcp", + "version": "0.1.0", + "license": "MIT", + "dependencies": { + "adm-zip": "^0.5.16" + }, + "bin": { + "dev-manager-mcp": "bin/cli.js" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/adm-zip": { + "version": "0.5.16", + "resolved": "https://registry.npmjs.org/adm-zip/-/adm-zip-0.5.16.tgz", + "integrity": "sha512-TGw5yVi4saajsSEgz25grObGHEUaDrniwvA2qwSC060KfqGPdglhvPMA2lPIoxs3PQIItj2iag35fONcQqgUaQ==", + "license": "MIT", + "engines": { + "node": ">=12.0" + } + } + } +} diff --git a/npx-cli/package.json b/npx-cli/package.json index 2281f11..9790b2c 100644 --- a/npx-cli/package.json +++ b/npx-cli/package.json @@ -1,10 +1,10 @@ { - "name": "mcp-dev-manager", + "name": "dev-manager-mcp", "private": false, "version": "0.1.0", "main": "index.js", "bin": { - "mcp-dev-manager": "bin/cli.js" + "dev-manager-mcp": "bin/cli.js" }, "keywords": [ "mcp", diff --git a/scripts/README.md b/scripts/README.md new file mode 100644 index 0000000..3b96b19 --- /dev/null +++ b/scripts/README.md @@ -0,0 +1,52 @@ +# Manual Release Scripts + +## manual-release.sh + +Creates a single-platform npm package for testing or manual publishing. + +### Usage + +```bash +./scripts/manual-release.sh +``` + +This script will: +1. Detect your current platform (macOS/Linux/Windows, x64/ARM64) +2. Install the Rust target if needed +3. Build the release binary +4. Create the dist structure +5. Zip the binary +6. Install npm dependencies +7. Create a .tgz package + +### Testing Locally + +After running the script: + +```bash +# Install globally to test +npm install -g npx-cli/dev-manager-mcp-*.tgz + +# Run it +dev-manager-mcp --help + +# Uninstall when done +npm uninstall -g dev-manager-mcp +``` + +### Publishing Manually + +```bash +cd npx-cli +npm login +npm publish dev-manager-mcp-*.tgz --access public +``` + +### Important Notes + +- This creates a **single-platform** package (only your current platform) +- For production releases with **all platforms**, use GitHub Actions +- The manual script is useful for: + - Local testing before setting up CI/CD + - Quick iterations during development + - Publishing platform-specific test versions diff --git a/scripts/manual-release.sh b/scripts/manual-release.sh new file mode 100755 index 0000000..53f8d5d --- /dev/null +++ b/scripts/manual-release.sh @@ -0,0 +1,111 @@ +#!/bin/bash +set -e + +echo "🚀 Manual Release Script for dev-manager-mcp" +echo "" + +# Detect platform +OS=$(uname -s | tr '[:upper:]' '[:lower:]') +ARCH=$(uname -m) + +# Map to our platform naming +if [[ "$OS" == "darwin" ]]; then + if [[ "$ARCH" == "arm64" ]]; then + PLATFORM="macos-arm64" + TARGET="aarch64-apple-darwin" + else + PLATFORM="macos-x64" + TARGET="x86_64-apple-darwin" + fi + BINARY_NAME="dev-manager-mcp" +elif [[ "$OS" == "linux" ]]; then + if [[ "$ARCH" == "aarch64" || "$ARCH" == "arm64" ]]; then + PLATFORM="linux-arm64" + TARGET="aarch64-unknown-linux-musl" + else + PLATFORM="linux-x64" + TARGET="x86_64-unknown-linux-musl" + fi + BINARY_NAME="dev-manager-mcp" +elif [[ "$OS" =~ "mingw" || "$OS" =~ "msys" || "$OS" =~ "cygwin" ]]; then + if [[ "$ARCH" == "aarch64" || "$ARCH" == "arm64" ]]; then + PLATFORM="windows-arm64" + TARGET="aarch64-pc-windows-msvc" + else + PLATFORM="windows-x64" + TARGET="x86_64-pc-windows-msvc" + fi + BINARY_NAME="dev-manager-mcp.exe" +else + echo "❌ Unsupported platform: $OS-$ARCH" + exit 1 +fi + +echo "📦 Detected platform: $PLATFORM" +echo "🎯 Target: $TARGET" +echo "" + +# Check if rustup target is installed +if ! rustup target list --installed | grep -q "$TARGET"; then + echo "📥 Installing Rust target: $TARGET" + rustup target add "$TARGET" + echo "" +fi + +# Build the binary +echo "🔨 Building release binary..." +cargo build --release --target "$TARGET" +echo "" + +# Create npx-cli dist structure +echo "📁 Creating dist structure..." +mkdir -p npx-cli/dist/"$PLATFORM" +echo "" + +# Copy and zip the binary +BINARY_PATH="target/$TARGET/release/$BINARY_NAME" +if [[ ! -f "$BINARY_PATH" ]]; then + echo "❌ Binary not found at: $BINARY_PATH" + exit 1 +fi + +echo "📦 Zipping binary..." +if command -v zip &> /dev/null; then + zip -j npx-cli/dist/"$PLATFORM"/dev-manager-mcp.zip "$BINARY_PATH" +else + echo "❌ 'zip' command not found. Please install zip." + exit 1 +fi +echo "" + +# Install npm dependencies if needed +if [[ ! -d "npx-cli/node_modules" ]]; then + echo "📥 Installing npm dependencies..." + cd npx-cli + npm install + cd .. + echo "" +fi + +# Pack the npm package +echo "📦 Creating npm package..." +cd npx-cli +npm pack +cd .. +echo "" + +# Find the created tarball +TARBALL=$(ls npx-cli/dev-manager-mcp-*.tgz | head -n1) + +echo "✅ Package created successfully!" +echo "" +echo "📦 Package: $TARBALL" +echo "🏷️ Platform: $PLATFORM" +echo "" +echo "Next steps:" +echo " 1. Test locally: npm install -g $TARBALL" +echo " 2. Login to npm: cd npx-cli && npm login" +echo " 3. Publish: npm publish --access public" +echo "" +echo "⚠️ Note: This package only contains the $PLATFORM binary." +echo " For multi-platform support, use GitHub Actions CI/CD." diff --git a/src/main.rs b/src/main.rs index 1ff8ac8..f2725a4 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,7 +1,7 @@ use clap::{Parser, Subcommand}; #[derive(Parser)] -#[command(name = "mcp-dev-manager")] +#[command(name = "dev-manager-mcp")] #[command(about = "MCP development server manager with shared session state")] struct Cli { #[command(subcommand)] @@ -31,7 +31,7 @@ async fn main() -> anyhow::Result<()> { let cli = Cli::parse(); match cli.command.unwrap_or(Command::Daemon { port: 3009 }) { - Command::Daemon { port } => mcp_dev_manager::run_daemon(port).await, - Command::Stdio { daemon_url } => mcp_dev_manager::run_stdio_proxy(&daemon_url).await, + Command::Daemon { port } => dev_manager_mcp::run_daemon(port).await, + Command::Stdio { daemon_url } => dev_manager_mcp::run_stdio_proxy(&daemon_url).await, } }