Skip to content

ci: publish Python packages to PyPI via trusted publishing - #1828

Merged
mishushakov merged 2 commits into
mainfrom
devin/1788793531-pypi-trusted-publishing
Sep 8, 2026
Merged

ci: publish Python packages to PyPI via trusted publishing#1828
mishushakov merged 2 commits into
mainfrom
devin/1788793531-pypi-trusted-publishing

Conversation

@devin-ai-integration

@devin-ai-integration devin-ai-integration Bot commented Sep 7, 2026

Copy link
Copy Markdown
Contributor

Summary

Drops the long-lived PYPI_TOKEN and publishes e2b, e2b-code-interpreter and e2b-desktop to PyPI through GitHub OIDC trusted publishing, the same way the npm packages already publish with --provenance.

-uv publish --token ${PYPI_TOKEN} --check-url https://pypi.org/simple/
+uv publish --trusted-publishing always --check-url https://pypi.org/simple/

applied to the three postPublish scripts (run by changeset publish inside changesets/action) and to the RC step in publish_candidates.yml. always rather than the default automatic so a missing OIDC token fails immediately instead of falling back to prompting for credentials. PYPI_TOKEN is removed from publish_packages.yml's workflow_call secrets and the release step env; both callers already use secrets: inherit, and both publish workflows already carry id-token: write, so nothing else in the workflows changes.

Also removes the NPM_TOKEN: '' env from the changesets step. It was the changesets/changesets#1152 workaround for older changesets/action versions that wrote a literal undefined token to ~/.npmrc when the var was unset; the pinned v1.9.0 only writes .npmrc if (process.env.NPM_TOKEN) and otherwise uses OIDC, so '' and unset are equivalent and no NPM_TOKEN secret exists in the repo.

Required PyPI configuration (before merging)

PyPI verifies the OIDC job_workflow_ref claim, which for a same-repo reusable workflow names the called file (OWNER/REPO/.github/workflows/<called>.yml@<ref>), not release.yml / release-candidate.yml. Register a GitHub Actions trusted publisher on each of the three PyPI projects (e2b, e2b-code-interpreter, e2b-desktop) for each of:

Owner Repository Workflow filename Environment
e2b-dev E2B publish_packages.yml (none)
e2b-dev E2B publish_candidates.yml (none)

Leave the environment empty; the publish jobs don't run in a GitHub environment. Once the first trusted-publishing release succeeds, the PYPI_TOKEN repository secret can be revoked and deleted.

Link to Devin session: https://app.devin.ai/sessions/75f1691f0870441a85dd0bc09797fef4
Open in Devin Desktop: https://app.devin.ai/desktop/session/75f1691f0870441a85dd0bc09797fef4?variant=devin
Requested by: @mishushakov

Co-Authored-By: mish@e2b.dev <mish@e2b.dev>
@devin-ai-integration

Copy link
Copy Markdown
Contributor Author

🤖 Devin AI Engineer

I'll be helping with this pull request! Here's what you should know:

✅ I will automatically:

  • Address comments on this PR. Add '(aside)' to your comment to have me ignore it.
  • Look at CI failures and help fix them

Note: I can only respond to comments from users who have write access to this repository.

⚙️ Control Options:

  • Disable automatic comment, CI, and merge conflict monitoring

@cla-bot cla-bot Bot added the cla-signed label Sep 7, 2026
@changeset-bot

changeset-bot Bot commented Sep 7, 2026

Copy link
Copy Markdown

⚠️ No Changeset found

Latest commit: cdb2fdd

Merging this PR will not cause a version bump for any packages. If these changes should not result in a new version, you're good to go. If these changes should result in a version bump, you need to add a changeset.

This PR includes no changesets

When changesets are added to this PR, you'll see the packages that this PR includes changesets for and the associated semver types

Click here to learn what changesets are, and how to add one.

Click here if you're a maintainer who wants to add a changeset to this PR

@devin-ai-integration devin-ai-integration Bot left a comment

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

TASTE.md review: no violations. The PR only changes CI publish workflows and package.json postPublish scripts (PyPI trusted publishing); it touches no SDK public surface, so none of T-1–T-74 (API shape, parity, streaming, timeouts, config, errors, docs) apply. 0 violations.

@github-actions

github-actions Bot commented Sep 7, 2026

Copy link
Copy Markdown
Contributor

Package Artifacts

Built from bbb74e4. Download artifacts from this workflow run.

JS SDK (e2b@2.47.1-devin-1788793531-pypi-trusted-publishing.0):

npm install ./e2b-2.47.1-devin-1788793531-pypi-trusted-publishing.0.tgz

CLI (@e2b/cli@2.18.2-devin-1788793531-pypi-trusted-publishing.0):

npm install ./e2b-cli-2.18.2-devin-1788793531-pypi-trusted-publishing.0.tgz

Code Interpreter JS SDK (@e2b/code-interpreter@2.8.1-devin-1788793531-pypi-trusted-publishing.0):

npm install ./e2b-code-interpreter-2.8.1-devin-1788793531-pypi-trusted-publishing.0.tgz

Desktop JS SDK (@e2b/desktop@2.4.1-devin-1788793531-pypi-trusted-publishing.0):

npm install ./e2b-desktop-2.4.1-devin-1788793531-pypi-trusted-publishing.0.tgz

Python SDK (e2b==2.47.0+devin.1788793531.pypi.trusted.publishing):

pip install ./e2b-2.47.0+devin.1788793531.pypi.trusted.publishing-py3-none-any.whl

Code Interpreter Python SDK (e2b-code-interpreter==2.10.0+devin.1788793531.pypi.trusted.publishing):

pip install ./e2b_code_interpreter-2.10.0+devin.1788793531.pypi.trusted.publishing-py3-none-any.whl

Desktop Python SDK (e2b-desktop==2.5.0+devin.1788793531.pypi.trusted.publishing):

pip install ./e2b_desktop-2.5.0+devin.1788793531.pypi.trusted.publishing-py3-none-any.whl

@mishushakov

Copy link
Copy Markdown
Member

do we need the NPM_TOKEN anywhere still or is it reset on purpose?

@devin-ai-integration

Copy link
Copy Markdown
Contributor Author

do we need the NPM_TOKEN anywhere still or is it reset on purpose?

Not needed anywhere — no secrets.NPM_TOKEN is referenced in the repo (the NPM_TOKEN: '' line predates this PR, from #1768). It's the workaround from changesets/changesets#1152 for older changesets/action versions, which unconditionally wrote //registry.npmjs.org/:_authToken=${NPM_TOKEN} to ~/.npmrc; an unset var produced a literal undefined token that npm sent instead of falling back to OIDC, so an empty string was the fix.

The pinned v1.9.0 (src/index.ts) only writes .npmrc if (process.env.NPM_TOKEN) and otherwise logs "No NPM_TOKEN found, but OIDC is available - using npm trusted publishing", so '' and unset now behave identically. It's a harmless no-op guard at this point; happy to drop the line in this PR if you'd rather not carry it.

@mishushakov
mishushakov marked this pull request as ready for review September 8, 2026 11:11
@mishushakov
mishushakov self-requested a review as a code owner September 8, 2026 11:11

@devin-ai-integration devin-ai-integration Bot left a comment

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

✅ Devin Review: No Issues Found

Devin Review analyzed this PR and found no bugs or issues to report.

Devin Review

Copy link
Copy Markdown
Member

can you drop it then?

Co-Authored-By: mish@e2b.dev <mish@e2b.dev>
@devin-ai-integration

Copy link
Copy Markdown
Contributor Author

can you drop it then?

Done in cdb2fddNPM_TOKEN: '' removed from the changesets step; PR description updated.

@claude claude Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Looks good, straightforward CI/publish config change.

What was reviewed: the --token--trusted-publishing always swap in the four publish call sites (RC workflow step and the three Python packages' postPublish scripts) — all consistent; confirmed PYPI_TOKEN is fully removed from publish_packages.yml's secrets and env with no other references left; verified id-token: write permission is already present in both publish_candidates.yml and publish_packages.yml, which OIDC trusted publishing requires. No SDK public-API surface changed, so no changeset is needed.

Extended reasoning...

Overview

This PR touches only CI publish tooling: .github/workflows/publish_candidates.yml, .github/workflows/publish_packages.yml, and the postPublish npm scripts in three Python package.json files (code-interpreter-python, desktop-python, python-sdk). It replaces PyPI token-based auth (uv publish --token ${PYPI_TOKEN}) with PyPI trusted publishing (uv publish --trusted-publishing always), and removes the now-unused PYPI_TOKEN secret plumbing from the reusable workflow.

Security risks

This change reduces attack surface by removing a long-lived PyPI API token from the secrets store in favor of short-lived OIDC-based trusted publishing, which is a security improvement. I confirmed id-token: write permission (required for OIDC token minting) is already present in both affected workflows, so the mechanism should function as intended, contingent on the PyPI-side trusted publisher registration described in the PR body (an out-of-band config step, not verifiable from the diff). Using always instead of the default automatic is a reasonable choice — it fails fast if OIDC isn't available rather than silently attempting other auth methods.

Level of scrutiny

This is a low-risk, mechanical, and consistent change across four call sites with no application logic or SDK behavior affected — appropriate for a light review. The change is easily reversible and doesn't touch runtime code paths.

Other factors

No changeset is needed since only internal postPublish build scripts changed, not public API surface. No test coverage applies since this is publish-time CI tooling. The PR author's description matches the actual diff exactly.

This review covers commit decbdb7, which is no longer the latest commit on this pull request; later commits are not covered by it.

@mishushakov
mishushakov merged commit 2a0b1d1 into main Sep 8, 2026
30 of 32 checks passed
@mishushakov
mishushakov deleted the devin/1788793531-pypi-trusted-publishing branch September 8, 2026 11:28
mishushakov added a commit that referenced this pull request Sep 9, 2026
## Summary

Runs the production publish job inside a GitHub environment so that the
environment's deployment branch policy — not only the `Check the ref`
preflight step — decides which refs may publish `latest` to npm and
PyPI.

```yaml
# .github/workflows/publish_packages.yml
jobs:
  test:
    runs-on: ubuntu-22.04
+   environment: deployment
```

It lives on the job in `publish_packages.yml`, not on `publish:` in
`release.yml`, because a job that `uses:` a reusable workflow only
accepts
`name/uses/with/secrets/strategy/needs/if/concurrency/permissions`
([docs](https://docs.github.com/en/actions/reference/workflows-and-actions/reusable-workflows#supported-keywords-for-jobs-that-call-a-reusable-workflow)).
The preflight ref check stays: it fails fast before the SDK test matrix
runs, whereas the environment rule only rejects the publish job itself.

`release-candidate.yml` / `publish_candidates.yml` are intentionally
left without an environment — candidates are cut from feature branches,
which a main-only policy would block.

## Required repository configuration

The `deployment` environment doesn't exist yet (GitHub would auto-create
it on first run with no rules). The Devin GitHub App has no admin on
this repo, so an admin needs to create it and restrict it to `main`:

```sh
gh api -X PUT repos/e2b-dev/E2B/environments/deployment \
  --input - <<'EOF'
{"deployment_branch_policy":{"protected_branches":false,"custom_branch_policies":true}}
EOF
gh api -X POST repos/e2b-dev/E2B/environments/deployment/deployment-branch-policies \
  -f name=main -f type=branch
```

Or in the UI: Settings → Environments → New environment `deployment` →
Deployment branches and tags → Selected branches and tags → add `main`.

No trusted-publisher change is needed: the PyPI publishers from #1828
and the npm publishers were registered with an empty environment, which
matches any environment. Optionally tighten them afterwards by setting
the environment to `deployment` on the `publish_packages.yml` publishers
(leave the `publish_candidates.yml` ones empty).


Link to Devin session:
https://app.devin.ai/sessions/a59b5e364c484ced9524a652f80a49f9
Open in Devin Desktop:
https://app.devin.ai/desktop/session/a59b5e364c484ced9524a652f80a49f9?variant=devin
Requested by: @mishushakov

Co-authored-by: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
Co-authored-by: mish@e2b.dev <mish@e2b.dev>
mishushakov added a commit that referenced this pull request Sep 9, 2026
## Summary

Deletes `release-candidate.yml` and the `publish_candidates.yml`
reusable workflow it called. Neither has been run since the candidate
flow was split out of `release.yml` in #1483 (2026-06-25); the last RC
published through the old combined workflow was
`e2b@2.24.1-connections.1189.0` on 2026-05-26, and the last PyPI RC was
`e2b==2.0.0rc2` (2025-08).

`release.yml` keeps its `release-${{ github.ref }}` concurrency group
and the main-only preflight check; only the comments pointing at the
candidate workflow are dropped. `publish_packages.yml` (now running in
the `deployment` environment, #1850) is unaffected.

## Follow-up (PyPI)

The trusted publishers registered for `publish_candidates.yml` on the
`e2b`, `e2b-code-interpreter` and `e2b-desktop` PyPI projects (see
#1828) no longer have a workflow behind them and can be removed. Since
the remaining `publish_packages.yml` publishers are now the only ones,
they can also be tightened to require the `deployment` environment.


Link to Devin session:
https://app.devin.ai/sessions/a59b5e364c484ced9524a652f80a49f9
Open in Devin Desktop:
https://app.devin.ai/desktop/session/a59b5e364c484ced9524a652f80a49f9?variant=devin
Requested by: @mishushakov

Co-authored-by: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
Co-authored-by: mish@e2b.dev <mish@e2b.dev>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant