From df2daf6df07d27a37e5e5889eba94e7c0f9eaddb Mon Sep 17 00:00:00 2001 From: Dave Thompson Date: Mon, 31 Aug 2026 18:16:05 -0400 Subject: [PATCH 1/2] release: enable cargo package publication Adds crates.io publication metadata and package validation for the public Rust library crates while keeping diagnostic and native binding build crates unpublished. Generated by GPT-5 via Codex under supervision of @3leapsdave Co-Authored-By: GPT-5 Role: devlead Committer-of-Record: @3leapsdave --- .github/workflows/release.yml | 13 ++++ Cargo.toml | 12 ++-- Makefile | 15 +++- RELEASE_CHECKLIST.md | 69 +++++++++++++++++++ .../typescript/sysprims/native/Cargo.toml | 2 + crates/sysprims-cli/Cargo.toml | 2 + crates/sysprims-core/Cargo.toml | 4 ++ crates/sysprims-core/README.md | 6 ++ crates/sysprims-proc/Cargo.toml | 4 ++ crates/sysprims-proc/README.md | 6 ++ crates/sysprims-session/Cargo.toml | 4 ++ crates/sysprims-session/README.md | 5 ++ crates/sysprims-signal/Cargo.toml | 4 ++ crates/sysprims-signal/README.md | 6 ++ crates/sysprims-timeout/Cargo.toml | 4 ++ crates/sysprims-timeout/README.md | 6 ++ ffi/sysprims-ffi/Cargo.toml | 2 + scripts/version-pack.mjs | 24 ++++++- scripts/version-pack.test.mjs | 6 +- 19 files changed, 186 insertions(+), 8 deletions(-) create mode 100644 crates/sysprims-core/README.md create mode 100644 crates/sysprims-proc/README.md create mode 100644 crates/sysprims-signal/README.md create mode 100644 crates/sysprims-timeout/README.md diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 286eca0..846b2a1 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -77,6 +77,18 @@ jobs: done echo "Go binding assets verified." + verify-cargo-package: + name: Verify Cargo Package + runs-on: ubuntu-latest + needs: validate-version + steps: + - uses: actions/checkout@v7 + - name: Install Rust 1.88.0 + uses: dtolnay/rust-toolchain@master + with: + toolchain: 1.88.0 + - name: Package crates without publishing + run: cargo package --workspace --no-verify # Build Linux targets using cargo-zigbuild for glibc version targeting build-linux: name: Build Linux @@ -663,6 +675,7 @@ jobs: needs: - validate-version - verify-go-bindings-assets + - verify-cargo-package - build-linux - build-macos - build-windows diff --git a/Cargo.toml b/Cargo.toml index 97ee799..9f25179 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -17,14 +17,16 @@ edition = "2021" rust-version = "1.88.0" license = "MIT OR Apache-2.0" repository = "https://github.com/3leaps/sysprims" +homepage = "https://github.com/3leaps/sysprims" authors = ["3leaps Architecture Council"] +publish = false [workspace.dependencies] -sysprims-core = { path = "crates/sysprims-core" } -sysprims-timeout = { path = "crates/sysprims-timeout" } -sysprims-signal = { path = "crates/sysprims-signal" } -sysprims-proc = { path = "crates/sysprims-proc" } -sysprims-session = { path = "crates/sysprims-session" } +sysprims-core = { version = "0.2.2", path = "crates/sysprims-core" } +sysprims-timeout = { version = "0.2.2", path = "crates/sysprims-timeout" } +sysprims-signal = { version = "0.2.2", path = "crates/sysprims-signal" } +sysprims-proc = { version = "0.2.2", path = "crates/sysprims-proc" } +sysprims-session = { version = "0.2.2", path = "crates/sysprims-session" } # Fulmen Ecosystem # NOTE: Pinned to ensure stable semantics across bindings and avoid accidental diff --git a/Makefile b/Makefile index 12c2e8d..39bcb62 100644 --- a/Makefile +++ b/Makefile @@ -15,7 +15,7 @@ .PHONY: check-windows check-windows-msvc check-windows-gnu .PHONY: build-release build-ffi cbindgen typescript-api-generate typescript-api-check .PHONY: build-local-go build-local-ffi-shared go-test header-go go-header go-prebuilt-darwin -.PHONY: release-clean release-download release-checksums release-sign +.PHONY: release-check release-clean release-download release-checksums release-sign .PHONY: release-export-keys release-verify-checksums release-verify-signatures .PHONY: release-verify-keys release-notes release-upload release-preflight .PHONY: release-guard-tag-version release-guard-tag-version-post @@ -96,6 +96,7 @@ help: ## Show available targets @echo "" @echo "Release (manual signing workflow):" @echo " release-preflight Verify all pre-tag requirements (REQUIRED before tagging)" + @echo " release-check Version consistency + cargo package (does not publish)" @echo " release-download Download CI artifacts from GitHub" @echo " release-checksums Generate SHA256SUMS and SHA512SUMS" @echo " release-sign Sign checksums (requires SYSPRIMS_MINISIGN_KEY)" @@ -699,6 +700,18 @@ release-verify-keys: ## Verify exported keys are public-only release-verify: release-verify-checksums release-verify-signatures release-verify-keys ## Run all release verification @echo "[ok] All release verifications passed" +release-check: version-check ## Version consistency + package check (does not publish) + @echo "Checking release readiness..." + @echo "" + @echo "Packaging workspace crates (does not cargo publish)..." + $(CARGO) package --workspace --no-verify + @echo "[ok] Package check passed" + @echo "" + @echo "Release checklist:" + @echo " ✓ Version consistency validated" + @echo " ✓ Package check passed" + @echo " ✓ cargo publish was not run" + release-notes: ## Copy release notes to dist @src="docs/releases/$(SYSPRIMS_RELEASE_TAG).md"; \ if [ -f "$$src" ]; then \ diff --git a/RELEASE_CHECKLIST.md b/RELEASE_CHECKLIST.md index 58b0196..55937c7 100644 --- a/RELEASE_CHECKLIST.md +++ b/RELEASE_CHECKLIST.md @@ -197,6 +197,75 @@ Notes: Integrity rule: anything we intentionally publish as a release asset must be covered by the signed checksum manifests. +### crates.io (library crates only, after the tag) + +Do this only after the exact release tag is on `origin` and points at the +intended release commit. The principal or Echo lead must explicitly cue the +upload. Token and owners stay out of the tree. + +`make release-check` / `cargo package --workspace --no-verify` creates local +tarballs. It does **not** publish anything to crates.io. On the first +publication of these crate names, dependent build verification happens in the +cued publish sequence after predecessor crates are indexed. + +What gets published: + +| Crate | crates.io | +|-------|-----------| +| `sysprims-core` | yes (first) | +| `sysprims-signal` | yes (after core is indexed) | +| `sysprims-session` | yes (after core is indexed) | +| `sysprims-proc` | yes (after signal is indexed) | +| `sysprims-timeout` | yes (last) | +| `sysprims-cli` | **never** (`publish = false`) | +| `sysprims-ffi` | **no** (`publish = false`) | +| `sysprims-ts-napi` | **no** (`publish = false`) | + +Workspace `publish` stays `false`. The five public Rust libraries opt in. + +Use a crates.io token scoped to the five library crate names. First upload of a +crate name requires `publish-new` and `publish-update`; later releases should +use update-only scope. Never store the token in this repository. + +Publish from a clean checkout of the tag: + +```bash +VERSION=$(cat VERSION) +git checkout "v${VERSION}" +cargo publish --dry-run -p sysprims-core +cargo publish -p sysprims-core +cargo info --registry crates-io "sysprims-core@${VERSION}" +cargo publish --dry-run -p sysprims-signal +cargo publish -p sysprims-signal +cargo info --registry crates-io "sysprims-signal@${VERSION}" +cargo publish --dry-run -p sysprims-session +cargo publish -p sysprims-session +cargo info --registry crates-io "sysprims-session@${VERSION}" +cargo publish --dry-run -p sysprims-proc +cargo publish -p sysprims-proc +cargo info --registry crates-io "sysprims-proc@${VERSION}" +cargo publish --dry-run -p sysprims-timeout +cargo publish -p sysprims-timeout +``` + +- [ ] Dry-run then publish each crate in dependency order. +- [ ] Confirm each predecessor with + `cargo info --registry crates-io @${VERSION}` before the next + dependent publish. +- [ ] On the first upload of these crate names, expect standalone dry-runs for + dependent crates to fail until predecessor crates are actually indexed. +- [ ] Do **not** `cargo publish -p sysprims-cli`, `sysprims-ffi`, or + `sysprims-ts-napi`. + +Negative control: + +```bash +cargo publish --dry-run -p sysprims-cli +cargo publish --dry-run -p sysprims-ffi +cargo publish --dry-run -p sysprims-ts-napi +# expected: error, crate cannot be published +``` + ## 2. Manual Signing (Local Machine) ### Set Environment Variables diff --git a/bindings/typescript/sysprims/native/Cargo.toml b/bindings/typescript/sysprims/native/Cargo.toml index 458fe79..1793e9c 100644 --- a/bindings/typescript/sysprims/native/Cargo.toml +++ b/bindings/typescript/sysprims/native/Cargo.toml @@ -4,7 +4,9 @@ version.workspace = true edition.workspace = true license.workspace = true repository.workspace = true +homepage.workspace = true authors.workspace = true +publish.workspace = true # NOTE: napi-build requires rustc >= 1.88. rust-version = "1.88.0" description = "Node-API addon for @3leaps/sysprims (TypeScript bindings)" diff --git a/crates/sysprims-cli/Cargo.toml b/crates/sysprims-cli/Cargo.toml index 498ff95..e219ca5 100644 --- a/crates/sysprims-cli/Cargo.toml +++ b/crates/sysprims-cli/Cargo.toml @@ -4,8 +4,10 @@ version.workspace = true edition.workspace = true license.workspace = true repository.workspace = true +homepage.workspace = true authors.workspace = true rust-version.workspace = true +publish.workspace = true description = "CLI tools for sysprims process utilities" [[bin]] diff --git a/crates/sysprims-core/Cargo.toml b/crates/sysprims-core/Cargo.toml index 34cfd7c..217ac50 100644 --- a/crates/sysprims-core/Cargo.toml +++ b/crates/sysprims-core/Cargo.toml @@ -4,9 +4,13 @@ version.workspace = true edition.workspace = true license.workspace = true repository.workspace = true +homepage.workspace = true authors.workspace = true rust-version.workspace = true +publish = true description = "Core types, errors, and platform abstractions for sysprims" +documentation = "https://docs.rs/sysprims-core" +readme = "README.md" [dependencies] rsfulmen = { workspace = true } diff --git a/crates/sysprims-core/README.md b/crates/sysprims-core/README.md new file mode 100644 index 0000000..74de575 --- /dev/null +++ b/crates/sysprims-core/README.md @@ -0,0 +1,6 @@ +# sysprims-core + +Core types, errors, and platform abstractions for the sysprims Rust crates. + +See the [sysprims repository](https://github.com/3leaps/sysprims) for +documentation, release notes, and supported platform policy. diff --git a/crates/sysprims-proc/Cargo.toml b/crates/sysprims-proc/Cargo.toml index 9cea982..6b3aaab 100644 --- a/crates/sysprims-proc/Cargo.toml +++ b/crates/sysprims-proc/Cargo.toml @@ -4,9 +4,13 @@ version.workspace = true edition.workspace = true license.workspace = true repository.workspace = true +homepage.workspace = true authors.workspace = true rust-version.workspace = true +publish = true description = "Process inspection and enumeration utilities" +documentation = "https://docs.rs/sysprims-proc" +readme = "README.md" [features] default = ["proc_ext"] diff --git a/crates/sysprims-proc/README.md b/crates/sysprims-proc/README.md new file mode 100644 index 0000000..b440002 --- /dev/null +++ b/crates/sysprims-proc/README.md @@ -0,0 +1,6 @@ +# sysprims-proc + +Process inspection and enumeration utilities for sysprims. + +See the [sysprims repository](https://github.com/3leaps/sysprims) for +documentation, release notes, and supported platform policy. diff --git a/crates/sysprims-session/Cargo.toml b/crates/sysprims-session/Cargo.toml index d670b64..4b7a896 100644 --- a/crates/sysprims-session/Cargo.toml +++ b/crates/sysprims-session/Cargo.toml @@ -4,9 +4,13 @@ version.workspace = true edition.workspace = true license.workspace = true repository.workspace = true +homepage.workspace = true authors.workspace = true rust-version.workspace = true +publish = true description = "GPL-free session and process group management (setsid, nohup)" +documentation = "https://docs.rs/sysprims-session" +readme = "README.md" [dependencies] sysprims-core.workspace = true diff --git a/crates/sysprims-session/README.md b/crates/sysprims-session/README.md index ff366ba..2c92441 100644 --- a/crates/sysprims-session/README.md +++ b/crates/sysprims-session/README.md @@ -1,5 +1,10 @@ # sysprims-session +Session and process group acquisition primitives for sysprims. + +See the [sysprims repository](https://github.com/3leaps/sysprims) for +documentation, release notes, and supported platform policy. + GPL-free session and process group management primitives. ## Overview diff --git a/crates/sysprims-signal/Cargo.toml b/crates/sysprims-signal/Cargo.toml index d3b71b9..61b599a 100644 --- a/crates/sysprims-signal/Cargo.toml +++ b/crates/sysprims-signal/Cargo.toml @@ -4,9 +4,13 @@ version.workspace = true edition.workspace = true license.workspace = true repository.workspace = true +homepage.workspace = true authors.workspace = true rust-version.workspace = true +publish = true description = "Signal dispatch and process group management" +documentation = "https://docs.rs/sysprims-signal" +readme = "README.md" [features] default = [] diff --git a/crates/sysprims-signal/README.md b/crates/sysprims-signal/README.md new file mode 100644 index 0000000..910ba97 --- /dev/null +++ b/crates/sysprims-signal/README.md @@ -0,0 +1,6 @@ +# sysprims-signal + +Signal dispatch and process group management primitives for sysprims. + +See the [sysprims repository](https://github.com/3leaps/sysprims) for +documentation, release notes, and supported platform policy. diff --git a/crates/sysprims-timeout/Cargo.toml b/crates/sysprims-timeout/Cargo.toml index 8185ce5..4ff0415 100644 --- a/crates/sysprims-timeout/Cargo.toml +++ b/crates/sysprims-timeout/Cargo.toml @@ -4,9 +4,13 @@ version.workspace = true edition.workspace = true license.workspace = true repository.workspace = true +homepage.workspace = true authors.workspace = true rust-version.workspace = true +publish = true description = "Process timeout with group-by-default tree management" +documentation = "https://docs.rs/sysprims-timeout" +readme = "README.md" [features] default = [] diff --git a/crates/sysprims-timeout/README.md b/crates/sysprims-timeout/README.md new file mode 100644 index 0000000..0d7b117 --- /dev/null +++ b/crates/sysprims-timeout/README.md @@ -0,0 +1,6 @@ +# sysprims-timeout + +Process timeout utilities with group-by-default tree management for sysprims. + +See the [sysprims repository](https://github.com/3leaps/sysprims) for +documentation, release notes, and supported platform policy. diff --git a/ffi/sysprims-ffi/Cargo.toml b/ffi/sysprims-ffi/Cargo.toml index 5b2a03c..0fa2238 100644 --- a/ffi/sysprims-ffi/Cargo.toml +++ b/ffi/sysprims-ffi/Cargo.toml @@ -4,8 +4,10 @@ version.workspace = true edition.workspace = true license.workspace = true repository.workspace = true +homepage.workspace = true authors.workspace = true rust-version.workspace = true +publish.workspace = true description = "C-ABI exports for sysprims" [lib] diff --git a/scripts/version-pack.mjs b/scripts/version-pack.mjs index 16808e1..76a22b2 100644 --- a/scripts/version-pack.mjs +++ b/scripts/version-pack.mjs @@ -196,7 +196,8 @@ function checkCargo(root, expected, errors) { dependency.source === null && dependency.req !== "*" && dependency.req !== expected && - dependency.req !== `=${expected}` + dependency.req !== `=${expected}` && + dependency.req !== `^${expected}` ) { errors.push( `Cargo internal dependency ${pkg.name} -> ${dependency.name} pins ${dependency.req}, expected ${expected}`, @@ -403,6 +404,26 @@ function updateJsonSurfaces(root, version) { writeJsonAtomic(lockPath, lock); } +function updateCargoWorkspaceDependencyPins(root, version) { + const cargoPath = join(root, "Cargo.toml"); + let cargoToml = readFileSync(cargoPath, "utf8"); + let replaced = 0; + cargoToml = cargoToml.replace( + /^(sysprims-[A-Za-z0-9_-]+\s*=\s*\{[^\n]*\})$/gm, + (line) => { + if (!/\bversion\s*=/.test(line)) { + fail(`workspace dependency is missing version pin: ${line}`); + } + replaced += 1; + return line.replace(/\bversion\s*=\s*"[^"]+"/, `version = "${version}"`); + } + ); + if (replaced === 0) { + fail("cannot find versioned sysprims workspace dependency pins"); + } + writeTextAtomic(cargoPath, cargoToml); +} + function withRollback(root, paths, operation) { const backupRoot = mkdtempSync(join(tmpdir(), "sysprims-version-pack-")); try { @@ -440,6 +461,7 @@ export function synchronize( run(root, "cargo", ["set-version", "--workspace", version], { stdio: "inherit", }); + updateCargoWorkspaceDependencyPins(root, version); afterCargoSetVersion?.(); updateJsonSurfaces(root, version); check(root, true); diff --git a/scripts/version-pack.test.mjs b/scripts/version-pack.test.mjs index e63b728..5963038 100644 --- a/scripts/version-pack.test.mjs +++ b/scripts/version-pack.test.mjs @@ -79,7 +79,7 @@ version = "0.2.1" edition = "2021" [workspace.dependencies] -sysprims-fixture = { path = "crate" } +sysprims-fixture = { version = "0.2.1", path = "crate" } serde = "0.2.1" `, ); @@ -248,6 +248,10 @@ test("sync updates owned fields and removes only stale resolution evidence", () readFileSync(join(root, "Cargo.lock"), "utf8"), /name = "sysprims-fixture"\nversion = "0\.2\.2"/, ); + assert.match( + readFileSync(join(root, "Cargo.toml"), "utf8"), + /sysprims-fixture = \{ version = "0\.2\.2", path = "crate" \}/, + ); assert.equal(version(root, "check").status, 0); }); From 87978ba5c0ae47c5c46aba2a0f6a6af9fa4cf5d0 Mon Sep 17 00:00:00 2001 From: Dave Thompson Date: Mon, 31 Aug 2026 20:37:33 -0400 Subject: [PATCH 2/2] build: sync TypeScript package lock Generated-by: OpenAI Codex (GPT-5) Supervised-by: @3leapsdave Role: devlead --- .../typescript/sysprims/package-lock.json | 124 ++++++++++++++++++ 1 file changed, 124 insertions(+) diff --git a/bindings/typescript/sysprims/package-lock.json b/bindings/typescript/sysprims/package-lock.json index abc09e9..5015341 100644 --- a/bindings/typescript/sysprims/package-lock.json +++ b/bindings/typescript/sysprims/package-lock.json @@ -26,6 +26,130 @@ "@3leaps/sysprims-win32-x64-msvc": "0.2.2" } }, + "node_modules/@3leaps/sysprims-darwin-arm64": { + "version": "0.2.2", + "resolved": "https://registry.npmjs.org/@3leaps/sysprims-darwin-arm64/-/sysprims-darwin-arm64-0.2.2.tgz", + "integrity": "sha512-lZUEcL8lwAL9RoYkICAxDb31sT4CiFj0KQ+/Ld3ytFAUsm11fxQeehWdLEjCXCWwHxPn8eVzMAB0J8q+ALeiMw==", + "cpu": [ + "arm64" + ], + "license": "MIT OR Apache-2.0", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@3leaps/sysprims-linux-arm64-gnu": { + "version": "0.2.2", + "resolved": "https://registry.npmjs.org/@3leaps/sysprims-linux-arm64-gnu/-/sysprims-linux-arm64-gnu-0.2.2.tgz", + "integrity": "sha512-HlXA/+P43DHEE1VvdfQA9uQj50XwN0Z89yplyfxkG98oqgOkg0tb2cQjANhD+TCImSB2lVMekiY6ANn58lgyQg==", + "cpu": [ + "arm64" + ], + "libc": [ + "glibc" + ], + "license": "MIT OR Apache-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@3leaps/sysprims-linux-arm64-musl": { + "version": "0.2.2", + "resolved": "https://registry.npmjs.org/@3leaps/sysprims-linux-arm64-musl/-/sysprims-linux-arm64-musl-0.2.2.tgz", + "integrity": "sha512-rbXH2f12koNup/dGoHCvSKsZG8eW1Mv5IJQJhopb/Kqmy4LTECkhWg5aGpxKJCISpPL43ZPf8cmMgNlOSNgcHg==", + "cpu": [ + "arm64" + ], + "libc": [ + "musl" + ], + "license": "MIT OR Apache-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@3leaps/sysprims-linux-x64-gnu": { + "version": "0.2.2", + "resolved": "https://registry.npmjs.org/@3leaps/sysprims-linux-x64-gnu/-/sysprims-linux-x64-gnu-0.2.2.tgz", + "integrity": "sha512-LWl0WudAa4zF7V67EGbGCLfrSBd75pQpaBJjaBhFnsQ7xerH6cvSvatXT1h2rkDAuVE1jIEsBktjsWnoWFOb3g==", + "cpu": [ + "x64" + ], + "libc": [ + "glibc" + ], + "license": "MIT OR Apache-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@3leaps/sysprims-linux-x64-musl": { + "version": "0.2.2", + "resolved": "https://registry.npmjs.org/@3leaps/sysprims-linux-x64-musl/-/sysprims-linux-x64-musl-0.2.2.tgz", + "integrity": "sha512-e9IAXG7JUmYj2eMek58439rI9A/pr/ns5OdNotuVPqDs+TDF+hNUVTy5qudcTVUFxf/Oi4QW3meAuaP/bOsRlw==", + "cpu": [ + "x64" + ], + "libc": [ + "musl" + ], + "license": "MIT OR Apache-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@3leaps/sysprims-win32-arm64-msvc": { + "version": "0.2.2", + "resolved": "https://registry.npmjs.org/@3leaps/sysprims-win32-arm64-msvc/-/sysprims-win32-arm64-msvc-0.2.2.tgz", + "integrity": "sha512-Pq047X5PC3o1+zo9t43Nz9E/5QgjlqnCsf5FAEMvvnzxz+Z3rRumhclx7P0qsGVaJcww0OuEQjZufwJkbtaKNw==", + "cpu": [ + "arm64" + ], + "license": "MIT OR Apache-2.0", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@3leaps/sysprims-win32-x64-msvc": { + "version": "0.2.2", + "resolved": "https://registry.npmjs.org/@3leaps/sysprims-win32-x64-msvc/-/sysprims-win32-x64-msvc-0.2.2.tgz", + "integrity": "sha512-BleN/fCNMjXdycTySB7pGglJbkYSMfwuGBj9BGuKWfXxYuTTtMpsk9+pNUskh8J/p5W39uGtXB8mO233YC64hw==", + "cpu": [ + "x64" + ], + "license": "MIT OR Apache-2.0", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=18" + } + }, "node_modules/@napi-rs/cli": { "version": "2.18.4", "resolved": "https://registry.npmjs.org/@napi-rs/cli/-/cli-2.18.4.tgz",