From 0b93c8406ea2721139e138ed6064227a4ef942f9 Mon Sep 17 00:00:00 2001 From: aniongithub Date: Tue, 21 Apr 2026 00:05:24 -0700 Subject: [PATCH 1/2] style: apply cargo fmt formatting Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- crates/devpod-mcp-core/src/devpod.rs | 7 ++- crates/devpod-mcp-core/src/docker.rs | 7 +-- crates/devpod-mcp-core/src/error.rs | 5 +- crates/devpod-mcp/src/tools.rs | 73 ++++++++++++++++++++++------ 4 files changed, 66 insertions(+), 26 deletions(-) diff --git a/crates/devpod-mcp-core/src/devpod.rs b/crates/devpod-mcp-core/src/devpod.rs index 24d55b6..72dbc03 100644 --- a/crates/devpod-mcp-core/src/devpod.rs +++ b/crates/devpod-mcp-core/src/devpod.rs @@ -131,7 +131,12 @@ pub async fn list() -> Result { // --------------------------------------------------------------------------- /// `devpod ssh --command` — execute a command in a workspace. -pub async fn ssh_exec(workspace: &str, command: &str, user: Option<&str>, workdir: Option<&str>) -> Result { +pub async fn ssh_exec( + workspace: &str, + command: &str, + user: Option<&str>, + workdir: Option<&str>, +) -> Result { let mut args = vec!["ssh", workspace, "--command", command]; if let Some(u) = user { args.push("--user"); diff --git a/crates/devpod-mcp-core/src/docker.rs b/crates/devpod-mcp-core/src/docker.rs index 85939b1..47d6c63 100644 --- a/crates/devpod-mcp-core/src/docker.rs +++ b/crates/devpod-mcp-core/src/docker.rs @@ -95,11 +95,7 @@ pub async fn inspect_container(docker: &Docker, name_or_id: &str) -> Result Result { +pub async fn container_logs(docker: &Docker, container_id: &str, tail: usize) -> Result { let options = LogsOptions:: { stdout: true, stderr: true, @@ -123,4 +119,3 @@ pub async fn container_logs( Ok(output) } - diff --git a/crates/devpod-mcp-core/src/error.rs b/crates/devpod-mcp-core/src/error.rs index 975e0ae..473f4ae 100644 --- a/crates/devpod-mcp-core/src/error.rs +++ b/crates/devpod-mcp-core/src/error.rs @@ -10,10 +10,7 @@ pub enum Error { DevPodNotFound, #[error("DevPod command failed (exit code {exit_code}): {stderr}")] - DevPodCommand { - exit_code: i32, - stderr: String, - }, + DevPodCommand { exit_code: i32, stderr: String }, #[error("IO error: {0}")] Io(#[from] std::io::Error), diff --git a/crates/devpod-mcp/src/tools.rs b/crates/devpod-mcp/src/tools.rs index 8d77332..2fea9ad 100644 --- a/crates/devpod-mcp/src/tools.rs +++ b/crates/devpod-mcp/src/tools.rs @@ -29,11 +29,16 @@ impl DevContainerMcp { // Workspace lifecycle // ----------------------------------------------------------------------- - #[tool(name = "devpod_up", description = "Create and start a DevPod workspace. Pass the source (git URL, local path, or image) and any flags as space-separated args. Returns full build output for self-healing.")] + #[tool( + name = "devpod_up", + description = "Create and start a DevPod workspace. Pass the source (git URL, local path, or image) and any flags as space-separated args. Returns full build output for self-healing." + )] async fn up( &self, #[tool(param)] - #[schemars(description = "All arguments for 'devpod up', e.g. 'https://github.com/org/repo --provider docker --id my-ws'")] + #[schemars( + description = "All arguments for 'devpod up', e.g. 'https://github.com/org/repo --provider docker --id my-ws'" + )] args: String, ) -> String { let parts: Vec<&str> = args.split_whitespace().collect(); @@ -56,7 +61,10 @@ impl DevContainerMcp { } } - #[tool(name = "devpod_delete", description = "Delete a DevPod workspace. Stops and removes all associated resources.")] + #[tool( + name = "devpod_delete", + description = "Delete a DevPod workspace. Stops and removes all associated resources." + )] async fn delete( &self, #[tool(param)] @@ -72,11 +80,16 @@ impl DevContainerMcp { } } - #[tool(name = "devpod_build", description = "Build a DevPod workspace image without starting it.")] + #[tool( + name = "devpod_build", + description = "Build a DevPod workspace image without starting it." + )] async fn build( &self, #[tool(param)] - #[schemars(description = "All arguments for 'devpod build', e.g. 'my-workspace --provider docker'")] + #[schemars( + description = "All arguments for 'devpod build', e.g. 'my-workspace --provider docker'" + )] args: String, ) -> String { let parts: Vec<&str> = args.split_whitespace().collect(); @@ -90,7 +103,10 @@ impl DevContainerMcp { // Workspace queries // ----------------------------------------------------------------------- - #[tool(name = "devpod_status", description = "Get the status of a DevPod workspace. Returns structured JSON with state (Running, Stopped, Busy, NotFound).")] + #[tool( + name = "devpod_status", + description = "Get the status of a DevPod workspace. Returns structured JSON with state (Running, Stopped, Busy, NotFound)." + )] async fn status( &self, #[tool(param)] @@ -106,7 +122,10 @@ impl DevContainerMcp { } } - #[tool(name = "devpod_list", description = "List all DevPod workspaces. Returns JSON array with workspace IDs, sources, providers, and status.")] + #[tool( + name = "devpod_list", + description = "List all DevPod workspaces. Returns JSON array with workspace IDs, sources, providers, and status." + )] async fn list(&self) -> String { match devpod::list().await { Ok(output) => format_output(&output), @@ -118,7 +137,10 @@ impl DevContainerMcp { // Command execution // ----------------------------------------------------------------------- - #[tool(name = "devpod_ssh", description = "Execute a command inside a DevPod workspace via SSH. Returns stdout, stderr, and exit code.")] + #[tool( + name = "devpod_ssh", + description = "Execute a command inside a DevPod workspace via SSH. Returns stdout, stderr, and exit code." + )] async fn ssh( &self, #[tool(param)] @@ -144,7 +166,10 @@ impl DevContainerMcp { // Logs // ----------------------------------------------------------------------- - #[tool(name = "devpod_logs", description = "Get logs from a DevPod workspace.")] + #[tool( + name = "devpod_logs", + description = "Get logs from a DevPod workspace." + )] async fn logs( &self, #[tool(param)] @@ -161,7 +186,10 @@ impl DevContainerMcp { // Provider management // ----------------------------------------------------------------------- - #[tool(name = "devpod_provider_list", description = "List all configured DevPod providers.")] + #[tool( + name = "devpod_provider_list", + description = "List all configured DevPod providers." + )] async fn provider_list(&self) -> String { match devpod::provider_list().await { Ok(output) => format_output(&output), @@ -189,7 +217,10 @@ impl DevContainerMcp { } } - #[tool(name = "devpod_provider_delete", description = "Delete a DevPod provider.")] + #[tool( + name = "devpod_provider_delete", + description = "Delete a DevPod provider." + )] async fn provider_delete( &self, #[tool(param)] @@ -206,7 +237,10 @@ impl DevContainerMcp { // Context management // ----------------------------------------------------------------------- - #[tool(name = "devpod_context_list", description = "List all DevPod contexts.")] + #[tool( + name = "devpod_context_list", + description = "List all DevPod contexts." + )] async fn context_list(&self) -> String { match devpod::context_list().await { Ok(output) => format_output(&output), @@ -214,7 +248,10 @@ impl DevContainerMcp { } } - #[tool(name = "devpod_context_use", description = "Switch to a different DevPod context.")] + #[tool( + name = "devpod_context_use", + description = "Switch to a different DevPod context." + )] async fn context_use( &self, #[tool(param)] @@ -231,7 +268,10 @@ impl DevContainerMcp { // Direct Docker (via bollard) // ----------------------------------------------------------------------- - #[tool(name = "devpod_container_inspect", description = "Inspect a Docker container directly — returns labels, ports, mounts, and state. Useful for details DevPod CLI doesn't expose.")] + #[tool( + name = "devpod_container_inspect", + description = "Inspect a Docker container directly — returns labels, ports, mounts, and state. Useful for details DevPod CLI doesn't expose." + )] async fn container_inspect( &self, #[tool(param)] @@ -248,7 +288,10 @@ impl DevContainerMcp { } } - #[tool(name = "devpod_container_logs", description = "Get Docker container logs directly via the Docker API. Supports tail parameter for last N lines.")] + #[tool( + name = "devpod_container_logs", + description = "Get Docker container logs directly via the Docker API. Supports tail parameter for last N lines." + )] async fn container_logs( &self, #[tool(param)] From bbd420e43af3b52c4272e49607180e1371931cd2 Mon Sep 17 00:00:00 2001 From: aniongithub Date: Tue, 21 Apr 2026 00:08:49 -0700 Subject: [PATCH 2/2] ci: use devcontainers/ci action for CI and release builds - CI: fmt/check/test/clippy all run inside the devcontainer - Release: Linux targets (x64, arm64) built inside devcontainer with cross-compilation; macOS targets built natively on macos-latest Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- .github/workflows/ci.yml | 18 +++++----- .github/workflows/release.yml | 63 +++++++++++++++++++++++------------ 2 files changed, 50 insertions(+), 31 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 151d2eb..89e0d85 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -6,18 +6,18 @@ on: push: branches: [main] -env: - CARGO_TERM_COLOR: always - jobs: check: name: Check & Test runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 - - uses: dtolnay/rust-toolchain@stable - - uses: Swatinem/rust-cache@v2 - - run: cargo check -p devpod-mcp-core -p devpod-mcp - - run: cargo test -p devpod-mcp-core -p devpod-mcp - - run: cargo clippy -p devpod-mcp-core -p devpod-mcp -- -D warnings - - run: cargo fmt --all -- --check + - name: Build and test in devcontainer + uses: devcontainers/ci@v0.3 + with: + push: never + runCmd: | + cargo fmt --all -- --check + cargo check -p devpod-mcp-core -p devpod-mcp + cargo test -p devpod-mcp-core -p devpod-mcp + cargo clippy -p devpod-mcp-core -p devpod-mcp -- -D warnings diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 617c7a2..1e39bb8 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -4,33 +4,60 @@ on: release: types: [created] -env: - CARGO_TERM_COLOR: always - permissions: contents: write jobs: build: + name: Build release binaries + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + + - name: Build all targets in devcontainer + uses: devcontainers/ci@v0.3 + with: + push: never + runCmd: | + set -e + + # Install cross-compilation toolchain for linux-arm64 + sudo apt-get update && sudo apt-get install -y gcc-aarch64-linux-gnu + rustup target add aarch64-unknown-linux-gnu x86_64-unknown-linux-gnu + mkdir -p ~/.cargo + echo '[target.aarch64-unknown-linux-gnu]' >> ~/.cargo/config.toml + echo 'linker = "aarch64-linux-gnu-gcc"' >> ~/.cargo/config.toml + + # Build all Linux targets + cargo build --release --target x86_64-unknown-linux-gnu -p devpod-mcp + cargo build --release --target aarch64-unknown-linux-gnu -p devpod-mcp + + # Copy binaries to output dir + mkdir -p /tmp/release + cp target/x86_64-unknown-linux-gnu/release/devpod-mcp /tmp/release/devpod-mcp-linux-x64 + cp target/aarch64-unknown-linux-gnu/release/devpod-mcp /tmp/release/devpod-mcp-linux-arm64 + chmod +x /tmp/release/* + + - name: Upload linux-x64 + uses: softprops/action-gh-release@v2 + with: + files: /tmp/release/devpod-mcp-linux-x64 + + - name: Upload linux-arm64 + uses: softprops/action-gh-release@v2 + with: + files: /tmp/release/devpod-mcp-linux-arm64 + + build-macos: name: Build ${{ matrix.artifact }} - runs-on: ${{ matrix.os }} + runs-on: macos-latest strategy: matrix: include: - - target: x86_64-unknown-linux-gnu - os: ubuntu-latest - artifact: devpod-mcp-linux-x64 - - target: aarch64-unknown-linux-gnu - os: ubuntu-latest - artifact: devpod-mcp-linux-arm64 - cross: true - target: x86_64-apple-darwin - os: macos-latest artifact: devpod-mcp-darwin-x64 - target: aarch64-apple-darwin - os: macos-latest artifact: devpod-mcp-darwin-arm64 - steps: - uses: actions/checkout@v4 - uses: dtolnay/rust-toolchain@stable @@ -40,14 +67,6 @@ jobs: with: key: ${{ matrix.target }} - - name: Install cross-compilation deps - if: matrix.cross - run: | - sudo apt-get update - sudo apt-get install -y gcc-aarch64-linux-gnu - echo '[target.aarch64-unknown-linux-gnu]' >> ~/.cargo/config.toml - echo 'linker = "aarch64-linux-gnu-gcc"' >> ~/.cargo/config.toml - - name: Build run: cargo build --release --target ${{ matrix.target }} -p devpod-mcp