putitoutthere 0.2.28 → 0.2.30
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 +40 -0
- package/MIGRATIONS.md +1274 -0
- package/README.md +343 -41
- package/action.yml +9 -0
- package/dist/action.d.ts.map +1 -1
- package/dist/action.js +28 -6
- package/dist/action.js.map +1 -1
- package/dist/cascade.js +14 -7
- package/dist/cascade.js.map +1 -1
- package/dist/check-crate-size.d.ts +26 -0
- package/dist/check-crate-size.d.ts.map +1 -0
- package/dist/check-crate-size.js +99 -0
- package/dist/check-crate-size.js.map +1 -0
- package/dist/check.d.ts.map +1 -1
- package/dist/check.js +116 -16
- package/dist/check.js.map +1 -1
- package/dist/cli.d.ts +17 -4
- package/dist/cli.d.ts.map +1 -1
- package/dist/cli.js +156 -19
- package/dist/cli.js.map +1 -1
- package/dist/completeness.js +6 -3
- package/dist/completeness.js.map +1 -1
- package/dist/config.d.ts +2 -0
- package/dist/config.d.ts.map +1 -1
- package/dist/config.js +20 -5
- package/dist/config.js.map +1 -1
- package/dist/ensure-tag.d.ts +17 -0
- package/dist/ensure-tag.d.ts.map +1 -0
- package/dist/ensure-tag.js +28 -0
- package/dist/ensure-tag.js.map +1 -0
- package/dist/env.js +6 -3
- package/dist/env.js.map +1 -1
- package/dist/error-codes.d.ts +89 -0
- package/dist/error-codes.d.ts.map +1 -1
- package/dist/error-codes.js +102 -0
- package/dist/error-codes.js.map +1 -1
- package/dist/git.d.ts +5 -0
- package/dist/git.d.ts.map +1 -1
- package/dist/git.js +19 -6
- package/dist/git.js.map +1 -1
- package/dist/glob.d.ts +9 -0
- package/dist/glob.d.ts.map +1 -1
- package/dist/glob.js +29 -1
- package/dist/glob.js.map +1 -1
- package/dist/handlers/crates.d.ts +15 -0
- package/dist/handlers/crates.d.ts.map +1 -1
- package/dist/handlers/crates.js +84 -9
- package/dist/handlers/crates.js.map +1 -1
- package/dist/handlers/npm-platform.d.ts +11 -0
- package/dist/handlers/npm-platform.d.ts.map +1 -1
- package/dist/handlers/npm-platform.js +91 -8
- package/dist/handlers/npm-platform.js.map +1 -1
- package/dist/handlers/npm.d.ts.map +1 -1
- package/dist/handlers/npm.js +59 -9
- package/dist/handlers/npm.js.map +1 -1
- package/dist/handlers/pypi.d.ts +0 -6
- package/dist/handlers/pypi.d.ts.map +1 -1
- package/dist/handlers/pypi.js +28 -10
- package/dist/handlers/pypi.js.map +1 -1
- package/dist/log.js +16 -8
- package/dist/log.js.map +1 -1
- package/dist/normalize-artifacts.js +8 -4
- package/dist/normalize-artifacts.js.map +1 -1
- package/dist/plan.d.ts +8 -0
- package/dist/plan.d.ts.map +1 -1
- package/dist/plan.js +125 -25
- package/dist/plan.js.map +1 -1
- package/dist/preflight.d.ts +79 -0
- package/dist/preflight.d.ts.map +1 -1
- package/dist/preflight.js +715 -33
- package/dist/preflight.js.map +1 -1
- package/dist/publish.d.ts +7 -0
- package/dist/publish.d.ts.map +1 -1
- package/dist/publish.js +49 -18
- package/dist/publish.js.map +1 -1
- package/dist/python-versions.d.ts +40 -0
- package/dist/python-versions.d.ts.map +1 -0
- package/dist/python-versions.js +142 -0
- package/dist/python-versions.js.map +1 -0
- package/dist/reconcile-types.d.ts +36 -0
- package/dist/reconcile-types.d.ts.map +1 -0
- package/dist/reconcile-types.js +9 -0
- package/dist/reconcile-types.js.map +1 -0
- package/dist/reconcile.d.ts +21 -0
- package/dist/reconcile.d.ts.map +1 -0
- package/dist/reconcile.js +54 -0
- package/dist/reconcile.js.map +1 -0
- package/dist/release-packages.d.ts +33 -0
- package/dist/release-packages.d.ts.map +1 -0
- package/dist/release-packages.js +74 -0
- package/dist/release-packages.js.map +1 -0
- package/dist/resolve-tag-commit.d.ts +22 -0
- package/dist/resolve-tag-commit.d.ts.map +1 -0
- package/dist/resolve-tag-commit.js +26 -0
- package/dist/resolve-tag-commit.js.map +1 -0
- package/dist/retry.js +14 -7
- package/dist/retry.js.map +1 -1
- package/dist/status-classify.d.ts +17 -0
- package/dist/status-classify.d.ts.map +1 -0
- package/dist/status-classify.js +37 -0
- package/dist/status-classify.js.map +1 -0
- package/dist/status-format.d.ts +9 -0
- package/dist/status-format.d.ts.map +1 -0
- package/dist/status-format.js +19 -0
- package/dist/status-format.js.map +1 -0
- package/dist/status-types.d.ts +31 -0
- package/dist/status-types.d.ts.map +1 -0
- package/dist/status-types.js +8 -0
- package/dist/status-types.js.map +1 -0
- package/dist/status.d.ts +28 -0
- package/dist/status.d.ts.map +1 -0
- package/dist/status.js +72 -0
- package/dist/status.js.map +1 -0
- package/dist/tag-template.js +2 -1
- package/dist/tag-template.js.map +1 -1
- package/dist/trailer.js +16 -9
- package/dist/trailer.js.map +1 -1
- package/dist/types.d.ts +11 -0
- package/dist/types.d.ts.map +1 -1
- package/dist/types.js +4 -2
- package/dist/types.js.map +1 -1
- package/dist/verbose.js +7 -4
- package/dist/verbose.js.map +1 -1
- package/dist/wheel-abi.d.ts +37 -0
- package/dist/wheel-abi.d.ts.map +1 -0
- package/dist/wheel-abi.js +118 -0
- package/dist/wheel-abi.js.map +1 -0
- package/dist/write-crate-version.d.ts +32 -0
- package/dist/write-crate-version.d.ts.map +1 -0
- package/dist/write-crate-version.js +52 -0
- package/dist/write-crate-version.js.map +1 -0
- package/dist/write-launcher.d.ts +82 -0
- package/dist/write-launcher.d.ts.map +1 -0
- package/dist/write-launcher.js +236 -0
- package/dist/write-launcher.js.map +1 -0
- package/dist/write-version.js +2 -1
- package/dist/write-version.js.map +1 -1
- package/package.json +4 -2
package/README.md
CHANGED
|
@@ -79,7 +79,7 @@ Optional inputs — `with:` block at the call site:
|
|
|
79
79
|
|------------------|--------------|--------------------------------------------------------------------------|
|
|
80
80
|
| `environment` | `release` | Your GitHub deployment environment is named differently. |
|
|
81
81
|
| `node_version` | `24` | You need a specific Node version for `kind = "npm"` build steps. |
|
|
82
|
-
| `python_version` | `3.12` |
|
|
82
|
+
| `python_version` | `3.12` | Deprecated — no longer affects `kind = "pypi"` builds. Wheel coverage is inferred from `requires-python` or pinned via [`python_versions`](#kind--pypi). |
|
|
83
83
|
|
|
84
84
|
### 1b. Recommended: drop in `.github/workflows/check.yml`
|
|
85
85
|
|
|
@@ -89,7 +89,11 @@ duplicate package name, a `depends_on` cycle, a missing
|
|
|
89
89
|
`[[package]].path` directory, globs that match no tracked files,
|
|
90
90
|
a `tag_format` collision, a missing `repository` field on an
|
|
91
91
|
`npm` package, missing `description` / `license` on a `crates`
|
|
92
|
-
package,
|
|
92
|
+
package, a `bundle_cli` binary the crate doesn't declare, a
|
|
93
|
+
`pyproject.toml` whose `[project].name` or `[build-system].build-backend`
|
|
94
|
+
disagrees with the configured `name` / `build`, a `Cargo.toml` whose
|
|
95
|
+
`[package].name` disagrees with the configured `name` / `crate`, or a
|
|
96
|
+
`features` list referencing a feature the crate doesn't declare — a
|
|
93
97
|
couple of seconds per PR, no per-target build, no `setup-python`
|
|
94
98
|
/ `setup-rust`. Findings are aggregated into one report so you
|
|
95
99
|
fix everything in one push instead of chasing errors across re-
|
|
@@ -112,6 +116,11 @@ does not run `setup-node` against your sources, and never holds a
|
|
|
112
116
|
publishable artifact in memory; its `permissions:` block is
|
|
113
117
|
`contents: read` only.
|
|
114
118
|
|
|
119
|
+
`check.yml` takes no inputs. The Node version is pinned internally
|
|
120
|
+
because no consumer build steps run on this code path — the
|
|
121
|
+
`node_version` knob on `build.yml` / `release.yml` does not exist
|
|
122
|
+
here. Wire `check.yml` exactly as shown above.
|
|
123
|
+
|
|
115
124
|
### 1c. Recommended: drop in `.github/workflows/build-check.yml`
|
|
116
125
|
|
|
117
126
|
Run the same plan + build matrix on every PR, with the publish step
|
|
@@ -224,14 +233,68 @@ version = 1 # required; only 1 is valid today
|
|
|
224
233
|
| `features` | string[] | Pass through to `cargo publish --features`. |
|
|
225
234
|
| `no_default_features` | bool | Pass `--no-default-features` to `cargo publish` when true. |
|
|
226
235
|
|
|
236
|
+
> [!IMPORTANT]
|
|
237
|
+
> **`Cargo.toml` MUST match the configured shape.** Preflight verifies
|
|
238
|
+
> these at PR time (via `check.yml`) and again before any publish side
|
|
239
|
+
> effect:
|
|
240
|
+
>
|
|
241
|
+
> - `[package].name` matches `[[package]].name` (or the `crate` override) —
|
|
242
|
+
> `PIOT_CRATES_NAME_MISMATCH`.
|
|
243
|
+
> - `[package].description` and `[package].license` (or `license-file`) are
|
|
244
|
+
> set — `PIOT_CRATES_MISSING_METADATA`.
|
|
245
|
+
> - Every entry in `features` (and in `bundle_cli.features`, when set) is
|
|
246
|
+
> declared in `[features]` — `PIOT_CRATES_FEATURE_NOT_DECLARED`.
|
|
247
|
+
> - When `bundle_cli.bin` is set, the target `Cargo.toml` declares a
|
|
248
|
+
> `[[bin]]` with that name (or the implicit binary derived from
|
|
249
|
+
> `[package].name`) — `PIOT_CRATES_MISSING_BIN`.
|
|
250
|
+
> - When `[package].version.workspace = true`, an ancestor `Cargo.toml`
|
|
251
|
+
> declares `[workspace.package].version` —
|
|
252
|
+
> `PIOT_CRATES_WORKSPACE_VERSION_MISMATCH`.
|
|
253
|
+
|
|
227
254
|
### `kind = "pypi"`
|
|
228
255
|
|
|
229
256
|
| Field | Type | Notes |
|
|
230
257
|
|--------------|------------------------|----------------------------------------------------|
|
|
231
258
|
| `pypi` | string | Override `name` → PyPI registered name. |
|
|
232
|
-
| `build` | enum | `maturin` \| `setuptools` \| `hatch`.
|
|
259
|
+
| `build` | enum | `maturin` \| `setuptools` \| `hatch`. Optional. Default `setuptools`. |
|
|
233
260
|
| `targets` | (string \| object)[] | Required when `build = "maturin"`. Triples or `{ triple, runner }` objects. |
|
|
234
261
|
| `bundle_cli` | table | Opt-in: cross-compile a Rust CLI per target and stage it into each wheel. Only valid with `build = "maturin"`. See [Recipes → Rust CLI inside a PyPI wheel](#rust-cli-inside-a-pypi-wheel). |
|
|
262
|
+
| `python_versions` | string[] | Optional override for the CPython versions wheels are built for, e.g. `["3.12", "3.13"]`. When omitted, the set is inferred from `[project].requires-python` and putitoutthere's checked-in released-CPython list (see below). |
|
|
263
|
+
|
|
264
|
+
> [!NOTE]
|
|
265
|
+
> **`kind = "pypi"` builds a wheel for every supported Python version.**
|
|
266
|
+
> By default the version set is inferred from `[project].requires-python`
|
|
267
|
+
> in your `pyproject.toml` — `requires-python = ">=3.10"` builds wheels
|
|
268
|
+
> for every released CPython minor version in putitoutthere's checked-in
|
|
269
|
+
> list that it allows. No configuration is needed for the common case;
|
|
270
|
+
> update putitoutthere when a new CPython minor should be included.
|
|
271
|
+
> To pin an explicit subset, set `python_versions` on the package. The
|
|
272
|
+
> build matrix fans across the resolved set (per `maturin` target); the
|
|
273
|
+
> sdist and a pure-Python `hatch` wheel are version-agnostic and built
|
|
274
|
+
> once. A `maturin` wheel that is itself Python-version-independent —
|
|
275
|
+
> `[tool.maturin].bindings = "bin"` (a `py3-none` Rust-binary wheel) or a
|
|
276
|
+
> pyo3 `abi3` extension (a `cp3x-abi3` wheel) — is likewise built once, on
|
|
277
|
+
> the newest resolved interpreter, instead of duplicated across the set
|
|
278
|
+
> (the duplicates otherwise collide at the `pypi-publish` download). When
|
|
279
|
+
> neither `python_versions` nor a parseable `requires-python` is present, a
|
|
280
|
+
> single wheel is built for `3.12`.
|
|
281
|
+
|
|
282
|
+
> [!IMPORTANT]
|
|
283
|
+
> **`pyproject.toml` MUST match the configured shape.** Preflight verifies
|
|
284
|
+
> these at PR time (via `check.yml`) and again before any publish side
|
|
285
|
+
> effect:
|
|
286
|
+
>
|
|
287
|
+
> - `[project].name` matches `[[package]].name` (or the `pypi` override) —
|
|
288
|
+
> `PIOT_PYPI_NAME_MISMATCH`.
|
|
289
|
+
> - `[build-system].build-backend`, when set, matches the configured
|
|
290
|
+
> `build` mode (`maturin` → `maturin`, `setuptools` →
|
|
291
|
+
> `setuptools.build_meta`, `hatch` → `hatchling.build`) —
|
|
292
|
+
> `PIOT_PYPI_BUILD_BACKEND_MISMATCH`.
|
|
293
|
+
> - When `[project].dynamic` contains `"version"`, either
|
|
294
|
+
> `[tool.hatch.version]` or `[tool.setuptools_scm]` declares the version
|
|
295
|
+
> source — `PIOT_PYPI_DYNAMIC_VERSION_NO_BACKEND`.
|
|
296
|
+
> - When `bundle_cli` is set, `[tool.maturin].include` covers
|
|
297
|
+
> `bundle_cli.stage_to` — `PIOT_PYPI_MATURIN_INCLUDE_MISSING`.
|
|
235
298
|
|
|
236
299
|
### `kind = "npm"`
|
|
237
300
|
|
|
@@ -270,6 +333,19 @@ version = 1 # required; only 1 is valid today
|
|
|
270
333
|
> (`"repository": "git+https://github.com/<owner>/<repo>.git"`) is also
|
|
271
334
|
> accepted.
|
|
272
335
|
|
|
336
|
+
> [!IMPORTANT]
|
|
337
|
+
> **`package.json`'s `name` MUST match the configured shape.** Preflight
|
|
338
|
+
> verifies this at PR time (via `check.yml`) and again before any publish
|
|
339
|
+
> side effect:
|
|
340
|
+
>
|
|
341
|
+
> - `name` matches `[[package]].name` (or the `npm` override) —
|
|
342
|
+
> `PIOT_NPM_NAME_MISMATCH`. `npm publish` packs the manifest `name`, but
|
|
343
|
+
> `putitoutthere`'s idempotency check (`npm view <name>`) and the tag /
|
|
344
|
+
> release-URL bookkeeping use the configured name; a divergence breaks
|
|
345
|
+
> idempotency and can publish under an unexpected name. Use the `npm`
|
|
346
|
+
> override when the registered name differs from `[[package]].name`
|
|
347
|
+
> (e.g. a scoped `@scope/foo`).
|
|
348
|
+
|
|
273
349
|
### Example: polyglot Rust library
|
|
274
350
|
|
|
275
351
|
One Rust crate feeds three artifacts:
|
|
@@ -345,6 +421,13 @@ scopes a non-default bump to specific packages.
|
|
|
345
421
|
The trailer matches anywhere in the commit body. If multiple `release:` lines
|
|
346
422
|
are present, the **last** one wins.
|
|
347
423
|
|
|
424
|
+
The parser is intentionally lenient on three points: the key is
|
|
425
|
+
case-insensitive (`Release:` and `RELEASE:` both match), leading
|
|
426
|
+
whitespace before `release:` is allowed, and an empty package list
|
|
427
|
+
(`release: minor []`) is equivalent to no list (`release: minor`).
|
|
428
|
+
The documented forms above are the canonical shape; the leniency
|
|
429
|
+
exists so commits authored under varied review styles still parse.
|
|
430
|
+
|
|
348
431
|
## Cascade
|
|
349
432
|
|
|
350
433
|
A package cascades into the release plan when a commit changes any file
|
|
@@ -360,6 +443,61 @@ Each handler's first move on publish is `isPublished` — check the registry
|
|
|
360
443
|
for the target version. Already there → skip cleanly. Lets you re-run failed
|
|
361
444
|
releases without fighting registry-immutable-publish semantics.
|
|
362
445
|
|
|
446
|
+
## Manual release
|
|
447
|
+
|
|
448
|
+
Releases are normally change-driven: a package ships when a commit touches
|
|
449
|
+
its `globs`. Sometimes you need to release a package that has **no new
|
|
450
|
+
commits** — most often a re-release after a release-pipeline bug is fixed.
|
|
451
|
+
The `release_packages` input on `release.yml` does exactly that.
|
|
452
|
+
|
|
453
|
+
Wire it to a `workflow_dispatch` trigger in your caller workflow:
|
|
454
|
+
|
|
455
|
+
```yaml
|
|
456
|
+
on:
|
|
457
|
+
push: { branches: [main] }
|
|
458
|
+
workflow_dispatch:
|
|
459
|
+
inputs:
|
|
460
|
+
release_packages:
|
|
461
|
+
description: 'Comma-separated name[@bump|version] list'
|
|
462
|
+
required: true
|
|
463
|
+
|
|
464
|
+
jobs:
|
|
465
|
+
release:
|
|
466
|
+
uses: thekevinscott/putitoutthere/.github/workflows/release.yml@v0
|
|
467
|
+
permissions:
|
|
468
|
+
contents: write
|
|
469
|
+
id-token: write
|
|
470
|
+
with:
|
|
471
|
+
release_packages: ${{ inputs.release_packages }}
|
|
472
|
+
```
|
|
473
|
+
|
|
474
|
+
Push-triggered runs leave `release_packages` empty (the `inputs` context is
|
|
475
|
+
empty outside `workflow_dispatch`), so the normal change-detected path is
|
|
476
|
+
unaffected. Triggering the workflow manually from the Actions tab with
|
|
477
|
+
`release_packages` set takes over.
|
|
478
|
+
|
|
479
|
+
Grammar — a comma-separated list of entries:
|
|
480
|
+
|
|
481
|
+
```
|
|
482
|
+
release_packages: lib-core@minor, lib-py@1.4.0, lib-js
|
|
483
|
+
```
|
|
484
|
+
|
|
485
|
+
Each entry is a package name optionally suffixed with a version spec:
|
|
486
|
+
|
|
487
|
+
| Entry | Effect |
|
|
488
|
+
|------------------|-------------------------------------------------------------------|
|
|
489
|
+
| `lib-js` | Release `lib-js`, bumping its last tag by `patch`. |
|
|
490
|
+
| `lib-core@minor` | Release `lib-core`, bumping its last tag by `minor` (or `major`). |
|
|
491
|
+
| `lib-py@1.4.0` | Release `lib-py` at exactly `1.4.0`. |
|
|
492
|
+
|
|
493
|
+
When `release_packages` is set, change detection and `depends_on` cascade
|
|
494
|
+
are bypassed entirely: **exactly** the named packages are released, and
|
|
495
|
+
nothing else — even a package with real pending changes is left out unless
|
|
496
|
+
you name it. An explicit version is used verbatim and is not checked
|
|
497
|
+
against the last tag; if that version is already on the registry the
|
|
498
|
+
publish-phase `isPublished` check skips it cleanly. Naming a package that
|
|
499
|
+
is not declared in `putitoutthere.toml` is an error.
|
|
500
|
+
|
|
363
501
|
## Trusted publishers
|
|
364
502
|
|
|
365
503
|
OIDC trusted publishers are the default and recommended auth path.
|
|
@@ -501,42 +639,13 @@ The engine publishes a per-platform sub-package per target
|
|
|
501
639
|
at the published version. npm's resolver installs exactly one sub-package
|
|
502
640
|
at consumer install time.
|
|
503
641
|
|
|
504
|
-
|
|
505
|
-
|
|
506
|
-
|
|
507
|
-
|
|
508
|
-
|
|
509
|
-
|
|
510
|
-
|
|
511
|
-
}
|
|
512
|
-
```
|
|
513
|
-
|
|
514
|
-
`bin/my-cli.js`:
|
|
515
|
-
|
|
516
|
-
```js
|
|
517
|
-
#!/usr/bin/env node
|
|
518
|
-
const { spawnSync } = require('node:child_process');
|
|
519
|
-
const { platform, arch } = process;
|
|
520
|
-
|
|
521
|
-
const triples = {
|
|
522
|
-
'linux-x64': 'x86_64-unknown-linux-gnu',
|
|
523
|
-
'linux-arm64': 'aarch64-unknown-linux-gnu',
|
|
524
|
-
'darwin-x64': 'x86_64-apple-darwin',
|
|
525
|
-
'darwin-arm64': 'aarch64-apple-darwin',
|
|
526
|
-
'win32-x64': 'x86_64-pc-windows-msvc',
|
|
527
|
-
};
|
|
528
|
-
|
|
529
|
-
const triple = triples[`${platform}-${arch}`];
|
|
530
|
-
if (!triple) {
|
|
531
|
-
console.error(`my-cli: unsupported platform ${platform}-${arch}`);
|
|
532
|
-
process.exit(1);
|
|
533
|
-
}
|
|
534
|
-
|
|
535
|
-
const pkg = `my-cli-${triple}`;
|
|
536
|
-
const binary = require.resolve(`${pkg}/bin/my-cli${platform === 'win32' ? '.exe' : ''}`);
|
|
537
|
-
const result = spawnSync(binary, process.argv.slice(2), { stdio: 'inherit' });
|
|
538
|
-
process.exit(result.status ?? 1);
|
|
539
|
-
```
|
|
642
|
+
With `[package.bundle_cli]` declared (below), the reusable workflow
|
|
643
|
+
generates the per-platform launcher and the matching `package.json#bin`
|
|
644
|
+
entry for you at build time — both writes are skipped when the consumer
|
|
645
|
+
already has either piece committed, so overrides remain trivial. To
|
|
646
|
+
override, commit your own `bin/<bundle_cli.bin>.js` at the package root
|
|
647
|
+
(or set `package.json#bin` explicitly); the workflow leaves both alone
|
|
648
|
+
when present.
|
|
540
649
|
|
|
541
650
|
Declare `[package.bundle_cli]` so the reusable workflow runs the
|
|
542
651
|
cross-compile for you:
|
|
@@ -568,6 +677,46 @@ per-target binary (the launcher above is committed source).
|
|
|
568
677
|
> don't fit the recipe — write your own release workflow
|
|
569
678
|
> instead.
|
|
570
679
|
|
|
680
|
+
> [!NOTE]
|
|
681
|
+
> **Linux binaries are statically linked against musl.** A
|
|
682
|
+
> binary compiled directly against the GitHub-hosted runner's
|
|
683
|
+
> glibc carries that glibc's version as a hard runtime
|
|
684
|
+
> requirement, so the package would break on any older Linux.
|
|
685
|
+
> The reusable workflow sidesteps that by swapping the Linux
|
|
686
|
+
> compile triple from `*-linux-gnu*` to `*-linux-musl*` before
|
|
687
|
+
> `cargo build` runs (the package's declared triple, the npm
|
|
688
|
+
> platform-package name, and everything else consumer-visible
|
|
689
|
+
> stay on the original `*-linux-gnu*`; only the binary inside
|
|
690
|
+
> switches). Your CLI crate must be musl-compatible:
|
|
691
|
+
>
|
|
692
|
+
> - If it makes HTTPS calls, prefer `reqwest` with `rustls-tls`
|
|
693
|
+
> features (the default since reqwest v0.13).
|
|
694
|
+
> - If it links openssl directly, enable the `vendored` feature
|
|
695
|
+
> on the `openssl` crate.
|
|
696
|
+
> - If it uses `git2`, enable `vendored-openssl` /
|
|
697
|
+
> `vendored-libgit2`.
|
|
698
|
+
> - If it uses `rusqlite` / `libsqlite3-sys`, enable the
|
|
699
|
+
> `bundled` feature.
|
|
700
|
+
> - If it uses `libpq-sys` / `mysqlclient-sys` (Postgres /
|
|
701
|
+
> MySQL clients), prefer a pure-Rust alternative
|
|
702
|
+
> (`sqlx` with `rustls`, `postgres-native-tls` swapped for
|
|
703
|
+
> `postgres-rustls`) — these crates have no clean static path.
|
|
704
|
+
>
|
|
705
|
+
> The musl build fails loudly at release time with a linker
|
|
706
|
+
> error if any of the above is missed, so a forgotten feature
|
|
707
|
+
> never produces a broken release — only a blocked one.
|
|
708
|
+
|
|
709
|
+
> [!WARNING]
|
|
710
|
+
> **Do not run `cargo build` in `npm run build` when `[package.bundle_cli]` is configured.**
|
|
711
|
+
> The reusable workflow compiles the Rust binary and stages it **after**
|
|
712
|
+
> your `npm run build` step, so the engine's musl binary always overwrites
|
|
713
|
+
> whatever `npm run build` staged. A build script that also runs cargo with
|
|
714
|
+
> the raw `-linux-gnu` triple and copies to `build/<triple>/` does wasted
|
|
715
|
+
> work silently. If you migrated from a hand-authored `scripts/build.cjs`
|
|
716
|
+
> to `[package.bundle_cli]`, remove the cargo invocation; keep only steps
|
|
717
|
+
> that compile or generate genuinely separate artifacts (TypeScript, assets,
|
|
718
|
+
> etc.).
|
|
719
|
+
|
|
571
720
|
Each per-platform sub-package needs its own npm trusted-publisher
|
|
572
721
|
registration (a policy on `my-cli` does not cover
|
|
573
722
|
`my-cli-x86_64-unknown-linux-gnu`).
|
|
@@ -687,8 +836,14 @@ no_default_features = false
|
|
|
687
836
|
```
|
|
688
837
|
|
|
689
838
|
The reusable workflow cross-compiles the binary per target and stages it
|
|
690
|
-
into the package source tree before maturin runs.
|
|
691
|
-
|
|
839
|
+
into the package source tree before maturin runs. The same musl
|
|
840
|
+
compatibility requirement that applies to bundled-cli npm packages
|
|
841
|
+
applies here — see [Linux binaries are statically linked against
|
|
842
|
+
musl](#bundled-cli-npm-family) above for the list of Cargo features to
|
|
843
|
+
flip when the build fails on a system-library dependency.
|
|
844
|
+
|
|
845
|
+
Your `pyproject.toml` ties the staged binary into a `console_scripts`
|
|
846
|
+
entry:
|
|
692
847
|
|
|
693
848
|
```toml
|
|
694
849
|
[project.scripts]
|
|
@@ -765,6 +920,153 @@ ignored by `hatch-vcs`; only the global form works.
|
|
|
765
920
|
If a Python package can't fit any of these three shapes, it's outside
|
|
766
921
|
putitoutthere's scope — write your own release workflow.
|
|
767
922
|
|
|
923
|
+
## Error codes
|
|
924
|
+
|
|
925
|
+
Every consumer-visible failure carries a stable `PIOT_*` code in the
|
|
926
|
+
GitHub Actions `::error::` annotation and in the corresponding log
|
|
927
|
+
line. Grep the run log for the code, then look it up here.
|
|
928
|
+
|
|
929
|
+
| Code | What trips it | Where it fires |
|
|
930
|
+
|------|---------------|----------------|
|
|
931
|
+
| `PIOT_NPM_MISSING_REPOSITORY` | An npm package's `package.json` is missing a non-empty `repository` field. Required by `npm publish --provenance`. | PR-time (`check.yml`) and publish-time preflight. See [`kind = "npm"`](#kind--npm). |
|
|
932
|
+
| `PIOT_NPM_NAME_MISMATCH` | `package.json`'s `name` disagrees with the configured `[[package]].name` (or `npm` override). `npm publish` packs the manifest name while piot's idempotency/tag bookkeeping uses the configured name. | PR-time and publish-time. See [`kind = "npm"`](#kind--npm). |
|
|
933
|
+
| `PIOT_CRATES_NAME_MISMATCH` | `Cargo.toml`'s `[package].name` disagrees with the configured `[[package]].name` (or `crate` override). | PR-time and publish-time. See [`kind = "crates"`](#kind--crates). |
|
|
934
|
+
| `PIOT_CRATES_MISSING_METADATA` | `Cargo.toml` lacks `[package].description` and/or `license` (or `license-file`). crates.io 400s without it. | PR-time and publish-time. |
|
|
935
|
+
| `PIOT_CRATES_FEATURE_NOT_DECLARED` | A `features` entry (on the package or in `bundle_cli.features`) is not declared in the crate's `[features]` table. | PR-time and publish-time. |
|
|
936
|
+
| `PIOT_CRATES_MISSING_BIN` | `bundle_cli.bin` is set but the target crate has no `[[bin]]` (or implicit-binary) of that name. | PR-time and publish-time. |
|
|
937
|
+
| `PIOT_CRATES_WORKSPACE_VERSION_MISMATCH` | `Cargo.toml` declares `version.workspace = true` but no ancestor declares `[workspace.package].version`. | PR-time and publish-time. |
|
|
938
|
+
| `PIOT_CRATES_FIRST_PUBLISH_TP_REJECTED` | crates.io returned 404 because the crate has never been published. Trusted Publishing binds to an already-published crate. Bootstrap with `CARGO_REGISTRY_TOKEN` (see [crates.io](#cratesio) above). | Publish-time only — the registry's response is the signal. |
|
|
939
|
+
| `PIOT_PYPI_STATIC_VERSION` | `pyproject.toml` declares a static `[project].version = "..."` literal. Use `[project].dynamic = ["version"]` instead (see [Python version source](#python-version-source--required-shape)). | PR-time and publish-time. |
|
|
940
|
+
| `PIOT_PYPI_NAME_MISMATCH` | `pyproject.toml`'s `[project].name` disagrees with the configured `[[package]].name` (or `pypi` override). | PR-time and publish-time. |
|
|
941
|
+
| `PIOT_PYPI_BUILD_BACKEND_MISMATCH` | `[build-system].build-backend` is set but doesn't match the configured `build` mode (`maturin` → `maturin`, `setuptools` → `setuptools.build_meta`, `hatch` → `hatchling.build`). | PR-time and publish-time. |
|
|
942
|
+
| `PIOT_PYPI_DYNAMIC_VERSION_NO_BACKEND` | `[project].dynamic` contains `"version"` but no `[tool.hatch.version]` or `[tool.setuptools_scm]` block declares the source. | PR-time and publish-time. |
|
|
943
|
+
| `PIOT_PYPI_MATURIN_INCLUDE_MISSING` | `bundle_cli` is set on a maturin package but `[tool.maturin].include` doesn't cover `bundle_cli.stage_to`. The cross-compiled binary wouldn't land in any wheel. | PR-time and publish-time. |
|
|
944
|
+
| `PIOT_AUTH_NO_TOKEN` | The publish job reached the registry-auth step with no token resolved (neither an OIDC-minted token nor a caller-provided long-lived token). Almost always means the reusable workflow's trusted-publisher exchange failed silently or the caller-provided secret was empty. | Publish-time only. |
|
|
945
|
+
| `PIOT_PUBLISH_EMPTY_PLAN` | `publish` was invoked but `plan` returned zero rows for a reason other than `release: skip`. The reusable workflow's gate normally prevents this; if it fires, the gate was bypassed or the engine is inconsistent. | Publish-time only. |
|
|
946
|
+
|
|
947
|
+
## Release health
|
|
948
|
+
|
|
949
|
+
The registry is the source of truth; git tags are putitoutthere's record
|
|
950
|
+
of what's been released (it derives "last released version" from them).
|
|
951
|
+
A few features keep the two in sync — `status` reports drift, while
|
|
952
|
+
`reconcile` and the publish-path auto-heal fix it.
|
|
953
|
+
|
|
954
|
+
### `status` — registry-vs-tag drift report
|
|
955
|
+
|
|
956
|
+
`status` reconciles, per package, the latest git tag against the
|
|
957
|
+
registry's latest published version — over the public registry APIs
|
|
958
|
+
(crates.io / npm / PyPI), no auth required — and flags any drift:
|
|
959
|
+
|
|
960
|
+
```
|
|
961
|
+
package tag registry state
|
|
962
|
+
mypkg-rust — 0.0.1 ⚠ published, untagged
|
|
963
|
+
mypkg-npm 0.0.1 0.0.1 ✓ in sync
|
|
964
|
+
mypkg-py 0.0.1 0.0.1 ✓ in sync
|
|
965
|
+
```
|
|
966
|
+
|
|
967
|
+
| State | Meaning |
|
|
968
|
+
|-------|---------|
|
|
969
|
+
| `in sync` | the latest tag matches the registry's latest version |
|
|
970
|
+
| `unreleased` | no tag, and nothing published |
|
|
971
|
+
| `published, untagged` | live on the registry but no tag — the drift that strands a package |
|
|
972
|
+
| `tagged, unpublished` | tagged, but the registry doesn't have that version |
|
|
973
|
+
| `version mismatch` | the tag and the registry disagree on the latest version |
|
|
974
|
+
| `registry unreachable` | the registry couldn't be reached (reported, never gated) |
|
|
975
|
+
|
|
976
|
+
Why it matters: a half-failed run that publishes a version but never
|
|
977
|
+
tags it leaves the package `published, untagged`. Because the planner
|
|
978
|
+
reads "last released" from tags, that package then looks unreleased,
|
|
979
|
+
skips its already-live version forever, and can never bump — while its
|
|
980
|
+
dependents keep bumping past it. `status` surfaces that in one line.
|
|
981
|
+
|
|
982
|
+
- `--check` exits non-zero on any drift state — run it as a CI gate so
|
|
983
|
+
drift can't merge unnoticed.
|
|
984
|
+
- `--json` emits the rows as machine-readable JSON.
|
|
985
|
+
|
|
986
|
+
`putitoutthere` is published to npm, so run it with `npx` (it reads your
|
|
987
|
+
git tags, so make sure they're fetched):
|
|
988
|
+
|
|
989
|
+
```bash
|
|
990
|
+
# Report drift across every package in putitoutthere.toml:
|
|
991
|
+
npx putitoutthere status
|
|
992
|
+
|
|
993
|
+
# Exit non-zero if anything has drifted:
|
|
994
|
+
npx putitoutthere status --check
|
|
995
|
+
echo $? # 1 when drifted, 0 when in sync
|
|
996
|
+
|
|
997
|
+
# Machine-readable rows:
|
|
998
|
+
npx putitoutthere status --json
|
|
999
|
+
# [{"package":"mypkg-rust","kind":"crates","tag":null,"tagVersion":null,
|
|
1000
|
+
# "registry":"0.0.1","registryUnreachable":false,
|
|
1001
|
+
# "state":"published, untagged","drift":true}, …]
|
|
1002
|
+
```
|
|
1003
|
+
|
|
1004
|
+
To gate every PR on release-state drift, add a step to any workflow —
|
|
1005
|
+
checking out tags so `status` can compare them against the registry:
|
|
1006
|
+
|
|
1007
|
+
```yaml
|
|
1008
|
+
- uses: actions/checkout@v4
|
|
1009
|
+
with:
|
|
1010
|
+
fetch-depth: 0 # status compares local tags vs the registry
|
|
1011
|
+
- run: npx putitoutthere status --check
|
|
1012
|
+
```
|
|
1013
|
+
|
|
1014
|
+
### `reconcile` — backfill missing tags on demand
|
|
1015
|
+
|
|
1016
|
+
`reconcile` fixes the `published, untagged` drift `status` reports: for
|
|
1017
|
+
every package that is live on its registry but has no tag, it creates the
|
|
1018
|
+
missing tag (and pushes it). It's the on-demand companion to auto-heal —
|
|
1019
|
+
where auto-heal only fires for a package caught in a release run,
|
|
1020
|
+
`reconcile` heals an **already-stuck** package without a release, so you
|
|
1021
|
+
can run it the moment `status` flags drift.
|
|
1022
|
+
|
|
1023
|
+
The tag is pointed at a sibling package already tagged at that version —
|
|
1024
|
+
the real release commit, e.g. a crate left untagged while its npm/PyPI
|
|
1025
|
+
siblings tagged the same merge — and at `HEAD` when no sibling tag exists.
|
|
1026
|
+
It reuses the exact drift detection `status` reports and the exact tagging
|
|
1027
|
+
`publish` heals with, so it can never create a tag a release wouldn't.
|
|
1028
|
+
|
|
1029
|
+
- Idempotent: a re-run is a no-op (already-correct tags are untouched).
|
|
1030
|
+
- `--dry-run` reports what it would create without writing anything.
|
|
1031
|
+
- `--json` emits the actions.
|
|
1032
|
+
|
|
1033
|
+
```bash
|
|
1034
|
+
# Backfill any missing tags across putitoutthere.toml:
|
|
1035
|
+
npx putitoutthere reconcile
|
|
1036
|
+
|
|
1037
|
+
# Preview without writing:
|
|
1038
|
+
npx putitoutthere reconcile --dry-run
|
|
1039
|
+
# mypkg-rust: 0.0.1 live, no tag → would create mypkg-rust-v0.0.1 at a1b2c3d (sibling)
|
|
1040
|
+
|
|
1041
|
+
# Machine-readable actions:
|
|
1042
|
+
npx putitoutthere reconcile --json
|
|
1043
|
+
```
|
|
1044
|
+
|
|
1045
|
+
Run it in CI with the release job's permissions (it pushes the tag),
|
|
1046
|
+
checking out tags first:
|
|
1047
|
+
|
|
1048
|
+
```yaml
|
|
1049
|
+
- uses: actions/checkout@v4
|
|
1050
|
+
with:
|
|
1051
|
+
fetch-depth: 0 # reconcile compares local tags vs the registry
|
|
1052
|
+
- run: npx putitoutthere reconcile
|
|
1053
|
+
```
|
|
1054
|
+
|
|
1055
|
+
### Auto-heal
|
|
1056
|
+
|
|
1057
|
+
The most common drift — a version live on the registry but missing its
|
|
1058
|
+
tag — heals itself. **There's nothing to run**: when a release runs and
|
|
1059
|
+
`publish` finds a version already published, it writes the missing tag
|
|
1060
|
+
(at the release commit) instead of skipping silently. A package stranded
|
|
1061
|
+
by an earlier half-failed run recovers on its **next release run** — it
|
|
1062
|
+
has no tag, so it's force-selected into the plan, found already-published,
|
|
1063
|
+
and tagged. No manual tag surgery. Idempotent: already-tagged packages
|
|
1064
|
+
are untouched.
|
|
1065
|
+
|
|
1066
|
+
If the repo has nothing else to release, don't wait for the next run —
|
|
1067
|
+
heal the stuck package now with the `reconcile` command above, or trigger
|
|
1068
|
+
a [manual release](#manual-release) for it (`release_packages`).
|
|
1069
|
+
|
|
768
1070
|
## Project layout
|
|
769
1071
|
|
|
770
1072
|
- [`CHANGELOG.md`](./CHANGELOG.md) — per-release changes.
|
package/action.yml
CHANGED
|
@@ -25,6 +25,15 @@ inputs:
|
|
|
25
25
|
build matrix's `version` row from the planner.
|
|
26
26
|
required: false
|
|
27
27
|
default: ''
|
|
28
|
+
release_packages:
|
|
29
|
+
description: >-
|
|
30
|
+
Manual-release spec, forwarded to `plan` / `publish` as
|
|
31
|
+
`--release-packages`. Comma-separated `name[@<bump|version>]`
|
|
32
|
+
entries; when set, change detection is bypassed and exactly the
|
|
33
|
+
named packages are released. Empty => normal change-detected
|
|
34
|
+
planning. Ignored for other commands.
|
|
35
|
+
required: false
|
|
36
|
+
default: ''
|
|
28
37
|
fail_on_error:
|
|
29
38
|
description: 'If true, non-zero CLI exit codes fail the step.'
|
|
30
39
|
required: false
|
package/dist/action.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"action.d.ts","sourceRoot":"","sources":["../src/action.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAMH,wBAAsB,IAAI,IAAI,OAAO,CAAC,IAAI,CAAC,
|
|
1
|
+
{"version":3,"file":"action.d.ts","sourceRoot":"","sources":["../src/action.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAMH,wBAAsB,IAAI,IAAI,OAAO,CAAC,IAAI,CAAC,CAiD1C"}
|
package/dist/action.js
CHANGED
|
@@ -13,6 +13,7 @@ export async function main() {
|
|
|
13
13
|
const command = process.env.INPUT_COMMAND ?? '';
|
|
14
14
|
const workingDirectory = process.env.INPUT_WORKING_DIRECTORY ?? '';
|
|
15
15
|
const versionInput = process.env.INPUT_VERSION ?? '';
|
|
16
|
+
const releasePackages = process.env.INPUT_RELEASE_PACKAGES ?? '';
|
|
16
17
|
const failOnError = (process.env.INPUT_FAIL_ON_ERROR ?? 'true').toLowerCase() !== 'false';
|
|
17
18
|
if (!command) {
|
|
18
19
|
process.stderr.write('putitoutthere action: missing required input `command`\n');
|
|
@@ -20,19 +21,40 @@ export async function main() {
|
|
|
20
21
|
}
|
|
21
22
|
// #276: write-version uses a different argv shape — `--path` (the
|
|
22
23
|
// package dir, sourced from `working_directory`) and `--version`.
|
|
23
|
-
//
|
|
24
|
-
//
|
|
24
|
+
// #366: write-crate-version shares that shape — `--path` (the
|
|
25
|
+
// bundled-cli crate dir, sourced from `working_directory`) and
|
|
26
|
+
// `--version`.
|
|
27
|
+
// #299: write-launcher takes `--path` (the matrix row's package dir,
|
|
28
|
+
// sourced from `working_directory`) and reads `--cwd` from
|
|
29
|
+
// process.cwd() — the runner working dir is the repo root, where
|
|
30
|
+
// `putitoutthere.toml` lives.
|
|
31
|
+
// No `--json` on any of them: these subcommands emit a single human
|
|
32
|
+
// line; there's no structured output to consume.
|
|
25
33
|
const argv = ['node', 'putitoutthere', command];
|
|
26
|
-
if (command === 'write-version') {
|
|
27
|
-
if (workingDirectory)
|
|
34
|
+
if (command === 'write-version' || command === 'write-crate-version') {
|
|
35
|
+
if (workingDirectory) {
|
|
28
36
|
argv.push('--path', workingDirectory);
|
|
29
|
-
|
|
37
|
+
}
|
|
38
|
+
if (versionInput) {
|
|
30
39
|
argv.push('--version', versionInput);
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
else if (command === 'write-launcher') {
|
|
43
|
+
if (workingDirectory) {
|
|
44
|
+
argv.push('--path', workingDirectory);
|
|
45
|
+
}
|
|
31
46
|
}
|
|
32
47
|
else {
|
|
33
48
|
argv.push('--json');
|
|
34
|
-
if (workingDirectory)
|
|
49
|
+
if (workingDirectory) {
|
|
35
50
|
argv.push('--cwd', workingDirectory);
|
|
51
|
+
}
|
|
52
|
+
// Manual release: forward `release_packages` to the CLI. Only
|
|
53
|
+
// `plan` / `publish` act on it; the flag parses harmlessly for the
|
|
54
|
+
// other commands that land in this branch.
|
|
55
|
+
if (releasePackages) {
|
|
56
|
+
argv.push('--release-packages', releasePackages);
|
|
57
|
+
}
|
|
36
58
|
}
|
|
37
59
|
const code = await run(argv);
|
|
38
60
|
if (code !== 0 && !failOnError) {
|
package/dist/action.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"action.js","sourceRoot":"","sources":["../src/action.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAEH,OAAO,EAAE,aAAa,EAAE,MAAM,UAAU,CAAC;AAEzC,OAAO,EAAE,GAAG,EAAE,MAAM,UAAU,CAAC;AAE/B,MAAM,CAAC,KAAK,UAAU,IAAI;IACxB,MAAM,OAAO,GAAG,OAAO,CAAC,GAAG,CAAC,aAAa,IAAI,EAAE,CAAC;IAChD,MAAM,gBAAgB,GAAG,OAAO,CAAC,GAAG,CAAC,uBAAuB,IAAI,EAAE,CAAC;IACnE,MAAM,YAAY,GAAG,OAAO,CAAC,GAAG,CAAC,aAAa,IAAI,EAAE,CAAC;IACrD,MAAM,WAAW,GACf,CAAC,OAAO,CAAC,GAAG,CAAC,mBAAmB,IAAI,MAAM,CAAC,CAAC,WAAW,EAAE,KAAK,OAAO,CAAC;IAExE,IAAI,CAAC,OAAO,EAAE,CAAC;QACb,OAAO,CAAC,MAAM,CAAC,KAAK,CAClB,0DAA0D,CAC3D,CAAC;QACF,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;IAClB,CAAC;IAED,kEAAkE;IAClE,kEAAkE;IAClE,iEAAiE;IACjE,
|
|
1
|
+
{"version":3,"file":"action.js","sourceRoot":"","sources":["../src/action.ts"],"names":[],"mappings":"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"}
|
package/dist/cascade.js
CHANGED
|
@@ -26,14 +26,16 @@ import { matchesAny } from './glob.js';
|
|
|
26
26
|
export function computeCascade(packages, changedFilesByPackage) {
|
|
27
27
|
assertNoCycles(packages);
|
|
28
28
|
const byName = new Map();
|
|
29
|
-
for (const p of packages)
|
|
29
|
+
for (const p of packages) {
|
|
30
30
|
byName.set(p.name, p);
|
|
31
|
+
}
|
|
31
32
|
// Pass 1: direct glob matches against this package's own diff.
|
|
32
33
|
const cascaded = new Set();
|
|
33
34
|
for (const p of packages) {
|
|
34
35
|
const files = changedFilesByPackage.get(p.name);
|
|
35
|
-
if (!files)
|
|
36
|
+
if (!files) {
|
|
36
37
|
continue;
|
|
38
|
+
}
|
|
37
39
|
for (const f of files) {
|
|
38
40
|
if (matchesAny(p.globs, f)) {
|
|
39
41
|
cascaded.add(p.name);
|
|
@@ -46,8 +48,9 @@ export function computeCascade(packages, changedFilesByPackage) {
|
|
|
46
48
|
while (changed) {
|
|
47
49
|
changed = false;
|
|
48
50
|
for (const p of packages) {
|
|
49
|
-
if (cascaded.has(p.name))
|
|
51
|
+
if (cascaded.has(p.name)) {
|
|
50
52
|
continue;
|
|
53
|
+
}
|
|
51
54
|
/* v8 ignore next -- depends_on default is always [] from the Zod schema */
|
|
52
55
|
const deps = p.depends_on ?? [];
|
|
53
56
|
if (deps.some((d) => cascaded.has(d))) {
|
|
@@ -67,15 +70,18 @@ export function computeCascade(packages, changedFilesByPackage) {
|
|
|
67
70
|
*/
|
|
68
71
|
export function assertNoCycles(packages) {
|
|
69
72
|
const byName = new Map();
|
|
70
|
-
for (const p of packages)
|
|
73
|
+
for (const p of packages) {
|
|
71
74
|
byName.set(p.name, p);
|
|
75
|
+
}
|
|
72
76
|
const color = new Map();
|
|
73
|
-
for (const p of packages)
|
|
77
|
+
for (const p of packages) {
|
|
74
78
|
color.set(p.name, 'white');
|
|
79
|
+
}
|
|
75
80
|
const visit = (name, path) => {
|
|
76
81
|
const c = color.get(name);
|
|
77
|
-
if (c === 'black')
|
|
82
|
+
if (c === 'black') {
|
|
78
83
|
return;
|
|
84
|
+
}
|
|
79
85
|
if (c === 'gray') {
|
|
80
86
|
const cycle = [...path.slice(path.indexOf(name)), name].join(' → ');
|
|
81
87
|
throw new Error(`putitoutthere.toml: depends_on cycle: ${cycle}`);
|
|
@@ -86,8 +92,9 @@ export function assertNoCycles(packages) {
|
|
|
86
92
|
// a name we iterated over packages for — that list IS the keys of
|
|
87
93
|
// byName, so this branch is unreachable under normal use. Defensive.
|
|
88
94
|
/* v8 ignore next */
|
|
89
|
-
if (!node)
|
|
95
|
+
if (!node) {
|
|
90
96
|
return;
|
|
97
|
+
}
|
|
91
98
|
/* v8 ignore next -- depends_on default is always [] from the Zod schema */
|
|
92
99
|
for (const dep of node.depends_on ?? []) {
|
|
93
100
|
if (!byName.has(dep)) {
|
package/dist/cascade.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"cascade.js","sourceRoot":"","sources":["../src/cascade.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;GAuBG;AAGH,OAAO,EAAE,UAAU,EAAE,MAAM,WAAW,CAAC;AAWvC,MAAM,UAAU,cAAc,CAC5B,QAA4B,EAC5B,qBAA4C;IAE5C,cAAc,CAAC,QAAQ,CAAC,CAAC;IAEzB,MAAM,MAAM,GAAG,IAAI,GAAG,EAAmB,CAAC;IAC1C,KAAK,MAAM,CAAC,IAAI,QAAQ;
|
|
1
|
+
{"version":3,"file":"cascade.js","sourceRoot":"","sources":["../src/cascade.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;GAuBG;AAGH,OAAO,EAAE,UAAU,EAAE,MAAM,WAAW,CAAC;AAWvC,MAAM,UAAU,cAAc,CAC5B,QAA4B,EAC5B,qBAA4C;IAE5C,cAAc,CAAC,QAAQ,CAAC,CAAC;IAEzB,MAAM,MAAM,GAAG,IAAI,GAAG,EAAmB,CAAC;IAC1C,KAAK,MAAM,CAAC,IAAI,QAAQ,EAAE,CAAC;QAAA,MAAM,CAAC,GAAG,CAAC,CAAC,CAAC,IAAI,EAAE,CAAC,CAAC,CAAC;IAAA,CAAC;IAElD,+DAA+D;IAC/D,MAAM,QAAQ,GAAG,IAAI,GAAG,EAAU,CAAC;IACnC,KAAK,MAAM,CAAC,IAAI,QAAQ,EAAE,CAAC;QACzB,MAAM,KAAK,GAAG,qBAAqB,CAAC,GAAG,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC;QAChD,IAAI,CAAC,KAAK,EAAE,CAAC;YAAA,SAAS;QAAA,CAAC;QACvB,KAAK,MAAM,CAAC,IAAI,KAAK,EAAE,CAAC;YACtB,IAAI,UAAU,CAAC,CAAC,CAAC,KAAK,EAAE,CAAC,CAAC,EAAE,CAAC;gBAC3B,QAAQ,CAAC,GAAG,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC;gBACrB,MAAM;YACR,CAAC;QACH,CAAC;IACH,CAAC;IAED,2DAA2D;IAC3D,IAAI,OAAO,GAAG,IAAI,CAAC;IACnB,OAAO,OAAO,EAAE,CAAC;QACf,OAAO,GAAG,KAAK,CAAC;QAChB,KAAK,MAAM,CAAC,IAAI,QAAQ,EAAE,CAAC;YACzB,IAAI,QAAQ,CAAC,GAAG,CAAC,CAAC,CAAC,IAAI,CAAC,EAAE,CAAC;gBAAA,SAAS;YAAA,CAAC;YACrC,2EAA2E;YAC3E,MAAM,IAAI,GAAG,CAAC,CAAC,UAAU,IAAI,EAAE,CAAC;YAChC,IAAI,IAAI,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,QAAQ,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC;gBACtC,QAAQ,CAAC,GAAG,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC;gBACrB,OAAO,GAAG,IAAI,CAAC;YACjB,CAAC;QACH,CAAC;IACH,CAAC;IAED,wBAAwB;IACxB,OAAO,QAAQ,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,QAAQ,CAAC,GAAG,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC;AACtD,CAAC;AAED;;;;;GAKG;AACH,MAAM,UAAU,cAAc,CAAC,QAA4B;IACzD,MAAM,MAAM,GAAG,IAAI,GAAG,EAAmB,CAAC;IAC1C,KAAK,MAAM,CAAC,IAAI,QAAQ,EAAE,CAAC;QAAA,MAAM,CAAC,GAAG,CAAC,CAAC,CAAC,IAAI,EAAE,CAAC,CAAC,CAAC;IAAA,CAAC;IAGlD,MAAM,KAAK,GAAG,IAAI,GAAG,EAAiB,CAAC;IACvC,KAAK,MAAM,CAAC,IAAI,QAAQ,EAAE,CAAC;QAAA,KAAK,CAAC,GAAG,CAAC,CAAC,CAAC,IAAI,EAAE,OAAO,CAAC,CAAC;IAAA,CAAC;IAEvD,MAAM,KAAK,GAAG,CAAC,IAAY,EAAE,IAAc,EAAQ,EAAE;QACnD,MAAM,CAAC,GAAG,KAAK,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC;QAC1B,IAAI,CAAC,KAAK,OAAO,EAAE,CAAC;YAAA,OAAO;QAAA,CAAC;QAC5B,IAAI,CAAC,KAAK,MAAM,EAAE,CAAC;YACjB,MAAM,KAAK,GAAG,CAAC,GAAG,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC,EAAE,IAAI,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;YACpE,MAAM,IAAI,KAAK,CAAC,yCAAyC,KAAK,EAAE,CAAC,CAAC;QACpE,CAAC;QACD,KAAK,CAAC,GAAG,CAAC,IAAI,EAAE,MAAM,CAAC,CAAC;QACxB,MAAM,IAAI,GAAG,MAAM,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC;QAC9B,oEAAoE;QACpE,kEAAkE;QAClE,qEAAqE;QACrE,oBAAoB;QACpB,IAAI,CAAC,IAAI,EAAE,CAAC;YAAA,OAAO;QAAA,CAAC;QACpB,2EAA2E;QAC3E,KAAK,MAAM,GAAG,IAAI,IAAI,CAAC,UAAU,IAAI,EAAE,EAAE,CAAC;YACxC,IAAI,CAAC,MAAM,CAAC,GAAG,CAAC,GAAG,CAAC,EAAE,CAAC;gBACrB,MAAM,IAAI,KAAK,CACb,gCAAgC,IAAI,8BAA8B,GAAG,GAAG,CACzE,CAAC;YACJ,CAAC;YACD,KAAK,CAAC,GAAG,EAAE,CAAC,GAAG,IAAI,EAAE,IAAI,CAAC,CAAC,CAAC;QAC9B,CAAC;QACD,KAAK,CAAC,GAAG,CAAC,IAAI,EAAE,OAAO,CAAC,CAAC;IAC3B,CAAC,CAAC;IAEF,KAAK,MAAM,CAAC,IAAI,QAAQ,EAAE,CAAC;QACzB,KAAK,CAAC,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,CAAC;IACpB,CAAC;AACH,CAAC"}
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Crate-size pre-merge check (#362).
|
|
3
|
+
*
|
|
4
|
+
* crates.io rejects any `.crate` upload over 10 MiB with `413 Payload
|
|
5
|
+
* Too Large`, but `cargo publish` surfaces that only mid-release, after
|
|
6
|
+
* the verification build has compiled the crate and every transitive
|
|
7
|
+
* dep — the release surprise the no-surprises design commitment exists
|
|
8
|
+
* to eliminate. `checkCratesPackageSize` reproduces the tarball with
|
|
9
|
+
* `cargo package` at PR time, so a tracked symlink dragging a build
|
|
10
|
+
* tree into the crate, or a missing `[package].exclude`, is caught on
|
|
11
|
+
* the PR that introduces it.
|
|
12
|
+
*
|
|
13
|
+
* Wired into `runChecks` (`check.ts`). The end-to-end path through the
|
|
14
|
+
* config loader is covered by
|
|
15
|
+
* `test/integration/check-crate-size.integration.test.ts`.
|
|
16
|
+
*/
|
|
17
|
+
import type { CheckFinding } from './check.js';
|
|
18
|
+
import type { Package } from './config.js';
|
|
19
|
+
/**
|
|
20
|
+
* One finding per `kind = "crates"` package whose `cargo package`
|
|
21
|
+
* `.crate` is over crates.io's upload limit. Empty when every crate is
|
|
22
|
+
* within the limit — or when a crate's size can't be determined, in
|
|
23
|
+
* which case the check skips it rather than inventing a finding.
|
|
24
|
+
*/
|
|
25
|
+
export declare function checkCratesPackageSize(packages: readonly Package[]): CheckFinding[];
|
|
26
|
+
//# sourceMappingURL=check-crate-size.d.ts.map
|