Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 0 additions & 6 deletions .github/dependabot.yml

This file was deleted.

115 changes: 19 additions & 96 deletions .github/workflows/cd.yaml
Original file line number Diff line number Diff line change
@@ -1,21 +1,9 @@
name: Publish to container registries
name: Build and Publish Docker Image

on:
release:
types: [published]
workflow_dispatch:
push:
branches:
- main
paths:
- src/**
- Dockerfile
- pyproject.toml
pull_request:
paths:
- src/**
- Dockerfile
- pyproject.toml
tags:
- 'v*'

jobs:
docker-hub:
Expand All @@ -25,103 +13,38 @@ jobs:
contents: read

steps:
- uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
- name: Checkout code
uses: actions/checkout@v4

- name: Set up QEMU
uses: docker/setup-qemu-action@4574d27a4764455b42196d70a065bc6853246a25 #v3.4.0
uses: docker/setup-qemu-action@v3

- name: Set up Docker Buildx
uses: docker/setup-buildx-action@f7ce87c1d6bead3e36075b2ce75da1f6cc28aaca #v3.9.0

- name: Extract tags and labels for Docker
id: meta
uses: docker/metadata-action@902fa8ec7d6ecbf8d84d538b9b233a880e428804 # v5.7.0
with:
images: ${{ github.repository }}
tags: |
type=sha,format=short,prefix=commit-
type=ref,event=tag
labels: |
maintainer="Sergey Parfenyuk"
org.opencontainers.image.source=https://github.com/sparfenyuk/mcp-proxy
org.opencontainers.image.description="Connect to MCP servers that run on SSE transport, or expose stdio servers as an SSE server using the MCP Proxy server."
org.opencontainers.image.licenses=MIT
uses: docker/setup-buildx-action@v3

- name: Log in to Docker Hub
uses: docker/login-action@9780b0c442fbb1117ed29e0efdff1e18412f7567 # v3.3.0
if: github.event_name != 'pull_request'
uses: docker/login-action@v3
with:
username: ${{ secrets.DOCKER_USERNAME }}
password: ${{ secrets.DOCKER_PASSWORD }}

- name: Build and push Docker image
uses: docker/build-push-action@ca877d9245402d1537745e0e356eab47c3520991 # v6.13.0
with:
context: .
platforms: linux/amd64,linux/arm64
push: ${{ github.event_name != 'pull_request' }}
tags: ${{ steps.meta.outputs.tags }}
labels: ${{ steps.meta.outputs.labels }}
annotations: ${{ steps.meta.outputs.annotations }}
cache-from: type=local,src=/tmp/.buildx-cache
cache-to: type=local,dest=/tmp/.buildx-cache

- name: Clean Docker cache
if: github.event_name != 'pull_request'
run: |
docker system prune --force

ghcr-io:
name: Push multi-arch Docker image to ghcr.io
runs-on: ubuntu-latest
permissions:
contents: read
packages: write

steps:
- uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2

- name: Set up QEMU
uses: docker/setup-qemu-action@4574d27a4764455b42196d70a065bc6853246a25 #v3.4.0

- name: Set up Docker Buildx
uses: docker/setup-buildx-action@f7ce87c1d6bead3e36075b2ce75da1f6cc28aaca #v3.9.0

- name: Extract tags and labels for Docker

- name: Extract metadata (tags, labels) for Docker
id: meta
uses: docker/metadata-action@902fa8ec7d6ecbf8d84d538b9b233a880e428804 # v5.7.0
uses: docker/metadata-action@v5
with:
images: ghcr.io/${{ github.repository }}
images: opentrace/mcp-proxy
tags: |
type=sha,format=short,prefix=commit-
type=ref,event=tag
labels: |
maintainer="Sergey Parfenyuk"
org.opencontainers.image.source=https://github.com/sparfenyuk/mcp-proxy
org.opencontainers.image.description="Connect to MCP servers that run on SSE transport, or expose stdio servers as an SSE server using the MCP Proxy server."
org.opencontainers.image.licenses=MIT

- name: Log in to GHCR
uses: docker/login-action@9780b0c442fbb1117ed29e0efdff1e18412f7567 # v3.3.0
if: github.event_name != 'pull_request'
with:
registry: ghcr.io
username: ${{ github.actor }}
password: ${{ secrets.GITHUB_TOKEN }}
type=semver,pattern={{version}}
type=semver,pattern={{major}}.{{minor}}
type=ref,event=branch

- name: Build and push Docker image
uses: docker/build-push-action@ca877d9245402d1537745e0e356eab47c3520991 # v6.13.0
uses: docker/build-push-action@v5
with:
context: .
platforms: linux/amd64,linux/arm64
push: ${{ github.event_name != 'pull_request' }}
push: true
tags: ${{ steps.meta.outputs.tags }}
labels: ${{ steps.meta.outputs.labels }}
annotations: ${{ steps.meta.outputs.annotations }}
cache-from: type=local,src=/tmp/.buildx-cache
cache-to: type=local,dest=/tmp/.buildx-cache

- name: Clean Docker cache
if: github.event_name != 'pull_request'
run: |
docker system prune --force
cache-from: type=gha
cache-to: type=gha,mode=max
98 changes: 6 additions & 92 deletions .github/workflows/ci.yaml
Original file line number Diff line number Diff line change
@@ -1,12 +1,10 @@
name: CI

on:
push:
branches:
- main
tags:
- "**"
pull_request: {}
workflow_dispatch:
pull_request:
branches:
- main

env:
CI: true
Expand All @@ -16,27 +14,6 @@ permissions:
contents: read

jobs:
lint:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2

- uses: astral-sh/setup-uv@0c5e2b8115b80b4c7c5ddf6ffdd634974642d182 # v5.4.1
with:
python-version: "3.12"

- name: Install dependencies
run: uv sync --frozen --all-extras --all-packages

- name: Ensure pip
run: uv run python -m ensurepip

- uses: pre-commit/action@2c7b3805fd2a0fd8c1884dcaebf91fc102a13ecd # v3.0.1
with:
extra_args: --all-files --verbose
env:
SKIP: no-commit-to-branch

mypy:
runs-on: ubuntu-latest
steps:
Expand Down Expand Up @@ -67,50 +44,14 @@ jobs:
with:
python-version: ${{ matrix.python-version }}

- run: mkdir coverage
- run: uv run --frozen pytest

- run: uv run --frozen coverage run -m pytest
env:
COVERAGE_FILE: coverage/.coverage.${{ runner.os }}-py${{ matrix.python-version }}-standard

- name: store coverage files
uses: actions/upload-artifact@65c4c4a1ddee5b72f698fdd19549f0f0fb45cf08 # v4.6.0
with:
name: coverage-${{ matrix.python-version }}
path: coverage
include-hidden-files: true

coverage:
runs-on: ubuntu-latest
needs: [test]
steps:
- uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2

- name: get coverage files
uses: actions/download-artifact@fa0a91b85d4f404e444e00e005971372dc801d16 # v4.1.8
with:
merge-multiple: true
path: coverage

- uses: astral-sh/setup-uv@0c5e2b8115b80b4c7c5ddf6ffdd634974642d182 # v5.4.1
with:
python-version: "3.12"

- run: uv sync --frozen
- run: uv run --frozen coverage combine coverage
- run: uv run --frozen coverage xml

- name: Upload coverage reports to Codecov
uses: codecov/codecov-action@1e68e06f1dbfde0e4cefc87efeba9e4643565303 # v5.1.2
with:
token: ${{ secrets.CODECOV_TOKEN }}

- run: uv run --frozen coverage report --fail-under 83

# https://github.com/marketplace/actions/alls-green#why used for branch protection checks
check:
if: always()
needs: [lint, test, coverage, mypy]
needs: [test, mypy]
runs-on: ubuntu-latest

steps:
Expand All @@ -120,30 +61,3 @@ jobs:
jobs: ${{ toJSON(needs) }}
allowed-skips: test-live

release:
needs: [check]
if: "success() && startsWith(github.ref, 'refs/tags/')"
runs-on: ubuntu-latest
environment: release

permissions:
id-token: write

steps:
- uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2

- uses: astral-sh/setup-uv@0c5e2b8115b80b4c7c5ddf6ffdd634974642d182 # v5.4.1
with:
python-version: "3.12"

- name: check GITHUB_REF matches package version
uses: samuelcolvin/check-python-version@758a13b52c26833cffda0f2ed4f3c9e54d9186d9 # v4.1
with:
version_file_path: pyproject.toml

- run: uv build

- name: Publish to PyPI
uses: pypa/gh-action-pypi-publish@67339c736fd9354cd4f8cb0b744f2b82a74b5c70 # v1.12.3
with:
skip-existing: true
40 changes: 17 additions & 23 deletions Dockerfile
Original file line number Diff line number Diff line change
@@ -1,33 +1,27 @@
# Build stage with explicit platform specification
FROM ghcr.io/astral-sh/uv:python3.12-alpine AS uv
FROM nikolaik/python-nodejs:python3.12-nodejs22-slim

# Install the project into /app
WORKDIR /app

# Enable bytecode compilation
ENV UV_COMPILE_BYTECODE=1
# Create user early in the build process
RUN useradd -u 1000 -m appuser || true

# Copy from the cache instead of linking since it's a mounted volume
ENV UV_LINK_MODE=copy
# Install uv for Python package management
RUN pip install --no-cache-dir uv

# Install the project's dependencies using the lockfile and settings
RUN --mount=type=cache,target=/root/.cache/uv \
--mount=type=bind,source=uv.lock,target=uv.lock \
--mount=type=bind,source=pyproject.toml,target=pyproject.toml \
uv sync --frozen --no-install-project --no-dev --no-editable
# Copy package configuration and source
COPY --chown=1000:1000 pyproject.toml uv.lock ./
COPY --chown=1000:1000 src/ src/

# Then, add the rest of the project source code and install it
# Installing separately from its dependencies allows optimal layer caching
ADD . /app
RUN --mount=type=cache,target=/root/.cache/uv \
uv sync --frozen --no-dev --no-editable
# Install mcp-proxy using uv
RUN uv sync --frozen --no-dev

# Final stage with explicit platform specification
FROM python:3.12-alpine

COPY --from=uv --chown=app:app /app/.venv /app/.venv

# Place executables in the environment at the front of the path
# Set environment variables
ENV PYTHONUNBUFFERED=1
ENV PATH="/app/.venv/bin:$PATH"
ENV UV_PYTHON_PREFERENCE=only-system

# Set final ownership and switch to non-root user
RUN chown -R 1000:1000 /app
USER 1000

ENTRYPOINT ["mcp-proxy"]
Loading