Skip to content

Commit e313474

Browse files
aaronsbclaude
andcommitted
Modernize multi-arch Docker build for monorepo structure
- Update Dockerfile for pnpm monorepo with proper workspace handling - Add smart build script with clean output and logging - Fix user permissions and directory structure for MCP server - Update GitHub Actions workflow for modern buildx and pnpm - Add proper entrypoint supporting both local-mcp-server and configure-mcp-server - Tested and verified working with production Glean instance - Supports both development (npm) and production (Docker) deployment 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude <[email protected]>
1 parent eebbc44 commit e313474

File tree

5 files changed

+603
-0
lines changed

5 files changed

+603
-0
lines changed

.dockerignore

Lines changed: 137 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,137 @@
1+
# Node.js
2+
node_modules/
3+
**/node_modules/
4+
npm-debug.log*
5+
yarn-debug.log*
6+
yarn-error.log*
7+
lerna-debug.log*
8+
9+
# Build outputs (except what we need)
10+
**/build/test/
11+
**/build/**/test/
12+
**/build/src/
13+
coverage/
14+
*.tgz
15+
*.tar.gz
16+
17+
# Development files
18+
.env
19+
.env.local
20+
.env.development.local
21+
.env.test.local
22+
.env.production.local
23+
24+
# IDE and editor files
25+
.vscode/
26+
.idea/
27+
*.swp
28+
*.swo
29+
*~
30+
31+
# OS files
32+
.DS_Store
33+
.DS_Store?
34+
._*
35+
.Spotlight-V100
36+
.Trashes
37+
ehthumbs.db
38+
Thumbs.db
39+
40+
# Git
41+
.git/
42+
.gitignore
43+
44+
# Documentation
45+
docs/
46+
*.md
47+
!README.md
48+
49+
# Test files
50+
**/*.test.*
51+
**/*.spec.*
52+
**/test/
53+
**/tests/
54+
55+
# Logs
56+
logs/
57+
*.log
58+
59+
# Runtime data
60+
pids/
61+
*.pid
62+
*.seed
63+
*.pid.lock
64+
65+
# Optional npm cache directory
66+
.npm
67+
68+
# Optional REPL history
69+
.node_repl_history
70+
71+
# Dependency directories that might exist in subdirs
72+
jspm_packages/
73+
74+
# Optional npm cache directory
75+
.npm
76+
77+
# Optional eslint cache
78+
.eslintcache
79+
80+
# Microbundle cache
81+
.rpt2_cache/
82+
.rts2_cache_cjs/
83+
.rts2_cache_es/
84+
.rts2_cache_umd/
85+
86+
# Optional REPL history
87+
.node_repl_history
88+
89+
# Output of 'npm pack'
90+
*.tgz
91+
92+
# Yarn Integrity file
93+
.yarn-integrity
94+
95+
# parcel-bundler cache (https://parceljs.org/)
96+
.cache
97+
.parcel-cache
98+
99+
# next.js build output
100+
.next
101+
102+
# nuxt.js build output
103+
.nuxt
104+
105+
# rollup.js default build output
106+
dist/
107+
108+
# Uncomment the public line in if your project uses Gatsby
109+
# https://nextjs.org/blog/next-9-1#public-directory-support
110+
# public
111+
112+
# Storybook build outputs
113+
.out
114+
.storybook-out
115+
116+
# Temporary folders
117+
tmp/
118+
temp/
119+
120+
# Docker files (avoid recursive copies)
121+
Dockerfile*
122+
docker-compose*
123+
.dockerignore
124+
125+
# CI/CD
126+
.github/
127+
.gitlab-ci.yml
128+
.travis.yml
129+
.circleci/
130+
131+
# Package manager
132+
package-lock.json
133+
yarn.lock
134+
# Keep pnpm-lock.yaml as it's needed for consistent builds
135+
136+
# Misc
137+
*.orig

.github/workflows/docker-build.yml

Lines changed: 112 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,112 @@
1+
name: Docker Build and Deploy
2+
3+
on:
4+
push:
5+
branches:
6+
- main
7+
tags:
8+
- 'v*'
9+
pull_request:
10+
branches:
11+
- main
12+
13+
env:
14+
REGISTRY: ghcr.io
15+
IMAGE_NAME: ${{ github.repository }}
16+
17+
jobs:
18+
test:
19+
runs-on: ubuntu-latest
20+
steps:
21+
- name: Checkout repository
22+
uses: actions/checkout@v4
23+
24+
- name: Install pnpm
25+
uses: pnpm/action-setup@v4
26+
with:
27+
version: 10.6.2
28+
29+
- name: Set up Node.js
30+
uses: actions/setup-node@v4
31+
with:
32+
node-version: '20'
33+
cache: 'pnpm'
34+
35+
- name: Install dependencies
36+
run: pnpm install --frozen-lockfile
37+
38+
- name: Run tests
39+
run: pnpm test
40+
41+
- name: Run linting
42+
run: pnpm lint
43+
44+
build-and-deploy:
45+
needs: test
46+
runs-on: ubuntu-latest
47+
permissions:
48+
contents: read
49+
packages: write
50+
51+
steps:
52+
- name: Checkout
53+
uses: actions/checkout@v4
54+
55+
- name: Set up QEMU
56+
uses: docker/setup-qemu-action@v3
57+
58+
- name: Set up Docker Buildx
59+
uses: docker/setup-buildx-action@v3
60+
61+
- name: Login to GitHub Container Registry
62+
if: github.event_name != 'pull_request'
63+
uses: docker/login-action@v3
64+
with:
65+
registry: ${{ env.REGISTRY }}
66+
username: ${{ github.actor }}
67+
password: ${{ secrets.GITHUB_TOKEN }}
68+
69+
- name: Extract metadata
70+
id: meta
71+
uses: docker/metadata-action@v5
72+
with:
73+
images: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}
74+
tags: |
75+
type=ref,event=branch
76+
type=ref,event=pr
77+
type=semver,pattern={{version}}
78+
type=semver,pattern={{major}}.{{minor}}
79+
type=semver,pattern={{major}}
80+
type=raw,value=latest,enable={{is_default_branch}}
81+
type=sha,format=long
82+
83+
- name: Build and push Docker image
84+
uses: docker/build-push-action@v5
85+
with:
86+
context: .
87+
platforms: linux/amd64,linux/arm64
88+
push: ${{ github.event_name != 'pull_request' }}
89+
tags: ${{ steps.meta.outputs.tags }}
90+
labels: ${{ steps.meta.outputs.labels }}
91+
cache-from: type=gha
92+
cache-to: type=gha,mode=max
93+
build-args: |
94+
DOCKER_HASH=${{ github.sha }}
95+
96+
cleanup:
97+
name: Cleanup old packages
98+
needs: build-and-deploy
99+
runs-on: ubuntu-latest
100+
if: github.event_name != 'pull_request'
101+
permissions:
102+
contents: read
103+
packages: write
104+
105+
steps:
106+
- name: Delete old package versions
107+
uses: actions/delete-package-versions@v5
108+
with:
109+
package-name: ${{ github.event.repository.name }}
110+
package-type: container
111+
min-versions-to-keep: 10
112+
delete-only-untagged-versions: true

Dockerfile

Lines changed: 81 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,81 @@
1+
# syntax=docker/dockerfile:1.4
2+
3+
# Build stage - multi-architecture support
4+
FROM node:20-bullseye AS builder
5+
6+
# Add metadata
7+
LABEL org.opencontainers.image.source="https://github.com/gleanwork/mcp-server"
8+
LABEL org.opencontainers.image.description="Glean MCP Server - Multi-architecture build"
9+
LABEL org.opencontainers.image.licenses="MIT"
10+
11+
# Install build dependencies
12+
RUN apt-get update && apt-get install -y \
13+
git \
14+
build-essential \
15+
python3 \
16+
&& rm -rf /var/lib/apt/lists/*
17+
18+
WORKDIR /app
19+
20+
# Install pnpm globally
21+
RUN npm install -g [email protected]
22+
23+
# Copy workspace configuration and root package files
24+
COPY pnpm-workspace.yaml package.json pnpm-lock.yaml ./
25+
26+
# Copy all package.json files to enable proper dependency resolution
27+
COPY packages/ ./packages/
28+
29+
# Install dependencies using pnpm (includes workspace packages)
30+
# Use --no-frozen-lockfile in case lockfile is out of sync
31+
RUN pnpm install --no-frozen-lockfile
32+
33+
# Copy source code
34+
COPY . .
35+
36+
# Build all packages
37+
RUN pnpm run build
38+
39+
# Production stage
40+
FROM node:20-bullseye-slim
41+
42+
WORKDIR /app
43+
44+
# Set docker hash as environment variable
45+
ARG DOCKER_HASH=unknown
46+
ENV DOCKER_HASH=$DOCKER_HASH
47+
48+
# Install pnpm in production stage too
49+
RUN npm install -g [email protected]
50+
51+
# Copy workspace config and package files
52+
COPY pnpm-workspace.yaml package.json pnpm-lock.yaml ./
53+
54+
# Copy built packages from builder stage (includes package.json files)
55+
COPY --from=builder /app/packages ./packages
56+
57+
# Install only production dependencies (skip prepare scripts)
58+
RUN pnpm install --no-frozen-lockfile --prod --ignore-scripts && \
59+
pnpm store prune
60+
61+
# Make the main server executable
62+
RUN chmod +x packages/local-mcp-server/build/index.js
63+
64+
# Create non-root user and required directories
65+
RUN groupadd -r mcp && useradd -r -g mcp -m mcp && \
66+
mkdir -p /app/logs && \
67+
mkdir -p /home/mcp/.local/state/glean && \
68+
mkdir -p /home/mcp/.cache && \
69+
chown -R mcp:mcp /app && \
70+
chown -R mcp:mcp /home/mcp
71+
72+
# Copy and set up entrypoint
73+
COPY docker-entrypoint.sh /usr/local/bin/
74+
RUN chmod +x /usr/local/bin/docker-entrypoint.sh
75+
76+
# Switch to non-root user
77+
USER mcp
78+
79+
# Default command runs the local MCP server
80+
ENTRYPOINT ["docker-entrypoint.sh"]
81+
CMD ["packages/local-mcp-server/build/index.js"]

0 commit comments

Comments
 (0)