Skip to content

ci: auto-create GitHub Release on tag push (port mcp-synology github-release job)#127

Merged
cmeans-claude-dev[bot] merged 1 commit into
mainfrom
feat/github-release-job-126
May 6, 2026
Merged

ci: auto-create GitHub Release on tag push (port mcp-synology github-release job)#127
cmeans-claude-dev[bot] merged 1 commit into
mainfrom
feat/github-release-job-126

Conversation

@cmeans-claude-dev
Copy link
Copy Markdown
Contributor

@cmeans-claude-dev cmeans-claude-dev Bot commented May 6, 2026

Closes #126. Adds a github-release job to publish.yml that runs after publish-pypi on every tag push. The job awk-extracts the matching CHANGELOG section and creates (or updates) a GitHub Release with those notes. Closes the Releases-page-drift gap that surfaced after v2.4.0 / v2.5.0 / v2.5.1 all landed on PyPI without matching GitHub Releases (manually backfilled on 2026-05-06).

What landed

.github/workflows/publish.yml:

  • New github-release job, needs: publish-pypi. Release only lands after the wheel is on PyPI.
  • permissions: contents: write enables gh release create / gh release edit.
  • Step 1 — awk-extract: anchored to ^## \[VERSION\] (literal brackets, matching the Keep-a-Changelog format mcp-clipboard uses), then strips leading blank lines, then sets a use_changelog=true/false step output.
  • Step 2 — idempotent create-or-update: if a Release for the tag already exists, edit it in place rather than failing with HTTP 422. Fall through to --generate-notes (auto commit-list) when the CHANGELOG has no matching entry.

CHANGELOG.md: [Unreleased] / ### Added entry.

Pattern source

Ported from cmeans/mcp-synology publish.yml. One CHANGELOG-format adaptation: mcp-synology uses ## 0.5.2 (2026-05-01) headings (no brackets) and its awk matcher anchors on ^## VERSION( |\(). mcp-clipboard uses Keep-a-Changelog ## [2.5.1] - 2026-05-05 (literal brackets), so the awk matcher here anchors on ^## \[VERSION\] instead. Verified locally against the live CHANGELOG: extraction returns the right notes block for v2.5.1, v2.5.0, v2.4.0, and v2.3.0.

Verification

  • uv run pytest -q: 599 passed, 19 deselected, 5 xfailed (no runtime code touched).
  • uv run ruff check src/ tests/ scripts/: clean.
  • python scripts/sync-server-json.py --check: in sync (2.5.1).
  • Local awk validation against the four most recent versions in the live CHANGELOG: each returns the correct notes block, with the next ## [ heading correctly terminating the section.

Out of scope

  • Backfilling Releases for v2.4.0 / v2.5.0 / v2.5.1 — already done manually on 2026-05-06.
  • SHA-pinning the existing actions/checkout@v6 and actions/setup-python@v6 references in publish.yml (mcp-synology uses SHA pins); that's a separate hardening pass.
  • Hand-crafted release titles like the v2.3.0 one. The awk extraction uses the bare vX.Y.Z tag as the title; richer titles can be a follow-up.

Test plan

  • CI green across lint, typecheck, test (3.11/3.12/3.13), integration-x11, version-sync, validate-server-json. (publish.yml only fires on tag push, so this PR's CI doesn't exercise the new job — it'll exercise on the next release tag.)
  • Next release tag pushed produces a GitHub Release with the matching CHANGELOG notes (no manual gh release create needed).
  • Re-running a partially-failed publish.yml doesn't crash on "release already exists"; it edits in place.

Co-Authored-By: Claude Opus 4.7 (1M context) noreply@anthropic.com

…release job)

Adds a github-release job to publish.yml that runs after publish-pypi
on every tag push. The job extracts the matching CHANGELOG section and
creates (or updates) a GitHub Release with those notes. Closes the
Releases-page-drift gap that surfaced after v2.4.0 / v2.5.0 / v2.5.1
all landed on PyPI without matching Releases (backfilled manually on
2026-05-06).

Pattern ported from cmeans/mcp-synology publish.yml with one
CHANGELOG-format adaptation:

- mcp-synology CHANGELOG uses `## VERSION (DATE)` headings (no
  brackets), and its awk matcher anchors on `^## VERSION( |\()`.
- mcp-clipboard CHANGELOG uses Keep-a-Changelog `## [VERSION] - DATE`
  headings (literal brackets). The awk matcher here anchors on
  `^## \[VERSION\]` to match. Verified locally against the live
  CHANGELOG: extraction returns the right notes block for v2.5.1,
  v2.5.0, v2.4.0, and v2.3.0.

Job shape:

- needs: publish-pypi (Release only lands after the wheel is on PyPI)
- permissions: contents: write (enables gh release create/edit)
- Step 1: awk-extract the matching CHANGELOG section to release_notes.md.
  Strip leading blank lines so the body doesn't start with an empty
  paragraph. Set use_changelog=true/false output.
- Step 2: Idempotent gh release create / edit. If a Release already
  exists for the tag (hand-crafted, or re-running a partially-failed
  publish), edit it in place rather than failing with HTTP 422. Fall
  through to --generate-notes (auto commit-list) when the CHANGELOG
  has no matching entry.

CHANGELOG entry under [Unreleased] / Added.

No runtime code change. 599 tests still pass; ruff clean; sync-server-
json --check passes.

Closes #126.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
@github-actions github-actions Bot added Awaiting CI Dev complete, waiting for CI/Codecov to pass before QA Ready for QA Dev work complete — QA can begin review and removed Awaiting CI Dev complete, waiting for CI/Codecov to pass before QA labels May 6, 2026
@codecov
Copy link
Copy Markdown

codecov Bot commented May 6, 2026

Codecov Report

✅ All modified and coverable lines are covered by tests.

📢 Thoughts on this report? Let us know!

@cmeans cmeans added the QA Active QA is actively reviewing; Dev should not push changes label May 6, 2026
@github-actions github-actions Bot removed the Ready for QA Dev work complete — QA can begin review label May 6, 2026
Copy link
Copy Markdown
Owner

@cmeans cmeans left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

QA Round 1 — Ready for QA Signoff

Single round, zero findings. The diff is faithfully ported from cmeans/mcp-synology publish.yml with the documented Keep-a-Changelog adaptation (awk anchor ^## \[VERSION\] instead of mcp-synology's ^## VERSION( |\()).

Code review. The job is small, well-commented, idempotent, and gated behind publish-pypi so the GitHub Release only lands after the wheel is on PyPI. permissions: contents: write is correctly scoped to this job only. The gh release view precheck before create vs edit handles re-runs of partially-failed publishes (avoids HTTP 422 on duplicate-tag create). The --generate-notes fallback is correctly only wired into the create branch — gh release edit cannot regenerate auto notes, so the existing body is preserved on re-run when no CHANGELOG entry exists. sed -i '/./,$!d' is the standard idiom to drop leading blank lines, so the Release body never starts with an empty paragraph.

Pattern-port fidelity. Side-by-side diff against mcp-synology publish.yml:117-180 shows: same needs:, same permissions, same fetch-depth: 0, same idempotency logic, same --generate-notes fallback. Only intentional change is the awk anchor format.

Awk extraction — live-verified against the current CHANGELOG.md:

Version Extracted lines Result
v2.5.1 17 starts with ### Fixed (mcp-name token)
v2.5.0 59 starts with ### Added (PRIMARY/Wayland + markdown + registry)
v2.4.0 52 starts with ### Fixed (macOS osascript stdin)
v2.3.0 164 starts with ### Added (reset_backend_cache helper)
v9.9.9 (negative test) 0 empty → use_changelog=false → falls through to --generate-notes

Each section terminates correctly at the next ## [ heading.

Local verification on head 41010f6:

  • uv run pytest -q599 passed, 19 deselected, 5 xfailed in 4.10s. The 19 deselected are the integration-marked tests (pyproject.toml: addopts = "-m 'not integration'"); they run separately in the integration-x11 CI job, which is green on this PR.
  • uv run ruff check src tests scripts → clean.
  • uv run mypy src → clean (4 source files).
  • python scripts/sync-server-json.py --check → in sync at 2.5.1.
  • Pre-existing 13-check CI suite all green incl codecov/patch.

Test-plan status. Checkbox 1 (CI green) is satisfied — ticked in the PR body. Checkboxes 2 (next tag push produces a Release with CHANGELOG notes) and 3 (re-run on partial failure edits in place rather than 422'ing) are post-merge verification — they only exercise on the next real vX.Y.Z tag push. Out-of-scope items in the PR body (SHA-pinning the existing @v6 action refs, hand-crafted release titles like v2.3.0:) are noted and not blocking.

Verdict: Ready for QA Signoff. Awaiting maintainer QA Approved.

@cmeans
Copy link
Copy Markdown
Owner

cmeans commented May 6, 2026

Adding Ready for QA Signoff — local verification clean (599 passed / 19 deselected integration / 5 xfailed; ruff, mypy, sync-server-json all clean), CI fully green, awk extraction live-verified against the current CHANGELOG for v2.5.1/v2.5.0/v2.4.0/v2.3.0, zero findings on round 1. Awaiting maintainer QA Approved.

@cmeans cmeans added Ready for QA Signoff QA passed — ready for maintainer final review and merge and removed QA Active QA is actively reviewing; Dev should not push changes labels May 6, 2026
Copy link
Copy Markdown
Owner

@cmeans cmeans left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM

@cmeans cmeans added QA Approved Manual QA testing completed and passed and removed Ready for QA Signoff QA passed — ready for maintainer final review and merge labels May 6, 2026
@cmeans-claude-dev cmeans-claude-dev Bot merged commit c19eadb into main May 6, 2026
35 checks passed
@cmeans-claude-dev cmeans-claude-dev Bot deleted the feat/github-release-job-126 branch May 6, 2026 16:35
@cmeans-claude-dev cmeans-claude-dev Bot mentioned this pull request May 7, 2026
5 tasks
cmeans-claude-dev Bot added a commit that referenced this pull request May 7, 2026
QA reviewer flagged the bump level: this release contains two `### Added` entries (CLI flags --version/--help/--check via #134, and the github-release job via #127), both of which are backwards-compatible feature additions. Per semver §6, that's a MINOR bump trigger, not patch.

This repo's own precedent confirms the convention:

- v2.5.0 (2026-05-05): minor bump for the same release shape (Added + Changed + Fixed aggregated). Direct precedent.
- v2.5.1 (2026-05-05): patch bump for fix-only release.
- v2.5.2 (proposed): mismatched -- patch bump for an Added-bearing release.

Concrete consumer impact of getting it wrong: a `~=2.5.0` pin would silently auto-upgrade across this release and pull in the new --check surface unannounced. With 2.6.0, that pin correctly does not match.

Mechanical fix:
- pyproject.toml: version "2.5.2" -> "2.6.0"
- server.json: top-level + packages[0] versions synced to 2.6.0 via scripts/sync-server-json.py
- CHANGELOG.md: heading "## [2.5.2] - 2026-05-07" -> "## [2.6.0] - 2026-05-07"

Branch name `release/v2.5.2` is now a cosmetic mismatch -- preserved to keep PR #135's QA review history intact rather than re-opening a fresh PR. The squash-merge will collapse this commit into the release commit and the branch will be deleted.

Local verification at this commit:
- python scripts/sync-server-json.py --check: in sync at 2.6.0
- uv run pytest -q: 610 passed, 19 deselected, 5 xfailed
- uv run ruff check src tests: clean
- uv run mypy src: clean
- uv build --wheel: dist/mcp_clipboard-2.6.0-py3-none-any.whl built cleanly
- grep "2.5.2" across pyproject.toml + server.json + CHANGELOG.md: zero hits

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
cmeans-claude-dev Bot added a commit that referenced this pull request May 7, 2026
Aggregates the changes that landed on `main` since v2.5.1:

- fix(windows): UTF-8 stdin encoding on PowerShell write paths so non-ASCII characters (em dash, curly quotes, non-Latin scripts) are no longer corrupted by clipboard_copy / clipboard_copy_markdown. Reproduced and verified on a QEMU Windows guest. Closes #129 via #131.
- feat(cli): --version, --help, --check flags on the mcp-clipboard binary so users can verify their install before any MCP-host wiring. --check exits 1 with a stderr diagnostic on failure so it doubles as a CI smoke check. Closes #130 via #134.
- ci: github-release job in publish.yml auto-creates the GitHub Release on tag push, with notes pulled from the matching CHANGELOG section. v2.6.0 is the first firing of this workflow on a real release. Closes #126 via #127.
- docs: README ## Setup rewritten into a five-step quick-start that acknowledges both pipx and uv as install runners; platform status corrected to reflect that Windows has been exercised on a QEMU guest as of v2.5.x. Closes #130 via #134.

Bumped MINOR per semver: this release contains backwards-compatible Added entries (CLI flags + github-release job), matching v2.5.0's precedent for the same release shape.

Three-file release commit per repo convention: pyproject.toml + server.json (synced) + CHANGELOG.md.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

QA Approved Manual QA testing completed and passed

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Auto-create GitHub Release on tag push (port mcp-synology github-release job)

1 participant