Skip to main content
Glama
docker-build.yml16.2 kB
name: Build and Deploy Container on: push: branches: - main - master workflow_dispatch: env: IMAGE_NAME: uma REGISTRY: us-central1-docker.pkg.dev permissions: contents: write packages: write attestations: write id-token: write jobs: build-and-push: runs-on: ubuntu-latest permissions: contents: write packages: write attestations: write id-token: write steps: - name: Checkout this repository uses: actions/checkout@v4 with: fetch-depth: 0 token: ${{ secrets.GITHUB_TOKEN }} - name: Check if source code already imported id: check-import run: | # Check if src/ or package.json exists (indicating source already imported) if [ -f "package.json" ] || [ -f "pyproject.toml" ] || [ -f "Cargo.toml" ] || [ -f "go.mod" ] || [ -d "src" ]; then echo "SOURCE_IMPORTED=true" >> $GITHUB_OUTPUT echo "Source code already imported, skipping clone" else echo "SOURCE_IMPORTED=false" >> $GITHUB_OUTPUT echo "Source code not yet imported, will clone from upstream" fi - name: Clone original source repository if: steps.check-import.outputs.SOURCE_IMPORTED == 'false' uses: actions/checkout@v4 with: repository: FitoDomik/gitlab-mcp-server ref: main path: upstream-source - name: Import source code to repository if: steps.check-import.outputs.SOURCE_IMPORTED == 'false' run: | echo "=== Importing source code from FitoDomik/gitlab-mcp-server ===" # Copy all source files (excluding .git and .github to avoid workflow permission issues) cd upstream-source find . -maxdepth 1 ! -name '.' ! -name '.git' ! -name '.github' -exec cp -r {} ../ \; cd .. # Remove the upstream-source directory rm -rf upstream-source # Configure git git config user.name "github-actions[bot]" git config user.email "github-actions[bot]@users.noreply.github.com" # Commit the source code git add -A git commit -m "feat: Import source code from FitoDomik/gitlab-mcp-server@main" -m "Source: https://github.com/FitoDomik/gitlab-mcp-server | Branch: main | Imported: $(date -u +%Y-%m-%dT%H:%M:%SZ)" # Push the commit git push origin main echo "=== Source code imported successfully ===" - name: Detect Dockerfile or generate fallback id: dockerfile run: | echo "=== Checking for Dockerfile ===" if [ -f "Dockerfile" ]; then echo "DOCKERFILE_SOURCE=existing" >> $GITHUB_OUTPUT echo "Using existing Dockerfile from source repository" cat Dockerfile else echo "DOCKERFILE_SOURCE=generated" >> $GITHUB_OUTPUT echo "No Dockerfile found, generating fallback..." if [ -f "package.json" ]; then echo "Detected: Node.js project" MAIN=$(jq -r '.main // "index.js"' package.json 2>/dev/null || echo "index.js") HAS_BUILD=$(jq -r '.scripts.build // empty' package.json 2>/dev/null) HAS_START=$(jq -r '.scripts.start // empty' package.json 2>/dev/null) if [ -f "pnpm-lock.yaml" ]; then PKG_MGR="pnpm" { echo "FROM node:20-alpine" echo "WORKDIR /app" echo "RUN corepack enable" echo "COPY package.json pnpm-lock.yaml ./" echo "RUN pnpm install --frozen-lockfile" echo "COPY . ." if [ -n "$HAS_BUILD" ]; then echo "RUN pnpm run build"; fi if [ -n "$HAS_START" ]; then echo 'CMD ["pnpm", "start"]' else echo "CMD [\"node\", \"$MAIN\"]"; fi } > Dockerfile elif [ -f "yarn.lock" ]; then PKG_MGR="yarn" # Yarn Berry (v2+) requires all files including .yarn dir for install { echo "FROM node:20-alpine" echo "WORKDIR /app" echo "RUN corepack enable" echo "COPY . ." echo "RUN yarn install --immutable" if [ -n "$HAS_BUILD" ]; then echo "RUN yarn build"; fi if [ -n "$HAS_START" ]; then echo 'CMD ["yarn", "start"]' else echo "CMD [\"node\", \"$MAIN\"]"; fi } > Dockerfile elif [ -f "bun.lockb" ]; then PKG_MGR="bun" { echo "FROM oven/bun:latest" echo "WORKDIR /app" echo "COPY package.json bun.lockb ./" echo "RUN bun install --frozen-lockfile" echo "COPY . ." if [ -n "$HAS_BUILD" ]; then echo "RUN bun run build"; fi if [ -n "$HAS_START" ]; then echo 'CMD ["bun", "start"]' else echo "CMD [\"bun\", \"$MAIN\"]"; fi } > Dockerfile else PKG_MGR="npm" { echo "FROM node:20-alpine" echo "WORKDIR /app" echo "COPY package*.json ./" echo "RUN npm ci || npm install" echo "COPY . ." if [ -n "$HAS_BUILD" ]; then echo "RUN npm run build"; fi if [ -n "$HAS_START" ]; then echo 'CMD ["npm", "start"]' else echo "CMD [\"node\", \"$MAIN\"]"; fi } > Dockerfile fi elif [ -f "pyproject.toml" ] || [ -f "requirements.txt" ]; then echo "Detected: Python project" if [ -f "main.py" ]; then ENTRY="main.py" elif [ -f "app.py" ]; then ENTRY="app.py" elif [ -f "server.py" ]; then ENTRY="server.py" else ENTRY="main.py"; fi { echo "FROM python:3.11-slim" echo "WORKDIR /app" echo "COPY requirements.txt* pyproject.toml* ./" # Install from requirements.txt if exists, otherwise from pyproject.toml # Don't hide errors with 2>/dev/null or || true if [ -f "requirements.txt" ]; then echo "RUN pip install --no-cache-dir -r requirements.txt" elif [ -f "pyproject.toml" ]; then echo "RUN pip install --no-cache-dir ." fi echo "COPY . ." echo "CMD [\"python\", \"$ENTRY\"]" } > Dockerfile elif [ -f "go.mod" ]; then echo "Detected: Go project" { echo "FROM golang:1.21-alpine AS builder" echo "WORKDIR /app" echo "COPY go.mod go.sum* ./" echo "RUN go mod download" echo "COPY . ." echo "RUN CGO_ENABLED=0 GOOS=linux go build -o /app/server ." echo "FROM alpine:latest" echo "WORKDIR /app" echo 'COPY --from=builder /app/server .' echo 'CMD ["./server"]' } > Dockerfile elif [ -f "Cargo.toml" ]; then echo "Detected: Rust project" { echo "FROM rust:1.75-alpine AS builder" echo "RUN apk add --no-cache musl-dev" echo "WORKDIR /app" echo "COPY . ." echo "RUN cargo build --release" echo "FROM alpine:latest" echo "WORKDIR /app" echo 'COPY --from=builder /app/target/release/* ./' echo 'CMD ["./app"]' } > Dockerfile else echo "Unknown project type, creating minimal Dockerfile" { echo "FROM alpine:latest" echo "WORKDIR /app" echo "COPY . ." echo 'CMD ["/bin/sh", "-c", "echo Container started"]' } > Dockerfile fi echo "=== Generated Dockerfile ===" cat Dockerfile fi echo "=== Build ready ===" - name: Wrap stdio server with HTTP/SSE transport (supergateway) run: | echo "=== Wrapping stdio MCP server with supergateway for HTTP/SSE transport ===" if [ ! -f "Dockerfile" ]; then echo "ERROR: No Dockerfile found to modify" exit 1 fi # Extract the original CMD from Dockerfile ORIGINAL_CMD=$(grep -E "^CMD[[:space:]]" Dockerfile | tail -1 | sed -E 's/^CMD[[:space:]]*//') if [ -z "$ORIGINAL_CMD" ]; then echo "WARNING: No CMD found in Dockerfile, using default" ORIGINAL_CMD='["node", "index.js"]' fi echo "Original CMD: $ORIGINAL_CMD" # Convert CMD array to command string for supergateway if echo "$ORIGINAL_CMD" | grep -q '^\['; then SHELL_CMD=$(echo "$ORIGINAL_CMD" | jq -r 'join(" ")' 2>/dev/null || echo "node index.js") else SHELL_CMD=$ORIGINAL_CMD fi echo "Shell command: $SHELL_CMD" # Use pre-detected start command if available WRAPPER_CMD="$SHELL_CMD" # Sanitize WRAPPER_CMD: remove any CMD prefix and convert JSON arrays WRAPPER_CMD=$(echo "$WRAPPER_CMD" | sed -E 's/^CMD[[:space:]]*//') if echo "$WRAPPER_CMD" | grep -q '^\['; then WRAPPER_CMD=$(echo "$WRAPPER_CMD" | jq -r 'join(" ")' 2>/dev/null || echo "$WRAPPER_CMD" | tr -d '[]"' | tr ',' ' ') fi echo "Wrapper will execute: $WRAPPER_CMD" # Check if this is a Node.js project if [ -f "package.json" ]; then echo "Node.js project detected - will use npx supergateway" # Remove the original CMD line sed -i -E '/^CMD[[:space:]]/d' Dockerfile # Add supergateway wrapper echo "" >> Dockerfile echo "# === Supergateway HTTP/SSE Wrapper ===" >> Dockerfile echo "# This server uses stdio transport internally, wrapped with supergateway for HTTP/SSE" >> Dockerfile echo "RUN npm install -g supergateway || yarn global add supergateway || true" >> Dockerfile echo "ENV PORT=8080" >> Dockerfile # Use double-quoted echo so $WRAPPER_CMD expands echo "CMD npx -y supergateway --stdio '$WRAPPER_CMD' --host 0.0.0.0 --port \${PORT:-8080} --ssePath /sse --messagePath /message" >> Dockerfile elif [ -f "pyproject.toml" ] || [ -f "requirements.txt" ]; then echo "Python project detected - will use mcp-proxy" # Add mcp-proxy to requirements if needed (ensure newline before appending) if [ -f "requirements.txt" ] && ! grep -q "mcp-proxy" requirements.txt; then echo "" >> requirements.txt echo "mcp-proxy" >> requirements.txt fi sed -i -E '/^CMD[[:space:]]/d' Dockerfile echo "" >> Dockerfile echo "# === MCP-Proxy HTTP/SSE Wrapper ===" >> Dockerfile echo "# This server uses stdio transport internally, wrapped with mcp-proxy for HTTP/SSE" >> Dockerfile echo "RUN pip install mcp-proxy" >> Dockerfile echo "ENV PORT=8080" >> Dockerfile echo "CMD mcp-proxy --host 0.0.0.0 --port \${PORT:-8080} --pass-environment $WRAPPER_CMD" >> Dockerfile else echo "Unknown project type - using npx supergateway wrapper" sed -i -E '/^CMD[[:space:]]/d' Dockerfile echo "" >> Dockerfile echo "# === Supergateway HTTP/SSE Wrapper ===" >> Dockerfile echo "# Install Node.js for supergateway if not present" >> Dockerfile echo "RUN apk add --no-cache nodejs npm 2>/dev/null || apt-get update && apt-get install -y nodejs npm 2>/dev/null || true" >> Dockerfile echo "RUN npm install -g supergateway || true" >> Dockerfile echo "ENV PORT=8080" >> Dockerfile echo "CMD npx -y supergateway --stdio '$WRAPPER_CMD' --host 0.0.0.0 --port \${PORT:-8080} --ssePath /sse --messagePath /message" >> Dockerfile fi echo "=== Modified Dockerfile with HTTP wrapper ===" cat Dockerfile echo "=== Wrapper injection complete ===" - name: Set up QEMU uses: docker/setup-qemu-action@v3 - name: Set up Docker Buildx uses: docker/setup-buildx-action@v3 - name: Authenticate to Google Cloud id: auth uses: google-github-actions/auth@v2 with: workload_identity_provider: projects/${{ secrets.GCP_PROJECT_NUMBER }}/locations/global/workloadIdentityPools/github-pool/providers/github-provider service_account: mcp-deployer@biomindtalks.iam.gserviceaccount.com project_id: biomindtalks - name: Set up Cloud SDK uses: google-github-actions/setup-gcloud@v2 with: project_id: biomindtalks - name: Configure Docker for Artifact Registry run: gcloud auth configure-docker us-central1-docker.pkg.dev --quiet - name: Extract metadata for Docker id: meta uses: docker/metadata-action@v5 with: images: us-central1-docker.pkg.dev/biomindtalks/docker-images/uma tags: | type=ref,event=branch type=sha,prefix= type=raw,value=latest,enable={{is_default_branch}} labels: | org.opencontainers.image.title=gitlab-mcp-server org.opencontainers.image.description=MCP Server: gitlab-mcp-server - Containerized deployment org.opencontainers.image.source=${{ github.server_url }}/${{ github.repository }} org.opencontainers.image.revision=${{ github.sha }} org.opencontainers.image.licenses=MIT - name: Build and push Docker image uses: docker/build-push-action@v5 with: context: . file: Dockerfile platforms: linux/amd64 push: true tags: ${{ steps.meta.outputs.tags }} labels: ${{ steps.meta.outputs.labels }} cache-from: type=gha cache-to: type=gha,mode=max build-args: | GITLAB_ACCESS_TOKEN=${{ secrets.GITLAB_ACCESS_TOKEN }} GITLAB_PROJECT_ID=${{ secrets.GITLAB_PROJECT_ID }} GITLAB_URL=${{ secrets.GITLAB_URL }} - name: Deploy to Cloud Run id: deploy uses: google-github-actions/deploy-cloudrun@v2 with: service: mcp-gitlab-mcp-server region: us-central1 image: us-central1-docker.pkg.dev/biomindtalks/docker-images/uma:latest flags: | --port=8080 --memory=512Mi --cpu=1 --max-instances=10 --min-instances=0 --allow-unauthenticated env_vars: | GITLAB_ACCESS_TOKEN=${{ secrets.GITLAB_ACCESS_TOKEN }} GITLAB_PROJECT_ID=${{ secrets.GITLAB_PROJECT_ID }} GITLAB_URL=${{ secrets.GITLAB_URL }} - name: Show Cloud Run URL run: | echo "Service URL: ${{ steps.deploy.outputs.url }}" echo "## Cloud Run Deployment" >> $GITHUB_STEP_SUMMARY echo "**Service:** mcp-gitlab-mcp-server" >> $GITHUB_STEP_SUMMARY echo "**Region:** us-central1" >> $GITHUB_STEP_SUMMARY echo "**URL:** ${{ steps.deploy.outputs.url }}" >> $GITHUB_STEP_SUMMARY

Latest Blog Posts

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/bioanywhere/uma'

If you have feedback or need assistance with the MCP directory API, please join our Discord server