From 7e64e9fdea8ca9f061214dd832ab1443105bbe53 Mon Sep 17 00:00:00 2001 From: "sz Snet (shunleite)" Date: Mon, 12 Oct 2026 03:07:32 +0000 Subject: [PATCH 1/2] fix(docker): remove duplicate Cargo registry source and add CI smoke tests --- .dockerignore | 5 +++ .github/workflows/docker.yml | 60 ++++++++++++++++++++++++++++++++++++ Dockerfile | 6 ++-- docker-compose.yml | 2 -- scripts/smoke-docker.py | 50 ++++++++++++++++++++++++++++++ 5 files changed, 117 insertions(+), 6 deletions(-) create mode 100644 .dockerignore create mode 100644 .github/workflows/docker.yml create mode 100644 scripts/smoke-docker.py diff --git a/.dockerignore b/.dockerignore new file mode 100644 index 0000000..5abd0f4 --- /dev/null +++ b/.dockerignore @@ -0,0 +1,5 @@ +.git +target +.cargo +*.log +*_out.html diff --git a/.github/workflows/docker.yml b/.github/workflows/docker.yml new file mode 100644 index 0000000..32a70b7 --- /dev/null +++ b/.github/workflows/docker.yml @@ -0,0 +1,60 @@ +name: Docker build and smoke test + +on: + push: + branches: + - main + - 'fix/docker-*' + paths: + - Dockerfile + - .dockerignore + - docker-compose.yml + - Cargo.toml + - Cargo.lock + - src/** + - scripts/smoke-docker.py + - .github/workflows/docker.yml + pull_request: + paths: + - Dockerfile + - .dockerignore + - docker-compose.yml + - Cargo.toml + - Cargo.lock + - src/** + - scripts/smoke-docker.py + - .github/workflows/docker.yml + workflow_dispatch: + +permissions: + contents: read + +concurrency: + group: docker-${{ github.workflow }}-${{ github.ref }} + cancel-in-progress: true + +jobs: + docker: + runs-on: ubuntu-latest + timeout-minutes: 30 + env: + PORT: '11380' + steps: + - uses: actions/checkout@v4 + + - name: Validate Compose configuration + run: docker compose config --quiet + + - name: Build and start the service + run: make docker-compose-up + + - name: Check HTTP startup and MCP protocol + run: python3 scripts/smoke-docker.py + + - name: Show container logs + if: always() + run: docker compose logs --no-color + + - name: Stop the service + if: always() + run: docker compose down diff --git a/Dockerfile b/Dockerfile index d01ada5..ca25595 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,5 +1,5 @@ # Build stage -FROM rust:bookworm as builder +FROM rust:bookworm AS builder WORKDIR /usr/src/app @@ -13,8 +13,6 @@ RUN apt-get update && apt-get install -y cmake clang RUN mkdir -p .cargo && \ echo '[source.crates-io]\n\ replace-with = "rsproxy-sparse"\n\ -[source.rsproxy]\n\ -registry = "sparse+https://rsproxy.cn/index/"\n\ [source.rsproxy-sparse]\n\ registry = "sparse+https://rsproxy.cn/index/"\n\ [registries.rsproxy]\n\ @@ -25,7 +23,7 @@ git-fetch-with-cli = true' > .cargo/config.toml COPY . . # Build the application -RUN cargo build --release +RUN cargo build --release --locked # Runtime stage FROM debian:bookworm-slim diff --git a/docker-compose.yml b/docker-compose.yml index 6a7f988..5244124 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -1,5 +1,3 @@ -version: '3.8' - services: sosearch: build: diff --git a/scripts/smoke-docker.py b/scripts/smoke-docker.py new file mode 100644 index 0000000..4156f08 --- /dev/null +++ b/scripts/smoke-docker.py @@ -0,0 +1,50 @@ +"""Validate the built container without depending on public search engines.""" + +import json +import os +import subprocess +import time +import urllib.error +import urllib.request + + +port = os.environ.get("PORT", "11380") +url = f"http://127.0.0.1:{port}/search" +deadline = time.monotonic() + 30 +while True: + try: + with urllib.request.urlopen(url, timeout=2): + raise AssertionError("HTTP must reject a missing query") + except urllib.error.HTTPError as error: + assert error.code == 400, f"Unexpected HTTP status: {error.code}" + break + except (urllib.error.URLError, TimeoutError): + if time.monotonic() >= deadline: + raise + time.sleep(1) +print("HTTP: service started and rejects missing query with 400") + +requests = [ + {"jsonrpc": "2.0", "id": 1, "method": "initialize", "params": {}}, + {"jsonrpc": "2.0", "id": 2, "method": "tools/list"}, + {"jsonrpc": "2.0", "id": 3, "method": "ping"}, +] +process = subprocess.run( + ["docker", "compose", "run", "--rm", "--no-deps", "-T", "sosearch", "./SoSearch", "--mcp"], + input="".join(json.dumps(request) + "\n" for request in requests), + capture_output=True, + text=True, + timeout=30, + check=True, +) +responses = [json.loads(line) for line in process.stdout.splitlines()] +assert len(responses) == 3, process.stdout +assert [response["id"] for response in responses] == [1, 2, 3] +assert all(response["jsonrpc"] == "2.0" for response in responses) +assert responses[0]["result"]["serverInfo"]["name"] == "sosearch" +assert responses[0]["result"]["capabilities"] == {"tools": {}} +tool = responses[1]["result"]["tools"][0] +assert tool["name"] == "web_search" +assert tool["inputSchema"]["required"] == ["query"] +assert responses[2]["result"] == {} +print("MCP: initialize, tool discovery and ping passed") From a009b17e2e953c46ac44b4e9e3017edec2697d3a Mon Sep 17 00:00:00 2001 From: "sz Snet (shunleite)" Date: Mon, 12 Oct 2026 03:27:27 +0000 Subject: [PATCH 2/2] fix(ci): include Makefile changes in Docker workflow triggers --- .github/workflows/docker.yml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.github/workflows/docker.yml b/.github/workflows/docker.yml index 32a70b7..44790c9 100644 --- a/.github/workflows/docker.yml +++ b/.github/workflows/docker.yml @@ -9,6 +9,7 @@ on: - Dockerfile - .dockerignore - docker-compose.yml + - Makefile - Cargo.toml - Cargo.lock - src/** @@ -19,6 +20,7 @@ on: - Dockerfile - .dockerignore - docker-compose.yml + - Makefile - Cargo.toml - Cargo.lock - src/**