Skip to content

Commit 760963e

Browse files
Copilotchrisdocgitstream-cm[bot]
authored
feat: add Docker image building to CI/CD workflows (#124)
* Initial plan * feat: add Docker image building to CI/CD workflows - Add .dockerignore file for efficient Docker builds - Implement multi-stage Dockerfile with security best practices - Add Docker build/push job to build-and-test workflow for main branch - Add Docker build/push job to release workflow for tagged releases - Configure GHCR (GitHub Container Registry) as target registry - Add semantic-release GitHub plugin for proper release detection - Update README with Docker usage documentation and examples - Add Docker configuration tests - Support multi-platform builds (amd64/arm64) - Implement proper image tagging strategy (latest, semver tags) Co-authored-by: chrisdoc <9047291+chrisdoc@users.noreply.github.com> * Update src/utils/httpServer.ts Co-authored-by: gitstream-cm[bot] <111687743+gitstream-cm[bot]@users.noreply.github.com> --------- Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com> Co-authored-by: chrisdoc <9047291+chrisdoc@users.noreply.github.com> Co-authored-by: Christoph Kieslich <c.kieslich@gmail.com> Co-authored-by: gitstream-cm[bot] <111687743+gitstream-cm[bot]@users.noreply.github.com>
1 parent e6ddef9 commit 760963e

10 files changed

Lines changed: 339 additions & 286 deletions

File tree

.dockerignore

Lines changed: 74 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,74 @@
1+
# Dependencies and build artifacts
2+
node_modules/
3+
dist/
4+
coverage/
5+
.nyc_output/
6+
7+
# Environment and configuration files
8+
.env
9+
.env.local
10+
.env.*.local
11+
.dotenvx/
12+
13+
# Development files
14+
.cm/
15+
.cursor/
16+
*.log
17+
npm-debug.log*
18+
yarn-debug.log*
19+
yarn-error.log*
20+
21+
# Git and version control
22+
.git/
23+
.gitignore
24+
25+
# IDE and editor files
26+
.vscode/
27+
.idea/
28+
*.swp
29+
*.swo
30+
*~
31+
32+
# OS generated files
33+
.DS_Store
34+
.DS_Store?
35+
._*
36+
.Spotlight-V100
37+
.Trashes
38+
ehthumbs.db
39+
Thumbs.db
40+
41+
# Test and coverage files
42+
tests/
43+
*.test.ts
44+
*.spec.ts
45+
46+
# Documentation
47+
*.md
48+
!README.md
49+
CHANGELOG.md
50+
CLAUDE.md
51+
docs/
52+
53+
# CI/CD files
54+
.github/
55+
.releaserc.json
56+
codecov.yml
57+
lefthook.yml
58+
59+
# Build tools config that aren't needed in container
60+
tsconfig.json
61+
tsup.config.ts
62+
biome.json
63+
kubb.config.ts
64+
commitlint.config.js
65+
mise.toml
66+
67+
# Temporary files
68+
tmp/
69+
temp/
70+
*.tmp
71+
72+
# OpenAPI spec (not needed in production container)
73+
openapi-spec.json
74+
scripts/

.github/workflows/build-and-test.yml

Lines changed: 41 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -65,11 +65,51 @@ jobs:
6565
env:
6666
HEVY_API_KEY: ${{ secrets.HEVY_API_KEY }}
6767

68+
docker-build:
69+
runs-on: ubuntu-latest
70+
needs: [build, integration-tests]
71+
if: github.ref == 'refs/heads/main'
72+
73+
steps:
74+
- name: Checkout repository
75+
uses: actions/checkout@v5
76+
77+
- name: Set up Docker Buildx
78+
uses: docker/setup-buildx-action@v3
79+
80+
- name: Log in to GitHub Container Registry
81+
uses: docker/login-action@v3
82+
with:
83+
registry: ghcr.io
84+
username: ${{ github.actor }}
85+
password: ${{ secrets.GITHUB_TOKEN }}
86+
87+
- name: Extract metadata
88+
id: meta
89+
uses: docker/metadata-action@v5
90+
with:
91+
images: ghcr.io/${{ github.repository }}
92+
tags: |
93+
type=ref,event=branch
94+
type=sha,prefix={{branch}}-
95+
type=raw,value=latest,enable={{is_default_branch}}
96+
97+
- name: Build and push Docker image
98+
uses: docker/build-push-action@v5
99+
with:
100+
context: .
101+
push: true
102+
tags: ${{ steps.meta.outputs.tags }}
103+
labels: ${{ steps.meta.outputs.labels }}
104+
platforms: linux/amd64,linux/arm64
105+
cache-from: type=gha
106+
cache-to: type=gha,mode=max
107+
68108
otel-cicd-action:
69109
if: always()
70110
name: OpenTelemetry Export Trace
71111
runs-on: ubuntu-latest
72-
needs: [build, integration-tests] # must run when all jobs are completed
112+
needs: [build, integration-tests, docker-build] # must run when all jobs are completed
73113
steps:
74114
- name: Export workflow
75115
uses: corentinmusard/otel-cicd-action@v2

.github/workflows/release.yml

Lines changed: 68 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,10 @@ jobs:
1515
issues: write # to be able to comment on released issues
1616
pull-requests: write # to be able to comment on released pull requests
1717
id-token: write # to enable use of OIDC for npm provenance
18+
packages: write # to push Docker images to GHCR
19+
outputs:
20+
version: ${{ steps.release.outputs.version }}
21+
released: ${{ steps.release.outputs.released }}
1822
steps:
1923
- uses: actions/checkout@v5
2024
- uses: actions/setup-node@v5
@@ -28,7 +32,70 @@ jobs:
2832
run: npx vitest run tests/integration
2933
env:
3034
HEVY_API_KEY: ${{ secrets.HEVY_API_KEY }}
31-
- run: npm run release
35+
- name: Store initial version
36+
id: initial_version
37+
run: echo "version=$(node -p "require('./package.json').version")" >> $GITHUB_OUTPUT
38+
39+
- name: Release
40+
id: release_step
41+
run: npm run release
3242
env:
3343
GITHUB_TOKEN: ${{ secrets.GH_TOKEN }}
3444
NPM_TOKEN: ${{ secrets.NPM_TOKEN }}
45+
46+
- name: Check if release was created
47+
id: release
48+
run: |
49+
NEW_VERSION=$(node -p "require('./package.json').version")
50+
if [ "$NEW_VERSION" != "${{ steps.initial_version.outputs.version }}" ]; then
51+
echo "version=$NEW_VERSION" >> $GITHUB_OUTPUT
52+
echo "released=true" >> $GITHUB_OUTPUT
53+
echo "Release created: $NEW_VERSION"
54+
else
55+
echo "released=false" >> $GITHUB_OUTPUT
56+
echo "No release created"
57+
fi
58+
env:
59+
GITHUB_TOKEN: ${{ secrets.GH_TOKEN }}
60+
NPM_TOKEN: ${{ secrets.NPM_TOKEN }}
61+
62+
docker-release:
63+
runs-on: ubuntu-latest
64+
needs: release
65+
if: needs.release.outputs.released == 'true'
66+
67+
steps:
68+
- name: Checkout repository
69+
uses: actions/checkout@v5
70+
71+
- name: Set up Docker Buildx
72+
uses: docker/setup-buildx-action@v3
73+
74+
- name: Log in to GitHub Container Registry
75+
uses: docker/login-action@v3
76+
with:
77+
registry: ghcr.io
78+
username: ${{ github.actor }}
79+
password: ${{ secrets.GITHUB_TOKEN }}
80+
81+
- name: Extract metadata
82+
id: meta
83+
uses: docker/metadata-action@v5
84+
with:
85+
images: ghcr.io/${{ github.repository }}
86+
tags: |
87+
type=semver,pattern={{version}},value=${{ needs.release.outputs.version }}
88+
type=semver,pattern={{major}}.{{minor}},value=${{ needs.release.outputs.version }}
89+
type=semver,pattern={{major}},value=${{ needs.release.outputs.version }}
90+
type=raw,value=latest
91+
92+
- name: Build and push Docker image
93+
uses: docker/build-push-action@v5
94+
with:
95+
context: .
96+
push: true
97+
tags: ${{ steps.meta.outputs.tags }}
98+
labels: ${{ steps.meta.outputs.labels }}
99+
platforms: linux/amd64,linux/arm64
100+
cache-from: type=gha
101+
cache-to: type=gha,mode=max

.releaserc.json

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,14 @@
55
"@semantic-release/release-notes-generator",
66
"@semantic-release/changelog",
77
"@semantic-release/npm",
8+
[
9+
"@semantic-release/github",
10+
{
11+
"successComment": false,
12+
"failTitle": false,
13+
"failComment": false
14+
}
15+
],
816
"@semantic-release/git"
917
],
1018
"releaseRules": [

Dockerfile

Lines changed: 28 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,19 +1,40 @@
1-
# Generated by https://smithery.ai. See: https://smithery.ai/docs/config#dockerfile
2-
FROM node:lts-alpine
1+
# Multi-stage Docker build for hevy-mcp
2+
# Build stage
3+
FROM node:lts-alpine as builder
34

45
WORKDIR /app
56

67
# Copy package files and install dependencies
78
COPY package*.json ./
8-
RUN npm ci --ignore-scripts
9+
RUN npm ci --ignore-scripts --no-optional
910

10-
# Copy the rest of the application
11+
# Copy source code and build
1112
COPY . ./
13+
RUN npm run build
14+
15+
# Production stage
16+
FROM node:lts-alpine as production
17+
18+
WORKDIR /app
19+
20+
# Copy package files for production dependencies
21+
COPY package*.json ./
22+
RUN npm ci --production --ignore-scripts --no-optional && npm cache clean --force
23+
24+
# Copy built application from builder stage
25+
COPY --from=builder /app/dist ./dist
26+
COPY --from=builder /app/src/generated ./src/generated
27+
28+
# Create non-root user
29+
RUN addgroup -g 1001 -S nodejs && \
30+
adduser -S nodejs -u 1001
31+
32+
# Change ownership of the app directory
33+
RUN chown -R nodejs:nodejs /app
34+
USER nodejs
1235

1336
ENV NODE_ENV=production
14-
ENV HEVY_API_KEY=your-api-key-here
1537

16-
# Build the application
17-
RUN npm run build
38+
EXPOSE 3000
1839

1940
CMD [ "npm", "start" ]

README.md

Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -147,6 +147,59 @@ npm run build
147147
npm start
148148
```
149149

150+
### Docker
151+
152+
The project includes a Dockerfile for containerized deployments. Docker images are automatically built and pushed to GitHub Container Registry (GHCR) during the CI/CD process.
153+
154+
#### Using Pre-built Images
155+
156+
Pull and run the latest image:
157+
158+
```bash
159+
docker run -d \
160+
--name hevy-mcp \
161+
-e HEVY_API_KEY=your_api_key_here \
162+
-p 3000:3000 \
163+
ghcr.io/chrisdoc/hevy-mcp:latest
164+
```
165+
166+
#### Building Locally
167+
168+
```bash
169+
# Build the image
170+
docker build -t hevy-mcp .
171+
172+
# Run the container
173+
docker run -d \
174+
--name hevy-mcp \
175+
-e HEVY_API_KEY=your_api_key_here \
176+
-p 3000:3000 \
177+
hevy-mcp
178+
```
179+
180+
#### Docker Compose Example
181+
182+
```yaml
183+
version: '3.8'
184+
services:
185+
hevy-mcp:
186+
image: ghcr.io/chrisdoc/hevy-mcp:latest
187+
environment:
188+
- HEVY_API_KEY=your_api_key_here
189+
- MCP_TRANSPORT=http
190+
- MCP_HTTP_HOST=0.0.0.0
191+
- MCP_HTTP_PORT=3000
192+
ports:
193+
- "3000:3000"
194+
restart: unless-stopped
195+
```
196+
197+
#### Available Image Tags
198+
199+
- `latest` - Latest stable release
200+
- `main` - Latest development build from main branch
201+
- `v1.8.8`, `v1.8`, `v1` - Semantic version tags for releases
202+
150203
## Available MCP Tools
151204

152205
The server implements the following MCP tools for interacting with the Hevy API:

0 commit comments

Comments
 (0)