Build from source → GHCR #2
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| name: Build from source → GHCR | |
| on: | |
| push: | |
| branches: [main, intarweb-dev] | |
| paths: | |
| - 'Dockerfile' | |
| - 'pyproject.toml' | |
| - 'uv.lock' | |
| - 'mcp_python_interpreter/**' | |
| - '.github/workflows/build-from-source.yml' | |
| workflow_dispatch: | |
| permissions: | |
| contents: read | |
| packages: write | |
| id-token: write | |
| env: | |
| IMAGE: ghcr.io/${{ github.repository_owner }}/mcp-python-interpreter | |
| PLATFORMS: linux/amd64,linux/arm64 | |
| jobs: | |
| build: | |
| runs-on: ubuntu-latest | |
| outputs: | |
| sha-tag: ${{ steps.sha.outputs.short }} | |
| steps: | |
| - uses: actions/checkout@v4 | |
| with: | |
| persist-credentials: false | |
| fetch-depth: 0 | |
| - uses: docker/setup-qemu-action@v3 | |
| - uses: docker/setup-buildx-action@v3 | |
| - uses: docker/login-action@v3 | |
| with: | |
| registry: ghcr.io | |
| username: ${{ github.actor }} | |
| password: ${{ secrets.GITHUB_TOKEN }} | |
| - name: 🏷️ Compute short SHA | |
| id: sha | |
| run: echo "short=${GITHUB_SHA:0:7}" >> "$GITHUB_OUTPUT" | |
| - name: 🚀 Build & push :sha-${{ steps.sha.outputs.short }} | |
| uses: docker/build-push-action@v6 | |
| id: build | |
| with: | |
| context: . | |
| file: ./Dockerfile | |
| platforms: ${{ env.PLATFORMS }} | |
| push: true | |
| provenance: mode=max | |
| sbom: true | |
| tags: ${{ env.IMAGE }}:sha-${{ steps.sha.outputs.short }} | |
| annotations: | | |
| index:org.opencontainers.image.source=https://github.com/${{ github.repository_owner }}/mcp-python-interpreter | |
| index:org.opencontainers.image.revision=${{ github.sha }} | |
| index:org.opencontainers.image.description=mcp-python-interpreter — from-source build of upstream HEAD + open PR patches | |
| - name: 🔬 Smoke test (image starts + binds port 8000) | |
| env: | |
| IMAGE: ${{ env.IMAGE }} | |
| SHA_SHORT: ${{ steps.sha.outputs.short }} | |
| run: | | |
| set -e | |
| docker pull "${IMAGE}:sha-${SHA_SHORT}" | |
| # Start container detached on host port 18000 -> container 8000. | |
| cid=$(docker run -d -p 18000:8000 "${IMAGE}:sha-${SHA_SHORT}") | |
| trap "docker rm -f $cid >/dev/null 2>&1 || true" EXIT | |
| # Wait up to 15s for the streamable-http endpoint to be reachable. | |
| for i in $(seq 1 15); do | |
| if docker exec "$cid" sh -c "python3 -c 'import socket; s=socket.socket(); s.settimeout(2); s.connect((\"127.0.0.1\", 8000))'" >/dev/null 2>&1; then | |
| echo " ✓ container listening on 8000 after ${i}s" | |
| break | |
| fi | |
| sleep 1 | |
| if [ "$i" = "15" ]; then | |
| echo " ✗ container never bound port 8000" | |
| docker logs "$cid" || true | |
| exit 1 | |
| fi | |
| done | |
| # GET on /mcp should return some HTTP status (not connection refused). | |
| # FastMCP returns 405/400 for GET (POST-only) which is fine — we just | |
| # care that the HTTP server is alive. | |
| code=$(curl -s -o /dev/null -w '%{http_code}' http://127.0.0.1:18000/mcp || echo "000") | |
| echo " HTTP /mcp → $code" | |
| if [ "$code" = "000" ]; then | |
| echo " ✗ /mcp not reachable" | |
| docker logs "$cid" || true | |
| exit 1 | |
| fi | |
| echo " ✓ smoke passed" | |
| - name: 🏷️ Promote :latest (smoke passed, intarweb-dev branch only) | |
| if: github.ref == 'refs/heads/intarweb-dev' | |
| env: | |
| IMAGE: ${{ env.IMAGE }} | |
| SHA_SHORT: ${{ steps.sha.outputs.short }} | |
| run: | | |
| docker buildx imagetools create \ | |
| --tag "${IMAGE}:latest" \ | |
| "${IMAGE}:sha-${SHA_SHORT}" |