putitoutthere 0.2.26 → 0.2.28
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 +2 -0
- package/MIGRATIONS.md +244 -0
- package/README.md +75 -64
- package/dist/check.d.ts.map +1 -1
- package/dist/check.js +10 -1
- package/dist/check.js.map +1 -1
- package/dist/config.d.ts +12 -0
- package/dist/config.d.ts.map +1 -1
- package/dist/config.js +59 -0
- package/dist/config.js.map +1 -1
- package/dist/error-codes.d.ts +10 -0
- package/dist/error-codes.d.ts.map +1 -1
- package/dist/error-codes.js +11 -0
- package/dist/error-codes.js.map +1 -1
- package/dist/handlers/pypi.d.ts +0 -8
- package/dist/handlers/pypi.d.ts.map +1 -1
- package/dist/handlers/pypi.js +25 -47
- package/dist/handlers/pypi.js.map +1 -1
- package/dist/plan.d.ts +1 -1
- package/dist/plan.d.ts.map +1 -1
- package/dist/plan.js +15 -2
- package/dist/plan.js.map +1 -1
- package/dist/preflight.d.ts +38 -0
- package/dist/preflight.d.ts.map +1 -1
- package/dist/preflight.js +86 -0
- package/dist/preflight.js.map +1 -1
- package/dist/publish.d.ts.map +1 -1
- package/dist/publish.js +7 -1
- package/dist/publish.js.map +1 -1
- package/dist/write-version.d.ts +20 -33
- package/dist/write-version.d.ts.map +1 -1
- package/dist/write-version.js +33 -73
- package/dist/write-version.js.map +1 -1
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -12,6 +12,7 @@ are prefixed `**BREAKING**` and link to the matching section in
|
|
|
12
12
|
|
|
13
13
|
### Added
|
|
14
14
|
|
|
15
|
+
- **`kind = "npm"` `build = "bundled-cli"` now supports `[package.bundle_cli]` so the reusable workflow runs the Rust cross-compile itself.** Mirror of the pypi `[package.bundle_cli]` recipe shipped in #282 — the reusable workflow now, for every per-target row with `matrix.kind == 'npm' && matrix.build == 'bundled-cli' && matrix.bundle_cli && matrix.target != 'main'`: (1) `rustup target add ${{ matrix.target }}`, (2) `cargo build --release --target ${{ matrix.target }} --bin ${{ matrix.bundle_cli.bin }}` against `crate_path` (with optional `--features` / `--no-default-features` for crates that gate the CLI behind a Cargo feature), (3) copies the resulting binary (`.exe` on Windows) into the per-target staging directory the engine's npm-platform handler reads (`${{ matrix.artifact_path }}`, which is `<pkg.path>/build/<triple>` for single-mode rows and `<pkg.path>/build/<mode>-<triple>` for multi-mode rows), and (4) runs a permanent defense-in-depth build-content guard that asserts the staged binary exists before `actions/upload-artifact` runs. Schema: `bin` (required), `crate_path` (default `"."`), `features` (default `[]`), `no_default_features` (default `false`). The block is only valid when at least one bundled-cli entry exists in `build` and `targets` is non-empty. Existing consumers who authored a `scripts/build.cjs` keep working — the workflow runs first, then `npm run build --if-present` runs the consumer's script (which sees `build/<triple>/` already populated and probably no-ops); migrating to the declarative block is opt-in. Every bundled-cli npm consumer to date has written essentially the same cross-compile script and hit bugs at the seam between that script and the engine (#287 was the most recent). Absorbing the script closes the largest remaining piece of the consumer integration surface that exists for no architectural reason. See [README → Recipes → Bundled-CLI npm family](./README.md#bundled-cli-npm-family) and [MIGRATIONS.md](./MIGRATIONS.md#npm-bundle_cli-absorbed-into-the-reusable-workflow). #298.
|
|
15
16
|
- **Reusable workflow `.github/workflows/check.yml` (`workflow_call`).** PR-time config-sanity gate. Wires into a consumer's `pull_request:` CI in one line: `uses: thekevinscott/putitoutthere/.github/workflows/check.yml@v0`. Drives the same `putitoutthere check` engine entry the publish path already uses (no parallel diagnostic code path — the operative rule from the [refreshed non-goal #8](./notes/design-commitments.md#non-goals)), so the consumer-observable check list is whatever `check` reports: `putitoutthere.toml` parse + schema + common-mistakes detector, unique package names, `depends_on` cycle / dangling-ref detection, `[[package]].path` directory exists, `globs` match a tracked file, `tag_format` collisions, npm `repository` field, crates `description` / `license`, pypi `pyproject.toml` + `bundle_cli` binary declaration, npm target triple mapping. Findings are aggregated into one round-trip rather than failing on the first. A few seconds per PR, no per-target build, no `setup-python` / `setup-rust`. Permissions: `contents: read` only — the workflow holds no publishable artifact and cannot mint a registry token. Complementary to `build.yml`: `check.yml` is the cheap config-sanity gate, `build.yml` is the heavier per-target build gate; wire both. See [README → check](./README.md#1b-recommended-drop-in-githubworkflowscheckyml) and [MIGRATIONS.md](./MIGRATIONS.md#new-checkyml-reusable-workflow-for-pr-time-config-sanity). #317 (workflow shell) + #319 (check list, shipped via #321).
|
|
16
17
|
- **Internal-only: `cargo-http-registry` alt-registry for e2e crates.io 429 fallback and first-publish coverage.** `e2e (polyglot-everything) / publish` reliably 429s on `cargo publish` for the polyglot rust fixture once a few PRs have burned crates.io's 24h-per-crate quota (the fixture publishes a fresh `0.0.<timestamp>` version on every run; the per-crate quota gets exhausted faster than it resets). `e2e-fixture-job.yml`'s publish job now installs and runs [`cargo-http-registry`](https://github.com/d-e-s-o/cargo-http-registry) (an off-the-shelf, auth-free cargo alt-registry — the "Verdaccio for cargo" the workflow has needed) as a background process on every crates-bearing matrix row. Two engine seams in `src/handlers/crates.ts` consume it: `PIOT_CRATES_REGISTRY_FALLBACK` retries `cargo publish --index <url>` against the alt-registry on a 429-rate-limit-shaped failure from real crates.io and emits a `::warning::` workflow command naming the URL so reviewers see the path was taken; `PIOT_CRATES_REGISTRY_PRIMARY` routes the publish *only* at the alt-registry (no real-crates.io attempt, no fallback) — reserved for any future `*-first-publish` crates fixture, symmetric with #304's npm/Verdaccio shape. Non-429 failures (auth, network, validation) surface verbatim — the fallback predicate is scoped narrowly to rate-limit prose. The handler now passes `--token <placeholder>` alongside `--index <url>` because cargo's CLI parser refuses to invoke publish with `--index` unless a token is explicitly named (a wart of the cargo CLI surface; cargo-http-registry accepts any token value). **Why `cargo-http-registry` and not a hand-rolled mock or another off-the-shelf registry**: an earlier iteration on this PR wired Kellnr; three CI rounds all 403'd because Kellnr (and alexandrie, ktra, cratery) are multi-tenant-shaped and reject fixture-style unrecognized identities. `cargo-http-registry`'s README explicitly disclaims auth ("if being asked to cargo login to the registry, any string may be used") and has a lightweight dep tree (tokio rt-only + warp + git2, no openssl-sys / sqlite-sys / aws-lc-sys), making `cargo install --locked` a ~70s cold cost — well inside the e2e job's existing budget. **No consumer surface changes**: both env vars are read only by the engine; the dogfood `release-rust.yml` continues to publish to real crates.io, and consumer `release.yml` flows that don't set either env var keep the publish path they have today byte-identical. See [MIGRATIONS.md](./MIGRATIONS.md#internal-cargo-http-registry-alt-registry-for-crates-e2e). #331.
|
|
17
18
|
- **Internal-only: first-publish e2e coverage via Verdaccio.** A new `js-vanilla-first-publish` fixture and matrix row in `.github/workflows/e2e-fixture.yml` exercises the empty-packument path (`npm view <name>` returns 404 before publish) that consumer-reported bugs cluster around. `e2e-fixture-job.yml`'s publish job now runs an in-job Verdaccio service container (`verdaccio/verdaccio:5`); per-fixture job isolation gives per-scenario wipe implicitly. The new fixture carries a `-placeholder` suffix on its package name that the workflow rewrites to `-${run_id}-${run_attempt}` at materialize time, so each scenario publishes against a packument that's truly empty. The post-publish tarball-verification step is now registry-agnostic — `REGISTRY_URL` env steers `npm view` at either real npm or Verdaccio with adapted retry/backoff. **No consumer surface changes**: `PIOT_NPM_REGISTRY` is an internal e2e seam read by `src/handlers/npm.ts` and `src/handlers/npm-platform.ts`; setting it bypasses provenance and the bootstrap-hint path. Steady-state e2e fixtures and consumer `release.yml` flows are untouched. See [MIGRATIONS.md](./MIGRATIONS.md#internal-verdaccio-e2e-coverage). #304 (parent #293).
|
|
@@ -26,6 +27,7 @@ are prefixed `**BREAKING**` and link to the matching section in
|
|
|
26
27
|
|
|
27
28
|
### Changed
|
|
28
29
|
|
|
30
|
+
- **BREAKING: `kind = "pypi"` packages must declare `[project].dynamic = ["version"]` in `pyproject.toml`.** Static `[project].version = "..."` literals are now rejected at PR time by `putitoutthere check` and again at publish-time preflight, both surfacing the new stable error code `PIOT_PYPI_STATIC_VERSION`. The most common Python-publishing footgun: putitoutthere does not edit `pyproject.toml` at release time (per the [no version computation](./notes/design-commitments.md#non-goals) design commitment), so a literal silently shipped the previous release's wheel/sdist because the build backend read whatever was on disk. The fix is the same across the three supported backends: `hatch-vcs` (recommended), `setuptools-scm`, and maturin's Cargo.toml-driven path — all accept `dynamic = ["version"]`. `pypi.writeVersion` and the `putitoutthere write-version` CLI subcommand also reject the literal shape (previously they rewrote it in place). The `replacePyProjectVersion` helper, the "preserves comments around the literal" carve-out, and the sibling-pyproject-and-Cargo.toml double-rewrite path are removed; under the dynamic contract `Cargo.toml` alone is the bump target for maturin and `SETUPTOOLS_SCM_PRETEND_VERSION` is the handoff for hatch-vcs / setuptools-scm. See [README → Python version source](./README.md#python-version-source--required-shape) and [MIGRATIONS.md](./MIGRATIONS.md#pypi-pyproject-toml-must-declare-dynamic--version). #333.
|
|
29
31
|
- **BREAKING: `publish` throws on an empty matrix instead of exiting clean.** The reusable workflow's `publish` step now fails red when the plan is empty, with `PIOT_PUBLISH_EMPTY_PLAN` in the message. Previously, an empty plan logged `info: publish: plan is empty; nothing to release` and returned `ok: true` — leaving consumers with green release runs that hadn't published anything. Skips belong at the workflow gate (the `if:` on the publish job that reads the plan job's matrix output), not in the publish step. See [MIGRATIONS.md](./MIGRATIONS.md#publish-throws-on-empty-matrix).
|
|
30
32
|
|
|
31
33
|
### Deprecated
|
package/MIGRATIONS.md
CHANGED
|
@@ -152,6 +152,250 @@ curl -s https://pypi.org/pypi/<name>/json | jq '.urls[].packagetype'
|
|
|
152
152
|
`pip install <name>` (or `uvx <name>`) should download `*.whl` and
|
|
153
153
|
skip the local build step entirely.
|
|
154
154
|
|
|
155
|
+
---
|
|
156
|
+
|
|
157
|
+
### pypi `pyproject.toml` must declare `dynamic = ["version"]`
|
|
158
|
+
|
|
159
|
+
**Summary.** Every `kind = "pypi"` package's `pyproject.toml` must
|
|
160
|
+
now declare `[project].dynamic = ["version"]`. Static
|
|
161
|
+
`[project].version = "x.y.z"` literals are rejected at PR time by
|
|
162
|
+
`putitoutthere check` and again at publish-time preflight, both
|
|
163
|
+
under the stable error code `PIOT_PYPI_STATIC_VERSION`. This is
|
|
164
|
+
the most common Python-publishing footgun: putitoutthere does not
|
|
165
|
+
edit `pyproject.toml` at release time (per the [no version
|
|
166
|
+
computation](./notes/design-commitments.md#non-goals) design
|
|
167
|
+
commitment), so a literal silently shipped the previous release's
|
|
168
|
+
wheel/sdist because the build backend read whatever was on disk.
|
|
169
|
+
Making the dynamic shape mandatory closes the failure mode at the
|
|
170
|
+
earliest knowable boundary — the consumer's own repo state, before
|
|
171
|
+
a release run is ever invoked.
|
|
172
|
+
|
|
173
|
+
**Required changes.**
|
|
174
|
+
|
|
175
|
+
Before — a static literal, accepted in v0.2.x:
|
|
176
|
+
|
|
177
|
+
```toml
|
|
178
|
+
[project]
|
|
179
|
+
name = "your-package"
|
|
180
|
+
version = "0.1.0"
|
|
181
|
+
```
|
|
182
|
+
|
|
183
|
+
After — `hatch-vcs` (recommended for new packages):
|
|
184
|
+
|
|
185
|
+
```toml
|
|
186
|
+
[build-system]
|
|
187
|
+
requires = ["hatchling", "hatch-vcs"]
|
|
188
|
+
build-backend = "hatchling.build"
|
|
189
|
+
|
|
190
|
+
[project]
|
|
191
|
+
name = "your-package"
|
|
192
|
+
dynamic = ["version"]
|
|
193
|
+
|
|
194
|
+
[tool.hatch.version]
|
|
195
|
+
source = "vcs"
|
|
196
|
+
```
|
|
197
|
+
|
|
198
|
+
After — `setuptools-scm` (for setuptools-backed projects):
|
|
199
|
+
|
|
200
|
+
```toml
|
|
201
|
+
[build-system]
|
|
202
|
+
requires = ["setuptools>=64", "setuptools-scm>=8"]
|
|
203
|
+
build-backend = "setuptools.build_meta"
|
|
204
|
+
|
|
205
|
+
[project]
|
|
206
|
+
name = "your-package"
|
|
207
|
+
dynamic = ["version"]
|
|
208
|
+
|
|
209
|
+
[tool.setuptools_scm]
|
|
210
|
+
```
|
|
211
|
+
|
|
212
|
+
After — `maturin` (Python packages built from a Rust crate): the
|
|
213
|
+
version source moves to the sibling `Cargo.toml`'s
|
|
214
|
+
`[package].version`. `pyproject.toml` declares only that the version
|
|
215
|
+
is dynamic:
|
|
216
|
+
|
|
217
|
+
```toml
|
|
218
|
+
[build-system]
|
|
219
|
+
requires = ["maturin>=1"]
|
|
220
|
+
build-backend = "maturin"
|
|
221
|
+
|
|
222
|
+
[project]
|
|
223
|
+
name = "your-package"
|
|
224
|
+
dynamic = ["version"]
|
|
225
|
+
```
|
|
226
|
+
|
|
227
|
+
No reusable-workflow input changes; the existing
|
|
228
|
+
`SETUPTOOLS_SCM_PRETEND_VERSION` injection in the build step already
|
|
229
|
+
hands the planned version to `hatch-vcs` / `setuptools-scm`, and the
|
|
230
|
+
existing `putitoutthere write-version` step already bumps
|
|
231
|
+
`Cargo.toml` for the maturin path.
|
|
232
|
+
|
|
233
|
+
**Deprecations removed.**
|
|
234
|
+
|
|
235
|
+
- `pypi.writeVersion` and the `putitoutthere write-version` CLI
|
|
236
|
+
subcommand no longer rewrite static literals in place — both now
|
|
237
|
+
surface `PIOT_PYPI_STATIC_VERSION`. Previously they would
|
|
238
|
+
silently overwrite the literal.
|
|
239
|
+
- The `replacePyProjectVersion` helper export and the
|
|
240
|
+
"bumps BOTH pyproject and sibling Cargo.toml on the static-literal
|
|
241
|
+
path" #276 carve-out are gone. Under the dynamic contract, `Cargo.toml`
|
|
242
|
+
alone is the bump target on the maturin path and the pyproject literal
|
|
243
|
+
has no role.
|
|
244
|
+
|
|
245
|
+
**Behavior changes without code changes.**
|
|
246
|
+
|
|
247
|
+
- `putitoutthere check` reports `PIOT_PYPI_STATIC_VERSION` against
|
|
248
|
+
every pypi package whose `pyproject.toml` has a literal
|
|
249
|
+
`[project].version`. Aggregated with the other preflight
|
|
250
|
+
findings — one round-trip, not one error at a time.
|
|
251
|
+
- The publish path runs `requirePypiVersionSource` between the
|
|
252
|
+
existing `requireCratesMetadata` and the artifact-completeness
|
|
253
|
+
check; a misconfigured pypi tree fails fast there even if the
|
|
254
|
+
PR-time `check.yml` gate was skipped.
|
|
255
|
+
|
|
256
|
+
**Verification.**
|
|
257
|
+
|
|
258
|
+
```bash
|
|
259
|
+
# In a repo that still declares a static version:
|
|
260
|
+
$ putitoutthere check
|
|
261
|
+
[PIOT_PYPI_STATIC_VERSION] packages/python/pyproject.toml declares a
|
|
262
|
+
static `[project].version` literal. Use `[project].dynamic =
|
|
263
|
+
["version"]` ...
|
|
264
|
+
|
|
265
|
+
# After the migration above:
|
|
266
|
+
$ putitoutthere check
|
|
267
|
+
# (no findings, exits 0)
|
|
268
|
+
```
|
|
269
|
+
|
|
270
|
+
Tracked at #333.
|
|
271
|
+
|
|
272
|
+
---
|
|
273
|
+
|
|
274
|
+
### `npm` `bundle_cli` absorbed into the reusable workflow
|
|
275
|
+
|
|
276
|
+
**Summary.** `kind = "npm"` packages with `build = "bundled-cli"`
|
|
277
|
+
no longer need to author a `scripts/build.cjs` that performs the
|
|
278
|
+
Rust cross-compile by hand. Declare `[package.bundle_cli]` in
|
|
279
|
+
`putitoutthere.toml` — same schema as the pypi/maturin block from
|
|
280
|
+
#282, minus `stage_to` (npm staging is determined entirely by the
|
|
281
|
+
matrix row's `artifact_path`) — and the reusable workflow runs
|
|
282
|
+
`rustup target add`, `cargo build --release --target <triple>
|
|
283
|
+
--bin <bin>` against `crate_path`, and the copy-into-staging step
|
|
284
|
+
itself. A defense-in-depth build-content guard asserts the staged
|
|
285
|
+
binary exists before `actions/upload-artifact` runs, so a broken
|
|
286
|
+
row never leaves the build runner. Mirror of the pypi wiring landed
|
|
287
|
+
in #282; closes the seam that #287 patched (env-var contract
|
|
288
|
+
between the consumer's build script and the engine).
|
|
289
|
+
|
|
290
|
+
**Required changes.** None for additive adoption. To migrate an
|
|
291
|
+
existing consumer with a hand-written `scripts/build.cjs`:
|
|
292
|
+
|
|
293
|
+
Before (`putitoutthere.toml`):
|
|
294
|
+
|
|
295
|
+
```toml
|
|
296
|
+
[[package]]
|
|
297
|
+
name = "my-cli"
|
|
298
|
+
kind = "npm"
|
|
299
|
+
build = "bundled-cli"
|
|
300
|
+
path = "packages/ts-cli"
|
|
301
|
+
globs = ["packages/ts-cli/**", "crates/my-cli/**"]
|
|
302
|
+
targets = [
|
|
303
|
+
"x86_64-unknown-linux-gnu",
|
|
304
|
+
"x86_64-apple-darwin",
|
|
305
|
+
# ...
|
|
306
|
+
]
|
|
307
|
+
```
|
|
308
|
+
|
|
309
|
+
`scripts/build.cjs` (consumer-owned):
|
|
310
|
+
|
|
311
|
+
```js
|
|
312
|
+
const { execFileSync } = require('node:child_process');
|
|
313
|
+
const { mkdirSync, copyFileSync } = require('node:fs');
|
|
314
|
+
const target = process.env.TARGET;
|
|
315
|
+
if (!target || target === 'main' || target === 'noarch') process.exit(0);
|
|
316
|
+
const binName = 'my-cli';
|
|
317
|
+
const ext = target.includes('windows') ? '.exe' : '';
|
|
318
|
+
execFileSync('rustup', ['target', 'add', target], { stdio: 'inherit' });
|
|
319
|
+
execFileSync('cargo', ['build', '--release', '--target', target, '--bin', binName],
|
|
320
|
+
{ cwd: '../../crates/my-cli', stdio: 'inherit' });
|
|
321
|
+
mkdirSync(`build/${target}`, { recursive: true });
|
|
322
|
+
copyFileSync(`../../crates/my-cli/target/${target}/release/${binName}${ext}`,
|
|
323
|
+
`build/${target}/${binName}${ext}`);
|
|
324
|
+
```
|
|
325
|
+
|
|
326
|
+
`package.json` (consumer-owned):
|
|
327
|
+
|
|
328
|
+
```json
|
|
329
|
+
{ "scripts": { "build": "tsc && node scripts/build.cjs" } }
|
|
330
|
+
```
|
|
331
|
+
|
|
332
|
+
After (`putitoutthere.toml`):
|
|
333
|
+
|
|
334
|
+
```toml
|
|
335
|
+
[[package]]
|
|
336
|
+
name = "my-cli"
|
|
337
|
+
kind = "npm"
|
|
338
|
+
build = "bundled-cli"
|
|
339
|
+
path = "packages/ts-cli"
|
|
340
|
+
globs = ["packages/ts-cli/**", "crates/my-cli/**"]
|
|
341
|
+
targets = [
|
|
342
|
+
"x86_64-unknown-linux-gnu",
|
|
343
|
+
"x86_64-apple-darwin",
|
|
344
|
+
# ...
|
|
345
|
+
]
|
|
346
|
+
|
|
347
|
+
[package.bundle_cli]
|
|
348
|
+
bin = "my-cli"
|
|
349
|
+
crate_path = "crates/my-cli"
|
|
350
|
+
# features = ["cli"] # if the binary is feature-gated
|
|
351
|
+
# no_default_features = false
|
|
352
|
+
```
|
|
353
|
+
|
|
354
|
+
`scripts/build.cjs`: **deleted.** The `build` script in
|
|
355
|
+
`package.json` typically becomes `"tsc"` (or whatever your
|
|
356
|
+
TypeScript launcher build was minus the `node scripts/build.cjs`
|
|
357
|
+
half).
|
|
358
|
+
|
|
359
|
+
**During migration both shapes coexist.** A consumer that still
|
|
360
|
+
ships `scripts/build.cjs` keeps working — the workflow's cargo
|
|
361
|
+
build runs first, then `npm run build --if-present` runs the
|
|
362
|
+
consumer's script (which sees `build/<triple>/` already populated
|
|
363
|
+
and probably no-ops). There's no transitional broken state and
|
|
364
|
+
no flag to set.
|
|
365
|
+
|
|
366
|
+
**Constraint.** The binary must build with a vanilla
|
|
367
|
+
`cargo build --release --target <triple> --bin <bin>` from
|
|
368
|
+
`crate_path`. Optional `features` / `no_default_features` cover
|
|
369
|
+
the `[[bin]] required-features = ["cli"]` shape. Crates that need
|
|
370
|
+
arbitrary env vars, alternate manifests, Zig-cc cross
|
|
371
|
+
toolchains, or other cargo flags don't fit the recipe and
|
|
372
|
+
should keep their own release workflow.
|
|
373
|
+
|
|
374
|
+
**Deprecations removed.** None.
|
|
375
|
+
|
|
376
|
+
**Behavior changes without code changes.** Existing consumers
|
|
377
|
+
without `[package.bundle_cli]` declared are unaffected — the
|
|
378
|
+
workflow's cargo build / stage / guard steps are gated on
|
|
379
|
+
`matrix.bundle_cli` being set, so consumers who still rely on
|
|
380
|
+
their hand-written `scripts/build.cjs` see the byte-identical
|
|
381
|
+
build matrix they saw before. The schema rejects `bundle_cli`
|
|
382
|
+
declared on a non-bundled-cli npm package (or with empty
|
|
383
|
+
`targets`), so a typo can't make the block silently inert.
|
|
384
|
+
|
|
385
|
+
**Verification.** After upgrading, switch one consumer's
|
|
386
|
+
`putitoutthere.toml` to declare `[package.bundle_cli]` and
|
|
387
|
+
remove the `node scripts/build.cjs` half of their `build` script.
|
|
388
|
+
The next release run's build job will, for each per-target row,
|
|
389
|
+
include three new step lines: `bundle_cli — add Rust target`,
|
|
390
|
+
`bundle_cli — cargo build for <triple> (<bin>)`, and
|
|
391
|
+
`bundle_cli — stage binary into <artifact_path>`, followed by
|
|
392
|
+
the `bundle_cli — verify <artifact_path>/<bin>` guard before
|
|
393
|
+
`Upload artifact`. The published per-platform tarball, when
|
|
394
|
+
downloaded and unpacked, contains the cross-compiled binary at
|
|
395
|
+
the same path the launcher resolves it from.
|
|
396
|
+
|
|
397
|
+
---
|
|
398
|
+
|
|
155
399
|
### New `check.yml` reusable workflow for PR-time config sanity
|
|
156
400
|
|
|
157
401
|
**Summary.** `putitoutthere` now ships a third reusable workflow,
|
package/README.md
CHANGED
|
@@ -242,6 +242,7 @@ version = 1 # required; only 1 is valid today
|
|
|
242
242
|
| `tag` | string | dist-tag. Default `latest`. |
|
|
243
243
|
| `build` | string \| array | `"napi"` \| `"bundled-cli"` (single mode), or an array of entries (each: a bare mode string or `{ mode, name }` with a [name template](#multi-mode-npm-family)). Omitted = vanilla. See [Recipes → Bundled-CLI npm family](#bundled-cli-npm-family). |
|
|
244
244
|
| `targets` | (string \| object)[] | Required when `build` is set. |
|
|
245
|
+
| `[package.bundle_cli]` | sub-table | Declarative cross-compile for `build = "bundled-cli"` rows. Fields: `bin` (required), `crate_path` (default `"."`), `features` (default `[]`), `no_default_features` (default `false`). See [Recipes → Bundled-CLI npm family](#bundled-cli-npm-family). |
|
|
245
246
|
|
|
246
247
|
> [!IMPORTANT]
|
|
247
248
|
> **`package.json` MUST declare a non-empty `repository` field.** `putitoutthere`
|
|
@@ -537,64 +538,35 @@ const result = spawnSync(binary, process.argv.slice(2), { stdio: 'inherit' });
|
|
|
537
538
|
process.exit(result.status ?? 1);
|
|
538
539
|
```
|
|
539
540
|
|
|
540
|
-
|
|
541
|
-
cross-compile
|
|
542
|
-
per `(target × main)` row, sets `TARGET=<triple>` /
|
|
543
|
-
`BUILD=bundled-cli` on the build step, and expects the script to
|
|
544
|
-
stage the compiled binary under `build/<triple>/<bin-name>` (with
|
|
545
|
-
`.exe` suffix on Windows). For every per-target row the engine
|
|
546
|
-
then packages `build/<triple>/` as the platform sub-package's
|
|
547
|
-
artifact; the `main` row is a no-op for the build script (the
|
|
548
|
-
launcher above is committed source). A minimal `package.json`:
|
|
541
|
+
Declare `[package.bundle_cli]` so the reusable workflow runs the
|
|
542
|
+
cross-compile for you:
|
|
549
543
|
|
|
550
|
-
```
|
|
551
|
-
|
|
552
|
-
|
|
553
|
-
|
|
554
|
-
|
|
555
|
-
|
|
556
|
-
|
|
557
|
-
|
|
544
|
+
```toml
|
|
545
|
+
[package.bundle_cli]
|
|
546
|
+
bin = "my-cli" # `cargo build --bin <this>`
|
|
547
|
+
crate_path = "crates/my-cli" # `cargo build` runs from here; defaults to `.`
|
|
548
|
+
# Optional, for crates that gate the CLI behind a Cargo feature
|
|
549
|
+
# (the `[[bin]] required-features = ["cli"]` shape):
|
|
550
|
+
# features = ["cli"]
|
|
551
|
+
# no_default_features = false
|
|
558
552
|
```
|
|
559
553
|
|
|
560
|
-
`
|
|
554
|
+
For every per-target row the workflow runs `rustup target add
|
|
555
|
+
<triple>`, then `cargo build --release --target <triple> --bin
|
|
556
|
+
<bin>` from `crate_path`, and copies the resulting binary
|
|
557
|
+
(with `.exe` suffix on Windows) into the per-target staging
|
|
558
|
+
directory. The engine then packages that directory as the
|
|
559
|
+
platform sub-package's artifact. The `main` row carries no
|
|
560
|
+
per-target binary (the launcher above is committed source).
|
|
561
561
|
|
|
562
|
-
|
|
563
|
-
|
|
564
|
-
|
|
565
|
-
|
|
566
|
-
|
|
567
|
-
|
|
568
|
-
|
|
569
|
-
|
|
570
|
-
// the launcher is committed source.
|
|
571
|
-
if (!target || target === 'main' || target === 'noarch') process.exit(0);
|
|
572
|
-
|
|
573
|
-
const binName = Object.keys(pkg.bin || {})[0] || pkg.name;
|
|
574
|
-
const ext = target.includes('windows') ? '.exe' : '';
|
|
575
|
-
|
|
576
|
-
execFileSync('rustup', ['target', 'add', target], { stdio: 'inherit' });
|
|
577
|
-
execFileSync(
|
|
578
|
-
'cargo',
|
|
579
|
-
['build', '--release', '--target', target, '--bin', binName],
|
|
580
|
-
{ cwd: '../../crates/my-cli', stdio: 'inherit' },
|
|
581
|
-
);
|
|
582
|
-
|
|
583
|
-
const dir = join('build', target);
|
|
584
|
-
mkdirSync(dir, { recursive: true });
|
|
585
|
-
copyFileSync(
|
|
586
|
-
join('../../crates/my-cli/target', target, 'release', binName + ext),
|
|
587
|
-
join(dir, binName + ext),
|
|
588
|
-
);
|
|
589
|
-
```
|
|
590
|
-
|
|
591
|
-
The build is consumer-owned because `cargo build` flags
|
|
592
|
-
(`--features`, env vars, alternate manifests, Zig-cc cross
|
|
593
|
-
toolchains) vary too much across crates to bake into the
|
|
594
|
-
workflow. For the simple "single workspace member, default
|
|
595
|
-
features" case the script above is the entire integration; tweak
|
|
596
|
-
it as your crate demands. `TARGET` and `BUILD` are the only env
|
|
597
|
-
vars the workflow guarantees.
|
|
562
|
+
> [!NOTE]
|
|
563
|
+
> **Constraint.** The binary must build with a vanilla
|
|
564
|
+
> `cargo build --release --target <triple> --bin <bin>` from
|
|
565
|
+
> `crate_path`, optionally with `--features` /
|
|
566
|
+
> `--no-default-features`. Crates that need env vars, alternate
|
|
567
|
+
> manifests, Zig-cc cross toolchains, or other cargo flags
|
|
568
|
+
> don't fit the recipe — write your own release workflow
|
|
569
|
+
> instead.
|
|
598
570
|
|
|
599
571
|
Each per-platform sub-package needs its own npm trusted-publisher
|
|
600
572
|
registration (a policy on `my-cli` does not cover
|
|
@@ -741,18 +713,57 @@ def entrypoint():
|
|
|
741
713
|
os.execv(binary, [str(binary), *sys.argv[1:]])
|
|
742
714
|
```
|
|
743
715
|
|
|
744
|
-
##
|
|
716
|
+
## Python version source — required shape
|
|
717
|
+
|
|
718
|
+
Every `kind = "pypi"` package **must** declare `[project].dynamic = ["version"]`
|
|
719
|
+
in its `pyproject.toml`. Static `[project].version = "..."` literals are
|
|
720
|
+
rejected at PR time by `putitoutthere check` (error code
|
|
721
|
+
`PIOT_PYPI_STATIC_VERSION`) and again at publish time before any side effect.
|
|
722
|
+
|
|
723
|
+
Why the requirement exists: putitoutthere does not edit `pyproject.toml`
|
|
724
|
+
at release time (per the "no version computation" design commitment) — a
|
|
725
|
+
static literal silently ships the previous release's wheel/sdist because
|
|
726
|
+
the build backend reads whatever is on disk. The fix is the same across
|
|
727
|
+
all supported Python build backends: declare the version as dynamic and
|
|
728
|
+
let the backend derive it.
|
|
729
|
+
|
|
730
|
+
### Recommended: `hatch-vcs`
|
|
731
|
+
|
|
732
|
+
The blessed path for new Python packages. The version comes from the
|
|
733
|
+
latest git tag at build time, so no manual `pyproject.toml` edit is ever
|
|
734
|
+
needed.
|
|
735
|
+
|
|
736
|
+
```toml
|
|
737
|
+
[build-system]
|
|
738
|
+
requires = ["hatchling", "hatch-vcs"]
|
|
739
|
+
build-backend = "hatchling.build"
|
|
740
|
+
|
|
741
|
+
[project]
|
|
742
|
+
name = "your-package"
|
|
743
|
+
dynamic = ["version"]
|
|
744
|
+
|
|
745
|
+
[tool.hatch.version]
|
|
746
|
+
source = "vcs"
|
|
747
|
+
```
|
|
748
|
+
|
|
749
|
+
The reusable workflow sets `SETUPTOOLS_SCM_PRETEND_VERSION` on the build
|
|
750
|
+
step to the planned version, which `hatch-vcs` honors. Per-package
|
|
751
|
+
variants like `SETUPTOOLS_SCM_PRETEND_VERSION_FOR_<PKG>` are silently
|
|
752
|
+
ignored by `hatch-vcs`; only the global form works.
|
|
753
|
+
|
|
754
|
+
### Also accepted
|
|
745
755
|
|
|
746
|
-
|
|
747
|
-
|
|
748
|
-
|
|
749
|
-
|
|
750
|
-
|
|
756
|
+
- **`setuptools-scm`** (for setuptools-backed projects): same idea, same
|
|
757
|
+
env-var handoff. Add `setuptools-scm` to `[build-system].requires`,
|
|
758
|
+
declare `dynamic = ["version"]`, and the workflow's
|
|
759
|
+
`SETUPTOOLS_SCM_PRETEND_VERSION` injection covers the build step.
|
|
760
|
+
- **Maturin** (for Python packages built from a Rust crate): pyproject
|
|
761
|
+
declares `dynamic = ["version"]`; the version source is the sibling
|
|
762
|
+
`Cargo.toml`'s `[package].version`. putitoutthere bumps `Cargo.toml`
|
|
763
|
+
before `maturin build` runs.
|
|
751
764
|
|
|
752
|
-
|
|
753
|
-
|
|
754
|
-
honor. Per-package variants like `SETUPTOOLS_SCM_PRETEND_VERSION_FOR_<PKG>`
|
|
755
|
-
are silently ignored by `hatch-vcs`; only the global form works.
|
|
765
|
+
If a Python package can't fit any of these three shapes, it's outside
|
|
766
|
+
putitoutthere's scope — write your own release workflow.
|
|
756
767
|
|
|
757
768
|
## Project layout
|
|
758
769
|
|
package/dist/check.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"check.d.ts","sourceRoot":"","sources":["../src/check.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;GAuBG;
|
|
1
|
+
{"version":3,"file":"check.d.ts","sourceRoot":"","sources":["../src/check.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;GAuBG;AAqBH,MAAM,WAAW,YAAY;IAC3B;yEACqE;IACrE,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB;iDAC6C;IAC7C,OAAO,EAAE,MAAM,CAAC;CACjB;AAED,MAAM,WAAW,YAAY;IAC3B,GAAG,EAAE,MAAM,CAAC;IACZ,mEAAmE;IACnE,UAAU,CAAC,EAAE,MAAM,CAAC;CACrB;AAED;;;;;;;;;GASG;AACH,wBAAgB,SAAS,CAAC,IAAI,EAAE,YAAY,GAAG,YAAY,EAAE,CAsC5D"}
|
package/dist/check.js
CHANGED
|
@@ -31,7 +31,7 @@ import { loadConfig } from './config.js';
|
|
|
31
31
|
import { ErrorCodes } from './error-codes.js';
|
|
32
32
|
import { matchesAny } from './glob.js';
|
|
33
33
|
import { assertTripleSupported } from './handlers/npm-platform.js';
|
|
34
|
-
import { checkCratesMetadata, checkProvenanceMetadata } from './preflight.js';
|
|
34
|
+
import { checkCratesMetadata, checkProvenanceMetadata, checkPypiVersionSource, } from './preflight.js';
|
|
35
35
|
import { formatTag } from './tag-template.js';
|
|
36
36
|
import { normalizeTarget } from './types.js';
|
|
37
37
|
/**
|
|
@@ -76,6 +76,7 @@ export function runChecks(opts) {
|
|
|
76
76
|
checkNpmRepository(packages, findings);
|
|
77
77
|
checkCratesPackageMetadata(packages, findings);
|
|
78
78
|
checkPyprojectAndBundleCli(packages, cwd, findings);
|
|
79
|
+
checkPypiVersion(packages, findings);
|
|
79
80
|
checkNpmTargetTriples(packages, findings);
|
|
80
81
|
return findings;
|
|
81
82
|
}
|
|
@@ -198,6 +199,14 @@ function checkPyprojectAndBundleCli(packages, cwd, findings) {
|
|
|
198
199
|
}
|
|
199
200
|
}
|
|
200
201
|
}
|
|
202
|
+
function checkPypiVersion(packages, findings) {
|
|
203
|
+
for (const f of checkPypiVersionSource(packages)) {
|
|
204
|
+
findings.push({
|
|
205
|
+
package: f.package,
|
|
206
|
+
message: `[${ErrorCodes.PYPI_STATIC_VERSION}] ${f.pyprojectPath} declares a static \`[project].version\` literal. Use \`[project].dynamic = ["version"]\` with hatch-vcs (recommended), setuptools-scm, or the maturin Cargo.toml-driven path — putitoutthere does not edit pyproject.toml at release time, so a literal silently ships the previous release.`,
|
|
207
|
+
});
|
|
208
|
+
}
|
|
209
|
+
}
|
|
201
210
|
function checkNpmTargetTriples(packages, findings) {
|
|
202
211
|
for (const p of packages) {
|
|
203
212
|
if (p.kind !== 'npm')
|
package/dist/check.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"check.js","sourceRoot":"","sources":["../src/check.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;GAuBG;AAEH,OAAO,EAAE,YAAY,EAAE,MAAM,oBAAoB,CAAC;AAClD,OAAO,EAAE,UAAU,EAAE,YAAY,EAAE,QAAQ,EAAE,MAAM,SAAS,CAAC;AAC7D,OAAO,EAAE,UAAU,EAAE,IAAI,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAEtD,OAAO,EAAE,KAAK,IAAI,SAAS,EAAE,MAAM,WAAW,CAAC;AAE/C,OAAO,EAAE,cAAc,EAAE,MAAM,cAAc,CAAC;AAC9C,OAAO,EAAE,UAAU,EAAgB,MAAM,aAAa,CAAC;AACvD,OAAO,EAAE,UAAU,EAAE,MAAM,kBAAkB,CAAC;AAC9C,OAAO,EAAE,UAAU,EAAE,MAAM,WAAW,CAAC;AACvC,OAAO,EAAE,qBAAqB,EAAE,MAAM,4BAA4B,CAAC;AACnE,OAAO,
|
|
1
|
+
{"version":3,"file":"check.js","sourceRoot":"","sources":["../src/check.ts"],"names":[],"mappings":"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"}
|
package/dist/config.d.ts
CHANGED
|
@@ -86,6 +86,12 @@ declare const NPM_PKG: z.ZodObject<{
|
|
|
86
86
|
triple: z.ZodString;
|
|
87
87
|
runner: z.ZodOptional<z.ZodString>;
|
|
88
88
|
}, z.core.$strict>]>>>;
|
|
89
|
+
bundle_cli: z.ZodOptional<z.ZodObject<{
|
|
90
|
+
bin: z.ZodString;
|
|
91
|
+
crate_path: z.ZodDefault<z.ZodString>;
|
|
92
|
+
features: z.ZodDefault<z.ZodArray<z.ZodString>>;
|
|
93
|
+
no_default_features: z.ZodDefault<z.ZodBoolean>;
|
|
94
|
+
}, z.core.$strict>>;
|
|
89
95
|
name: z.ZodString;
|
|
90
96
|
path: z.ZodString;
|
|
91
97
|
globs: z.ZodArray<z.ZodString>;
|
|
@@ -154,6 +160,12 @@ declare const PACKAGE: z.ZodDiscriminatedUnion<[z.ZodObject<{
|
|
|
154
160
|
triple: z.ZodString;
|
|
155
161
|
runner: z.ZodOptional<z.ZodString>;
|
|
156
162
|
}, z.core.$strict>]>>>;
|
|
163
|
+
bundle_cli: z.ZodOptional<z.ZodObject<{
|
|
164
|
+
bin: z.ZodString;
|
|
165
|
+
crate_path: z.ZodDefault<z.ZodString>;
|
|
166
|
+
features: z.ZodDefault<z.ZodArray<z.ZodString>>;
|
|
167
|
+
no_default_features: z.ZodDefault<z.ZodBoolean>;
|
|
168
|
+
}, z.core.$strict>>;
|
|
157
169
|
name: z.ZodString;
|
|
158
170
|
path: z.ZodString;
|
|
159
171
|
globs: z.ZodArray<z.ZodString>;
|
package/dist/config.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"config.d.ts","sourceRoot":"","sources":["../src/config.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;GAaG;AAIH,OAAO,EAAE,CAAC,EAAiB,MAAM,KAAK,CAAC;AAgBvC;;;;;GAKG;AACH,wBAAgB,oBAAoB,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAEzD;AAID,QAAA,MAAM,KAAK;;kBAIA,CAAC;AAsFZ,QAAA,MAAM,UAAU;;;;;;;;;;;kBAQL,CAAC;AAIZ,QAAA,MAAM,QAAQ;;;;;;;;;;;;;;;;;;;;;;;;;kBA+BX,CAAC;
|
|
1
|
+
{"version":3,"file":"config.d.ts","sourceRoot":"","sources":["../src/config.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;GAaG;AAIH,OAAO,EAAE,CAAC,EAAiB,MAAM,KAAK,CAAC;AAgBvC;;;;;GAKG;AACH,wBAAgB,oBAAoB,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAEzD;AAID,QAAA,MAAM,KAAK;;kBAIA,CAAC;AAsFZ,QAAA,MAAM,UAAU;;;;;;;;;;;kBAQL,CAAC;AAIZ,QAAA,MAAM,QAAQ;;;;;;;;;;;;;;;;;;;;;;;;;kBA+BX,CAAC;AAgFJ,QAAA,MAAM,OAAO;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;kBA+FT,CAAC;AAEL,QAAA,MAAM,OAAO;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;4BAAgE,CAAC;AAW9E,MAAM,MAAM,UAAU,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,KAAK,CAAC,CAAC;AAC/C,MAAM,MAAM,aAAa,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,UAAU,CAAC,CAAC;AACvD,MAAM,MAAM,WAAW,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,QAAQ,CAAC,CAAC;AACnD,MAAM,MAAM,UAAU,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,OAAO,CAAC,CAAC;AACjD,MAAM,MAAM,OAAO,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,OAAO,CAAC,CAAC;AAE9C,MAAM,WAAW,MAAM;IACrB,aAAa,EAAE,UAAU,CAAC;IAC1B,QAAQ,EAAE,OAAO,EAAE,CAAC;CACrB;AAID,wBAAgB,WAAW,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CA2BhD;AAED,wBAAgB,UAAU,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAa/C"}
|
package/dist/config.js
CHANGED
|
@@ -155,6 +155,35 @@ const PYPI_PKG = z
|
|
|
155
155
|
// without `targets` would produce only an sdist, and the staged
|
|
156
156
|
// binary wouldn't end up in any wheel.
|
|
157
157
|
{ message: 'bundle_cli requires at least one entry in `targets`' });
|
|
158
|
+
// #298: opt-in "cross-compile a Rust CLI into every per-platform tarball"
|
|
159
|
+
// recipe for `kind = "npm" && build = "bundled-cli"`. Mirror of the pypi
|
|
160
|
+
// `[package.bundle_cli]` block — same shape minus `stage_to`. The npm
|
|
161
|
+
// staging directory is fully determined by plan.ts via the matrix row's
|
|
162
|
+
// `artifact_path` (`<pkg.path>/build/<triple>` for single-mode rows or
|
|
163
|
+
// `<pkg.path>/build/<mode>-<triple>` for multi-mode rows), so there's
|
|
164
|
+
// no consumer-visible knob analogous to maturin's `[tool.maturin].include`
|
|
165
|
+
// path that `stage_to` aligns against.
|
|
166
|
+
const NPM_BUNDLE_CLI = z
|
|
167
|
+
.object({
|
|
168
|
+
// `cargo build --bin <this>`. Required — the binary name is the
|
|
169
|
+
// one thing piot can't infer.
|
|
170
|
+
bin: z.string().min(1),
|
|
171
|
+
// Directory to run `cargo build` from. Defaults to repo root ("."),
|
|
172
|
+
// which works for most workspace Cargo.toml layouts. Override when
|
|
173
|
+
// the crate lives outside a workspace (e.g. `crates/my-cli`).
|
|
174
|
+
crate_path: z.string().min(1).default('.'),
|
|
175
|
+
// Forwarded to `cargo build --features <list>` (mirror of the pypi
|
|
176
|
+
// shape added in #300). Required for crates that gate the CLI
|
|
177
|
+
// behind a Cargo feature (`[[bin]] required-features = ["cli"]`).
|
|
178
|
+
// Empty list = no `--features` flag.
|
|
179
|
+
features: z.array(z.string().min(1)).default([]),
|
|
180
|
+
// Forwarded to `cargo build --no-default-features` (mirror of the
|
|
181
|
+
// pypi shape added in #300). Pairs with `features` for crates that
|
|
182
|
+
// want a minimal CLI binary built from a disjoint feature set than
|
|
183
|
+
// the library's defaults.
|
|
184
|
+
no_default_features: z.boolean().default(false),
|
|
185
|
+
})
|
|
186
|
+
.strict();
|
|
158
187
|
const NPM_BUILD_MODE = z.enum(['napi', 'bundled-cli']);
|
|
159
188
|
// #dirsql: must mirror ALLOWED_NAME_VARIABLES in src/handlers/npm-platform.ts.
|
|
160
189
|
// `{version}` is intentionally excluded — see resolvePlatformName.
|
|
@@ -208,6 +237,7 @@ const NPM_PKG = z
|
|
|
208
237
|
tag: z.string().optional(),
|
|
209
238
|
build: NPM_BUILD.optional(),
|
|
210
239
|
targets: z.array(TARGET_ENTRY).optional(),
|
|
240
|
+
bundle_cli: NPM_BUNDLE_CLI.optional(),
|
|
211
241
|
})
|
|
212
242
|
.strict()
|
|
213
243
|
.superRefine((p, ctx) => {
|
|
@@ -264,6 +294,35 @@ const NPM_PKG = z
|
|
|
264
294
|
});
|
|
265
295
|
}
|
|
266
296
|
}
|
|
297
|
+
// #298: bundle_cli only applies to bundled-cli rows. Allowed shapes:
|
|
298
|
+
// build = "bundled-cli"
|
|
299
|
+
// build = ["bundled-cli", ...] / [{ mode = "bundled-cli", ... }, ...]
|
|
300
|
+
// Disallowed: bundle_cli with build = "napi" / undefined / array
|
|
301
|
+
// without a bundled-cli entry. The reusable workflow's cross-compile
|
|
302
|
+
// step gates on `matrix.build == 'bundled-cli'`, so a block declared
|
|
303
|
+
// on a non-bundled-cli package would be silently inert.
|
|
304
|
+
if (p.bundle_cli !== undefined) {
|
|
305
|
+
const hasBundledCli = p.build === 'bundled-cli' ||
|
|
306
|
+
(Array.isArray(p.build) &&
|
|
307
|
+
p.build.some((e) => (typeof e === 'string' ? e : e.mode) === 'bundled-cli'));
|
|
308
|
+
if (!hasBundledCli) {
|
|
309
|
+
ctx.addIssue({
|
|
310
|
+
code: z.ZodIssueCode.custom,
|
|
311
|
+
message: 'bundle_cli is only valid when build = "bundled-cli" (or a build array containing a bundled-cli entry) on npm packages',
|
|
312
|
+
path: ['bundle_cli'],
|
|
313
|
+
});
|
|
314
|
+
}
|
|
315
|
+
// #298: bundle_cli implies per-target compilation. An npm package
|
|
316
|
+
// without `targets` would produce only the main (noarch) row, and
|
|
317
|
+
// the staged binary would have nowhere to land.
|
|
318
|
+
if (p.targets === undefined || p.targets.length === 0) {
|
|
319
|
+
ctx.addIssue({
|
|
320
|
+
code: z.ZodIssueCode.custom,
|
|
321
|
+
message: 'bundle_cli requires at least one entry in `targets`',
|
|
322
|
+
path: ['bundle_cli'],
|
|
323
|
+
});
|
|
324
|
+
}
|
|
325
|
+
}
|
|
267
326
|
});
|
|
268
327
|
const PACKAGE = z.discriminatedUnion('kind', [CRATES_PKG, PYPI_PKG, NPM_PKG]);
|
|
269
328
|
const FILE = z
|