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
This commit is contained in:
Louis Knight-Webb
2025-11-01 10:45:13 +00:00
parent c2bbd9f5ce
commit a4efc7988e
10 changed files with 251 additions and 57 deletions

View File

@@ -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

32
Cargo.lock generated
View File

@@ -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"

View File

@@ -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]

View File

@@ -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

View File

@@ -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);
});

31
npx-cli/package-lock.json generated Normal file
View File

@@ -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"
}
}
}
}

View File

@@ -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",

52
scripts/README.md Normal file
View File

@@ -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

111
scripts/manual-release.sh Executable file
View File

@@ -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."

View File

@@ -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,
}
}