feat: add Woodpecker CI pipeline, Dockerfile, and k8s manifests #4

Closed
forgejo_admin wants to merge 3 commits from 3-add-woodpecker-ci-pipeline-dockerfile-an into main
Contributor

Summary

  • Add CI/CD and Kubernetes deployment infrastructure for notion-mcp-remote service
  • Implements Phase 3+4 of the MCP Gateway Migration plan: service pipeline + k8s manifests
  • Standardize codebase with ruff linting and formatting

Changes

  • Dockerfile: Multi-stage build with private PyPI registry support via PIP_INDEX_URL build arg
  • .woodpecker.yml: Lint step (ruff check + format) on push/PR; kaniko build-and-push to Harbor on push to main
  • k8s/deployment.yaml: Deployment (with health probes, resource limits, PVC mount, env from k8s Secret), Service (port 8000), and PersistentVolumeClaim (1Gi)
  • k8s/servicemonitor.yaml: Prometheus ServiceMonitor scraping /metrics every 30s
  • k8s/kustomization.yaml: Kustomize resource list
  • .dockerignore: Exclude .git, k8s/, caches from container image
  • pyproject.toml: Add [tool.ruff] config (target-version py310, line-length 120, select E/F/W/I) and ruff dev dependency
  • server.py: Reorganize imports with isort: off/on guards for load-order-sensitive imports; ruff format applied
  • client_patch.py: ruff format applied (line length adjustments)
  • qa_test.py: ruff format applied (line length adjustments)

Test Plan

  • ruff check . passes with zero errors
  • ruff format --check . passes with zero reformats
  • Woodpecker lint step passes on PR event
  • Container builds successfully with kaniko on push to main
  • ArgoCD picks up k8s manifests after Terraform onboarding (Phase 4)

Review Checklist

  • No secrets committed
  • No unnecessary file changes
  • Commit messages are descriptive
  • Dockerfile uses multi-stage build pattern
  • No namespace in k8s manifests (ArgoCD controls placement)
  • Resource limits set on deployment
  • plan-2026-02-25-mcp-gateway-migration -- Phase 3 (Remote Service Pipelines) and Phase 4 (Terraform Onboarding)
  • Closes #3
  • service-onboarding-sop -- service onboarding pattern followed
  • namespace-conventions -- no namespace in k8s manifests per convention
## Summary - Add CI/CD and Kubernetes deployment infrastructure for notion-mcp-remote service - Implements Phase 3+4 of the MCP Gateway Migration plan: service pipeline + k8s manifests - Standardize codebase with ruff linting and formatting ## Changes - `Dockerfile`: Multi-stage build with private PyPI registry support via `PIP_INDEX_URL` build arg - `.woodpecker.yml`: Lint step (ruff check + format) on push/PR; kaniko build-and-push to Harbor on push to main - `k8s/deployment.yaml`: Deployment (with health probes, resource limits, PVC mount, env from k8s Secret), Service (port 8000), and PersistentVolumeClaim (1Gi) - `k8s/servicemonitor.yaml`: Prometheus ServiceMonitor scraping /metrics every 30s - `k8s/kustomization.yaml`: Kustomize resource list - `.dockerignore`: Exclude .git, k8s/, caches from container image - `pyproject.toml`: Add `[tool.ruff]` config (target-version py310, line-length 120, select E/F/W/I) and ruff dev dependency - `server.py`: Reorganize imports with `isort: off/on` guards for load-order-sensitive imports; ruff format applied - `client_patch.py`: ruff format applied (line length adjustments) - `qa_test.py`: ruff format applied (line length adjustments) ## Test Plan - [x] `ruff check .` passes with zero errors - [x] `ruff format --check .` passes with zero reformats - [ ] Woodpecker lint step passes on PR event - [ ] Container builds successfully with kaniko on push to main - [ ] ArgoCD picks up k8s manifests after Terraform onboarding (Phase 4) ## Review Checklist - [x] No secrets committed - [x] No unnecessary file changes - [x] Commit messages are descriptive - [x] Dockerfile uses multi-stage build pattern - [x] No namespace in k8s manifests (ArgoCD controls placement) - [x] Resource limits set on deployment ## Related Notes - `plan-2026-02-25-mcp-gateway-migration` -- Phase 3 (Remote Service Pipelines) and Phase 4 (Terraform Onboarding) - Closes #3 - `service-onboarding-sop` -- service onboarding pattern followed - `namespace-conventions` -- no namespace in k8s manifests per convention
feat: add Woodpecker CI pipeline, Dockerfile, and k8s manifests
All checks were successful
ci/woodpecker/pr/woodpecker Pipeline was successful
195f890a7f
Add CI/CD and deployment infrastructure for notion-mcp-remote:

- Dockerfile: multi-stage build with private PyPI support (PIP_INDEX_URL)
- .woodpecker.yml: lint step (ruff) on push/PR, kaniko build+push to
  Harbor on push to main
- k8s/deployment.yaml: Deployment, Service, and PVC with health probes,
  resource limits, and env vars from k8s Secret
- k8s/servicemonitor.yaml: Prometheus ServiceMonitor for /metrics
- k8s/kustomization.yaml: Kustomize resource list
- .dockerignore: exclude .git, k8s, caches from image
- pyproject.toml: add ruff config and dev dependency
- server.py, client_patch.py, qa_test.py: ruff format applied

Implements Phase 3+4 of plan-2026-02-25-mcp-gateway-migration.
Closes #3.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Author
Contributor

PR #4 Review

BLOCKERS

1. Missing strategy: Recreate on Deployment (k8s/deployment.yaml)
The PVC notion-mcp-data uses accessModes: [ReadWriteOnce], which means only one pod can mount it at a time. The default deployment strategy is RollingUpdate, which spins up a new pod before terminating the old one. With an RWO PVC, the new pod will fail to schedule because the volume is still mounted by the old pod. Add under spec::

spec:
  replicas: 1
  strategy:
    type: Recreate

NITS

None.

SOP COMPLIANCE

  • Branch named after issue (3-add-woodpecker-ci-pipeline-dockerfile-an references issue #3)
  • PR body follows template-pr-body (Summary, Changes, Test Plan, Review Checklist, Related Notes all present)
  • Related Notes references the plan slug (plan-2026-02-25-mcp-gateway-migration)
  • No secrets, .env files, or credentials committed
  • No unnecessary file changes (formatting changes in server.py, client_patch.py, qa_test.py are ruff-related, within scope)
  • Commit messages are descriptive

CODE REVIEW SUMMARY

Area Status Notes
Dockerfile PASS Multi-stage, PIP_INDEX_URL/PIP_EXTRA_INDEX_URL as ARGs, no creds in final image, correct CMD and EXPOSE
.woodpecker.yml PASS Correct repo, ruff pinned to 0.15.2, kaniko build+push, Harbor secrets, path exclude for .argocd-source-*, Forgejo creds via environment+from_secret
k8s/deployment.yaml BLOCKER Missing strategy: Recreate (required for RWO PVC). All other items correct: no namespace, correct image, harbor-creds, port 8000, health probes, resource limits, env from secret, PVC mount
k8s/kustomization.yaml PASS Lists all resources
k8s/servicemonitor.yaml PASS Named port http, /metrics, 30s interval
.dockerignore PASS Excludes .git, k8s/, .woodpecker.yml
pyproject.toml (ruff) PASS py310, 120 line length, E/F/W/I select
server.py PASS Import reorder with isort guards is intentional for load-order sensitivity
Security PASS No secrets, credentials, or .env files in diff

VERDICT: NOT APPROVED

One blocker: missing strategy: Recreate on the Deployment. Fix that and this is ready for a clean pass.

## PR #4 Review ### BLOCKERS **1. Missing `strategy: Recreate` on Deployment** (`k8s/deployment.yaml`) The PVC `notion-mcp-data` uses `accessModes: [ReadWriteOnce]`, which means only one pod can mount it at a time. The default deployment strategy is `RollingUpdate`, which spins up a new pod before terminating the old one. With an RWO PVC, the new pod will fail to schedule because the volume is still mounted by the old pod. Add under `spec:`: ```yaml spec: replicas: 1 strategy: type: Recreate ``` ### NITS None. ### SOP COMPLIANCE - [x] Branch named after issue (`3-add-woodpecker-ci-pipeline-dockerfile-an` references issue #3) - [x] PR body follows `template-pr-body` (Summary, Changes, Test Plan, Review Checklist, Related Notes all present) - [x] Related Notes references the plan slug (`plan-2026-02-25-mcp-gateway-migration`) - [x] No secrets, .env files, or credentials committed - [x] No unnecessary file changes (formatting changes in `server.py`, `client_patch.py`, `qa_test.py` are ruff-related, within scope) - [x] Commit messages are descriptive ### CODE REVIEW SUMMARY | Area | Status | Notes | |------|--------|-------| | Dockerfile | PASS | Multi-stage, PIP_INDEX_URL/PIP_EXTRA_INDEX_URL as ARGs, no creds in final image, correct CMD and EXPOSE | | .woodpecker.yml | PASS | Correct repo, ruff pinned to 0.15.2, kaniko build+push, Harbor secrets, path exclude for .argocd-source-*, Forgejo creds via environment+from_secret | | k8s/deployment.yaml | **BLOCKER** | Missing `strategy: Recreate` (required for RWO PVC). All other items correct: no namespace, correct image, harbor-creds, port 8000, health probes, resource limits, env from secret, PVC mount | | k8s/kustomization.yaml | PASS | Lists all resources | | k8s/servicemonitor.yaml | PASS | Named port `http`, /metrics, 30s interval | | .dockerignore | PASS | Excludes .git, k8s/, .woodpecker.yml | | pyproject.toml (ruff) | PASS | py310, 120 line length, E/F/W/I select | | server.py | PASS | Import reorder with isort guards is intentional for load-order sensitivity | | Security | PASS | No secrets, credentials, or .env files in diff | ### VERDICT: NOT APPROVED One blocker: missing `strategy: Recreate` on the Deployment. Fix that and this is ready for a clean pass.
fix: add strategy Recreate to deployment for RWO PVC compatibility
All checks were successful
ci/woodpecker/pr/woodpecker Pipeline was successful
7219230cac
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Author
Contributor

Fixed: added strategy: Recreate to Deployment spec. Required because the PVC uses ReadWriteOnce — RollingUpdate would deadlock waiting for the old pod to release the volume.

Fixed: added `strategy: Recreate` to Deployment spec. Required because the PVC uses ReadWriteOnce — RollingUpdate would deadlock waiting for the old pod to release the volume.
Author
Contributor

PR #4 Review (Fresh Re-review)

BLOCKERS

None. All previous blockers have been resolved.

NITS

  1. .woodpecker.yml -- ${CI_COMMIT_SHA} curly braces in plugin settings (lines 24, 27)
    The deployment-lessons SOP states: "Use $CI_COMMIT_SHA (without curly braces) in plugin settings. Curly braces (${CI_COMMIT_SHA}) conflict with Woodpecker's compiler."
    Used in two places:

    • tags: ${CI_COMMIT_SHA} (line 24)
    • BUILD_SHA=${CI_COMMIT_SHA} (line 27, inside build_args)

    Recommendation: change to $CI_COMMIT_SHA in both locations. However, since Woodpecker behavior may vary by version and this is a documented-but-not-always-fatal issue, flagging as nit rather than blocker. Verify in first pipeline run.

  2. pyproject.toml -- URLs still point to GitHub
    Homepage, Repository, and Issues URLs reference https://github.com/ldraney/notion-mcp-remote. The repo now lives on Forgejo. Pre-existing issue, not introduced by this PR -- no action required for merge.

SOP COMPLIANCE

  • Branch named after issue (3-add-woodpecker-ci-pipeline-dockerfile-an references issue #3)
  • PR body follows template-pr-body (all 5 sections: Summary, Changes, Test Plan, Review Checklist, Related Notes)
  • Related Notes references plan slug (plan-2026-02-25-mcp-gateway-migration)
  • No secrets, .env files, or credentials committed
  • No unnecessary file changes (formatting changes in server.py, client_patch.py, qa_test.py are ruff-related and in scope)
  • Commit messages are descriptive
  • No namespace in k8s manifests (per namespace-conventions)

DETAILED CHECKLIST

Item Status Notes
Dockerfile multi-stage PASS build stage + final python:3.12-slim
Dockerfile PIP_INDEX_URL/PIP_EXTRA_INDEX_URL ARGs PASS Both present, PIP_EXTRA_INDEX_URL defaults to pypi.org
Dockerfile no creds in final image PASS Creds only in build stage
Dockerfile CMD python server.py PASS CMD ["python", "server.py"]
Dockerfile EXPOSE 8000 PASS Present
.woodpecker.yml repo notion-mcp-remote/server PASS Correct Harbor repo path
.woodpecker.yml PIP_INDEX_URL build arg PASS Passed via build_args with $$ secret interpolation
.woodpecker.yml path exclude .argocd-source-* PASS k8s/.argocd-source-* excluded
.woodpecker.yml harbor secrets PASS harbor_username and harbor_password from_secret
.woodpecker.yml ruff pinned PASS ruff==0.15.2
.woodpecker.yml no || true PASS None found
k8s/deployment.yaml NO namespace PASS No namespace field
k8s/deployment.yaml correct image PASS harbor.tail5b443a.ts.net/notion-mcp-remote/server:latest
k8s/deployment.yaml imagePullSecrets PASS harbor-creds
k8s/deployment.yaml port 8000 PASS containerPort, service port, targetPort all 8000
k8s/deployment.yaml health probes PASS Liveness + readiness on /health:8000
k8s/deployment.yaml resource limits PASS 256Mi memory limit (matches deployment-lessons recommendation)
k8s/deployment.yaml strategy: Recreate PASS Present (previous blocker -- now fixed)
k8s/deployment.yaml env from secret PASS 5 env vars from notion-mcp-remote secret
k8s/deployment.yaml PVC at /app/data PASS notion-mcp-data 1Gi RWO mounted at /app/data
k8s/kustomization.yaml lists all resources PASS deployment.yaml + servicemonitor.yaml
k8s/servicemonitor.yaml port http PASS Named port http, path /metrics, interval 30s
.dockerignore proper exclusions PASS .git, pycache, k8s/, .woodpecker.yml excluded
Ruff config in pyproject.toml PASS py310, line-length 120, E/F/W/I
Port 8000 consistent PASS Dockerfile, deployment, service all aligned

VERDICT: APPROVED

Clean pass. All checklist items verified. The ${CI_COMMIT_SHA} curly brace usage is a minor SOP compliance nit -- monitor during first pipeline run and fix if Woodpecker compiler chokes. Ready for merge.

## PR #4 Review (Fresh Re-review) ### BLOCKERS **None.** All previous blockers have been resolved. ### NITS 1. **`.woodpecker.yml` -- `${CI_COMMIT_SHA}` curly braces in plugin settings (lines 24, 27)** The `deployment-lessons` SOP states: "Use `$CI_COMMIT_SHA` (without curly braces) in plugin settings. Curly braces (`${CI_COMMIT_SHA}`) conflict with Woodpecker's compiler." Used in two places: - `tags: ${CI_COMMIT_SHA}` (line 24) - `BUILD_SHA=${CI_COMMIT_SHA}` (line 27, inside `build_args`) Recommendation: change to `$CI_COMMIT_SHA` in both locations. However, since Woodpecker behavior may vary by version and this is a documented-but-not-always-fatal issue, flagging as nit rather than blocker. Verify in first pipeline run. 2. **`pyproject.toml` -- URLs still point to GitHub** `Homepage`, `Repository`, and `Issues` URLs reference `https://github.com/ldraney/notion-mcp-remote`. The repo now lives on Forgejo. Pre-existing issue, not introduced by this PR -- no action required for merge. ### SOP COMPLIANCE - [x] Branch named after issue (`3-add-woodpecker-ci-pipeline-dockerfile-an` references issue #3) - [x] PR body follows `template-pr-body` (all 5 sections: Summary, Changes, Test Plan, Review Checklist, Related Notes) - [x] Related Notes references plan slug (`plan-2026-02-25-mcp-gateway-migration`) - [x] No secrets, .env files, or credentials committed - [x] No unnecessary file changes (formatting changes in `server.py`, `client_patch.py`, `qa_test.py` are ruff-related and in scope) - [x] Commit messages are descriptive - [x] No namespace in k8s manifests (per `namespace-conventions`) ### DETAILED CHECKLIST | Item | Status | Notes | |------|--------|-------| | Dockerfile multi-stage | PASS | `build` stage + final `python:3.12-slim` | | Dockerfile PIP_INDEX_URL/PIP_EXTRA_INDEX_URL ARGs | PASS | Both present, PIP_EXTRA_INDEX_URL defaults to pypi.org | | Dockerfile no creds in final image | PASS | Creds only in build stage | | Dockerfile CMD python server.py | PASS | `CMD ["python", "server.py"]` | | Dockerfile EXPOSE 8000 | PASS | Present | | .woodpecker.yml repo notion-mcp-remote/server | PASS | Correct Harbor repo path | | .woodpecker.yml PIP_INDEX_URL build arg | PASS | Passed via build_args with `$$` secret interpolation | | .woodpecker.yml path exclude .argocd-source-* | PASS | `k8s/.argocd-source-*` excluded | | .woodpecker.yml harbor secrets | PASS | `harbor_username` and `harbor_password` from_secret | | .woodpecker.yml ruff pinned | PASS | `ruff==0.15.2` | | .woodpecker.yml no `\|\| true` | PASS | None found | | k8s/deployment.yaml NO namespace | PASS | No namespace field | | k8s/deployment.yaml correct image | PASS | `harbor.tail5b443a.ts.net/notion-mcp-remote/server:latest` | | k8s/deployment.yaml imagePullSecrets | PASS | `harbor-creds` | | k8s/deployment.yaml port 8000 | PASS | containerPort, service port, targetPort all 8000 | | k8s/deployment.yaml health probes | PASS | Liveness + readiness on /health:8000 | | k8s/deployment.yaml resource limits | PASS | 256Mi memory limit (matches deployment-lessons recommendation) | | k8s/deployment.yaml strategy: Recreate | PASS | Present (previous blocker -- now fixed) | | k8s/deployment.yaml env from secret | PASS | 5 env vars from `notion-mcp-remote` secret | | k8s/deployment.yaml PVC at /app/data | PASS | `notion-mcp-data` 1Gi RWO mounted at /app/data | | k8s/kustomization.yaml lists all resources | PASS | deployment.yaml + servicemonitor.yaml | | k8s/servicemonitor.yaml port http | PASS | Named port `http`, path /metrics, interval 30s | | .dockerignore proper exclusions | PASS | .git, __pycache__, k8s/, .woodpecker.yml excluded | | Ruff config in pyproject.toml | PASS | py310, line-length 120, E/F/W/I | | Port 8000 consistent | PASS | Dockerfile, deployment, service all aligned | ### VERDICT: APPROVED Clean pass. All checklist items verified. The `${CI_COMMIT_SHA}` curly brace usage is a minor SOP compliance nit -- monitor during first pipeline run and fix if Woodpecker compiler chokes. Ready for merge.
fix: remove curly braces from CI_COMMIT_SHA for Woodpecker compatibility
All checks were successful
ci/woodpecker/pr/woodpecker Pipeline was successful
72f5f6873a
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
forgejo_admin closed this pull request 2026-03-03 04:15:54 +00:00
All checks were successful
ci/woodpecker/pr/woodpecker Pipeline was successful

Pull request closed

Sign in to join this conversation.
No description provided.