Skip to content

Commit b2e8003

Browse files
authored
Merge pull request #9 from abeckDev/copilot/add-docker-image-workflow
Add Docker containerization with automated GHCR publishing
2 parents ddd38c3 + 71ce4c2 commit b2e8003

4 files changed

Lines changed: 270 additions & 0 deletions

File tree

.dockerignore

Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
# .NET build artifacts
2+
**/bin/
3+
**/obj/
4+
**/out/
5+
**/TestResults/
6+
7+
# Visual Studio / VS Code
8+
.vs/
9+
.vscode/
10+
*.user
11+
*.suo
12+
*.userprefs
13+
14+
# Git
15+
.git/
16+
.gitignore
17+
.gitattributes
18+
19+
# Docker
20+
Dockerfile
21+
.dockerignore
22+
23+
# Documentation
24+
*.md
25+
LICENSE
26+
27+
# CI/CD
28+
.github/
29+
30+
# Development containers
31+
.devcontainer/
32+
33+
# Environment files
34+
*.env
35+
appsettings.Development.json
36+
37+
# Logs
38+
*.log
39+
40+
# Test artifacts
41+
coverage*.json
42+
coverage*.xml
43+
*.trx
44+
45+
# OS files
46+
.DS_Store
47+
Thumbs.db
Lines changed: 68 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,68 @@
1+
name: Docker Build and Publish
2+
3+
on:
4+
push:
5+
branches: [ "main" ]
6+
pull_request:
7+
branches: [ "main" ]
8+
9+
env:
10+
REGISTRY: ghcr.io
11+
IMAGE_NAME: ${{ github.repository }}
12+
13+
jobs:
14+
build-and-push:
15+
runs-on: ubuntu-latest
16+
permissions:
17+
contents: read
18+
packages: write
19+
20+
steps:
21+
- name: Checkout repository
22+
uses: actions/checkout@v4
23+
24+
- name: Set up Docker Buildx
25+
uses: docker/setup-buildx-action@v3
26+
27+
- name: Log in to GitHub Container Registry
28+
if: github.event_name == 'push' && github.ref == 'refs/heads/main'
29+
uses: docker/login-action@v3
30+
with:
31+
registry: ${{ env.REGISTRY }}
32+
username: ${{ github.actor }}
33+
password: ${{ secrets.GITHUB_TOKEN }}
34+
35+
- name: Extract metadata (tags, labels) for Docker
36+
id: meta
37+
uses: docker/metadata-action@v5
38+
with:
39+
images: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}
40+
tags: |
41+
type=ref,event=branch
42+
type=ref,event=pr
43+
type=sha,prefix=${{ github.ref_name }}-
44+
type=semver,pattern={{version}}
45+
type=semver,pattern={{major}}.{{minor}}
46+
type=raw,value=latest,enable={{is_default_branch}}
47+
# Note: semver tags require Git tags in the format v1.2.3
48+
49+
- name: Build Docker image
50+
uses: docker/build-push-action@v6
51+
with:
52+
context: .
53+
push: false
54+
tags: ${{ steps.meta.outputs.tags }}
55+
labels: ${{ steps.meta.outputs.labels }}
56+
cache-from: type=gha
57+
cache-to: type=gha,mode=max
58+
59+
- name: Push Docker image to GitHub Container Registry
60+
if: github.event_name == 'push' && github.ref == 'refs/heads/main'
61+
uses: docker/build-push-action@v6
62+
with:
63+
context: .
64+
push: true
65+
tags: ${{ steps.meta.outputs.tags }}
66+
labels: ${{ steps.meta.outputs.labels }}
67+
cache-from: type=gha
68+
cache-to: type=gha,mode=max

Dockerfile

Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,54 @@
1+
# Build stage
2+
FROM mcr.microsoft.com/dotnet/sdk:9.0-bookworm-slim AS build
3+
WORKDIR /src
4+
5+
# Copy solution and project files
6+
COPY ["AbeckDev.DbTimetable.sln", "./"]
7+
COPY ["AbeckDev.DbTimetable.Mcp/AbeckDev.DbTimetable.Mcp.csproj", "AbeckDev.DbTimetable.Mcp/"]
8+
COPY ["AbeckDev.DbTimetable.Mcp.Test/AbeckDev.DbTimetable.Mcp.Test.csproj", "AbeckDev.DbTimetable.Mcp.Test/"]
9+
10+
# Restore dependencies
11+
RUN dotnet restore "AbeckDev.DbTimetable.Mcp/AbeckDev.DbTimetable.Mcp.csproj"
12+
13+
# Copy the rest of the source code
14+
COPY . .
15+
16+
# Build the application
17+
WORKDIR "/src/AbeckDev.DbTimetable.Mcp"
18+
RUN dotnet build "AbeckDev.DbTimetable.Mcp.csproj" -c Release -o /app/build
19+
20+
# Publish stage
21+
FROM build AS publish
22+
RUN dotnet publish "AbeckDev.DbTimetable.Mcp.csproj" -c Release -o /app/publish /p:UseAppHost=false
23+
24+
# Runtime stage
25+
FROM mcr.microsoft.com/dotnet/aspnet:9.0-bookworm-slim AS final
26+
WORKDIR /app
27+
28+
# Install curl for healthcheck
29+
RUN apt-get update && apt-get install -y --no-install-recommends curl && rm -rf /var/lib/apt/lists/*
30+
31+
# Create a non-root user
32+
RUN groupadd -r appuser && useradd -r -g appuser appuser
33+
34+
# Copy published files
35+
COPY --from=publish /app/publish .
36+
37+
# Change ownership to non-root user
38+
RUN chown -R appuser:appuser /app
39+
40+
# Switch to non-root user
41+
USER appuser
42+
43+
# Expose port
44+
EXPOSE 3001
45+
46+
# Set environment variables
47+
ENV ASPNETCORE_URLS=http://0.0.0.0:3001
48+
49+
# Health check
50+
HEALTHCHECK --interval=30s --timeout=3s --start-period=5s --retries=3 \
51+
CMD curl -f http://localhost:3001/ || exit 1
52+
53+
# Entry point
54+
ENTRYPOINT ["dotnet", "AbeckDev.DbTimetable.Mcp.dll"]

README.md

Lines changed: 101 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
# DB-TimetableAPI-MCPServer
22

33
[![.NET CI with Code Coverage](https://github.com/abeckDev/DB-TimetableAPI-MCPServer/actions/workflows/dotnet-ci.yml/badge.svg)](https://github.com/abeckDev/DB-TimetableAPI-MCPServer/actions/workflows/dotnet-ci.yml)
4+
[![Docker Build and Publish](https://github.com/abeckDev/DB-TimetableAPI-MCPServer/actions/workflows/docker-publish.yml/badge.svg)](https://github.com/abeckDev/DB-TimetableAPI-MCPServer/actions/workflows/docker-publish.yml)
45
[![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)
56
[![codecov](https://codecov.io/github/abeckDev/DB-TimetableAPI-MCPServer/graph/badge.svg?token=RJR3H1JRPW)](https://codecov.io/github/abeckDev/DB-TimetableAPI-MCPServer)
67

@@ -187,6 +188,90 @@ dotnet build
187188

188189
The project should build successfully. If you encounter any issues, ensure you have .NET 9.0 SDK installed.
189190

191+
### Using Docker (Recommended for Production)
192+
193+
The easiest way to run the MCP server is using Docker. Pre-built images are automatically published to the GitHub Container Registry.
194+
195+
#### Pull and Run the Latest Image
196+
197+
```bash
198+
# Pull the latest image from GitHub Container Registry
199+
docker pull ghcr.io/abeckdev/db-timetableapi-mcpserver:latest
200+
201+
# Run the container with your API credentials
202+
docker run -d \
203+
--name db-timetable-mcp \
204+
-p 3001:3001 \
205+
-e DeutscheBahnApi__ClientId="your-actual-client-id" \
206+
-e DeutscheBahnApi__ApiKey="your-actual-api-key" \
207+
-e DeutscheBahnApi__BaseUrl="https://apis.deutschebahn.com/db-api-marketplace/apis/timetables/v1/" \
208+
ghcr.io/abeckdev/db-timetableapi-mcpserver:latest
209+
210+
# Check if the container is running
211+
docker ps
212+
213+
# View logs
214+
docker logs db-timetable-mcp
215+
```
216+
217+
The MCP server will be accessible at `http://localhost:3001/mcp`.
218+
219+
#### Build Your Own Docker Image
220+
221+
If you prefer to build the Docker image locally:
222+
223+
```bash
224+
# Build the image
225+
docker build -t db-timetableapi-mcpserver:local .
226+
227+
# Run the container
228+
docker run -d \
229+
--name db-timetable-mcp \
230+
-p 3001:3001 \
231+
-e DeutscheBahnApi__ClientId="your-actual-client-id" \
232+
-e DeutscheBahnApi__ApiKey="your-actual-api-key" \
233+
-e DeutscheBahnApi__BaseUrl="https://apis.deutschebahn.com/db-api-marketplace/apis/timetables/v1/" \
234+
db-timetableapi-mcpserver:local
235+
```
236+
237+
#### Docker Compose
238+
239+
You can also use Docker Compose for easier management. Create a `docker-compose.yml` file:
240+
241+
```yaml
242+
services:
243+
mcp-server:
244+
image: ghcr.io/abeckdev/db-timetableapi-mcpserver:latest
245+
container_name: db-timetable-mcp
246+
ports:
247+
- "3001:3001"
248+
environment:
249+
- DeutscheBahnApi__ClientId=your-actual-client-id
250+
- DeutscheBahnApi__ApiKey=your-actual-api-key
251+
- DeutscheBahnApi__BaseUrl=https://apis.deutschebahn.com/db-api-marketplace/apis/timetables/v1/
252+
restart: unless-stopped
253+
```
254+
255+
Then run:
256+
257+
```bash
258+
docker compose up -d
259+
```
260+
261+
#### Docker Image Tags
262+
263+
Images are tagged with multiple identifiers for flexibility:
264+
265+
- `latest` - The most recent build from the main branch
266+
- `main` - Same as latest, tracks the main branch
267+
- `main-<sha>` - Specific commit SHA from the main branch (e.g., `main-abc1234`)
268+
269+
Example pulling a specific version:
270+
271+
```bash
272+
docker pull ghcr.io/abeckdev/db-timetableapi-mcpserver:main-a1b2c3d
273+
```
274+
190275
---
191276

192277
## ⚙️ Configuration
@@ -680,9 +765,25 @@ All pull requests automatically run:
680765
- ✅ All unit tests
681766
- ✅ Code coverage analysis
682767
- ✅ Coverage threshold checks (70% minimum)
768+
- ✅ Docker image build verification
683769

684770
Coverage reports are available as workflow artifacts.
685771

772+
### Continuous Deployment
773+
774+
When changes are pushed to the `main` branch:
775+
- ✅ Docker images are automatically built
776+
- ✅ Images are tagged with multiple identifiers (latest, main, commit SHA)
777+
- ✅ Images are published to GitHub Container Registry (ghcr.io)
778+
- ✅ Images are publicly accessible at `ghcr.io/abeckdev/db-timetableapi-mcpserver`
779+
780+
The Docker image includes:
781+
- Multi-stage build for optimized size
782+
- .NET 9.0 runtime on Debian Bookworm (slim variant)
783+
- Non-root user for enhanced security
784+
- Health check endpoint
785+
- Proper port exposure (3001)
786+
686787
---
687788

688789
## 🤝 Contributing

0 commit comments

Comments
 (0)