feat(deploy): ensure deploy-server executes graceful shutdown before replacing binaries and auto-restarts server

This commit is contained in:
Riz Ashraf committed 2026-10-05 10:46:38 +01:00
1 parent 2cd912858a
commit bd8a892101
1 file changed
+76 -66
+76 -66
View File
@@ -1,174 +1,184 @@
set shell := ["pwsh.exe", "-NoProfile", "-c"] set shell := ["pwsh.exe", "-NoProfile", "-c"]
# Display available commands
default: default:
@just --list @just --list
# Build and deploy everything across Windows and WSL # Build and deploy everything across Windows and WSL
all: build-all deploy-all all: build-all deploy-all
# Build everything across Windows and WSL # Build all binaries across Windows and WSL
build-all: build-win build-wsl build-all: build-win build-wsl
@Write-Host "Build complete across both OS boundaries." -ForegroundColor Green @Write-Host "Build complete across both OS boundaries." -ForegroundColor Green
# Deploy everything across Windows and WSL # Deploy all binaries and agent rules across Windows and WSL
deploy-all: deploy-win deploy-wsl deploy-all: deploy-win deploy-wsl
@Write-Host "Deployment complete across both OS boundaries." -ForegroundColor Green @Write-Host "Deployment complete across both OS boundaries." -ForegroundColor Green
# Gracefully shut down the running server
shutdown-server:
@Write-Host "Shutting down MCP server gracefully..." -ForegroundColor Cyan
-$token = Get-Content "$env:USERPROFILE\.gemini\mcp_memory\admin.token" -ErrorAction SilentlyContinue; if ($token) { Invoke-RestMethod -Uri "http://127.0.0.1:3000/shutdown" -Method Post -Headers @{ Authorization = "Bearer $token" } -ErrorAction SilentlyContinue } else { Invoke-RestMethod -Uri "http://127.0.0.1:3000/shutdown" -Method Post -ErrorAction SilentlyContinue }
Start-Sleep -Seconds 1
-if (Get-Process mcp-memory-server, mcp-memory-stub, mcp-memory-nvim -ErrorAction SilentlyContinue) { Stop-Process -Name mcp-memory-server, mcp-memory-stub, mcp-memory-nvim -Force -ErrorAction SilentlyContinue }
Start-Sleep -Seconds 2
# Build Windows-native binaries # Build Windows-native binaries
build-win: build-server build-stub-win build-nvim-win build-win: build-server build-stub-win build-nvim-win
# Deploy Windows-native binaries # Deploy Windows-native binaries and rules
deploy-win: deploy-server deploy-stub-win deploy-nvim-win deploy-rules-win deploy-win: deploy-server deploy-stub-win deploy-nvim-win deploy-rules-win
# ========================================================= # =========================================================
# 1. LIFECYCLE (Start / Stop / Restart) # 1. LIFECYCLE (Start / Stop / Restart / Verify / Version)
# ========================================================= # =========================================================
# Stop the background server # Stop the background server gracefully and verify port release
stop port="3000": stop port="3000":
@Write-Host "Shutting down MCP server gracefully on port {{port}}..." -ForegroundColor Cyan @Write-Host "Shutting down MCP server gracefully on port {{port}}..." -ForegroundColor Cyan
-& { $token = Get-Content "$env:USERPROFILE\.gemini\mcp_memory\admin.token" -ErrorAction SilentlyContinue; if ($token) { $hdr = @{ Authorization = "Bearer $($token.Trim())" }; $res = Invoke-RestMethod -Uri "http://127.0.0.1:{{port}}/shutdown" -Method Post -Headers $hdr -ErrorAction SilentlyContinue } else { $res = Invoke-RestMethod -Uri "http://127.0.0.1:{{port}}/shutdown" -Method Post -ErrorAction SilentlyContinue }; if ($res) { Write-Host "✅ Shutdown acknowledged with version details:" -ForegroundColor Green; $res | ConvertTo-Json -Depth 5 }; $stopped = $false; for ($i = 1; $i -le 10; $i++) { $conn = Get-NetTCPConnection -LocalPort {{port}} -State Listen -ErrorAction SilentlyContinue; $proc = Get-Process mcp-memory-server -ErrorAction SilentlyContinue; if (-not $conn -and -not $proc) { $stopped = $true; Write-Host '✅ Server process and port {{port}} fully terminated.' -ForegroundColor Green; break }; Start-Sleep -Seconds 1 }; if (-not $stopped) { Write-Host '⚠️ Server process did not exit within 10s. Force killing process...' -ForegroundColor Yellow; Get-Process mcp-memory-server -ErrorAction SilentlyContinue | Stop-Process -Force -ErrorAction SilentlyContinue; Get-NetTCPConnection -LocalPort {{port}} -State Listen -ErrorAction SilentlyContinue | Select-Object -ExpandProperty OwningProcess | ForEach-Object { Stop-Process -Id $_ -Force -ErrorAction SilentlyContinue }; Start-Sleep -Seconds 2 } } -& { $token = Get-Content "$env:USERPROFILE\.gemini\mcp_memory\admin.token" -ErrorAction SilentlyContinue; if ($token) { $hdr = @{ Authorization = "Bearer $($token.Trim())" }; $res = Invoke-RestMethod -Uri "http://127.0.0.1:{{port}}/shutdown" -Method Post -Headers $hdr -ErrorAction SilentlyContinue } else { $res = Invoke-RestMethod -Uri "http://127.0.0.1:{{port}}/shutdown" -Method Post -ErrorAction SilentlyContinue }; if ($res) { Write-Host "✅ Shutdown acknowledged with version details:" -ForegroundColor Green; $res | ConvertTo-Json -Depth 5 }; $stopped = $false; for ($i = 1; $i -le 10; $i++) { $conn = Get-NetTCPConnection -LocalPort {{port}} -State Listen -ErrorAction SilentlyContinue; $proc = Get-Process mcp-memory-server -ErrorAction SilentlyContinue; if (-not $conn -and -not $proc) { $stopped = $true; Write-Host '✅ Server process and port {{port}} fully terminated.' -ForegroundColor Green; break }; Start-Sleep -Seconds 1 }; if (-not $stopped) { Write-Host '⚠️ Server process did not exit within 10s. Force killing process...' -ForegroundColor Yellow; Get-Process mcp-memory-server -ErrorAction SilentlyContinue | Stop-Process -Force -ErrorAction SilentlyContinue; Get-NetTCPConnection -LocalPort {{port}} -State Listen -ErrorAction SilentlyContinue | Select-Object -ExpandProperty OwningProcess | ForEach-Object { Stop-Process -Id $_ -Force -ErrorAction SilentlyContinue }; Start-Sleep -Seconds 2 } }
@Write-Host "✅ Shutdown complete. Port {{port}} and process are free." -ForegroundColor Green @Write-Host "✅ Shutdown complete. Port {{port}} and process are free." -ForegroundColor Green
# Start the server in the background (nohup equivalent) # Start the server in the background and verify /health
start port="3000": start port="3000":
@if (Get-NetTCPConnection -LocalPort {{port}} -State Listen -ErrorAction SilentlyContinue) { Write-Host '⚠️ Server is already running on port {{port}}. Skipping start.' -ForegroundColor Yellow; exit 0 }; Write-Host 'Starting MCP server in background on port {{port}}...' -ForegroundColor Cyan; $env:MCP_PORT='{{port}}'; $env:MCP_UDP_PORT1=$([int]{{port}} + 1); $env:MCP_UDP_PORT2=$([int]{{port}} + 2); .\start_server.ps1 -Port {{port}}; Write-Host 'Waiting up to 40 seconds for /health endpoint...' -ForegroundColor Cyan; for ($i = 1; $i -le 40; $i++) { try { $h = Invoke-RestMethod -Uri 'http://127.0.0.1:{{port}}/health' -ErrorAction Stop; Write-Host "✅ Server is UP! Running Version: $($h.version.app_version) [$($h.version.target_os)/$($h.version.target_arch)]" -ForegroundColor Green; exit 0 } catch { Start-Sleep -Seconds 1 } }; Write-Error '❌ Server failed to start on port {{port}} within 40 seconds.'; exit 1 @if (Get-NetTCPConnection -LocalPort {{port}} -State Listen -ErrorAction SilentlyContinue) { Write-Host '⚠️ Server is already running on port {{port}}. Skipping start.' -ForegroundColor Yellow; exit 0 }; Write-Host 'Starting MCP server in background on port {{port}}...' -ForegroundColor Cyan; $env:MCP_PORT='{{port}}'; $env:MCP_UDP_PORT1=$([int]{{port}} + 1); $env:MCP_UDP_PORT2=$([int]{{port}} + 2); .\start_server.ps1 -Port {{port}}; Write-Host 'Waiting up to 40 seconds for /health endpoint...' -ForegroundColor Cyan; for ($i = 1; $i -le 40; $i++) { try { $h = Invoke-RestMethod -Uri 'http://127.0.0.1:{{port}}/health' -ErrorAction Stop; Write-Host "✅ Server is UP! Running Version: $($h.version.app_version) [$($h.version.target_os)/$($h.version.target_arch)]" -ForegroundColor Green; exit 0 } catch { Start-Sleep -Seconds 1 } }; Write-Error '❌ Server failed to start on port {{port}} within 40 seconds.'; exit 1
# Verify the deployment is healthy and serving correct content # Verify deployment health and UI payload response
verify port="3000": verify port="3000":
.\verify_deployment.ps1 -Port {{port}} .\verify_deployment.ps1 -Port {{port}}
# Display active running version and binary CLI version details # Display active running server version and deployed binary CLI version
version port="3000": version port="3000":
@Write-Host "--- Running Server API Version ---" -ForegroundColor Cyan @Write-Host "--- Running Server API Version ---" -ForegroundColor Cyan
-& { try { $h = Invoke-RestMethod -Uri "http://127.0.0.1:{{port}}/health" -ErrorAction Stop; Write-Host "HTTP /health: $($h.version.app_version) [$($h.version.target_os)/$($h.version.target_arch)]" -ForegroundColor Green } catch { Write-Host "HTTP /health: Server not reachable on port {{port}}" -ForegroundColor Yellow } } -& { try { $h = Invoke-RestMethod -Uri "http://127.0.0.1:{{port}}/health" -ErrorAction Stop; Write-Host "HTTP /health: $($h.version.app_version) [$($h.version.target_os)/$($h.version.target_arch)]" -ForegroundColor Green } catch { Write-Host "HTTP /health: Server not reachable on port {{port}}" -ForegroundColor Yellow } }
@Write-Host "--- Deployed Binary CLI Version ---" -ForegroundColor Cyan @Write-Host "--- Deployed Binary CLI Version ---" -ForegroundColor Cyan
-mcp-memory-server --version -mcp-memory-server --version
# Restart the server and verify # Gracefully stop, start, and verify the background server
restart port="3000": (stop port) (start port) (verify port) restart port="3000": (stop port) (start port) (verify port)
# Run server directly in foreground for debugging
run-server:
cargo run -p mcp-memory-server
# ========================================================= # =========================================================
# 2. BUILD (Compile Binaries) # 2. BUILD (Compile Binaries)
# ========================================================= # =========================================================
# Build Windows-native server binary in release mode
build-server: build-server:
@Write-Host "Building Windows Server..." -ForegroundColor Cyan @Write-Host "Building Windows Server..." -ForegroundColor Cyan
cargo build --release -p mcp-memory-server cargo build --release -p mcp-memory-server
# Build Windows-native stub binary in release mode
build-stub-win: build-stub-win:
@Write-Host "Building Windows Stub..." -ForegroundColor Cyan @Write-Host "Building Windows Stub..." -ForegroundColor Cyan
cargo build --release -p mcp-memory-stub cargo build --release -p mcp-memory-stub
# Build Windows-native Neovim plugin binary
build-nvim-win:
@Write-Host "Building Windows Nvim..." -ForegroundColor Cyan
cargo build --release -p mcp-memory-nvim
# Build all WSL-native binaries
build-wsl: build-stub build-nvim
# Build WSL-native stub binary in release mode
build-stub:
@Write-Host "Building WSL Stub natively..." -ForegroundColor Cyan
wsl.exe -d Ubuntu -e bash -c 'export PATH="/home/riz/.cargo/bin:$PATH" && cd /mnt/c/Users/reazul.ashraf/workspace/rust/mcp-memory && cargo build --release -p mcp-memory-stub'
# Build WSL-native Neovim plugin binary in release mode
build-nvim:
@Write-Host "Building WSL Nvim natively..." -ForegroundColor Cyan
wsl.exe -d Ubuntu -e bash -c 'export PATH="/home/riz/.cargo/bin:$PATH" && cd /mnt/c/Users/reazul.ashraf/workspace/rust/mcp-memory && cargo build --release -p mcp-memory-nvim'
# ========================================================= # =========================================================
# 3. DEPLOY (Move Binaries to Global Path) # 3. DEPLOY (Move Binaries and Rules to Global Paths)
# ========================================================= # =========================================================
# Stage updated server binary, perform graceful shutdown, and auto-restart
deploy-server: deploy-server:
@Write-Host "Deploying Windows Server..." -ForegroundColor Cyan @Write-Host "Deploying Windows Server (Staged Hot-Swap)..." -ForegroundColor Cyan
-Stop-Process -Name mcp-memory-server -Force -ErrorAction SilentlyContinue
Start-Sleep -Seconds 1
-& { $targets = @("$env:USERPROFILE\.local\bin\mcp-memory-server.exe", "$env:USERPROFILE\.cargo\bin\mcp-memory-server.exe"); foreach ($target in $targets) { if (Test-Path (Split-Path $target)) { if (Test-Path $target) { $bak = "$target.old.$([guid]::NewGuid().ToString().Substring(0,8))"; Move-Item -Force $target $bak -ErrorAction SilentlyContinue }; Copy-Item -Force target\release\mcp-memory-server.exe $target; Get-ChildItem "$(Split-Path $target)\mcp-memory-server.exe.old.*" -ErrorAction SilentlyContinue | Remove-Item -Force -ErrorAction SilentlyContinue } } } -& { $targets = @("$env:USERPROFILE\.local\bin\mcp-memory-server.exe", "$env:USERPROFILE\.cargo\bin\mcp-memory-server.exe"); foreach ($target in $targets) { if (Test-Path (Split-Path $target)) { if (Test-Path $target) { $bak = "$target.old.$([guid]::NewGuid().ToString().Substring(0,8))"; Move-Item -Force $target $bak -ErrorAction SilentlyContinue }; Copy-Item -Force target\release\mcp-memory-server.exe $target; Get-ChildItem "$(Split-Path $target)\mcp-memory-server.exe.old.*" -ErrorAction SilentlyContinue | Remove-Item -Force -ErrorAction SilentlyContinue } } }
-just stop
just start
# Deploy Windows-native stub binary
deploy-stub-win: deploy-stub-win:
@Write-Host "Deploying Windows Stub..." -ForegroundColor Cyan @Write-Host "Deploying Windows Stub..." -ForegroundColor Cyan
-Stop-Process -Name mcp-memory-stub -Force -ErrorAction SilentlyContinue -Stop-Process -Name mcp-memory-stub -Force -ErrorAction SilentlyContinue
-if (Test-Path "C:\Users\reazul.ashraf\.local\bin\mcp-memory-stub.exe") { Move-Item -Force "C:\Users\reazul.ashraf\.local\bin\mcp-memory-stub.exe" "C:\Users\reazul.ashraf\.local\bin\mcp-memory-stub.exe.old" -ErrorAction SilentlyContinue; Remove-Item -Force "C:\Users\reazul.ashraf\.local\bin\mcp-memory-stub.exe.old" -ErrorAction SilentlyContinue } -if (Test-Path "C:\Users\reazul.ashraf\.local\bin\mcp-memory-stub.exe") { Move-Item -Force "C:\Users\reazul.ashraf\.local\bin\mcp-memory-stub.exe" "C:\Users\reazul.ashraf\.local\bin\mcp-memory-stub.exe.old" -ErrorAction SilentlyContinue; Remove-Item -Force "C:\Users\reazul.ashraf\.local\bin\mcp-memory-stub.exe.old" -ErrorAction SilentlyContinue }
Copy-Item -Force target\release\mcp-memory-stub.exe "C:\Users\reazul.ashraf\.local\bin\" Copy-Item -Force target\release\mcp-memory-stub.exe "C:\Users\reazul.ashraf\.local\bin\"
# Deploy Windows-native Neovim plugin binary
deploy-nvim-win: deploy-nvim-win:
@Write-Host "Deploying Windows Nvim..." -ForegroundColor Cyan @Write-Host "Deploying Windows Nvim..." -ForegroundColor Cyan
-Stop-Process -Name mcp-memory-nvim -Force -ErrorAction SilentlyContinue -Stop-Process -Name mcp-memory-nvim -Force -ErrorAction SilentlyContinue
-if (Test-Path "C:\Users\reazul.ashraf\.local\bin\mcp-memory-nvim.exe") { Move-Item -Force "C:\Users\reazul.ashraf\.local\bin\mcp-memory-nvim.exe" "C:\Users\reazul.ashraf\.local\bin\mcp-memory-nvim.exe.old" -ErrorAction SilentlyContinue; Remove-Item -Force "C:\Users\reazul.ashraf\.local\bin\mcp-memory-nvim.exe.old" -ErrorAction SilentlyContinue } -if (Test-Path "C:\Users\reazul.ashraf\.local\bin\mcp-memory-nvim.exe") { Move-Item -Force "C:\Users\reazul.ashraf\.local\bin\mcp-memory-nvim.exe" "C:\Users\reazul.ashraf\.local\bin\mcp-memory-nvim.exe.old" -ErrorAction SilentlyContinue; Remove-Item -Force "C:\Users\reazul.ashraf\.local\bin\mcp-memory-nvim.exe.old" -ErrorAction SilentlyContinue }
Copy-Item -Force target\release\mcp-memory-nvim.exe "C:\Users\reazul.ashraf\.local\bin\" Copy-Item -Force target\release\mcp-memory-nvim.exe "C:\Users\reazul.ashraf\.local\bin\"
# Deploy global Agent rules to Windows config directory
deploy-rules-win: deploy-rules-win:
@Write-Host "Deploying global Agent rules (Windows)..." -ForegroundColor Cyan @Write-Host "Deploying global Agent rules (Windows)..." -ForegroundColor Cyan
if (!(Test-Path "C:\Users\reazul.ashraf\.gemini\config\rules")) { New-Item -ItemType Directory -Force -Path "C:\Users\reazul.ashraf\.gemini\config\rules" | Out-Null } if (!(Test-Path "C:\Users\reazul.ashraf\.gemini\config\rules")) { New-Item -ItemType Directory -Force -Path "C:\Users\reazul.ashraf\.gemini\config\rules" | Out-Null }
Copy-Item -Force -Recurse agent-rules\* "C:\Users\reazul.ashraf\.gemini\config\rules\" Copy-Item -Force -Recurse agent-rules\* "C:\Users\reazul.ashraf\.gemini\config\rules\"
# Build WSL-native binaries # Deploy all WSL-native binaries and agent rules
build-wsl: build-stub build-nvim
# Deploy WSL-native binaries
deploy-wsl: deploy-stub deploy-nvim deploy-rules-wsl deploy-wsl: deploy-stub deploy-nvim deploy-rules-wsl
build-stub: # Deploy WSL-native stub binary
@Write-Host "Building WSL Stub natively..." -ForegroundColor Cyan
wsl.exe -d Ubuntu -e bash -c 'export PATH="/home/riz/.cargo/bin:$PATH" && cd /mnt/c/Users/reazul.ashraf/workspace/rust/mcp-memory && cargo build --release -p mcp-memory-stub'
deploy-stub: deploy-stub:
@Write-Host "Deploying WSL Stub natively..." -ForegroundColor Cyan @Write-Host "Deploying WSL Stub natively..." -ForegroundColor Cyan
wsl.exe -d Ubuntu -e bash -c 'export PATH="/home/riz/.cargo/bin:$PATH" && cd /mnt/c/Users/reazul.ashraf/workspace/rust/mcp-memory && cp target/release/mcp-memory-stub /home/riz/.local/bin/' wsl.exe -d Ubuntu -e bash -c 'export PATH="/home/riz/.cargo/bin:$PATH" && cd /mnt/c/Users/reazul.ashraf/workspace/rust/mcp-memory && cp target/release/mcp-memory-stub /home/riz/.local/bin/'
build-nvim: # Deploy WSL-native Neovim plugin binary
@Write-Host "Building WSL Nvim natively..." -ForegroundColor Cyan
wsl.exe -d Ubuntu -e bash -c 'export PATH="/home/riz/.cargo/bin:$PATH" && cd /mnt/c/Users/reazul.ashraf/workspace/rust/mcp-memory && cargo build --release -p mcp-memory-nvim'
deploy-nvim: deploy-nvim:
@Write-Host "Deploying WSL Nvim natively..." -ForegroundColor Cyan @Write-Host "Deploying WSL Nvim natively..." -ForegroundColor Cyan
wsl.exe -d Ubuntu -e bash -c 'export PATH="/home/riz/.cargo/bin:$PATH" && cd /mnt/c/Users/reazul.ashraf/workspace/rust/mcp-memory && cp target/release/mcp-memory-nvim /home/riz/.local/bin/' wsl.exe -d Ubuntu -e bash -c 'export PATH="/home/riz/.cargo/bin:$PATH" && cd /mnt/c/Users/reazul.ashraf/workspace/rust/mcp-memory && cp target/release/mcp-memory-nvim /home/riz/.local/bin/'
# Deploy global Agent rules to WSL config directory
deploy-rules-wsl: deploy-rules-wsl:
@Write-Host "Deploying global Agent rules (WSL)..." -ForegroundColor Cyan @Write-Host "Deploying global Agent rules (WSL)..." -ForegroundColor Cyan
wsl.exe -d Ubuntu -e bash -c 'mkdir -p /home/riz/.gemini/config/rules && cp -r /mnt/c/Users/reazul.ashraf/workspace/rust/mcp-memory/agent-rules/* /home/riz/.gemini/config/rules/' wsl.exe -d Ubuntu -e bash -c 'mkdir -p /home/riz/.gemini/config/rules && cp -r /mnt/c/Users/reazul.ashraf/workspace/rust/mcp-memory/agent-rules/* /home/riz/.gemini/config/rules/'
# Fast parallel test execution using cargo-nextest (default test command) # =========================================================
# 4. TESTING & COVERAGE
# =========================================================
# Run fast parallel unit tests via cargo-nextest
test: test:
@Write-Host "Running fast parallel tests via cargo-nextest..." -ForegroundColor Cyan @Write-Host "Running fast parallel tests via cargo-nextest..." -ForegroundColor Cyan
cargo nextest run --workspace cargo nextest run --workspace
# Standard sequential cargo test execution # Run standard sequential cargo unit tests
test-cargo: test-cargo:
@Write-Host "Running standard unit tests across workspace..." -ForegroundColor Cyan @Write-Host "Running standard unit tests across workspace..." -ForegroundColor Cyan
cargo test --workspace cargo test --workspace
# Run configuration tests to ensure eagerTools parity # Run configuration parity tests for eagerTools
test-config: test-config:
cargo test --release -p mcp-memory-server --test parity_test cargo test --release -p mcp-memory-server --test parity_test
# Run tests and generate code coverage report with matching rustup LLVM toolchain # Run workspace unit tests and generate code coverage report
test-coverage: test-coverage:
@Write-Host "Running tests with code coverage..." -ForegroundColor Cyan @Write-Host "Running tests with code coverage..." -ForegroundColor Cyan
cargo llvm-cov --workspace --ignore-filename-regex "stub" -- --test-threads=1 cargo llvm-cov --workspace --ignore-filename-regex "stub" -- --test-threads=1
# Auto-generate and verify unit tests using local Ollama model
# Start the server directly
startup-server:
cargo run -p mcp-memory-server
# --- Explicit Lifecycle Chaining Commands ---
# all-* commands handle the FULL end-to-end process: Stop -> Build -> Deploy -> Start
all-server-win port="3000": (stop port) build-server deploy-server (start port) (verify port)
all-stub-win: build-stub-win deploy-stub-win
all-nvim-win: build-nvim-win deploy-nvim-win
all-stub-wsl: build-stub deploy-stub
all-nvim-wsl: build-nvim deploy-nvim
all-server-wsl port="3000": (stop port) build-server deploy-server (start port) (verify port)
build-nvim-win:
@Write-Host 'Building Windows Nvim...' -ForegroundColor Cyan
cargo build --release -p mcp-memory-nvim
# Auto-generate & verify unit tests using local NAS Ollama ($0 tokens)
improve-tests file="server/src/ollama.rs": improve-tests file="server/src/ollama.rs":
improve-test {{file}} improve-test {{file}}
# =========================================================
# 5. EXPLICIT LIFECYCLE CHAINING
# =========================================================
# Complete lifecycle: stop, build, deploy, start, and verify Windows server
all-server-win port="3000": (stop port) build-server deploy-server (start port) (verify port)
# Complete lifecycle: build and deploy Windows stub
all-stub-win: build-stub-win deploy-stub-win
# Complete lifecycle: build and deploy Windows Neovim plugin
all-nvim-win: build-nvim-win deploy-nvim-win
# Complete lifecycle: build and deploy WSL stub
all-stub-wsl: build-stub deploy-stub
# Complete lifecycle: build and deploy WSL Neovim plugin
all-nvim-wsl: build-nvim deploy-nvim
# Complete lifecycle: stop, build, deploy, start, and verify WSL server
all-server-wsl port="3000": (stop port) build-server deploy-server (start port) (verify port)