diff --git a/.dockerignore b/.dockerignore new file mode 100644 index 0000000..ff58e11 --- /dev/null +++ b/.dockerignore @@ -0,0 +1,47 @@ +# .NET build artifacts +**/bin/ +**/obj/ +**/out/ +**/TestResults/ + +# Visual Studio / VS Code +.vs/ +.vscode/ +*.user +*.suo +*.userprefs + +# Git +.git/ +.gitignore +.gitattributes + +# Docker +Dockerfile +.dockerignore + +# Documentation +*.md +LICENSE + +# CI/CD +.github/ + +# Development containers +.devcontainer/ + +# Environment files +*.env +appsettings.Development.json + +# Logs +*.log + +# Test artifacts +coverage*.json +coverage*.xml +*.trx + +# OS files +.DS_Store +Thumbs.db diff --git a/.github/workflows/docker-publish.yml b/.github/workflows/docker-publish.yml new file mode 100644 index 0000000..81a5031 --- /dev/null +++ b/.github/workflows/docker-publish.yml @@ -0,0 +1,68 @@ +name: Docker Build and Publish + +on: + push: + branches: [ "main" ] + pull_request: + branches: [ "main" ] + +env: + REGISTRY: ghcr.io + IMAGE_NAME: ${{ github.repository }} + +jobs: + build-and-push: + runs-on: ubuntu-latest + permissions: + contents: read + packages: write + + steps: + - name: Checkout repository + uses: actions/checkout@v4 + + - name: Set up Docker Buildx + uses: docker/setup-buildx-action@v3 + + - name: Log in to GitHub Container Registry + if: github.event_name == 'push' && github.ref == 'refs/heads/main' + uses: docker/login-action@v3 + with: + registry: ${{ env.REGISTRY }} + username: ${{ github.actor }} + password: ${{ secrets.GITHUB_TOKEN }} + + - name: Extract metadata (tags, labels) for Docker + id: meta + uses: docker/metadata-action@v5 + with: + images: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }} + tags: | + type=ref,event=branch + type=ref,event=pr + type=sha,prefix=${{ github.ref_name }}- + type=semver,pattern={{version}} + type=semver,pattern={{major}}.{{minor}} + type=raw,value=latest,enable={{is_default_branch}} + # Note: semver tags require Git tags in the format v1.2.3 + + - name: Build Docker image + uses: docker/build-push-action@v6 + with: + context: . + push: false + tags: ${{ steps.meta.outputs.tags }} + labels: ${{ steps.meta.outputs.labels }} + cache-from: type=gha + cache-to: type=gha,mode=max + + - name: Push Docker image to GitHub Container Registry + if: github.event_name == 'push' && github.ref == 'refs/heads/main' + uses: docker/build-push-action@v6 + with: + context: . + push: true + tags: ${{ steps.meta.outputs.tags }} + labels: ${{ steps.meta.outputs.labels }} + cache-from: type=gha + cache-to: type=gha,mode=max diff --git a/Dockerfile b/Dockerfile new file mode 100644 index 0000000..ad80b36 --- /dev/null +++ b/Dockerfile @@ -0,0 +1,54 @@ +# Build stage +FROM mcr.microsoft.com/dotnet/sdk:9.0-bookworm-slim AS build +WORKDIR /src + +# Copy solution and project files +COPY ["AbeckDev.DbTimetable.sln", "./"] +COPY ["AbeckDev.DbTimetable.Mcp/AbeckDev.DbTimetable.Mcp.csproj", "AbeckDev.DbTimetable.Mcp/"] +COPY ["AbeckDev.DbTimetable.Mcp.Test/AbeckDev.DbTimetable.Mcp.Test.csproj", "AbeckDev.DbTimetable.Mcp.Test/"] + +# Restore dependencies +RUN dotnet restore "AbeckDev.DbTimetable.Mcp/AbeckDev.DbTimetable.Mcp.csproj" + +# Copy the rest of the source code +COPY . . + +# Build the application +WORKDIR "/src/AbeckDev.DbTimetable.Mcp" +RUN dotnet build "AbeckDev.DbTimetable.Mcp.csproj" -c Release -o /app/build + +# Publish stage +FROM build AS publish +RUN dotnet publish "AbeckDev.DbTimetable.Mcp.csproj" -c Release -o /app/publish /p:UseAppHost=false + +# Runtime stage +FROM mcr.microsoft.com/dotnet/aspnet:9.0-bookworm-slim AS final +WORKDIR /app + +# Install curl for healthcheck +RUN apt-get update && apt-get install -y --no-install-recommends curl && rm -rf /var/lib/apt/lists/* + +# Create a non-root user +RUN groupadd -r appuser && useradd -r -g appuser appuser + +# Copy published files +COPY --from=publish /app/publish . + +# Change ownership to non-root user +RUN chown -R appuser:appuser /app + +# Switch to non-root user +USER appuser + +# Expose port +EXPOSE 3001 + +# Set environment variables +ENV ASPNETCORE_URLS=http://0.0.0.0:3001 + +# Health check +HEALTHCHECK --interval=30s --timeout=3s --start-period=5s --retries=3 \ + CMD curl -f http://localhost:3001/ || exit 1 + +# Entry point +ENTRYPOINT ["dotnet", "AbeckDev.DbTimetable.Mcp.dll"] diff --git a/README.md b/README.md index 71bfd51..e42f997 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,7 @@ # DB-TimetableAPI-MCPServer [![.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) +[![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) [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT) [![codecov](https://codecov.io/github/abeckDev/DB-TimetableAPI-MCPServer/graph/badge.svg?token=RJR3H1JRPW)](https://codecov.io/github/abeckDev/DB-TimetableAPI-MCPServer) @@ -187,6 +188,90 @@ dotnet build The project should build successfully. If you encounter any issues, ensure you have .NET 9.0 SDK installed. +### Using Docker (Recommended for Production) + +The easiest way to run the MCP server is using Docker. Pre-built images are automatically published to the GitHub Container Registry. + +#### Pull and Run the Latest Image + +```bash +# Pull the latest image from GitHub Container Registry +docker pull ghcr.io/abeckdev/db-timetableapi-mcpserver:latest + +# Run the container with your API credentials +docker run -d \ + --name db-timetable-mcp \ + -p 3001:3001 \ + -e DeutscheBahnApi__ClientId="your-actual-client-id" \ + -e DeutscheBahnApi__ApiKey="your-actual-api-key" \ + -e DeutscheBahnApi__BaseUrl="https://apis.deutschebahn.com/db-api-marketplace/apis/timetables/v1/" \ + ghcr.io/abeckdev/db-timetableapi-mcpserver:latest + +# Check if the container is running +docker ps + +# View logs +docker logs db-timetable-mcp +``` + +The MCP server will be accessible at `http://localhost:3001/mcp`. + +#### Build Your Own Docker Image + +If you prefer to build the Docker image locally: + +```bash +# Build the image +docker build -t db-timetableapi-mcpserver:local . + +# Run the container +docker run -d \ + --name db-timetable-mcp \ + -p 3001:3001 \ + -e DeutscheBahnApi__ClientId="your-actual-client-id" \ + -e DeutscheBahnApi__ApiKey="your-actual-api-key" \ + -e DeutscheBahnApi__BaseUrl="https://apis.deutschebahn.com/db-api-marketplace/apis/timetables/v1/" \ + db-timetableapi-mcpserver:local +``` + +#### Docker Compose + +You can also use Docker Compose for easier management. Create a `docker-compose.yml` file: + +```yaml +services: + mcp-server: + image: ghcr.io/abeckdev/db-timetableapi-mcpserver:latest + container_name: db-timetable-mcp + ports: + - "3001:3001" + environment: + - DeutscheBahnApi__ClientId=your-actual-client-id + - DeutscheBahnApi__ApiKey=your-actual-api-key + - DeutscheBahnApi__BaseUrl=https://apis.deutschebahn.com/db-api-marketplace/apis/timetables/v1/ + restart: unless-stopped +``` + +Then run: + +```bash +docker compose up -d +``` + +#### Docker Image Tags + +Images are tagged with multiple identifiers for flexibility: + +- `latest` - The most recent build from the main branch +- `main` - Same as latest, tracks the main branch +- `main-` - Specific commit SHA from the main branch (e.g., `main-abc1234`) + +Example pulling a specific version: + +```bash +docker pull ghcr.io/abeckdev/db-timetableapi-mcpserver:main-a1b2c3d +``` + --- ## ⚙️ Configuration @@ -680,9 +765,25 @@ All pull requests automatically run: - ✅ All unit tests - ✅ Code coverage analysis - ✅ Coverage threshold checks (70% minimum) +- ✅ Docker image build verification Coverage reports are available as workflow artifacts. +### Continuous Deployment + +When changes are pushed to the `main` branch: +- ✅ Docker images are automatically built +- ✅ Images are tagged with multiple identifiers (latest, main, commit SHA) +- ✅ Images are published to GitHub Container Registry (ghcr.io) +- ✅ Images are publicly accessible at `ghcr.io/abeckdev/db-timetableapi-mcpserver` + +The Docker image includes: +- Multi-stage build for optimized size +- .NET 9.0 runtime on Debian Bookworm (slim variant) +- Non-root user for enhanced security +- Health check endpoint +- Proper port exposure (3001) + --- ## 🤝 Contributing