Skip to content

feat: publish-target plugin system (npm / jsr / pypi / vscode-marketplace / open-vsx) - #153

Open
theoephraim wants to merge 4 commits into
mainfrom
claude/goofy-haslett-323848
Open

feat: publish-target plugin system (npm / jsr / pypi / vscode-marketplace / open-vsx)#153
theoephraim wants to merge 4 commits into
mainfrom
claude/goofy-haslett-323848

Conversation

@theoephraim

Copy link
Copy Markdown
Member

What

Packages can now publish to multiple targets at once. Each target is an instance of a plugin implementing one interface (src/core/targets/); built-ins: npm, custom (the existing shell-command escape hatch), jsr, pypi, vscode-marketplace, open-vsx.

// .bumpy/_config.json
{
  "targets": {
    "npm": { "provenance": true },                                  // type-level defaults
    "ghp": { "type": "npm", "registry": "https://npm.pkg.github.com" } // named reusable instance
  },
  "packages": {
    "my-lib": { "publishTargets": ["npm", "ghp", "jsr"] },
    "my-extension": { "publishTargets": [{ "type": "vscode-marketplace", "azureCredential": true }, "open-vsx"] },
    "my-py-tool": { "publishTargets": ["pypi"] } // stub package.json + pyproject.toml
  }
}

Design highlights

  • Per-target resume: publish state is tracked per target instance in the GitHub release metadata (the metadata layer already supported this — the instance name is the key). npm succeeded + Open VSX failed → the retry re-runs only Open VSX. A registry-level guard (checkPublished before every publish) also prevents republish errors when metadata is lost.
  • Shared artifacts: targets declaring the same artifact kind share one build — a single .vsix (packaged once via vsce package --no-dependencies) uploads to both the Marketplace and Open VSX, byte-identical. npm's pack-then-publish flow is the same mechanism (npm-tarball).
  • Publish-time version sync (prepare() hook): jsr.json and pyproject.toml versions sync from package.json in the working tree at publish time — commit them as 0.0.0 and forget them. No version-phase changes needed.
  • Capabilities: targets declare what they support; Marketplace/Open VSX (no prereleases), JSR/PyPI (no dist-tags → no snapshots) record skipped instead of failing mid-release.
  • Trust model unchanged: name references in package.json are plain data; inline shell commands stay gated by allowCustomCommands. Broken target config degrades read-only commands to a warning; publish flows refuse loudly.
  • Back-compat: publishCommand/skipNpmPublish map onto targets with the same metadata keys — in-flight releases resume cleanly, and all 411 pre-existing tests pass unmodified.

Validated against real setups

  • varlock: the vscode extension's skipNpmPublish + vsce/ovsx script chain collapses to two targets (incl. azureCredential: true for its Azure-OIDC Marketplace auth) — and gains per-registry retry its cmd1 && cmd2 setup can't do.
  • mirrordown: all four hand-rolled publish scripts (npm idempotency, jsr version sync, catalog resolution, jsr idempotency) are covered by built-ins; config collapses to ["npm", "jsr"].

Review

Multi-angle review (8 finders → 12 verified candidates) ran on the branch; all confirmed findings are fixed in the hardening commit (lenient discovery, unified registry fallback chain, structured skip kinds, capability-driven private-package drop, parallel published-checks). Not yet done: a live end-to-end publish — first real validation will be a varlock release after migrating its config.

Credits

JSR publishing behavior (publish-time jsr.json sync, claim-first bootstrap, catalog-resolution requirement) is modeled on Drake Costa's setup in mirrordown — thanks Drake! 🙏

Test plan

  • 464 tests (53 new: resolver/back-compat/trust-gate, multi-target pipeline incl. per-target retry + shared artifacts + registry guard, jsr/pypi/vscode arg-building, pyproject.toml helpers, lenient discovery)
  • Real-config smoke tests: workspace discovery + target resolution against varlock and mirrordown checkouts

…ilt-in)

Packages can now publish to multiple targets via per-package publishTargets
config and a root targets map (type defaults + named reusable instances).
Targets execute per-package with per-target retry state in GitHub release
metadata, shared artifacts (one .vsix -> marketplace + Open VSX), and a
registry-level pre-publish idempotency guard.

JSR publishing (publish-time jsr.json version sync, claim-first bootstrap
detection) is modeled on Drake Costa's (@Saeris) setup in mirrordown.

Legacy publishCommand/skipNpmPublish fields keep working via back-compat
mapping in the resolver.
Python packages ride bumpy's package.json spine via a private stub package;
the target syncs [project].version into pyproject.toml at publish time,
builds with uv into an isolated per-version dist dir, and uploads via
uv publish (PyPI trusted publishing / UV_PUBLISH_TOKEN). PyPI project name
comes from pyproject.toml. Prereleases/snapshots are skipped (PEP 440
doesn't cover semver suffixes).
- prepare() lifecycle hook — publish-time version syncs (jsr.json,
  pyproject.toml) get a formal home instead of living in publish/buildArtifact
- broken target config no longer breaks read-only commands: discovery records
  targetsError and publish flows refuse loudly instead
- structured TargetOutcome.skipKind replaces magic reason strings coupled
  across pipeline and publish command
- npmEffectiveRegistry() is the single registry fallback chain (options ->
  bumpy registry -> publishConfig.registry) for publish args, existence
  checks, prerelease counters, labels, and URLs
- refusesPrivatePackages capability flag replaces hardcoded npm-type check
  in the resolver
- jsr.json version sync uses the formatting-preserving updateJsonFields
- per-target published checks run in parallel; shared stringArrayOption/
  stringOption helpers de-triplicate option coercion
@github-actions

Copy link
Copy Markdown

bumpy-frog

The changes in this PR will be included in the next version bump.

minor Minor releases

  • @varlock/bumpy 1.18.1 → 1.19.0

Bump files in this PR

Click here if you want to add another bump file to this PR


This comment is maintained by bumpy.

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.

1 participant