Repository navigation
feat: create initial release workflow for automated pypi publishing - #28
Conversation
There was a problem hiding this comment.
Pull request overview
Note
Copilot was unable to run its full agentic suite in this review.
Adds a documented and automated release process to publish kubeflow-mcp to Test PyPI (manual) and PyPI (on GitHub Release), using PyPI trusted publishing (OIDC) instead of API tokens.
Changes:
- Introduces a GitHub Actions
Releaseworkflow to build, test, and publish to Test PyPI / PyPI via OIDC. - Adds release documentation describing versioning rules, release paths, and trusted publisher setup.
Reviewed changes
Copilot reviewed 2 out of 2 changed files in this pull request and generated 3 comments.
| File | Description |
|---|---|
| docs/release/RELEASE.md | Documents the release process, prerequisites, and trusted publishing configuration. |
| .github/workflows/release.yaml | Automates build/test/package validation and publishing to Test PyPI/PyPI and attaching release assets. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| - name: Check version and release target | ||
| id: vars | ||
| env: | ||
| RELEASE_TAG: ${{ github.event.release.tag_name }} | ||
| run: | | ||
| python - <<'PY' | ||
| import ast | ||
| import os | ||
| from pathlib import Path | ||
| import re | ||
| import tomllib |
| python - <<'PY' | ||
| import ast | ||
| import os | ||
| from pathlib import Path | ||
| import re | ||
| import tomllib | ||
|
|
||
| with open("pyproject.toml", "rb") as f: | ||
| project = tomllib.load(f)["project"] |
| module = ast.parse(open("kubeflow_mcp/__init__.py", encoding="utf-8").read()) | ||
| code_version = None | ||
| for node in module.body: | ||
| if ( | ||
| isinstance(node, ast.Assign) | ||
| and len(node.targets) == 1 | ||
| and isinstance(node.targets[0], ast.Name) | ||
| and node.targets[0].id == "__version__" | ||
| and isinstance(node.value, ast.Constant) | ||
| and isinstance(node.value.value, str) | ||
| ): | ||
| code_version = node.value.value | ||
| break |
|
Thanks @Krishna-kg732 for working on this! A few small items before /lgtm - happy to merge after these. |
|
|
||
| - name: Setup build environment | ||
| run: | | ||
| make verify |
There was a problem hiding this comment.
I think we can add uv sync --extra dev (or make install) before make verify .. bare checkout won't have deps, wdyt?
There was a problem hiding this comment.
okayy ,sounds good
| with: | ||
| verbose: true | ||
|
|
||
| github-release: |
There was a problem hiding this comment.
IIUC github-release job runs on release: published then calls softprops/action-gh-release to create a release .. release already exists. what if we use upload_url from github.event.release to attach dist/* assets instead
| else: | ||
| target_ref = os.environ["GITHUB_REF"] | ||
|
|
||
| is_prerelease = bool(re.search(r"(?:dev|rc)", project_version, re.IGNORECASE)) |
There was a problem hiding this comment.
is_prerelease regex (?:dev|rc) misses a, b, alpha, beta .. what does SDK release workflow use? maybe can also use PEP 440 parsing ?
There was a problem hiding this comment.
yeah okayy , I will change that
| name: ${{ needs.prepare.outputs.artifact-name }} | ||
| path: dist/ | ||
|
|
||
| publish-test-pypi: |
There was a problem hiding this comment.
IIUC workflow_dispatch can publish any branch to Test PyPI with no tag match.. maybe consider requiring version in branch name or manual input
There was a problem hiding this comment.
Thanks @Krishna-kg732 , version input validation helps but I afraid you can't publish without matching both files. But we should also add a branches filter to lock workflow_dispatch to main and release-*.
that weill prevent someone accidentally publishing a feature branch to Test PyPI. One-line fix:
workflow_dispatch:
branches: [main, 'release-*']
There was a problem hiding this comment.
cool , makes sense
will update it
|
PR description mentions about scripts/check_version.py exists ? |
| .PHONY: format | ||
| format: ## Auto-fix lint and formatting issues | ||
| @uv run ruff check --fix . | ||
| @uv run ruff format . |
There was a problem hiding this comment.
Duplicate format target.. it seems this block adds a format: target without --group dev, then the existing one follows with --group dev. Make uses the last definition, so this block is dead code. Please remove it.
| print(f"pyproject.toml: {project_version}") | ||
| print(f"__init__.py: {code_version}") | ||
|
|
||
| if project_version != code_version: |
There was a problem hiding this comment.
--release-tag comparison doesn't strip a v prefix. If someone tags v0.1.0 (very common convention), this check fails against 0.1.0 in pyproject.toml.
There was a problem hiding this comment.
Either strip the prefix: tag = args.release_tag.removeprefix("v") or document in RELEASE.md that tags must be bare versions (no v).
There was a problem hiding this comment.
workflow_dispatch should have a branches.. filter restricting to main and release-*.
There was a problem hiding this comment.
Without this, any branch can trigger a Test PyPI publish. Since v0.1.0 is imminent, this is worth adding now even if the full release-branch workflow (cherry-picks, branch protection) is follow-up scope.
wdyt?
There was a problem hiding this comment.
This is not yet resolved.. workflow_dispatch.branches is ignored by GitHub Actions, so feature branches can still publish to Test PyPI. Please add a job-level 'if' (or fail step) locking dispatch to 'main' + 'release-*' only.
|
also can you fix PR title to pass Check PR Title .. ⬆️ |
|
/ok-to-test |
|
Almost there, fork Test PyPI run looks good. 🙌 One blocker before /lgtm: workflow_dispatch.branches is ignored by GitHub Actions, so feature branches can still publish to Test PyPI. Please add a job-level 'if' (or fail step) locking dispatch to 'main' + 'release-*' only. After that I'm happy to /lgtm. @Krishna-kg732 QQ: after the PR merges, someone with admin on kubeflow/mcp-server must create two GitHub Environments and wire them to Test PyPI / PyPI or the publish jobs will fail even though the workflow YAML is correct.. right? |
|
@andreyvelich @thesuperzapper @kramaranya Once above reviews resolved, happy to /lgtm. |
yes , the release will fail if the github environment is not configured. workflow dryrun with test-PyPi : test PyPi : https://test.pypi.org/project/kubeflow-mcp-krishna-kg732 it will default to the test pypi workflow. |
|
@Krishna-kg732 one item left before lgtm: the workflow_dispatch.branches filter is ignored by Actions .. Fork TestPyPI run looked good. Happy to lgtm once that guard lands. Post-merge we’ll still need test-pypi + release GitHub Environments + trusted publishers .. |
|
/milestone v0.1 |
Signed-off-by: Krishna Gupta <Krishnagupta.kg2k6@gmail.com>
Signed-off-by: Krishna Gupta <Krishnagupta.kg2k6@gmail.com>
Signed-off-by: Krishna Gupta <Krishnagupta.kg2k6@gmail.com>
…workflow Signed-off-by: Krishna Gupta <Krishnagupta.kg2k6@gmail.com>
Signed-off-by: Krishna Gupta <Krishnagupta.kg2k6@gmail.com>
Signed-off-by: Krishna Gupta <Krishnagupta.kg2k6@gmail.com>
- single-source version via hatchling dynamic (kubeflow_mcp/__init__.py) - rewrite release.yaml to mirror kubeflow/sdk (prepare/build/tag/publish) - retain Test PyPI dry run on workflow_dispatch - make docker-publish.yaml reusable and invoke it from the release flow - add 'make release' version-bump target - git-cliff/changelog left disabled pending PR kubeflow#72 - drop scripts/check_version.py (obsolete under single-source version) Signed-off-by: Krishna Gupta <Krishnagupta.kg2k6@gmail.com>
fc3d1ef to
edfcc8d
Compare
|
@andreyvelich @abhijeet-dhumal @jaiakash quick summary of what's needed to cut an actual release with this workflow:
Approve the release environment gate when the run reaches it. |
workflow_dispatch has no branches: filter (GitHub Actions ignores that key outside push/pull_request), so any branch could previously be dispatched and published to Test PyPI. Add a fail-fast first step in prepare that rejects any dispatch not from main or release-*, before checkout runs. Update the Test PyPI dry run docs to match: the RC bump must land on main (or release-X.Y) before it can be dispatched. Addresses review feedback from @abhijeet-dhumal on kubeflow#28. Signed-off-by: Krishna Gupta <Krishnagupta.kg2k6@gmail.com>
|
@Krishna-kg732 on the workflow_dispatch to Test PyPI branch guard: I still think locking dispatch to main / release-* (job-level if) is the right contract so that workflow_dispatch.branches can be ignored by Actions by avoiding any writable branch hitting Test PyPI.. |
|
/lgtm |
|
[APPROVALNOTIFIER] This PR is APPROVED This pull-request has been approved by: abhijeet-dhumal The full list of commands accepted by this bot can be found here. The pull request process is described here DetailsNeeds approval from an approver in each of these files:
Approvers can indicate their approval by writing |
The github-release job published the release directly (draft: false) while
attaching dist/* assets. With immutable releases enabled a published release
locks its assets, so the asset upload fails. Create the release as a draft,
upload the assets, then publish it with 'gh release edit --draft=false', and
pin softprops/action-gh-release to a commit SHA (v3), matching the SDK.
Also brings the workflow to parity with kubeflow/sdk:
- remove the Test PyPI publish job and its workflow_dispatch version input;
workflow_dispatch is now '{}' and runs the same full release as a push
- drop the per-job 'if: github.event_name == push' guards, so a dispatch
behaves identically to a version-bump push
- bump astral-sh/setup-uv v4 -> v7
- update RELEASE.md for the dispatch and pre-release flows, and drop the
Test PyPI trusted-publisher setup
Two guards are kept as deliberate deviations from the SDK: dispatch is
restricted to main/release-* (per review on #28, and now more important since
a dispatch cuts a real release), and the version must be X.Y.Z[rcN] so an
unrelated edit to __init__.py while the version is a 0.1.0-dev placeholder
cannot cut a bogus release.
Signed-off-by: Krishna Gupta <Krishnagupta.kg2k6@gmail.com>
Automate the release pipeline for kubeflow-mcp (SDK-aligned)
Replaces the earlier manual-publish design with a version-file-driven, automated pipeline modeled on the kubeflow/sdk release workflow. Merging a version bump now drives branch creation, tagging, build, and publish automatically instead of requiring a manually published GitHub Release.
What changed
pushtomain/release-*, filtered onkubeflow_mcp/__init__.pychanging, plusworkflow_dispatchfor a Test PyPI dry run only. The oldrelease: publishedtrigger,force_prod, anddry_runinputs are gone.pyproject.tomlnow reads its version dynamically fromkubeflow_mcp/__init__.pyvia[tool.hatch.version].scripts/check_version.pyis deleted since there is nothing left to keep in sync.preparecreates or cherry-picks ontorelease-X.Y,create-tagtags the release. Both mirror the SDK's workflow.make release VERSION=X.Y.Z[rcN]: new Makefile target that bumps the version. (Changelog generation via git-cliff is intentionally left commented out pending chore(ci): add git-cliff changelog generation #72, which adds it separately.)docker-publish.yamlis now a reusableworkflow_callinvoked from the release pipeline after tagging (tags pushed byGITHUB_TOKENcan't retrigger a tag-push workflow, so it's called directly). Also fixes a pre-existing bug where an RC build could get taggedlatest.workflow_dispatchfrom any branch other thanmain/release-*, since GitHub Actions silently ignores abranches:filter on that trigger. Addresses review feedback from @abhijeet-dhumal.Environments required (unchanged)
releaseandtest-pypiGitHub environments with required reviewers.kubeflow-mcp(ownerkubeflow, repomcp-server, workflowrelease.yaml).Verified
make verifyandmake test-pythonpass.workflow_dispatchpath end to end on a fork (prepare/build/Test PyPI publish all green).main,release-0.1, and a feature branch ref.Out of scope
git-cliff), tracked separately in chore(ci): add git-cliff changelog generation #72.Closes #16
cc: @abhijeet-dhumal @andreyvelich @jaiakash