diff --git a/.dockerignore b/.dockerignore new file mode 100644 index 0000000..02e317f --- /dev/null +++ b/.dockerignore @@ -0,0 +1,69 @@ +# Python cache +__pycache__/ +*.py[cod] +*$py.class +*.so +.Python +build/ +develop-eggs/ +dist/ +downloads/ +eggs/ +.eggs/ +lib/ +lib64/ +parts/ +sdist/ +var/ +wheels/ +*.egg-info/ +.installed.cfg +*.egg + +# Virtual environments +.venv/ +venv/ +ENV/ +env/ + +# Testing +.pytest_cache/ +.coverage +htmlcov/ +.tox/ +.hypothesis/ + +# IDE +.vscode/ +.idea/ +*.swp +*.swo +*~ + +# Git +.git/ +.gitignore + +# Documentation +docs/ +*.md +!README.md + +# CI/CD +.github/ + +# Development files +.env +.env.* +CLAUDE.md + +# Logs +logs/ +*.log + +# macOS +.DS_Store + +# Test data +tests/ +data.ms/ \ No newline at end of file diff --git a/.github/workflows/publish.yml b/.github/workflows/publish.yml index 9d9c3b9..e121787 100644 --- a/.github/workflows/publish.yml +++ b/.github/workflows/publish.yml @@ -1,4 +1,4 @@ -name: Publish to PyPI +name: Publish to PyPI and Docker Hub on: push: @@ -24,7 +24,7 @@ jobs: echo "version_changed=true" >> $GITHUB_OUTPUT echo "new_version=$NEW_VERSION" >> $GITHUB_OUTPUT - build-and-publish: + build-and-publish-pypi: needs: check-version if: needs.check-version.outputs.version_changed == 'true' runs-on: ubuntu-latest @@ -54,4 +54,44 @@ jobs: uses: pypa/gh-action-pypi-publish@release/v1 with: verbose: true - print-hash: true \ No newline at end of file + print-hash: true + + build-and-publish-docker: + needs: check-version + if: needs.check-version.outputs.version_changed == 'true' + runs-on: ubuntu-latest + + steps: + - uses: actions/checkout@v3 + + - name: Set up QEMU + uses: docker/setup-qemu-action@v3 + + - name: Set up Docker Buildx + uses: docker/setup-buildx-action@v3 + + - name: Log in to Docker Hub + uses: docker/login-action@v3 + with: + username: ${{ secrets.DOCKERHUB_USERNAME }} + password: ${{ secrets.DOCKERHUB_TOKEN }} + + - name: Extract metadata + id: meta + uses: docker/metadata-action@v5 + with: + images: meilisearch/meilisearch-mcp + tags: | + type=raw,value=${{ needs.check-version.outputs.new_version }} + type=raw,value=latest + + - name: Build and push Docker image + uses: docker/build-push-action@v5 + with: + context: . + platforms: linux/amd64,linux/arm64 + push: true + tags: ${{ steps.meta.outputs.tags }} + labels: ${{ steps.meta.outputs.labels }} + cache-from: type=gha + cache-to: type=gha,mode=max \ No newline at end of file diff --git a/Dockerfile b/Dockerfile new file mode 100644 index 0000000..875e69c --- /dev/null +++ b/Dockerfile @@ -0,0 +1,28 @@ +# Use Python 3.12 slim image for smaller size +FROM python:3.12-slim + +# Set working directory +WORKDIR /app + +# Install system dependencies +RUN apt-get update && apt-get install -y --no-install-recommends \ + curl \ + && rm -rf /var/lib/apt/lists/* + +# Install uv for faster Python package management +RUN curl -LsSf https://astral.sh/uv/install.sh | sh +ENV PATH="/root/.local/bin:${PATH}" + +# Copy project files +COPY pyproject.toml README.md ./ +COPY src/ ./src/ + +# Install the package +RUN uv pip install --system . + +# Set default environment variables +ENV MEILI_HTTP_ADDR=http://meilisearch:7700 +ENV MEILI_MASTER_KEY="" + +# Run the MCP server +CMD ["python", "-m", "src.meilisearch_mcp"] \ No newline at end of file diff --git a/README.md b/README.md index 476ebd2..6ade03e 100644 --- a/README.md +++ b/README.md @@ -137,6 +137,48 @@ source .venv/bin/activate # On Windows: .venv\Scripts\activate uv pip install -e . ``` +### Using Docker + +Perfect for containerized environments like n8n workflows! + +#### From Docker Hub (Recommended) + +```bash +# Pull the latest image +docker pull meilisearch/meilisearch-mcp:latest + +# Or a specific version +docker pull meilisearch/meilisearch-mcp:0.5.0 + +# Run the container +docker run -it \ + -e MEILI_HTTP_ADDR=http://your-meilisearch:7700 \ + -e MEILI_MASTER_KEY=your-master-key \ + meilisearch/meilisearch-mcp:latest +``` + +#### Build from Source + +```bash +# Build your own image +docker build -t meilisearch-mcp . +docker run -it \ + -e MEILI_HTTP_ADDR=http://your-meilisearch:7700 \ + -e MEILI_MASTER_KEY=your-master-key \ + meilisearch-mcp +``` + +#### Integration with n8n + +For n8n workflows, you can use the Docker image directly in your setup: +```yaml +meilisearch-mcp: + image: meilisearch/meilisearch-mcp:latest + environment: + - MEILI_HTTP_ADDR=http://meilisearch:7700 + - MEILI_MASTER_KEY=masterKey +``` + ## 🛠️ What Can You Do?
diff --git a/tests/test_docker_integration.py b/tests/test_docker_integration.py new file mode 100644 index 0000000..ece7a41 --- /dev/null +++ b/tests/test_docker_integration.py @@ -0,0 +1,77 @@ +""" +Integration tests for Docker image build. + +These tests verify that the Docker image can be built successfully. +""" +import subprocess +import pytest +import shutil + + +# Check if Docker is available +def docker_available(): + """Check if Docker is available on the system.""" + if not shutil.which("docker"): + return False + # Try to run docker version to ensure it's working + try: + result = subprocess.run( + ["docker", "version"], + capture_output=True, + text=True, + timeout=5 + ) + return result.returncode == 0 + except (subprocess.TimeoutExpired, FileNotFoundError): + return False + + +# Skip all tests in this module if Docker is not available +pytestmark = pytest.mark.skipif( + not docker_available(), + reason="Docker not available on this system" +) + + +def test_docker_build(): + """Test that the Docker image can be built successfully.""" + result = subprocess.run( + ["docker", "build", "-t", "meilisearch-mcp-test", "."], + capture_output=True, + text=True + ) + assert result.returncode == 0, f"Docker build failed: {result.stderr}" + + +def test_docker_image_runs(): + """Test that the Docker image can run and show help.""" + # First build the image + build_result = subprocess.run( + ["docker", "build", "-t", "meilisearch-mcp-test", "."], + capture_output=True, + text=True + ) + if build_result.returncode != 0: + pytest.skip(f"Docker build failed: {build_result.stderr}") + + # Try to run the container and check it starts + result = subprocess.run( + [ + "docker", "run", "--rm", + "-e", "MEILI_HTTP_ADDR=http://localhost:7700", + "-e", "MEILI_MASTER_KEY=test", + "meilisearch-mcp-test", + "python", "-c", "import src.meilisearch_mcp; print('MCP module loaded successfully')" + ], + capture_output=True, + text=True, + timeout=30 + ) + + assert result.returncode == 0, f"Docker run failed: {result.stderr}" + assert "MCP module loaded successfully" in result.stdout + + +if __name__ == "__main__": + # Run tests + pytest.main([__file__, "-v"]) \ No newline at end of file