trusted-publish
Every registry here is push-model: the registry never pulls or builds — a GitHub Actions workflow builds and uploads. Trusted publishing makes that upload tokenless: the workflow proves its identity over OIDC and the registry mints a short-lived, auto-expiring upload token. Setup is therefore three artifacts: a workflow in the repo, a trust entry on the registry, and a release to trigger the run.
The trust entry binds four identity fields — owner, repository, workflow filename, environment. The workflow file is the credential: these values must match the registry config character-for-character. This skill fixes them — workflow .github/workflows/publish-<registry>.yml, environment named after the registry — so every run produces the same names and the user types the same values.
Steps
Resolve the repo. The argument, when given, is the repo path; otherwise use the current directory. Record owner/repo from git remote get-url origin. Done when the path is a git repo with a github.com origin. A GitLab origin narrows provider support — check the reference file's provider line before continuing; any other host means trusted publishing is unavailable: report that and stop.
Detect every ecosystem. Walk the repo for package manifests and map each to its registry:
| Manifest |
Registry |
Reference |
pyproject.toml / setup.py |
PyPI |
references/pypi.md |
package.json without "private": true |
npm |
references/npm.md |
Cargo.toml with a [package] section and publish not disabled |
crates.io |
references/crates-io.md |
*.gemspec, *.nuspec, anything else |
varies |
references/other-registries.md |
Done when every manifest in the repo is either mapped to a registry or reported to the user as out of scope. Several matches (a monorepo) → run steps 3–6 once per registry.
Write the publish workflow. Read the registry's reference file, then write .github/workflows/publish-<registry>.yml from its template, substituting the package name and build specifics. When a publish workflow already exists, reconcile it with the template — id-token: write on the publish job only, current action versions — and carry its filename into step 4. Commit and push. Done when the workflow file is on the default branch.
Configure trust on the registry — a logged-in web step only the user can perform. Hand them the reference file's config URL and the four identity-field values exactly as they appear in the workflow just written, then wait for their confirmation. First-ever publish branches per registry — PyPI pre-registers a pending publisher and stays fully tokenless; npm and crates.io need one manual first release — the reference file carries that branch. Done when the user confirms the trust entry is saved.
Release. Set the new version in the manifest, commit, push, then gh release create v<version> --generate-notes. Watch with gh run watch. Done when the run is green. An OIDC-exchange failure (invalid-publisher, 403 on token exchange) means an identity-field mismatch — re-verify step 4's four values against the workflow file, have the user correct the trust entry, and re-run the workflow.
Verify on the registry. Published means fetchable: poll the reference file's version endpoint until the new version appears — allow a minute of index lag — then report the package URL to the user. Done when the registry's own API returns the new version; that, with the package URL in hand, closes the skill.
1---2name: trusted-publish3description: trusted-publish4---56# trusted-publish78Every registry here is **push-model**: the registry never pulls or builds — a GitHub Actions workflow builds and uploads. **Trusted publishing** makes that upload tokenless: the workflow proves its identity over OIDC and the registry mints a short-lived, auto-expiring upload token. Setup is therefore three artifacts: a workflow in the repo, a trust entry on the registry, and a release to trigger the run.910The trust entry binds four **identity fields** — owner, repository, workflow filename, environment. The workflow file *is* the credential: these values must match the registry config character-for-character. This skill fixes them — workflow `.github/workflows/publish-<registry>.yml`, environment named after the registry — so every run produces the same names and the user types the same values.1112## Steps13141. **Resolve the repo.** The argument, when given, is the repo path; otherwise use the current directory. Record `owner/repo` from `git remote get-url origin`. Done when the path is a git repo with a github.com origin. A GitLab origin narrows provider support — check the reference file's provider line before continuing; any other host means trusted publishing is unavailable: report that and stop.15162. **Detect every ecosystem.** Walk the repo for package manifests and map each to its registry:1718 | Manifest | Registry | Reference |19 |---|---|---|20 | `pyproject.toml` / `setup.py` | PyPI | [references/pypi.md](references/pypi.md) |21 | `package.json` without `"private": true` | npm | [references/npm.md](references/npm.md) |22 | `Cargo.toml` with a `[package]` section and `publish` not disabled | crates.io | [references/crates-io.md](references/crates-io.md) |23 | `*.gemspec`, `*.nuspec`, anything else | varies | [references/other-registries.md](references/other-registries.md) |2425 Done when every manifest in the repo is either mapped to a registry or reported to the user as out of scope. Several matches (a monorepo) → run steps 3–6 once per registry.26273. **Write the publish workflow.** Read the registry's reference file, then write `.github/workflows/publish-<registry>.yml` from its template, substituting the package name and build specifics. When a publish workflow already exists, reconcile it with the template — `id-token: write` on the publish job only, current action versions — and carry *its* filename into step 4. Commit and push. Done when the workflow file is on the default branch.28294. **Configure trust on the registry** — a logged-in web step only the user can perform. Hand them the reference file's config URL and the four identity-field values exactly as they appear in the workflow just written, then wait for their confirmation. First-ever publish branches per registry — PyPI pre-registers a *pending publisher* and stays fully tokenless; npm and crates.io need one manual first release — the reference file carries that branch. Done when the user confirms the trust entry is saved.30315. **Release.** Set the new version in the manifest, commit, push, then `gh release create v<version> --generate-notes`. Watch with `gh run watch`. Done when the run is green. An OIDC-exchange failure (`invalid-publisher`, 403 on token exchange) means an identity-field mismatch — re-verify step 4's four values against the workflow file, have the user correct the trust entry, and re-run the workflow.32336. **Verify on the registry.** *Published* means fetchable: poll the reference file's version endpoint until the new version appears — allow a minute of index lag — then report the package URL to the user. Done when the registry's own API returns the new version; that, with the package URL in hand, closes the skill.