putitoutthere 0.2.10 → 0.2.11

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
@@ -12,6 +12,7 @@ are prefixed `**BREAKING**` and link to the matching section in
12
12
 
13
13
  ### Added
14
14
 
15
+ - **Reusable workflow accepts a caller-provided `CARGO_REGISTRY_TOKEN` via `secrets:`.** OIDC trusted publishers remain the default and recommended path, but Trusted Publishing on crates.io binds to an *already-published* crate — so the first publish of a brand-new crate has no OIDC path available, and consumers were forced to either fork the workflow or run `cargo publish` outside it. The `workflow_call` surface now declares an optional `CARGO_REGISTRY_TOKEN` secret; when set, the `rust-lang/crates-io-auth-action` OIDC exchange is skipped and the caller-provided token is exported to `$GITHUB_ENV` for the engine's crates handler to read. Callers without a token keep the OIDC path unchanged. The "Auth: OIDC trusted publishers ... Long-lived registry tokens are explicitly NOT supported" framing in `release.yml`'s header has been softened to match. See [README → Trusted publishers → crates.io](./README.md#cratesio) and [MIGRATIONS.md](./MIGRATIONS.md#crates-token-fallback). #283.
15
16
  - **Preflight check: every cascaded `kind = "npm"` package must declare a non-empty `repository` field in `package.json`.** `putitoutthere` invokes `npm publish --provenance` on the OIDC trusted-publisher path, and the npm CLI hard-requires this field so the registry can verify the artifact was built from the repo the trusted publisher declares. A missing or empty field previously surfaced as a confusing tail-end npm error after the runner had spun up, OIDC had been negotiated, and the artifact had been built — wasting a full release run on a precondition checkable in milliseconds. The new `requireProvenanceMetadata` runs alongside `requireAuth` in `src/publish.ts`, before any side effects, and reports every failing package in one error rather than failing on the first. Surfaces a new stable error code, `PIOT_NPM_MISSING_REPOSITORY`. Both the canonical object form (`{ type, url, directory? }`) and the legacy single-string form are accepted; only an empty `url` (or no `url` at all) fails. The npm handler's inline backstop is also tightened to match the same predicate (previously `!pkg.repository` slipped `{}`, `{ type: 'git' }`, and whitespace strings through). Documented in [README → `kind = "npm"`](./README.md#kind--npm). See [MIGRATIONS.md](./MIGRATIONS.md#npm-package-json-must-declare-repository). #280.
16
17
  - **Reusable workflow `.github/workflows/build.yml` (`workflow_call`).** PR-time build verification: runs the same plan + build matrix that `release.yml` runs, calling a shared internal `_matrix.yml` reusable workflow so action pins, per-target build steps, and runner selection cannot drift between the two paths. `build.yml` declares only `permissions: contents: read` and contains no publish job, no `id-token: write`, no OIDC trusted-publisher exchange, and no registry auth — the bytes required to publish do not exist on this code path. Two optional inputs forwarded to `_matrix.yml`: `node_version` (default `24`), `python_version` (default `3.12`). Concurrency is keyed on `github.ref` with `cancel-in-progress: true` so PR pushes supersede stale runs (release.yml's repository-keyed group with `cancel-in-progress: false` is unchanged). An `actionlint`-job grep assertion rejects any future patch that adds `id-token: write` to `build.yml` or `_matrix.yml`. See [README → Build check](./README.md#1b-optional-drop-in-githubworkflowsbuild-checkyml) and [MIGRATIONS.md](./MIGRATIONS.md#new-buildyml-reusable-workflow-for-pr-time-build-verification).
17
18
  - **`PIOT_PUBLISH_EMPTY_PLAN` error code.** Surfaced when `publish` is invoked with an empty matrix. Joins `PIOT_AUTH_NO_TOKEN` in the stable error-code vocabulary; foreign agents debugging a failed publish can fingerprint on the code without parsing prose.
package/MIGRATIONS.md CHANGED
@@ -21,6 +21,66 @@ Each section covers five things, in order:
21
21
 
22
22
  ## Unreleased
23
23
 
24
+ ### Crates token fallback
25
+
26
+ **Summary.** The reusable workflow now accepts an optional
27
+ `CARGO_REGISTRY_TOKEN` via `secrets:`. Trusted Publishing on
28
+ crates.io binds to an *already-published* crate, so the very
29
+ first publish of a brand-new crate has no OIDC path available;
30
+ without this fallback consumers had to either fork the workflow
31
+ or publish once outside it. OIDC trusted publishers remain the
32
+ default and recommended path — when the secret is unset,
33
+ behavior is byte-for-byte unchanged. When the secret is set,
34
+ the `rust-lang/crates-io-auth-action` OIDC exchange is skipped
35
+ and the caller-provided token is exported to `$GITHUB_ENV` as
36
+ `CARGO_REGISTRY_TOKEN` for the engine's crates handler to read.
37
+ The header comment in `.github/workflows/release.yml` has been
38
+ softened to match: previously *"Long-lived registry tokens are
39
+ explicitly NOT supported via this workflow"*; now OIDC is
40
+ described as the default with the token fallback called out for
41
+ first-publish bootstrap. #283.
42
+
43
+ **Required changes.** None for consumers already on the OIDC
44
+ path. To bootstrap a brand-new crate or to use the workflow on
45
+ an account where Trusted Publishing isn't available, wire the
46
+ secret in the caller's `release.yml`:
47
+
48
+ | Before | After |
49
+ | -------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
50
+ | `uses: thekevinscott/putitoutthere/.github/workflows/release.yml@v0` | `uses: thekevinscott/putitoutthere/.github/workflows/release.yml@v0`<br>`secrets:`<br>` CARGO_REGISTRY_TOKEN: ${{ secrets.CARGO_REGISTRY_TOKEN }}` |
51
+
52
+ The repo-level secret holding the crates.io API token can be
53
+ named anything; the *workflow* secret it gets passed as must
54
+ be `CARGO_REGISTRY_TOKEN` exactly — the reusable workflow keys
55
+ on that name. Drop the `secrets:` block from the caller's
56
+ `release.yml` once Trusted Publishing is registered against
57
+ the now-existing crate; subsequent publishes are zero-secret.
58
+
59
+ **Deprecations removed.** None.
60
+
61
+ **Behavior changes without code changes.** None when the secret
62
+ is unset (OIDC path unchanged). When the secret is set, the
63
+ publish job's "Authenticate with crates.io (OIDC)" step is
64
+ conditionally skipped and a new "Export CARGO_REGISTRY_TOKEN
65
+ (caller-provided)" step writes the secret to `$GITHUB_ENV`
66
+ gated on the same condition. The gate reads the secret through a
67
+ job-level `CALLER_CARGO_REGISTRY_TOKEN` env var because GitHub
68
+ Actions does not allow the `secrets` context inside step-level
69
+ `if:` conditions ([context availability](https://docs.github.com/en/actions/learn-github-actions/contexts#context-availability));
70
+ this is an internal mechanism — consumers don't see or set
71
+ `CALLER_CARGO_REGISTRY_TOKEN` themselves.
72
+
73
+ **Verification.** Wire `CARGO_REGISTRY_TOKEN` to a valid
74
+ crates.io API token in the caller repo and trigger a release.
75
+ The publish-job logs should show "Authenticate with crates.io
76
+ (OIDC)" as `skipped`, "Export CARGO_REGISTRY_TOKEN (OIDC)" as
77
+ `skipped`, and "Export CARGO_REGISTRY_TOKEN (caller-provided)"
78
+ as `success`. The crate publishes; the only difference visible
79
+ in the registry is the publish was authorised against the
80
+ caller-provided token rather than an OIDC-minted ephemeral one.
81
+
82
+ ---
83
+
24
84
  ### npm package.json must declare `repository`
25
85
 
26
86
  **Summary.** Every cascaded `kind = "npm"` package's `package.json`
package/README.md CHANGED
@@ -301,23 +301,48 @@ releases without fighting registry-immutable-publish semantics.
301
301
 
302
302
  ## Trusted publishers
303
303
 
304
- OIDC trusted publishers — the only auth path supported. Long-lived
305
- registry tokens are not reachable through the workflow.
306
-
307
- For all three registries the fields are the same: **your** repository
308
- owner/name, **your** workflow filename (`release.yml`), and optionally
309
- a GitHub environment name. Note: you register against your *own*
310
- repository, not against `thekevinscott/putitoutthere` — see "How
311
- auth flows" below for the why.
304
+ OIDC trusted publishers are the default and recommended auth path.
305
+ The reusable workflow also accepts a long-lived `CARGO_REGISTRY_TOKEN`
306
+ via `secrets:` for cases where Trusted Publishing isn't reachable —
307
+ most commonly the very first publish of a brand-new crate, since
308
+ Trusted Publishing on crates.io binds to an *already-published* crate
309
+ and there's no pending-publisher equivalent. When set, the OIDC
310
+ exchange is skipped and the caller-provided token is used instead.
311
+ Drop the secret once Trusted Publishing is registered against the
312
+ existing crate.
313
+
314
+ For all three registries the OIDC fields are the same: **your**
315
+ repository owner/name, **your** workflow filename (`release.yml`),
316
+ and optionally a GitHub environment name. Note: you register against
317
+ your *own* repository, not against `thekevinscott/putitoutthere` —
318
+ see "How auth flows" below for the why.
312
319
 
313
320
  ### crates.io
314
321
 
315
- 1. Publish your crate once through the normal `cargo` flow so the crate
316
- exists. (Trusted publishing needs a crate owner record.)
322
+ 1. **First publish (brand-new crate).** Trusted Publishing binds to
323
+ an existing crate, so the first `cargo publish` has no OIDC path.
324
+ Either run `cargo publish` once locally with your account's API
325
+ token, or pass `CARGO_REGISTRY_TOKEN` to the reusable workflow via
326
+ `secrets:` to bootstrap through this workflow:
327
+
328
+ ```yaml
329
+ jobs:
330
+ release:
331
+ uses: thekevinscott/putitoutthere/.github/workflows/release.yml@v0
332
+ secrets:
333
+ CARGO_REGISTRY_TOKEN: ${{ secrets.CARGO_REGISTRY_TOKEN }}
334
+ ```
335
+
336
+ When `CARGO_REGISTRY_TOKEN` is set, the OIDC step
337
+ (`rust-lang/crates-io-auth-action`) is skipped and the caller-
338
+ provided token is exported to the publish step's environment.
317
339
  2. Go to `https://crates.io/crates/<crate>/settings` → **Trusted Publishing**
318
340
  → **Add**.
319
341
  3. Fill in: your repo owner, your repo name, workflow filename
320
342
  (`release.yml`), environment (optional).
343
+ 4. Drop the `CARGO_REGISTRY_TOKEN` secret from the workflow once
344
+ Trusted Publishing is registered; subsequent publishes are
345
+ zero-secret on the OIDC path.
321
346
 
322
347
  ### PyPI
323
348
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "putitoutthere",
3
- "version": "0.2.10",
3
+ "version": "0.2.11",
4
4
  "description": "Polyglot release orchestrator for crates.io, PyPI, and npm",
5
5
  "license": "MIT",
6
6
  "repository": {