Skip to content

ci(release): tag, publish to PyPI and release when a stable pin lands - #14

Open
pimlock wants to merge 13 commits into
mainfrom
feat/8-release/pimlock
Open

pimlock wants to merge 13 commits into
mainfrom
feat/8-release/pimlock

Conversation

@pimlock

@pimlock pimlock commented Nov 8, 2026 •

Copy link
Copy Markdown
Contributor

Summary

Adds the release workflow. When a PR that moves the core pin to a stable tag merges into main, release.yml tags vX.Y.Z, publishes openshell X.Y.Z to PyPI with trusted publishing, and publishes the GitHub release. This is what 0.1.8 needs. The trusted publisher on PyPI is set up (#9): ai-openshell/sdk-python, release.yml, environment pypi.

Stacked on #3 only, so it doesn't wait for #12 or #13.

Related Issue

Closes #8. Part of NVIDIA/OpenShell#5287, phase 6.

Changes

  • .github/workflows/release.yml runs on every push to main, and by hand on main with an optional commit SHA. Four jobs:
    1. tag (contents: write, pull-requests: read): scripts/release/tag-release.sh stops unless the pin changed to a stable core tag. Otherwise it tags vX.Y.Z at the merged commit and creates a draft release, with the merged PR's description plus "Matches OpenShell core vX.Y.Z" as notes. It skips a tag or draft that exists, refuses a tag at another commit, and ends the run when the release is already published.
    2. build (no tokens): checks out the tag and runs mise run build at version X.Y.Z. That builds, verifies the wheel, and installs the wheel and sdist on Python 3.11. It then writes openshell-checksums-sha256.txt and uploads everything as an artifact. The mise tool cache is off in this job.
    3. publish-pypi (id-token: write only, environment pypi): publishes dist/ with pypa/gh-action-pypi-publish, which also uploads attestations.
    4. github-release (contents: write): attaches the wheel, sdist and checksums to the draft, then publishes it. Publishing comes last, so the release never shows without its files.
  • scripts/release/tag-release.sh is sdk-go's tag-release.sh, adapted: it reads the pin from mise.toml, creates a draft, prints tag=vX.Y.Z for the later jobs, and drops the Go module proxy step.
  • Build version: mise.toml set SETUPTOOLS_SCM_PRETEND_VERSION_FOR_OPENSHELL = "0.0.0" in [env], and mise's [env] wins over the shell, so mise run build always produced 0.0.0. It now reads OPENSHELL_SDK_VERSION, with 0.0.0 as the default.
  • CONTRIBUTING.md describes the release flow, re-runs and manual releases. An infra review for scripts/release/ comes back with ci: require an infra review for CI and release scripts again #15.

Differences from #8's design:

Before merging

How 0.1.8 goes out

  1. Core tags v0.1.8 and publishes its images.
  2. Run mise run core:bump -- v0.1.8 locally (ci(core): propose core release bumps and track drift on core main #13), or update the pin by hand. CI and E2E run on the PR.
  3. Merging that PR runs release.yml, which tags v0.1.8, publishes openshell 0.1.8 to PyPI and publishes the GitHub release.

Testing

  • tag-release.sh --dry-run:
    • with the pin unchanged, it does nothing;
    • with --force on a stable pin, it would create the tag and a draft with the PR's description;
    • with a pre-release pin, it does nothing;
    • when the push adds mise.toml (feat: build, test, and package the Python SDK standalone #3 landing on main), it does nothing.
  • OPENSHELL_SDK_VERSION=0.1.8 mise run build builds, verifies and installs openshell-0.1.8. Without it, the version stays 0.0.0.
  • mise run ci, shellcheck, actionlint and zizmor (online, auditor persona) pass.
  • The first real run: the 0.1.8 release.

Point packaging, ruff, pytest, and coverage at the new layout and write
generated bindings to src/openshell/_proto. Exclude the bindings from ty
explicitly, since they are about to be committed. Drop pyelftools, which only
core's packaging tooling uses.

Signed-off-by: Piotr Mlocek <pmlocek@nvidia.com>
sandbox_test.py read core's sdk/conformance/oauth-client-credentials.json
through a path outside the package. Copy it to tests/fixtures. The
conformance:check task added with the build tooling compares it with core at
the pinned tag.

Signed-off-by: Piotr Mlocek <pmlocek@nvidia.com>
scripts/fetch-core.sh fetches only the pinned NVIDIA/OpenShell ref.
OPENSHELL_CORE_REF in mise.toml is the single pin. uv.lock starts from core's
v0.1.7 lockfile, so the generator and runtime versions match core.
proto:check and conformance:check catch drift from the pin, and build
installs the wheel and sdist on the oldest supported Python.

Signed-off-by: Piotr Mlocek <pmlocek@nvidia.com>
Output of mise run proto:gen. The files are byte-identical to openshell/_proto
in the openshell 0.1.7 wheel on PyPI. .gitattributes marks them as generated.

Signed-off-by: Piotr Mlocek <pmlocek@nvidia.com>
The aggregating ci job is the single required check, named like sdk-go's.

Signed-off-by: Piotr Mlocek <pmlocek@nvidia.com>
Signed-off-by: Piotr Mlocek <pmlocek@nvidia.com>
proto:fetch exports openshell.proto and credential_driver.proto from core at
OPENSHELL_CORE_REF, and buf adds their imports, the same way sdk-go reads the
protos through a buf git input. The generator compiles whatever proto/ holds
instead of keeping its own file list. grpcio-tools still generates the
bindings, and they are unchanged. fetch-core.sh is now only for the vendored
conformance fixtures.

Signed-off-by: Piotr Mlocek <pmlocek@nvidia.com>
mise reads .python-version once idiomatic_version_file_enable_tools includes
python, so the version lives in one file instead of mise.toml plus whatever uv
picks. mise still pins uv and buf exactly. [tool.uv] required-version rejects
uv older than 0.10 when uv is installed without mise.

Signed-off-by: Piotr Mlocek <pmlocek@nvidia.com>
AGENTS.md, CONTRIBUTING.md and SECURITY.md describe this repository's
tasks, conventions and reporting path. CODEOWNERS gives the SDK to the
sdk team, with maintainers as fallback and infra for CI. Dependabot
updates the uv lockfile weekly and actions daily, and CodeQL scans the
Python sources, without the generated bindings.

Signed-off-by: Piotr Mlocek <pmlocek@nvidia.com>
The infra rule comes back after the move (#15), so CI changes needed
for 0.1.8 don't wait on one time zone.

Signed-off-by: Piotr Mlocek <pmlocek@nvidia.com>
@pimlock
pimlock force-pushed the feat/8-release/pimlock branch from 0f3b4de to 8b4a2f3 Compare November 9, 2026 00:15
The PR template asks for the summary, issue, changes, testing with
mise run ci, and the DCO and Conventional Commits checklist. The bug and
feature forms follow core's design-first forms, asking for SDK, Python
and gateway versions and Python examples. Gateway, CLI and sandbox
problems go to NVIDIA/OpenShell, and vulnerabilities to SECURITY.md.

Signed-off-by: Piotr Mlocek <pmlocek@nvidia.com>
release.yml runs on pushes to main and stops unless the pin moved to a
stable core tag. Then:

- tag: scripts/release/tag-release.sh (from sdk-go's) tags vX.Y.Z at
  the merged commit and creates a draft GitHub release with the PR's
  description as notes. It skips what exists, a published release ends
  the run, and a push that adds the pin (the import) releases nothing.
- build: mise run build at the tag's version, plus checksums, uploaded
  as an artifact. No tokens, no tool cache.
- publish-pypi: trusted publishing from the pypi environment, the only
  job with id-token: write.
- github-release: attach the files and checksums, then publish the
  draft.

The tag job uses GITHUB_TOKEN instead of the bot App, so the release
doesn't wait for #7; nothing triggers on tags. The build version now
comes from OPENSHELL_SDK_VERSION when it is set, because the pretend
version in mise.toml couldn't be overridden from the shell.
CONTRIBUTING.md describes the release flow.

Signed-off-by: Piotr Mlocek <pmlocek@nvidia.com>
@pimlock
pimlock force-pushed the feat/8-release/pimlock branch from 8b4a2f3 to 79eed08 Compare November 9, 2026 00:24
@pimlock
pimlock force-pushed the feat/standalone-build/pimlock branch from d1b24ec to f8d3b3f Compare November 9, 2026 15:33
Base automatically changed from feat/standalone-build/pimlock to main November 9, 2026 15:51
Signed-off-by: Piotr Mlocek <pmlocek@nvidia.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

ci: tag lockstep releases and publish to PyPI with trusted publishing

1 participant