feat(release): publish to every supported ecosystem from one release dispatch (Maven Central + NuGet + consolidated release.yml) - #138
Conversation
… Maven Central + NuGet channels, consolidated release.yml, Packagist root manifest Two new distribution channels, both download-on-first-run launchers mirroring gem/composer (version-matched GitHub release binary, SHA256SUMS verification, HTTPS-only incl. redirects, per-user cache, SOCKET_PATCH_BIN airgap escape): - Maven Central: dev.socket:socket-patch — dependency-free Java 11 launcher jar (java -jar / mvn dependency:copy / jbang), Central Portal publishing with GPG signing + sources/javadoc jars, env https_proxy support. - NuGet: SocketSecurity.SocketPatch — net8.0 dotnet tool (`dotnet tool install -g` puts socket-patch on PATH), executability-gated cache with self-heal, no shell-outs (BCL zip/tar). Release pipeline consolidation: - Delete release-ecosystems.yml: its `release: published` trigger could never fire (GitHub suppresses events from GITHUB_TOKEN-created releases), so the launcher-gem/Packagist jobs only ever ran by hand. All publishing now fans out from the single workflow_dispatch in release.yml, with launcher jobs gated on `needs: [version, github-release]`. - Fix the no-op tag guard (shallow tagless checkout) with a stateless `git ls-remote` check that permits same-commit retries; make github-release idempotent (view -> upload --clobber); probe crates.io before publishing; add `environment: pypi` + skip-existing; migrate both gems to one RubyGems OIDC trusted-publishing job; port the Packagist ping. - npm: add prepack:tsc so the published ./schema export actually contains dist/ (it pointed at a gitignored, never-built directory); install devDependencies with `npm install --no-save --ignore-scripts` — NOT `npm ci`, which refuses the lockfile's hollow optional platform entries (version-sync refreshes the lock while the new platform packages are unpublished; reproduced with npm 11.6.2); tolerate staged-but-unapproved versions on re-runs; refresh package-lock.json. - Packagist requires the manifest at the VCS repo root: move composer.json there (bin -> composer/socket-patch/bin/socket-patch) with a fail-closed .gitattributes export-ignore allowlist so the dist zip ships only the composer files. Deliberate side effect (documented): GitHub "Source code" release archives now contain only the allowlisted files. - version-sync.sh stamps the four new version sites; lint-ecosystems builds the jar (`mvn package -Dgpg.skip=true`) and packs the tool (`dotnet pack`) and validates the root composer.json. Both launchers e2e-verified against the live v3.3.0 release: fresh-cache download + SHA verify + `socket-patch 3.3.0`, cache hit, stub escape with exit-code propagation, non-executable-override fall-through, and (.NET) mode-stripped-cache self-heal. One-time registry setup (environments, trusted publishers, dev.socket DNS claim, GPG key, nuget.org policy) is listed in the PR description; RubyGems, Packagist, Maven Central and NuGet jobs skip gracefully (or fail loud where sanctioned) until credentials land. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
|
Warning Review the following alerts detected in dependencies. According to your organization's Security Policy, it is recommended to resolve "Warn" alerts. Learn more about Socket for GitHub.
|
Summary
One
Releasedispatch now publishes (or stages) the CLI to every supported ecosystem package manager: crates.io, npm, PyPI, RubyGems (both gems), Packagist/Composer, Maven Central (new), and NuGet (new) — with idempotent jobs so "Re-run failed jobs" recovers a partial release.New distribution channels
Both mirror the gem/composer download-on-first-run launcher pattern: fetch the version-matched
socket-patch-<triple>archive from the GitHub release, verify againstSHA256SUMS(HTTPS-only including redirects), cache per-user under the sharedsocket-patch/bin/<version>/<target>/scheme, honorSOCKET_PATCH_BINas the airgap escape.dev.socket:socket-patchmvn dependency:copy -Dartifact=dev.socket:socket-patch:<V>+java -jar, orjbang dev.socket:socket-patch:<V>SocketSecurity.SocketPatchdotnet tool install -g SocketSecurity.SocketPatch→socket-patchon PATHmaven/socket-patch/— dependency-free Java 11 launcher (Launcher.javais a line-faithful port of the gem launcher, incl. the security comments); pom wired for the Central Publisher Portal (GPG signing, sources+javadoc jars,central-publishing-maven-plugin, reproducible-build timestamp,<!-- x-version-sync -->marker). Honorshttps_proxy/HTTPS_PROXYenv proxies (the JVM default ignores them; every sibling launcher honors them).nuget/socket-patch/— net8.0 dotnet tool (RollForward=Major); the managed shim is the launcher: no shell-outs (BCL zip/tar+gzip), manual redirect vetting, executability-gated cache with self-heal (a mode-stripped cache entry is re-downloaded instead of looping on Permission denied).Release pipeline consolidation (
release.yml)release-ecosystems.yml— itsrelease: publishedtrigger could never fire: the release is created withGITHUB_TOKEN, and GitHub suppresses workflow events caused by that token (apush: tags:trigger would be suppressed for the same reason). The launcher-gem and Packagist jobs only ever ran by hand. Everything now fans out from the oneworkflow_dispatch, launcher jobs gated onneeds: [version, github-release].git rev-parse v$VERSIONalways fail, so the "tag already exists" check always passed. Replaced with a statelessgit ls-remotecheck that also permits same-commit retries.gh release view → upload --clobberfallback; crates.io published-version probes;skip-existing: trueon both PyPI uploads; the existinggem list --remoteguards;--skip-duplicateon NuGet; repo1 probe on Maven Central; staged-but-unapproved npm versions tolerated on re-run.rubygems/configure-rubygems-credentials(one exchange covers both gems);RUBYGEMS_API_KEYand the zizmor suppressions are gone. (rubygems/release-gemwas evaluated and rejected — it runsrake release, which re-tags.)environment: pypi(publishers registered without an environment constraint still match, so this lands safely before the tightening below).prepack: tsc— the published./schemaexport pointed at a gitignored, never-builtdist/; every published tarball shipped a broken export.npm install --no-save --ignore-scripts, notnpm ci: version-sync refreshes the lockfile while the release's platform packages are unpublished, so npm records them as hollow optional stubs andnpm cirefuses the file (reproduced with npm 11.6.2:lock file's @socketsecurity/socket-patch-*@ does not satisfy ...@3.3.0).npm installskips unresolvable optionals — verified for both the published (3.3.0) and unpublished (simulated 9.9.9) states. The refreshed lockfile is committed, sonpm ciworks again for local dev.composer.jsonat the VCS repo root — the channel could never publish as previously built. Manifest moved to the root (bin→composer/socket-patch/bin/socket-patch) with a fail-closed.gitattributesexport-ignore allowlist so the dist zip ships only the composer files..gitattributes+ CHANGELOG): GitHub's auto-generated "Source code" release archives now contain only the allowlisted files; clone the repo for full source.scripts/version-sync.shstamps the four new version sites (pom marker line,Launcher.javaconstant, csproj<Version>,Program.csfallback);lint-ecosystemsin CI now builds the jar (mvn package -Dgpg.skip=true), packs the tool (dotnet pack), and validates the root composer.json. All newuses:are SHA-pinned and were verified against their tags (pin-check-compliant).Verification
socket-patch 3.3.0(exit 0); cache hit on second run;SOCKET_PATCH_BINstub honored with child exit code (7) propagated; non-executable override falls through; (.NET) mode-stripped cache entry self-heals via re-download. Fullmvn packageproduces jar+sources+javadoc with correctMain-Class/Implementation-Version;dotnet packclean with the informational version equal to the package version.bash scripts/version-sync.sh 3.3.0is a byte-exact no-op;git archiveof the tree ships exactly the composer allowlist; YAML/shellcheck/php -l/gem build/javac --release 11all green; no references to the deleted workflow remain.npm cirelease-breaker above.One-time registry setup (maintainer checklist)
Publishing jobs skip gracefully with a
::noticeuntil these land:pypi,maven-central,nuget(and confirmrubygems,packagist); optionally add required reviewers to gate publishes. Delete the now-unusedRUBYGEMS_API_KEYsecret.socket-patchandsocket-patch-bundler, ownerSocketDev, reposocket-patch, workflowrelease.yml, environmentrubygems(create shortly before the release; check the expiry window on the form).socket-patchpublisher to require environmentpypi; register the pending publisher forsocket-patch-hookif it has never published.https://github.com/SocketDev/socket-patchunder thesocketsecurityvendor; addPACKAGIST_USERNAME/PACKAGIST_TOKENto thepackagistenvironment; confirm the GitHub auto-update hook.dev.socketvia DNS TXT onsocket.dev→ portal user token →CENTRAL_USERNAME/CENTRAL_PASSWORD; generate a release GPG keypair, publish the public key tokeyserver.ubuntu.com+keys.openpgp.org, storeCENTRAL_GPG_PRIVATE_KEY/CENTRAL_GPG_PASSPHRASE.SocketSecurity.*prefix reservation); Trusted Publishing policy (repoSocketDev/socket-patch, workflow filerelease.yml, environmentnuget);NUGET_USERsecret + a scopedNUGET_API_KEYfallback (keep armed for the inaugural push).Confirm on the first real release:
npm stage listoutput format (the staged-version re-run tolerance greps it best-effort);actions/setup-javaskipping GPG import when the key secret is empty; whether nuget.org allows a brand-new package ID via an OIDC temp key.🤖 Generated with Claude Code