Skip to content

feat: build, test, and package the Python SDK standalone - #3

Merged
pimlock merged 12 commits into
mainfrom
feat/standalone-build/pimlock
Nov 9, 2026
Merged

pimlock merged 12 commits into
mainfrom
feat/standalone-build/pimlock

Conversation

@pimlock

@pimlock pimlock commented Nov 4, 2026 •

Copy link
Copy Markdown
Contributor

Summary

Makes the imported SDK build, test and package without a core checkout. This is the second step of the move, the counterpart of ai-openshell/sdk-go#4. It's stacked on the history import in #2; once #2 merges, this PR retargets to main. Tracking: NVIDIA/OpenShell#5287.

Nothing changes for users. The wheel this builds matches the openshell==0.1.7 wheel on PyPI:

  • every generated binding file and every hand-written module is byte-identical;
  • the package name, openshell._proto and the Google protobuf runtime are unchanged.

The one difference: core's wheel also shipped the SDK's *_test.py files inside openshell/, and this one doesn't, because the tests now live in tests/.

Commits

The history extraction and verification scripts are not in this PR. They are kept at the migration/v0.1.7 tag, so main never carries them and nothing has to delete them later. The README links the tag.

  1. Layout: packaging, ruff, pytest and coverage point at src/ and tests/. Generated bindings go to src/openshell/_proto. pyelftools is dropped; only core's packaging tooling used it.

  2. Fixture: the OAuth conformance fixture is vendored from core's sdk/conformance/, as sdk-go did.

  3. Build tooling:

    • OPENSHELL_CORE_REF = "v0.1.7" in mise.toml is the single pin. scripts/fetch-core.sh fetches core files at that ref.
    • uv.lock starts from core's v0.1.7 lockfile, so the generator and runtime versions match core.
    • New tasks: proto:check, conformance:check, and a build task that verifies the wheel and installs the wheel and sdist on Python 3.11.
  4. Generated bindings, alone in their own commit so reviewers can skip it. .gitattributes marks them linguist-generated.

  5. CI, described below.

  6. README.

  7. buf export for the protos. proto:fetch runs

    buf export "https://github.com/NVIDIA/OpenShell.git#ref=${OPENSHELL_CORE_REF},depth=1,subdir=proto" \
      --path openshell.proto --path credential_driver.proto --output proto

    This is the same buf git input sdk-go uses. Buf follows the imports, so proto/ holds exactly the six files the SDK compiles, and the generator compiles whatever is there instead of keeping its own list.

    Generation stays on grpcio-tools, and the bindings are unchanged. fetch-core.sh is left only for the vendored fixtures, because buf export handles only .proto files. With buf installed, a buf breaking drift check against core main (as sdk-go plans) is a small follow-up.

  8. One Python pin. .python-version (3.14.5) is read by both uv and mise, through mise's idiomatic_version_file_enable_tools = ["python"]. mise still pins uv and buf exactly. [tool.uv] required-version = ">=0.10" rejects older uv when it's installed without mise.

  9. Repository basics, based on sdk-go's:

    • AGENTS.md, CONTRIBUTING.md and SECURITY.md cover tasks, conventions, the release model and how to report a vulnerability. New files need only the SPDX-License-Identifier line (ci: shared CI checks across the SDK repositories #11).
    • .github/CODEOWNERS gives everything to @ai-openshell/sdk, with @ai-openshell/maintainers as fallback. Unlike sdk-go, CI doesn't need an @ai-openshell/infra review yet, so the 0.1.8 PRs don't wait on one time zone; ci: require an infra review for CI and release scripts again #15 tracks adding it back.
    • .github/dependabot.yml updates uv.lock weekly and actions daily, with a 7-day cooldown.
    • codeql.yml scans the Python sources, leaving out src/openshell/_proto.
  10. PR and issue templates, based on core's (sdk-go has none):

  • The PR template keeps core's sections, without core's state:accepted gate and release notes section. The testing checklist runs mise run ci.
  • The bug and feature forms follow core's design-first forms. They ask for the SDK, Python and gateway versions, and use Python examples.
  • config.yml turns off blank issues and links gateway, CLI and sandbox problems to NVIDIA/OpenShell, and vulnerabilities to SECURITY.md.

Why the generated bindings are committed

In core, the protos sit next to the code, so generating at build time is free and committing the output would only duplicate it. That's why core doesn't commit them. Here the protos live in another repository, so the choice is between needing the network on every build and needing it only when the pin moves. The wheel and sdist contain the same files either way, so users see no difference.

Commit the bindings (this PR) Generate in CI, commit nothing
Building a wheel or sdist No network, no grpcio-tools; distro and conda rebuilds from the sdist work Needs the core protos first; the sdist must be built after generation or it ships without bindings
pip install git+https://github.com/ai-openshell/sdk-python@<ref> Works, which helps during the PyPI handover and for core's e2e Breaks, unless the install itself fetches over the network
Contributor setup Clone, uv sync, test Fetch core protos before running tests
Pin-bump PRs Show exactly how the generated API changed One-line ref change; the API effect is invisible without an extra step
Reproducibility What's in the commit is what ships Depends on the toolchain at release time
Repository noise Large generated diffs on bumps (collapsed via linguist-generated); ~750 KB per full rewrite None
Drift risk Hand edits or a generator bump without regeneration; proto:check must stay required None in git; the wheel verifier must keep checking that every binding is present

Go has no choice here: go get downloads source and never runs a generator, so sdk-go must commit its .pb.go files. Python does have a choice. Committing has the fewest moving parts while the PyPI handover, release automation and core's e2e switch are in flux. It also makes any later change to the generated code reviewable as a diff, including the Protobuf-Py switch proposed in #1. If core starts publishing a versioned schema artifact, generating in CI becomes a clean option, and switching wouldn't affect users.

CI

The jobs:

  • checks: format, lint, typecheck, proto:check, conformance:check.
  • test (3.11) … test (3.14): a matrix over every version that requires-python and the classifiers claim. Core only tested 3.14.
  • build: wheel and sdist, wheel verification, and an install smoke test on 3.11.
  • ci: depends on all of the above and fails unless every one succeeded.

ci is the one check to require, and it has the same name as sdk-go's single job. That consistency is deliberate: ai-openshell will manage many repositories together (SDKs first, then packages, windows and the provider repos). Shared names let rulesets, automation and people treat them alike. A repo can add or split jobs behind ci without touching its ruleset.

Remembering conventions won't scale, so it's worth enforcing them. Options:

  • Org ruleset in ai-openshell/infra requiring the status check ci on every SDK repository. A repo whose workflow doesn't report ci can't merge, so drift shows up on the first PR.
  • Reusable workflows in a shared repository (for example ai-openshell/.github) for the common parts: checkout plus mise, actionlint and zizmor, and the final ci aggregator. Each repo then supplies only its language-specific jobs.
  • Shared mise task names. sdk-go and this repo already agree on ci, core:version, proto:gen, proto:check and conformance:check. A scheduled audit in ai-openshell/infra could check every repo for the required tasks and the ci check and open an issue on drift.

Splitting sdk-go's single job into the same checks / test / build / ci shape would be a natural follow-up there.

Validation

  • mise run ci passes locally: 240 tests, ruff, ty, proto:check, conformance:check, wheel and sdist build, and the install smoke test on 3.11.
  • UV_PYTHON=3.11 mise run test passes: 240 tests.
  • The bindings and hand-written modules are byte-identical to the openshell==0.1.7 wheel on PyPI.

Open items

Comment thread LICENSE Resolved
@pimlock
pimlock force-pushed the feat/standalone-build/pimlock branch from b87461c to bb17196 Compare November 4, 2026 23:51
@pimlock

This comment was marked as outdated.

Comment thread pyproject.toml Resolved
Comment thread pyproject.toml Resolved
@pimlock
pimlock requested a review from SDAChess November 9, 2026 15:28
Base automatically changed from chore/import-history/pimlock to main November 9, 2026 15:33
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>
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>
Signed-off-by: Piotr Mlocek <pmlocek@nvidia.com>
@pimlock
pimlock force-pushed the feat/standalone-build/pimlock branch from d1b24ec to f8d3b3f Compare November 9, 2026 15:33
@pimlock
pimlock merged commit a091c9e into main Nov 9, 2026
10 checks passed
@pimlock
pimlock deleted the feat/standalone-build/pimlock branch November 9, 2026 15:51
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.

2 participants