putitoutthere 0.2.34 → 0.2.36

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CHANGELOG.md CHANGED
@@ -16,6 +16,7 @@ are prefixed `**BREAKING**` and link to the matching section in
16
16
 
17
17
  ### Fixed
18
18
 
19
+ - Fixed: `putitoutthere`'s pre-build version rewrite now follows Cargo **workspace version inheritance** — a crate that sources its version from `[workspace.package].version` via `version.workspace = true` (the idiomatic polyglot layout: one Rust core wrapped by a PyO3 wheel and a napi addon) is now bumped correctly instead of failing the release. The maturin `write-version` (#276) and npm/pypi bundled-cli `write-crate-version` (#366) steps both rewrote only a literal `[package].version` and threw `Cargo.toml: no [package].version field found` on an inheriting member, because the inherited version lives in a different file — the workspace root — that neither step read. So a release for that layout was blocked, not merely mis-versioned. The rewrite path now resolves the manifest first: a literal `[package].version` is bumped in place byte-for-byte as before, while an inheriting member walks up to the nearest ancestor `[workspace]` `Cargo.toml` and rewrites its `[workspace.package].version`. Unparseable or version-less manifests fall through to the prior literal-only path (`replaceCargoVersion`'s existing error), so single-crate layouts are byte-for-byte unchanged. New `src/find-workspace-root.ts`, `src/replace-workspace-package-version.ts`, and `src/write-resolved-cargo-version.ts`; `write-version.ts` / `write-crate-version.ts` now delegate to the resolver. See [MIGRATIONS.md](./MIGRATIONS.md#version-bump-follows-cargo-workspace-inheritance). #428. (verified by: unit/ubuntu-latest)
19
20
  - Fixed: `putitoutthere publish` now writes a package's git tag when that version is already live on the registry but untagged, instead of skipping it silently. The publish loop's already-published branch (`isPublished` → true) returned *before* the tag-creation step, so a version that reached the registry on a half-failed run (the run died after publishing but before tagging — e.g. a cancelled downstream job) was left published-but-untagged and never self-healed: piot derives "last released version" from git tags, so on every later run the package looked unreleased, fell back to `first_version` (already live), skipped again, and stayed stuck forever — while its dependents kept bumping and publishing, opening unflagged version skew. The skip branch now calls a shared, idempotent `ensureTag` (new `src/ensure-tag.ts`; a no-op when the tag already exists, and reused by the normal post-publish tag step), so a stuck package self-heals on its next release run. See [MIGRATIONS.md](./MIGRATIONS.md#publish-path-auto-heal-missing-tags). #407. (verified by: integration, unit/ubuntu-latest)
20
21
  - Fixed: a `kind = "pypi"` `build = "maturin"` package whose wheel is **Python-version-independent** — `[tool.maturin].bindings = "bin"` (a Rust-binary wheel tagged `py3-none`) or a pyo3 `abi3` / `abi3-pyXY` extension (a stable-ABI wheel tagged `cp3x-abi3`) — no longer fans its wheel build across every CPython version `requires-python` / `python_versions` resolves to. Such a wheel is byte-identical regardless of the interpreter that built it, so the fan produced N duplicate wheels: each fanned row uploaded under its own `-py<ver>` artifact, and the documented consumer `pypi-publish` recipe's `actions/download-artifact … merge-multiple: true` then extracted N copies of the same wheel filename onto one path concurrently — a torn write that failed `twine check` with `zipfile.BadZipFile` (observed on a `bindings = "bin"`, `requires-python = ">=3.9"` package: six identical wheels per platform, the x86_64-linux copy losing its extraction race while macOS/aarch64 won theirs). The planner (`src/plan.ts`) now collapses the fan to a single wheel row per target for these packages — built once on the newest resolved interpreter, keeping the historical unsuffixed `<pkg>-wheel-<triple>` artifact name — and the new `src/wheel-abi.ts` detects version independence from the package's real `pyproject.toml` (`[tool.maturin].bindings`, `[tool.maturin].features`) and `Cargo.toml` (an `abi3` / `abi3-pyXY` feature on the `pyo3` / `pyo3-ffi` dependency). Ordinary per-version extension modules (no abi3, no `bindings = "bin"`) still fan and keep their `-py<ver>` suffixes; the sdist row is unchanged. Also saves the redundant N−1 wheel builds. Detection is conservative — an unrecognized abi3 setup (workspace-inherited `pyo3`, a target-specific dependency table) falls back to fanning, never worse than before. See [README → `kind = "pypi"`](./README.md#kind--pypi) and [MIGRATIONS.md](./MIGRATIONS.md#pypi-version-independent-wheels-build-once). #401. (verified by: integration, unit/ubuntu-latest)
21
22
  - Fixed: `kind = "npm"` releases no longer abort mid-matrix on npm's Sigstore/Rekor transparency-log dedupe (`TLOG_CREATE_ENTRY_ERROR`, HTTP 409, "an equivalent entry already exists in the transparency log"). npm's retry-on-transient-network-error re-submits a byte-identical `--provenance` attestation after a flaky response, and Rekor rejects the duplicate with a 409. Previously only the registry-PUT edition of that race (`E403` "cannot publish over the previously published versions", matched by `looksLikePublishOverRace`) was tolerated, so the attestation edition fell through to the generic `throw` and killed the publish job — for a multi-platform (`napi` / `bundled-cli`) package that meant the first sub-package published, the next 409'd, and the remaining sub-packages **and the main package** were left unpublished (a partial release; observed on a 3-language repo's first `0.0.1`). Unlike the E403 shape, a 409 from Rekor does **not** by itself prove the artifact landed (the first submit may have written the transparency-log entry but still failed the registry upload), so both the main-package (`src/handlers/npm.ts`) and per-platform (`src/handlers/npm-platform.ts`) publish catches now re-probe `npm view` to disambiguate: present on the registry ⇒ treated as a benign duplicate (`already-published` / counted as published, and the cascade's `optionalDependencies` rewrite still runs); absent ⇒ a genuine partial publish, surfaced as an actionable error telling the operator to re-run the release so a fresh attestation (new `runID`/attempt ⇒ new Rekor entry) gets past the dedupe. The engine cannot retry the attestation in-process. See [MIGRATIONS.md](./MIGRATIONS.md#npm-tlog_create_entry_error-409-provenance-retry-race). #399. (verified by: unit/ubuntu-latest)
package/MIGRATIONS.md CHANGED
@@ -21,6 +21,39 @@ Each section covers five things, in order:
21
21
 
22
22
  ## Unreleased
23
23
 
24
+ ### Version bump follows Cargo workspace inheritance
25
+
26
+ **Summary.** piot's pre-build version rewrite — the maturin `write-version`
27
+ step (#276) and the npm / pypi bundled-cli `write-crate-version` step
28
+ (#366) — now understands Cargo **workspace version inheritance**. A crate
29
+ whose `[package]` declares `version.workspace = true` and inherits its
30
+ version from `[workspace.package].version` at the workspace root (the
31
+ standard shape for a single Rust core re-exposed as a PyO3 wheel and a napi
32
+ addon) previously failed these steps with `Cargo.toml: no [package].version
33
+ field found`. The rewrite now resolves the inheritance: a literal
34
+ `[package].version` is bumped in place, while an inheriting member's version
35
+ is bumped at the workspace root's `[workspace.package].version`.
36
+
37
+ **Required changes.** None. Additive — a repo layout that previously errored
38
+ at release now works. If you kept a duplicated literal `[package].version`
39
+ in each crate to work around this, you may switch the members to
40
+ `version.workspace = true` and let piot bump the workspace root.
41
+
42
+ **Deprecations removed.** None.
43
+
44
+ **Behavior changes without code changes.** A `kind = "pypi"`
45
+ `build = "maturin"` or `kind = "npm"` `build = "bundled-cli"` package whose
46
+ crate inherits its version from the workspace no longer fails the build
47
+ step; the produced wheel / cross-compiled binary now carries the planned
48
+ release version. Single-crate layouts with a literal `[package].version` are
49
+ byte-for-byte unchanged.
50
+
51
+ **Verification.** In a cargo workspace whose root declares
52
+ `[workspace.package]` `version = "X"` and whose maturin/napi crate sets
53
+ `version.workspace = true`, run a release: the published wheel / `.node`
54
+ carries the planned release version (not the stale on-disk `X`), and the
55
+ workspace-root `Cargo.toml` is the manifest rewritten during the build.
56
+
24
57
  ### verify: publish trust posture
25
58
 
26
59
  **Summary.** New command `putitoutthere verify` reports, per package, how
@@ -0,0 +1,13 @@
1
+ /**
2
+ * Walk up from `startDir` for the Cargo.toml that declares a `[workspace]`
3
+ * table — the root that a `version.workspace = true` member inherits its
4
+ * version from. Returns that directory, or `null` if none exists between
5
+ * `startDir` and the filesystem root. #428.
6
+ *
7
+ * Sync `readFileSync` with an ENOENT skip (not an `existsSync` precheck)
8
+ * to avoid the TOCTOU shape CodeQL flags, matching the rest of the
9
+ * version-write path. A malformed ancestor Cargo.toml is not treated as
10
+ * the workspace root; the walk continues past it.
11
+ */
12
+ export declare function findWorkspaceRoot(startDir: string): string | null;
13
+ //# sourceMappingURL=find-workspace-root.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"find-workspace-root.d.ts","sourceRoot":"","sources":["../src/find-workspace-root.ts"],"names":[],"mappings":"AAKA;;;;;;;;;;GAUG;AACH,wBAAgB,iBAAiB,CAAC,QAAQ,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,CA4BjE"}
@@ -0,0 +1,46 @@
1
+ import { readFileSync } from 'node:fs';
2
+ import { dirname, join } from 'node:path';
3
+ import { parse as parseToml } from 'smol-toml';
4
+ /**
5
+ * Walk up from `startDir` for the Cargo.toml that declares a `[workspace]`
6
+ * table — the root that a `version.workspace = true` member inherits its
7
+ * version from. Returns that directory, or `null` if none exists between
8
+ * `startDir` and the filesystem root. #428.
9
+ *
10
+ * Sync `readFileSync` with an ENOENT skip (not an `existsSync` precheck)
11
+ * to avoid the TOCTOU shape CodeQL flags, matching the rest of the
12
+ * version-write path. A malformed ancestor Cargo.toml is not treated as
13
+ * the workspace root; the walk continues past it.
14
+ */
15
+ export function findWorkspaceRoot(startDir) {
16
+ let dir = startDir;
17
+ for (;;) {
18
+ let source = '';
19
+ try {
20
+ source = readFileSync(join(dir, 'Cargo.toml'), 'utf8');
21
+ }
22
+ catch (err) {
23
+ if (err.code !== 'ENOENT') {
24
+ throw err;
25
+ }
26
+ }
27
+ if (source) {
28
+ let parsed;
29
+ try {
30
+ parsed = parseToml(source);
31
+ }
32
+ catch {
33
+ parsed = undefined;
34
+ }
35
+ if (parsed && typeof parsed === 'object' && 'workspace' in parsed) {
36
+ return dir;
37
+ }
38
+ }
39
+ const parent = dirname(dir);
40
+ if (parent === dir) {
41
+ return null;
42
+ }
43
+ dir = parent;
44
+ }
45
+ }
46
+ //# sourceMappingURL=find-workspace-root.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"find-workspace-root.js","sourceRoot":"","sources":["../src/find-workspace-root.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAE,MAAM,SAAS,CAAC;AACvC,OAAO,EAAE,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;AAE1C,OAAO,EAAE,KAAK,IAAI,SAAS,EAAE,MAAM,WAAW,CAAC;AAE/C;;;;;;;;;;GAUG;AACH,MAAM,UAAU,iBAAiB,CAAC,QAAgB;IAChD,IAAI,GAAG,GAAG,QAAQ,CAAC;IACnB,SAAS,CAAC;QACR,IAAI,MAAM,GAAG,EAAE,CAAC;QAChB,IAAI,CAAC;YACH,MAAM,GAAG,YAAY,CAAC,IAAI,CAAC,GAAG,EAAE,YAAY,CAAC,EAAE,MAAM,CAAC,CAAC;QACzD,CAAC;QAAC,OAAO,GAAG,EAAE,CAAC;YACb,IAAK,GAA6B,CAAC,IAAI,KAAK,QAAQ,EAAE,CAAC;gBACrD,MAAM,GAAG,CAAC;YACZ,CAAC;QACH,CAAC;QACD,IAAI,MAAM,EAAE,CAAC;YACX,IAAI,MAAe,CAAC;YACpB,IAAI,CAAC;gBACH,MAAM,GAAG,SAAS,CAAC,MAAM,CAAC,CAAC;YAC7B,CAAC;YAAC,MAAM,CAAC;gBACP,MAAM,GAAG,SAAS,CAAC;YACrB,CAAC;YACD,IAAI,MAAM,IAAI,OAAO,MAAM,KAAK,QAAQ,IAAI,WAAW,IAAI,MAAM,EAAE,CAAC;gBAClE,OAAO,GAAG,CAAC;YACb,CAAC;QACH,CAAC;QACD,MAAM,MAAM,GAAG,OAAO,CAAC,GAAG,CAAC,CAAC;QAC5B,IAAI,MAAM,KAAK,GAAG,EAAE,CAAC;YACnB,OAAO,IAAI,CAAC;QACd,CAAC;QACD,GAAG,GAAG,MAAM,CAAC;IACf,CAAC;AACH,CAAC"}
@@ -0,0 +1,9 @@
1
+ /**
2
+ * Rewrites the first `version = "..."` under the `[workspace.package]`
3
+ * table, preserving the rest of the file byte-for-byte. Throws if the
4
+ * field is absent. The `[workspace.package]` mirror of
5
+ * `replaceCargoVersion` (`src/handlers/crates.ts`), for crates that
6
+ * inherit their version via `version.workspace = true`. #428.
7
+ */
8
+ export declare function replaceWorkspacePackageVersion(source: string, version: string): string;
9
+ //# sourceMappingURL=replace-workspace-package-version.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"replace-workspace-package-version.d.ts","sourceRoot":"","sources":["../src/replace-workspace-package-version.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AACH,wBAAgB,8BAA8B,CAAC,MAAM,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,GAAG,MAAM,CActF"}
@@ -0,0 +1,23 @@
1
+ /**
2
+ * Rewrites the first `version = "..."` under the `[workspace.package]`
3
+ * table, preserving the rest of the file byte-for-byte. Throws if the
4
+ * field is absent. The `[workspace.package]` mirror of
5
+ * `replaceCargoVersion` (`src/handlers/crates.ts`), for crates that
6
+ * inherit their version via `version.workspace = true`. #428.
7
+ */
8
+ export function replaceWorkspacePackageVersion(source, version) {
9
+ // [workspace.package] header, then the first `version = "x.y.z"` line.
10
+ const re = /(\[workspace\.package\][\s\S]*?)(^\s*version\s*=\s*")([^"]*)(")/m;
11
+ const m = re.exec(source);
12
+ if (!m) {
13
+ throw new Error('Cargo.toml: no [workspace.package].version field found');
14
+ }
15
+ const [, pre, prefix, old, suffix] = m;
16
+ if (old === version) {
17
+ return source;
18
+ }
19
+ const start = m.index + pre.length;
20
+ const end = start + prefix.length + old.length + suffix.length;
21
+ return source.slice(0, start) + prefix + version + suffix + source.slice(end);
22
+ }
23
+ //# sourceMappingURL=replace-workspace-package-version.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"replace-workspace-package-version.js","sourceRoot":"","sources":["../src/replace-workspace-package-version.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AACH,MAAM,UAAU,8BAA8B,CAAC,MAAc,EAAE,OAAe;IAC5E,uEAAuE;IACvE,MAAM,EAAE,GAAG,kEAAkE,CAAC;IAC9E,MAAM,CAAC,GAAG,EAAE,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC;IAC1B,IAAI,CAAC,CAAC,EAAE,CAAC;QACP,MAAM,IAAI,KAAK,CAAC,wDAAwD,CAAC,CAAC;IAC5E,CAAC;IACD,MAAM,CAAC,EAAE,GAAG,EAAE,MAAM,EAAE,GAAG,EAAE,MAAM,CAAC,GAAG,CAAwD,CAAC;IAC9F,IAAI,GAAG,KAAK,OAAO,EAAE,CAAC;QACpB,OAAO,MAAM,CAAC;IAChB,CAAC;IACD,MAAM,KAAK,GAAG,CAAC,CAAC,KAAK,GAAG,GAAG,CAAC,MAAM,CAAC;IACnC,MAAM,GAAG,GAAG,KAAK,GAAG,MAAM,CAAC,MAAM,GAAG,GAAG,CAAC,MAAM,GAAG,MAAM,CAAC,MAAM,CAAC;IAC/D,OAAO,MAAM,CAAC,KAAK,CAAC,CAAC,EAAE,KAAK,CAAC,GAAG,MAAM,GAAG,OAAO,GAAG,MAAM,GAAG,MAAM,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC;AAChF,CAAC"}
@@ -1 +1 @@
1
- {"version":3,"file":"write-crate-version.d.ts","sourceRoot":"","sources":["../src/write-crate-version.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;GAkBG;AAOH;;;;;;;;;;GAUG;AACH,wBAAgB,yBAAyB,CAAC,QAAQ,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,GAAG,MAAM,EAAE,CAiBrF"}
1
+ {"version":3,"file":"write-crate-version.d.ts","sourceRoot":"","sources":["../src/write-crate-version.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;GAkBG;AAOH;;;;;;;;;;GAUG;AACH,wBAAgB,yBAAyB,CAAC,QAAQ,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,GAAG,MAAM,EAAE,CAerF"}
@@ -17,9 +17,9 @@
17
17
  * `Cargo.toml`, so it needs a manifest-direct bump with no pyproject
18
18
  * gate. This is that command.
19
19
  */
20
- import { readFileSync, writeFileSync } from 'node:fs';
20
+ import { readFileSync } from 'node:fs';
21
21
  import { join } from 'node:path';
22
- import { replaceCargoVersion } from './handlers/crates.js';
22
+ import { writeResolvedCargoVersion } from './write-resolved-cargo-version.js';
23
23
  /**
24
24
  * Rewrite `[package].version` in the crate at `crateDir` to `version`
25
25
  * and return the list of absolute paths that were modified.
@@ -43,10 +43,6 @@ export function writeCrateVersionForBuild(crateDir, version) {
43
43
  }
44
44
  throw err;
45
45
  }
46
- const updated = replaceCargoVersion(original, version);
47
- if (updated !== original) {
48
- writeFileSync(cargoPath, updated, 'utf8');
49
- }
50
- return [cargoPath];
46
+ return writeResolvedCargoVersion(crateDir, original, version);
51
47
  }
52
48
  //# sourceMappingURL=write-crate-version.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"write-crate-version.js","sourceRoot":"","sources":["../src/write-crate-version.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;GAkBG;AAEH,OAAO,EAAE,YAAY,EAAE,aAAa,EAAE,MAAM,SAAS,CAAC;AACtD,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;AAEjC,OAAO,EAAE,mBAAmB,EAAE,MAAM,sBAAsB,CAAC;AAE3D;;;;;;;;;;GAUG;AACH,MAAM,UAAU,yBAAyB,CAAC,QAAgB,EAAE,OAAe;IACzE,MAAM,SAAS,GAAG,IAAI,CAAC,QAAQ,EAAE,YAAY,CAAC,CAAC;IAC/C,IAAI,QAAgB,CAAC;IACrB,IAAI,CAAC;QACH,QAAQ,GAAG,YAAY,CAAC,SAAS,EAAE,MAAM,CAAC,CAAC;IAC7C,CAAC;IAAC,OAAO,GAAG,EAAE,CAAC;QACb,IAAK,GAA6B,CAAC,IAAI,KAAK,QAAQ,EAAE,CAAC;YACrD,MAAM,IAAI,KAAK,CACb,gDAAgD,SAAS,wGAAwG,EACjK,EAAE,KAAK,EAAE,GAAG,EAAE,CACf,CAAC;QACJ,CAAC;QACD,MAAM,GAAG,CAAC;IACZ,CAAC;IACD,MAAM,OAAO,GAAG,mBAAmB,CAAC,QAAQ,EAAE,OAAO,CAAC,CAAC;IACvD,IAAI,OAAO,KAAK,QAAQ,EAAE,CAAC;QAAA,aAAa,CAAC,SAAS,EAAE,OAAO,EAAE,MAAM,CAAC,CAAC;IAAA,CAAC;IACtE,OAAO,CAAC,SAAS,CAAC,CAAC;AACrB,CAAC"}
1
+ {"version":3,"file":"write-crate-version.js","sourceRoot":"","sources":["../src/write-crate-version.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;GAkBG;AAEH,OAAO,EAAE,YAAY,EAAE,MAAM,SAAS,CAAC;AACvC,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;AAEjC,OAAO,EAAE,yBAAyB,EAAE,MAAM,mCAAmC,CAAC;AAE9E;;;;;;;;;;GAUG;AACH,MAAM,UAAU,yBAAyB,CAAC,QAAgB,EAAE,OAAe;IACzE,MAAM,SAAS,GAAG,IAAI,CAAC,QAAQ,EAAE,YAAY,CAAC,CAAC;IAC/C,IAAI,QAAgB,CAAC;IACrB,IAAI,CAAC;QACH,QAAQ,GAAG,YAAY,CAAC,SAAS,EAAE,MAAM,CAAC,CAAC;IAC7C,CAAC;IAAC,OAAO,GAAG,EAAE,CAAC;QACb,IAAK,GAA6B,CAAC,IAAI,KAAK,QAAQ,EAAE,CAAC;YACrD,MAAM,IAAI,KAAK,CACb,gDAAgD,SAAS,wGAAwG,EACjK,EAAE,KAAK,EAAE,GAAG,EAAE,CACf,CAAC;QACJ,CAAC;QACD,MAAM,GAAG,CAAC;IACZ,CAAC;IACD,OAAO,yBAAyB,CAAC,QAAQ,EAAE,QAAQ,EAAE,OAAO,CAAC,CAAC;AAChE,CAAC"}
@@ -0,0 +1,16 @@
1
+ /**
2
+ * Rewrite a crate's version to `version`, following Cargo workspace
3
+ * inheritance (#428):
4
+ *
5
+ * - a literal `[package].version = "x.y.z"` is rewritten in place;
6
+ * - a member that inherits via `version.workspace = true` has the
7
+ * workspace root's `[workspace.package].version` rewritten instead.
8
+ *
9
+ * `crateDir` is the crate's directory and `cargoSource` its already-read
10
+ * `Cargo.toml` (callers own the file-missing error message and hand the
11
+ * bytes here). Returns the absolute path(s) actually modified. Throws when
12
+ * the crate declares no resolvable version source (via `replaceCargoVersion`),
13
+ * or when it inherits but no ancestor `[workspace]` exists.
14
+ */
15
+ export declare function writeResolvedCargoVersion(crateDir: string, cargoSource: string, version: string): string[];
16
+ //# sourceMappingURL=write-resolved-cargo-version.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"write-resolved-cargo-version.d.ts","sourceRoot":"","sources":["../src/write-resolved-cargo-version.ts"],"names":[],"mappings":"AASA;;;;;;;;;;;;;GAaG;AACH,wBAAgB,yBAAyB,CACvC,QAAQ,EAAE,MAAM,EAChB,WAAW,EAAE,MAAM,EACnB,OAAO,EAAE,MAAM,GACd,MAAM,EAAE,CAmCV"}
@@ -0,0 +1,57 @@
1
+ import { readFileSync, writeFileSync } from 'node:fs';
2
+ import { join } from 'node:path';
3
+ import { parse as parseToml } from 'smol-toml';
4
+ import { findWorkspaceRoot } from './find-workspace-root.js';
5
+ import { replaceCargoVersion } from './handlers/crates.js';
6
+ import { replaceWorkspacePackageVersion } from './replace-workspace-package-version.js';
7
+ /**
8
+ * Rewrite a crate's version to `version`, following Cargo workspace
9
+ * inheritance (#428):
10
+ *
11
+ * - a literal `[package].version = "x.y.z"` is rewritten in place;
12
+ * - a member that inherits via `version.workspace = true` has the
13
+ * workspace root's `[workspace.package].version` rewritten instead.
14
+ *
15
+ * `crateDir` is the crate's directory and `cargoSource` its already-read
16
+ * `Cargo.toml` (callers own the file-missing error message and hand the
17
+ * bytes here). Returns the absolute path(s) actually modified. Throws when
18
+ * the crate declares no resolvable version source (via `replaceCargoVersion`),
19
+ * or when it inherits but no ancestor `[workspace]` exists.
20
+ */
21
+ export function writeResolvedCargoVersion(crateDir, cargoSource, version) {
22
+ // Detect inheritance from the parsed manifest. An unparseable manifest
23
+ // falls through to the literal path, preserving the pre-#428 regex
24
+ // behavior for odd-but-writable manifests.
25
+ let pkgVersion;
26
+ try {
27
+ pkgVersion = parseToml(cargoSource).package?.version;
28
+ }
29
+ catch {
30
+ pkgVersion = undefined;
31
+ }
32
+ const inherits = !!pkgVersion &&
33
+ typeof pkgVersion === 'object' &&
34
+ pkgVersion.workspace === true;
35
+ if (!inherits) {
36
+ // Literal `[package].version` (or genuinely absent — replaceCargoVersion
37
+ // throws the same "no [package].version" error the callers relied on).
38
+ const cargoPath = join(crateDir, 'Cargo.toml');
39
+ const updated = replaceCargoVersion(cargoSource, version);
40
+ if (updated !== cargoSource) {
41
+ writeFileSync(cargoPath, updated, 'utf8');
42
+ }
43
+ return [cargoPath];
44
+ }
45
+ const root = findWorkspaceRoot(crateDir);
46
+ if (root === null) {
47
+ throw new Error(`Cargo.toml: ${join(crateDir, 'Cargo.toml')} sets \`version.workspace = true\` but no ancestor [workspace] Cargo.toml was found. Declare [workspace.package].version at the workspace root.`);
48
+ }
49
+ const rootPath = join(root, 'Cargo.toml');
50
+ const rootSource = readFileSync(rootPath, 'utf8');
51
+ const updated = replaceWorkspacePackageVersion(rootSource, version);
52
+ if (updated !== rootSource) {
53
+ writeFileSync(rootPath, updated, 'utf8');
54
+ }
55
+ return [rootPath];
56
+ }
57
+ //# sourceMappingURL=write-resolved-cargo-version.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"write-resolved-cargo-version.js","sourceRoot":"","sources":["../src/write-resolved-cargo-version.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAE,aAAa,EAAE,MAAM,SAAS,CAAC;AACtD,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;AAEjC,OAAO,EAAE,KAAK,IAAI,SAAS,EAAE,MAAM,WAAW,CAAC;AAE/C,OAAO,EAAE,iBAAiB,EAAE,MAAM,0BAA0B,CAAC;AAC7D,OAAO,EAAE,mBAAmB,EAAE,MAAM,sBAAsB,CAAC;AAC3D,OAAO,EAAE,8BAA8B,EAAE,MAAM,wCAAwC,CAAC;AAExF;;;;;;;;;;;;;GAaG;AACH,MAAM,UAAU,yBAAyB,CACvC,QAAgB,EAChB,WAAmB,EACnB,OAAe;IAEf,uEAAuE;IACvE,mEAAmE;IACnE,2CAA2C;IAC3C,IAAI,UAAmB,CAAC;IACxB,IAAI,CAAC;QACH,UAAU,GAAI,SAAS,CAAC,WAAW,CAAyC,CAAC,OAAO,EAAE,OAAO,CAAC;IAChG,CAAC;IAAC,MAAM,CAAC;QACP,UAAU,GAAG,SAAS,CAAC;IACzB,CAAC;IACD,MAAM,QAAQ,GACZ,CAAC,CAAC,UAAU;QACZ,OAAO,UAAU,KAAK,QAAQ;QAC7B,UAAsC,CAAC,SAAS,KAAK,IAAI,CAAC;IAE7D,IAAI,CAAC,QAAQ,EAAE,CAAC;QACd,yEAAyE;QACzE,uEAAuE;QACvE,MAAM,SAAS,GAAG,IAAI,CAAC,QAAQ,EAAE,YAAY,CAAC,CAAC;QAC/C,MAAM,OAAO,GAAG,mBAAmB,CAAC,WAAW,EAAE,OAAO,CAAC,CAAC;QAC1D,IAAI,OAAO,KAAK,WAAW,EAAE,CAAC;YAAA,aAAa,CAAC,SAAS,EAAE,OAAO,EAAE,MAAM,CAAC,CAAC;QAAA,CAAC;QACzE,OAAO,CAAC,SAAS,CAAC,CAAC;IACrB,CAAC;IAED,MAAM,IAAI,GAAG,iBAAiB,CAAC,QAAQ,CAAC,CAAC;IACzC,IAAI,IAAI,KAAK,IAAI,EAAE,CAAC;QAClB,MAAM,IAAI,KAAK,CACb,eAAe,IAAI,CAAC,QAAQ,EAAE,YAAY,CAAC,iJAAiJ,CAC7L,CAAC;IACJ,CAAC;IACD,MAAM,QAAQ,GAAG,IAAI,CAAC,IAAI,EAAE,YAAY,CAAC,CAAC;IAC1C,MAAM,UAAU,GAAG,YAAY,CAAC,QAAQ,EAAE,MAAM,CAAC,CAAC;IAClD,MAAM,OAAO,GAAG,8BAA8B,CAAC,UAAU,EAAE,OAAO,CAAC,CAAC;IACpE,IAAI,OAAO,KAAK,UAAU,EAAE,CAAC;QAAA,aAAa,CAAC,QAAQ,EAAE,OAAO,EAAE,MAAM,CAAC,CAAC;IAAA,CAAC;IACvE,OAAO,CAAC,QAAQ,CAAC,CAAC;AACpB,CAAC"}
@@ -1 +1 @@
1
- {"version":3,"file":"write-version.d.ts","sourceRoot":"","sources":["../src/write-version.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;GAiBG;AAaH;;;;;;;;;;;;;;GAcG;AACH,wBAAgB,oBAAoB,CAAC,MAAM,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,GAAG,MAAM,EAAE,CAuD9E"}
1
+ {"version":3,"file":"write-version.d.ts","sourceRoot":"","sources":["../src/write-version.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;GAiBG;AAaH;;;;;;;;;;;;;;GAcG;AACH,wBAAgB,oBAAoB,CAAC,MAAM,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,GAAG,MAAM,EAAE,CAqD9E"}
@@ -16,11 +16,11 @@
16
16
  * for the same reason, so a CLI-direct invocation surfaces the same
17
17
  * actionable error rather than building an under-versioned artifact.
18
18
  */
19
- import { readFileSync, writeFileSync } from 'node:fs';
19
+ import { readFileSync } from 'node:fs';
20
20
  import { join } from 'node:path';
21
21
  import { parse as parseToml } from 'smol-toml';
22
22
  import { ErrorCodes } from './error-codes.js';
23
- import { replaceCargoVersion } from './handlers/crates.js';
23
+ import { writeResolvedCargoVersion } from './write-resolved-cargo-version.js';
24
24
  const DYNAMIC_VERSION_DOC_POINTER = 'https://thekevinscott.github.io/putitoutthere/guide/dynamic-versions';
25
25
  /**
26
26
  * Rewrite the version source for a maturin package and return the list
@@ -82,11 +82,7 @@ export function writeVersionForBuild(pkgDir, version) {
82
82
  /* v8 ignore next -- non-ENOENT read errors surface as-is */
83
83
  throw err;
84
84
  }
85
- const cargoUpdated = replaceCargoVersion(cargoOriginal, version);
86
- if (cargoUpdated !== cargoOriginal) {
87
- writeFileSync(cargoPath, cargoUpdated, 'utf8');
88
- }
89
- return [cargoPath];
85
+ return writeResolvedCargoVersion(pkgDir, cargoOriginal, version);
90
86
  }
91
87
  function isDynamicVersion(project) {
92
88
  const { dynamic } = project;
@@ -1 +1 @@
1
- {"version":3,"file":"write-version.js","sourceRoot":"","sources":["../src/write-version.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"write-version.js","sourceRoot":"","sources":["../src/write-version.ts"],"names":[],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "putitoutthere",
3
- "version": "0.2.34",
3
+ "version": "0.2.36",
4
4
  "description": "Polyglot release orchestrator for crates.io, PyPI, and npm",
5
5
  "license": "MIT",
6
6
  "repository": {
@@ -45,7 +45,7 @@
45
45
  "@eslint/js": "^10.0.1",
46
46
  "@stylistic/eslint-plugin": "^5.10.0",
47
47
  "@types/node": "^25.6.0",
48
- "@vercel/ncc": "^0.38.0",
48
+ "@vercel/ncc": "^0.44.0",
49
49
  "@vitest/coverage-v8": "^2.1.0",
50
50
  "eslint": "^10.2.1",
51
51
  "msw": "^2.13.4",