Plan: Forgejo PyPI Migration — CI Pipelines for All Python Repos

plan-2026-03-01-forgejo-pypi-migration Plan

plan complete

Vision

Every Python repo on the platform has a Woodpecker CI pipeline that lints, tests, builds, and publishes to Forgejo's private PyPI registry. No more public pypi.org leaks. One pattern, all repos.

Projects & Repos Touched

Repo Package Origin Woodpecker Phase
forgejo-sdk ldraney-forgejo-sdk Forgejo Activated (ID 17) 1 DONE
forgejo-mcp ldraney-forgejo-mcp Forgejo Activated (ID 18) 1 DONE
pal-e-auth pal-e-auth-ldraney Forgejo Activated (ID 4) 1 DONE
pal-e-docs-mcp pal-e-docs-mcp Forgejo Activated (ID 19) 1 DONE
gcal-sdk gcal-sdk-ldraney Forgejo Activated (ID 12) 2 DONE
gmail-sdk gmail-sdk-ldraney Forgejo Activated (ID 10) 2 DONE
linkedin-sdk ldraney-linkedin-sdk Forgejo Activated (ID 14) 2 DONE
notion-sdk notion-sdk-ldraney Forgejo Activated (ID 8) 2 DONE
notion-mcp ldraney-notion-mcp Forgejo Activated (ID 9) 2 DONE
gcal-mcp gcal-mcp-ldraney Forgejo Activated (ID 13) 2 DONE
gmail-mcp gmail-mcp-ldraney Forgejo Activated (ID 11) 2 DONE
linkedin-mcp-scheduler linkedin-mcp-scheduler-ldraney Forgejo Activated (ID 15) 2 DONE
mcp-remote-auth mcp-remote-auth-ldraney Forgejo Activated (ID 7) 2 DONE
gcal-mcp-remote gcal-mcp-remote-ldraney GitHub Not activated 3
gmail-mcp-remote gmail-mcp-remote-ldraney GitHub Not activated 3
linkedin-scheduler-remote linkedin-scheduler-remote-ldraney GitHub Not activated 3
notion-mcp-remote notion-mcp-remote-ldraney GitHub Not activated 3
Not included: ebay-sdk and ebay-oauth are not on Forgejo at all. They can be migrated later if needed. woodpecker-sdk is already done (reference implementation).

Context

woodpecker-sdk just shipped the proven pipeline pattern: .woodpecker.yml (lint/test/publish), ruff config in pyproject.toml, Forgejo PyPI publishing via twine, global Woodpecker secrets for auth. PR #6 merged after 3 QA rounds. The pattern is validated and ready to roll out.
22 packages are currently on public pypi.org with no reason to be public. The Forgejo PyPI registry is live and proven. This plan applies the woodpecker-sdk pipeline template to every remaining Python repo.
What's already done:
  • [x] Forgejo PyPI registry verified and operational
  • [x] ~/.pypirc configured for Forgejo
  • [x] Woodpecker global secrets set: forgejo_publish_user, forgejo_publish_token, forgejo_pypi_url, forgejo_url, forgejo_user, forgejo_password
  • [x] woodpecker-sdk pipeline template proven (PR #6, plan-2026-02-28-woodpecker-sdk-mcp Phase 2)
  • [x] All Phase 1 + Phase 2 repos activated in Woodpecker (16 of 16 non-remote repos)
  • [x] Phase 1 complete: 4/4 repos merged, pipelines green, packages published to Forgejo PyPI
  • [x] Phase 2 complete: 9/9 repos merged, all pipelines green, packages published to Forgejo PyPI (2026-03-01)
  • [x] All 13 packages verified installable from Forgejo PyPI via pip install --index-url (2026-03-01)

Previous Plan

plan-2026-02-28-woodpecker-sdk-mcp — Phase 2 established the pattern

Depends On

None — pattern is proven, global secrets are set, registry is live

Decisions Made

Decision Rationale
Reuse woodpecker-sdk pipeline template exactly Proven across 3 QA rounds. Lint (ruff pinned), test (pytest), publish (build + twine, main-only). Adapt test step per repo (some repos have tests, some don't).
Repos with GitHub origin: switch to Forgejo Woodpecker watches Forgejo. Local clones pointing at GitHub need origin updated to Forgejo. Forgejo repos already exist for all of these (mirrors or manual creates).
Phase by readiness, not by service Phase 1 = Forgejo-native repos (ready now, parallel agents). Phase 2 = GitHub-origin repos (need origin switch first). Phase 3 = remote MCP proxies (lowest priority, may be deprecated).
Parallel agents per phase Each repo gets its own agent in a worktree. The work is identical and independent — perfect for parallelization. One issue per repo, one PR per repo.
Skip tests step if repo has no tests Some repos (especially older MCPs) may not have a tests/ directory. Pipeline should still lint and publish, just skip the test step.
Ruff config: same rules everywhere py310, line-length 120, E/F/W/I. Matches woodpecker-sdk and <code>todo-ruff-standardization</code>.
Delete old .woodpecker.yaml files pal-e-auth had both .woodpecker.yaml (old pypi.org) and .woodpecker.yml (new Forgejo). Woodpecker loads both, causing secret conflicts. Must delete old files.
Clean up stale repo-level pypi_token secrets 10 repos had old pypi_token repo-level secrets from pypi.org era. Must be cleaned up before Phase 2 to avoid the same issue.

Phases

Phase 1 — Forgejo-native repos (COMPLETE)

Slug: phase-2026-03-01-1-forgejo-native
Status: COMPLETE — 4/4 repos merged, all pipelines verified green, all packages published to Forgejo PyPI.
Results:
Repo Issue PR Pipeline Notes
forgejo-sdk <a href="https://forgejo.tail5b443a.ts.net/forgejo_admin/forgejo-sdk/issues/1">#1</a> <a href="https://forgejo.tail5b443a.ts.net/forgejo_admin/forgejo-sdk/pulls/2">#2 merged</a> #3 all green 40/40 tests, published to Forgejo PyPI
forgejo-mcp <a href="https://forgejo.tail5b443a.ts.net/forgejo_admin/forgejo-mcp/issues/1">#1</a> <a href="https://forgejo.tail5b443a.ts.net/forgejo_admin/forgejo-mcp/pulls/2">#2 merged</a> #1 lint+publish OK, #4 409 (version exists) No tests, version bump needed for re-publish
pal-e-auth <a href="https://forgejo.tail5b443a.ts.net/forgejo_admin/pal-e-auth/issues/7">#7</a> <a href="https://forgejo.tail5b443a.ts.net/forgejo_admin/pal-e-auth/pulls/8">#8 merged</a> #8 all green 29/29 tests, old .woodpecker.yaml deleted, published to Forgejo PyPI
pal-e-docs-mcp <a href="https://forgejo.tail5b443a.ts.net/forgejo_admin/pal-e-docs-mcp/issues/3">#3</a> <a href="https://forgejo.tail5b443a.ts.net/forgejo_admin/pal-e-docs-mcp/pulls/4">#4 merged</a> #1 all green No tests, published to Forgejo PyPI
Lessons learned:
  • Repos activated BEFORE merge miss the push webhook — use Forgejo API commit to trigger
  • Old .woodpecker.yaml files cause dual-config loading — MUST delete before adding .woodpecker.yml
  • Stale repo-level pypi_token secrets cause pipeline errors when deleted but still referenced
  • forgejo-sdk tests require forgejo_url/forgejo_user/forgejo_password secrets — added as globals
  • Poetry-core build backend doesn't expose dev deps as pip extras — test step needs explicit pip install
  • 409 Conflict on publish = version already exists in registry (expected, bump version to fix)
  • Woodpecker repos can be activated via API: POST /api/repos?forge_remote_id={id} (no UI needed)
  • Woodpecker secret hierarchy: global > org > repo (most specific wins). Global secrets are ideal for shared Forgejo PyPI creds.

Phase 2a — Pre-work (COMPLETE)

Status: COMPLETE — all pre-work done 2026-03-01
Discovery: All 9 Phase 2 repos already had old-pattern .woodpecker.yaml pipelines publishing to pypi.org via pypi_token, plus ruff formatting already applied in prior work. The actual Phase 2 work is narrower than initially scoped: delete old .woodpecker.yaml, create new .woodpecker.yml (Forgejo PyPI pattern), and add [tool.ruff] config to pyproject.toml. All 9 repos have tests/ directories and pyproject.toml.
Completed:
  • [x] Deleted stale pypi_token repo-level secrets from all 9 repos (Woodpecker IDs 7-15). Non-pypi secrets (notion_api_key, google_oauth_token, linkedin_*) left in place for test steps.
  • [x] Checked for old .woodpecker.yaml files — confirmed present in all 9 repos (deleted by agents as part of pipeline swap)
  • [x] Switched local git origin from GitHub to Forgejo for 6 cloned repos: gcal-sdk, linkedin-sdk, notion-mcp, gmail-mcp, linkedin-mcp-scheduler, mcp-remote-auth
  • [x] Cloned 3 missing repos from Forgejo: gmail-sdk, notion-sdk, gcal-mcp
  • [x] Pulled latest from Forgejo on all 9 repos — all local clones now current with Forgejo main

Phase 2b — Pipeline swap (COMPLETE)

Slug: phase-2026-03-01-2-github-origin
Goal: Replace old pypi.org .woodpecker.yaml with new Forgejo PyPI .woodpecker.yml on all 9 repos. Add [tool.ruff] config. Fix lint issues.
Owner: Agent (parallel — one agent per repo, 9 agents spawned simultaneously)
Status: COMPLETE — 9/9 PRs merged, all pipelines green, all packages published to Forgejo PyPI.
Results:
Repo Issue PR Pipeline Notes
gcal-sdk <a href="https://forgejo.tail5b443a.ts.net/forgejo_admin/gcal-sdk/issues/6">#6</a> <a href="https://forgejo.tail5b443a.ts.net/forgejo_admin/gcal-sdk/pulls/7">#7 merged</a> #12 success google_oauth_token secret in test step
gmail-sdk <a href="https://forgejo.tail5b443a.ts.net/forgejo_admin/gmail-sdk/issues/4">#4</a> <a href="https://forgejo.tail5b443a.ts.net/forgejo_admin/gmail-sdk/pulls/5">#5 merged</a> #9 success
linkedin-sdk <a href="https://forgejo.tail5b443a.ts.net/forgejo_admin/linkedin-sdk/issues/6">#6</a> <a href="https://forgejo.tail5b443a.ts.net/forgejo_admin/linkedin-sdk/pulls/7">#7 merged</a> #14 success linkedin_access_token, linkedin_person_id in test step
notion-sdk <a href="https://forgejo.tail5b443a.ts.net/forgejo_admin/notion-sdk/issues/8">#8</a> <a href="https://forgejo.tail5b443a.ts.net/forgejo_admin/notion-sdk/pulls/9">#9 merged</a> #15 success notion_api_key secret in test step
notion-mcp <a href="https://forgejo.tail5b443a.ts.net/forgejo_admin/notion-mcp/issues/4">#4</a> <a href="https://forgejo.tail5b443a.ts.net/forgejo_admin/notion-mcp/pulls/5">#5 merged</a> #8 success
gcal-mcp <a href="https://forgejo.tail5b443a.ts.net/forgejo_admin/gcal-mcp/issues/4">#4</a> <a href="https://forgejo.tail5b443a.ts.net/forgejo_admin/gcal-mcp/pulls/5">#5 merged</a> #8 success
gmail-mcp <a href="https://forgejo.tail5b443a.ts.net/forgejo_admin/gmail-mcp/issues/4">#4</a> <a href="https://forgejo.tail5b443a.ts.net/forgejo_admin/gmail-mcp/pulls/5">#5 merged</a> #8 success Fixed [dependency-groups] -&gt; [project.optional-dependencies]
linkedin-mcp-scheduler <a href="https://forgejo.tail5b443a.ts.net/forgejo_admin/linkedin-mcp-scheduler/issues/4">#4</a> <a href="https://forgejo.tail5b443a.ts.net/forgejo_admin/linkedin-mcp-scheduler/pulls/5">#5 merged</a> #9 success
mcp-remote-auth <a href="https://forgejo.tail5b443a.ts.net/forgejo_admin/mcp-remote-auth/issues/4">#4</a> <a href="https://forgejo.tail5b443a.ts.net/forgejo_admin/mcp-remote-auth/pulls/5">#5 merged</a> #8 success

Phase 3 — Remote MCP proxies (low priority, may deprecate)

Slug: phase-2026-03-01-3-remote-proxies
Goal: Evaluate whether the -remote MCP proxy repos are still needed. If yes, apply pipeline. If deprecated, archive.
Owner: Agent
Issue: TBD
Status: Not started
Repos: gcal-mcp-remote, gmail-mcp-remote, linkedin-scheduler-remote, notion-mcp-remote

Phase 4 — Yank public pypi.org packages + update references

Slug: phase-2026-03-01-4-yank-and-update
Goal: Yank all 22 packages on pypi.org to signal deprecation. Update all pip install references across the platform to use Forgejo's --index-url.
Owner: Main session + Agent
Issue: TBD
Status: Not started

Key Files

Phase File Repo Change
1-2 .woodpecker.yml Each repo CI pipeline (copied from woodpecker-sdk template)
1-2 pyproject.toml Each repo Add [tool.ruff] section
1-2 src/**/*.py, tests/**/*.py Each repo Ruff format fixes (mechanical)
4 Various config files Platform-wide Update pip install references to Forgejo index

Verification

  • [x] All Phase 1 repos (4/4) have .woodpecker.yml + ruff config + published to Forgejo PyPI
  • [x] Woodpecker pipelines green on all Phase 1 repos
  • [x] pal-e-docs-mcp PR reviewed, merged, pipeline verified
  • [x] Phase 2a pre-work complete: secrets cleaned, origins switched, repos cloned
  • [x] Phase 2b: 9/9 PRs opened, QA-reviewed, merged
  • [x] Phase 2b: All 9 Woodpecker pipelines green, packages published to Forgejo PyPI
  • [x] pip install --index-url {forgejo} {package} verified for all 13 packages (2026-03-01)
  • [ ] Public pypi.org packages yanked (Phase 4)

Next Plan Seeds

  • Automated version bumping (tag-based release across all repos)
  • Shared .woodpecker.yml template repo (DRY pipeline config)
  • Local pip config to default to Forgejo index (pip config set global.index-url)
  • plan-2026-02-28-woodpecker-sdk-mcp — Phase 2 established the pattern (reference implementation)
  • todo-forgejo-pypi — originating TODO
  • todo-ruff-standardization — ruff config standardization (achieved by this plan)
  • plan-2026-02-25-mcp-gateway-migration — may deprecate Phase 3 repos
  • service-onboarding-sop — container images → Harbor, Python packages → Forgejo