From e82c0cdb942ddbe644335ed04a8611dd11efac75 Mon Sep 17 00:00:00 2001 From: Barry Walker Date: Tue, 13 Jan 2026 14:15:56 -0500 Subject: [PATCH] Add Woodpecker CI pipeline with semantic versioning MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Pipeline stages: - restore: Restore NuGet dependencies - build: Build in Release mode - test: Run test suite - version: Determine version from git tags - package-nuget: Create NuGet package (main branch) - package-docker: Build multi-arch Docker image (tags only) - release: Create GitHub release with artifacts (tags only) Versioning: - Tags (v1.0.0) → version 1.0.0 - Main branch → version X.Y.Z-dev.main.abc1234 - PRs → version X.Y.Z-dev.branch.abc1234 Docker image published to ghcr.io/barryw/paperlessmcp 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 --- .woodpecker.yml | 110 ++++++++++++++++++++++++++++++++++++++++ PaperlessMCP/Dockerfile | 15 ++++-- README.md | 19 +++++++ 3 files changed, 139 insertions(+), 5 deletions(-) create mode 100644 .woodpecker.yml diff --git a/.woodpecker.yml b/.woodpecker.yml new file mode 100644 index 0000000..1e47d96 --- /dev/null +++ b/.woodpecker.yml @@ -0,0 +1,110 @@ +variables: + - &dotnet_image "mcr.microsoft.com/dotnet/sdk:10.0-preview" + - &docker_image "woodpeckerci/plugin-docker-buildx" + +when: + - event: [push, pull_request, tag] + +steps: + # Restore dependencies + restore: + image: *dotnet_image + commands: + - dotnet restore + + # Build the project + build: + image: *dotnet_image + commands: + - dotnet build --no-restore -c Release + depends_on: [restore] + + # Run tests + test: + image: *dotnet_image + commands: + - dotnet test --no-build -c Release --logger "console;verbosity=detailed" + depends_on: [build] + + # Determine version from git tags + version: + image: alpine/git + commands: + - | + if [ -n "$CI_COMMIT_TAG" ]; then + # Use tag as version (strip 'v' prefix if present) + VERSION=$(echo "$CI_COMMIT_TAG" | sed 's/^v//') + else + # Generate dev version from branch and short SHA + BRANCH=$(echo "$CI_COMMIT_BRANCH" | sed 's/[^a-zA-Z0-9]/-/g') + SHORT_SHA=$(echo "$CI_COMMIT_SHA" | cut -c1-7) + # Get latest tag or default to 0.0.0 + LATEST_TAG=$(git describe --tags --abbrev=0 2>/dev/null || echo "v0.0.0") + BASE_VERSION=$(echo "$LATEST_TAG" | sed 's/^v//') + VERSION="${BASE_VERSION}-dev.${BRANCH}.${SHORT_SHA}" + fi + echo "$VERSION" > .version + echo "Building version: $VERSION" + depends_on: [test] + + # Package as NuGet (for library distribution) + package-nuget: + image: *dotnet_image + commands: + - VERSION=$(cat .version) + - echo "Packaging version $VERSION" + - dotnet pack PaperlessMCP/PaperlessMCP.csproj --no-build -c Release -o ./artifacts /p:Version=$VERSION /p:PackageVersion=$VERSION + - ls -la ./artifacts/ + depends_on: [version] + when: + - event: [push, tag] + branch: main + + # Build and push Docker image (tags only) + package-docker: + image: *docker_image + settings: + repo: ghcr.io/barryw/paperlessmcp + dockerfile: PaperlessMCP/Dockerfile + context: PaperlessMCP + platforms: + - linux/amd64 + - linux/arm64 + tag: [latest, "${CI_COMMIT_TAG}"] + build_args: + - VERSION=${CI_COMMIT_TAG} + registry: ghcr.io + username: + from_secret: github_username + password: + from_secret: github_token + depends_on: [version] + when: + - event: tag + + # Build Docker for PRs (no push, just verify it builds) + docker-verify: + image: *docker_image + settings: + repo: ghcr.io/barryw/paperlessmcp + dockerfile: PaperlessMCP/Dockerfile + context: PaperlessMCP + platforms: + - linux/amd64 + dry_run: true + depends_on: [version] + when: + - event: pull_request + + # Create GitHub release (tags only) + release: + image: woodpeckerci/plugin-github-release + settings: + api_key: + from_secret: github_token + files: + - artifacts/*.nupkg + title: ${CI_COMMIT_TAG} + depends_on: [package-nuget, package-docker] + when: + - event: tag diff --git a/PaperlessMCP/Dockerfile b/PaperlessMCP/Dockerfile index a83ebb4..d4a5dc9 100644 --- a/PaperlessMCP/Dockerfile +++ b/PaperlessMCP/Dockerfile @@ -1,5 +1,6 @@ # Build stage FROM mcr.microsoft.com/dotnet/sdk:10.0-preview AS build +ARG VERSION=0.0.0-dev WORKDIR /src # Copy project file and restore dependencies @@ -8,12 +9,20 @@ RUN dotnet restore # Copy source code and build COPY . . -RUN dotnet publish -c Release -o /app/publish +RUN dotnet publish -c Release -o /app/publish /p:Version=${VERSION} # Runtime stage FROM mcr.microsoft.com/dotnet/aspnet:10.0-preview AS runtime +ARG VERSION=0.0.0-dev WORKDIR /app +# OCI Labels +LABEL org.opencontainers.image.title="PaperlessMCP" +LABEL org.opencontainers.image.description="Model Context Protocol server for Paperless-ngx" +LABEL org.opencontainers.image.version="${VERSION}" +LABEL org.opencontainers.image.source="https://github.com/barryw/PaperlessMCP" +LABEL org.opencontainers.image.licenses="MIT" + # Copy published application COPY --from=build /app/publish . @@ -24,9 +33,5 @@ ENV MCP_PORT=5000 # Expose port for HTTP transport EXPOSE 5000 -# Health check -HEALTHCHECK --interval=30s --timeout=10s --start-period=5s --retries=3 \ - CMD curl -f http://localhost:5000/mcp || exit 1 - # Run the application (HTTP mode by default) ENTRYPOINT ["dotnet", "PaperlessMCP.dll"] diff --git a/README.md b/README.md index 407e679..d83ef04 100644 --- a/README.md +++ b/README.md @@ -139,6 +139,25 @@ dotnet build # Build dotnet test # Run tests ``` +### CI/CD + +This project uses [Woodpecker CI](https://woodpecker-ci.org/) for continuous integration: + +| Event | Actions | +|-------|---------| +| **Push/PR** | Build → Test | +| **Push to main** | Build → Test → Package NuGet | +| **Tag (vX.Y.Z)** | Build → Test → Package → Docker → GitHub Release | + +**Versioning:** Semantic versioning via git tags. Create a release with: + +```bash +git tag v1.0.0 +git push origin v1.0.0 +``` + +**Docker Images:** `ghcr.io/barryw/paperlessmcp:latest` + ### Project Structure ```