putitoutthere 0.1.43 → 0.1.45
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 +115 -0
- package/MIGRATIONS.md +632 -0
- package/README.md +342 -94
- package/action.yml +5 -5
- package/dist/action.js +2 -2
- package/dist/action.js.map +1 -1
- package/dist/cascade.d.ts +1 -1
- package/dist/cascade.js +2 -2
- package/dist/cli.d.ts +9 -6
- package/dist/cli.d.ts.map +1 -1
- package/dist/cli.js +12 -426
- package/dist/cli.js.map +1 -1
- package/dist/config.d.ts +13 -61
- package/dist/config.d.ts.map +1 -1
- package/dist/config.js +31 -45
- package/dist/config.js.map +1 -1
- package/dist/git.js +1 -1
- package/dist/git.js.map +1 -1
- package/dist/handlers/npm-platform.d.ts.map +1 -1
- package/dist/handlers/npm-platform.js +5 -1
- package/dist/handlers/npm-platform.js.map +1 -1
- package/dist/handlers/pypi.d.ts.map +1 -1
- package/dist/handlers/pypi.js +29 -6
- package/dist/handlers/pypi.js.map +1 -1
- package/dist/plan.d.ts +0 -1
- package/dist/plan.d.ts.map +1 -1
- package/dist/plan.js +19 -18
- package/dist/plan.js.map +1 -1
- package/dist/preflight.js +1 -1
- package/dist/preflight.js.map +1 -1
- package/dist/publish.d.ts +0 -10
- package/dist/publish.d.ts.map +1 -1
- package/dist/publish.js +6 -52
- package/dist/publish.js.map +1 -1
- package/dist/types.d.ts +1 -21
- package/dist/types.d.ts.map +1 -1
- package/dist/types.js.map +1 -1
- package/package.json +5 -3
- package/dist/auth.d.ts +0 -68
- package/dist/auth.d.ts.map +0 -1
- package/dist/auth.js +0 -285
- package/dist/auth.js.map +0 -1
- package/dist/doctor.d.ts +0 -115
- package/dist/doctor.d.ts.map +0 -1
- package/dist/doctor.js +0 -292
- package/dist/doctor.js.map +0 -1
- package/dist/init.d.ts +0 -59
- package/dist/init.d.ts.map +0 -1
- package/dist/init.js +0 -181
- package/dist/init.js.map +0 -1
- package/dist/keyring.d.ts +0 -35
- package/dist/keyring.d.ts.map +0 -1
- package/dist/keyring.js +0 -88
- package/dist/keyring.js.map +0 -1
- package/dist/oidc-policy.d.ts +0 -145
- package/dist/oidc-policy.d.ts.map +0 -1
- package/dist/oidc-policy.js +0 -383
- package/dist/oidc-policy.js.map +0 -1
- package/dist/preflight-run.d.ts +0 -47
- package/dist/preflight-run.d.ts.map +0 -1
- package/dist/preflight-run.js +0 -197
- package/dist/preflight-run.js.map +0 -1
- package/dist/registries/crates-trust.d.ts +0 -69
- package/dist/registries/crates-trust.d.ts.map +0 -1
- package/dist/registries/crates-trust.js +0 -95
- package/dist/registries/crates-trust.js.map +0 -1
- package/dist/release.d.ts +0 -36
- package/dist/release.d.ts.map +0 -1
- package/dist/release.js +0 -102
- package/dist/release.js.map +0 -1
- package/dist/templates.d.ts +0 -43
- package/dist/templates.d.ts.map +0 -1
- package/dist/templates.js +0 -310
- package/dist/templates.js.map +0 -1
- package/dist/token-scope.d.ts +0 -65
- package/dist/token-scope.d.ts.map +0 -1
- package/dist/token-scope.js +0 -163
- package/dist/token-scope.js.map +0 -1
- package/dist/token.d.ts +0 -211
- package/dist/token.d.ts.map +0 -1
- package/dist/token.js +0 -719
- package/dist/token.js.map +0 -1
package/MIGRATIONS.md
ADDED
|
@@ -0,0 +1,632 @@
|
|
|
1
|
+
# Migration guide
|
|
2
|
+
|
|
3
|
+
How to upgrade between versions of `putitoutthere`. Sections are ordered
|
|
4
|
+
newest-first; each one is self-contained. Every observable change to
|
|
5
|
+
public API gets a section — additive changes as well as breaking ones —
|
|
6
|
+
because versioning is not yet strictly semver.
|
|
7
|
+
|
|
8
|
+
Each section covers five things, in order:
|
|
9
|
+
|
|
10
|
+
1. **Summary** — what changed and why.
|
|
11
|
+
2. **Required changes** — before/after diffs for config, CLI flags, and
|
|
12
|
+
action inputs.
|
|
13
|
+
3. **Deprecations removed** — anything previously warned about that is
|
|
14
|
+
now gone.
|
|
15
|
+
4. **Behavior changes without code changes** — same API, different
|
|
16
|
+
runtime behavior (tag format, exit codes, default values).
|
|
17
|
+
5. **Verification** — commands you can run to confirm the upgrade
|
|
18
|
+
worked, with the expected output.
|
|
19
|
+
|
|
20
|
+
---
|
|
21
|
+
|
|
22
|
+
## Unreleased
|
|
23
|
+
|
|
24
|
+
### `[[package]].paths` renamed to `globs`
|
|
25
|
+
|
|
26
|
+
**Summary.** The `path` / `paths` pair in `[[package]]` was confusing —
|
|
27
|
+
singular and plural differed only in a trailing `s` while meaning two
|
|
28
|
+
unrelated things (the package working directory vs. the cascade-trigger
|
|
29
|
+
globs). Renaming `paths` → `globs` removes the trailing-S collision.
|
|
30
|
+
|
|
31
|
+
**Required changes.**
|
|
32
|
+
|
|
33
|
+
| Before | After |
|
|
34
|
+
|-----|-----|
|
|
35
|
+
| `paths = ["src/**", "pyproject.toml"]` | `globs = ["src/**", "pyproject.toml"]` |
|
|
36
|
+
|
|
37
|
+
Every `[[package]]` block in `putitoutthere.toml` needs the rename.
|
|
38
|
+
Configs declaring `paths` now fail validation under `.strict()`.
|
|
39
|
+
|
|
40
|
+
**Deprecations removed.** None.
|
|
41
|
+
|
|
42
|
+
**Behavior changes without code changes.** None — the field's semantics
|
|
43
|
+
are unchanged.
|
|
44
|
+
|
|
45
|
+
**Verification.** `pnpm exec putitoutthere plan` (or the next reusable-
|
|
46
|
+
workflow run) loads cleanly. A config still declaring `paths` fails
|
|
47
|
+
load with a Zod error pointing at the unknown key.
|
|
48
|
+
|
|
49
|
+
### Removed: diagnostic CLI surface, GitHub-App auth, trust-policy validation
|
|
50
|
+
|
|
51
|
+
**Summary.** Eight things removed in one pass, none consumer-observable
|
|
52
|
+
under the new "reusable workflow + OIDC-only" surface:
|
|
53
|
+
|
|
54
|
+
- `[package.trust_policy]` config block (false security: typo-catcher
|
|
55
|
+
for npm/PyPI; the only real check was the crates.io registry
|
|
56
|
+
cross-check, which required a separate token most consumers wouldn't
|
|
57
|
+
set up).
|
|
58
|
+
- `putitoutthere doctor` subcommand (its main job was the trust-policy
|
|
59
|
+
validation above).
|
|
60
|
+
- `putitoutthere preflight` subcommand (the internal `requireAuth`
|
|
61
|
+
gate inside `publish` is preserved).
|
|
62
|
+
- `putitoutthere token list/inspect` subcommands (operator-debugging
|
|
63
|
+
surface for long-lived registry tokens — none exist under OIDC-only).
|
|
64
|
+
- `putitoutthere auth login/logout/status` subcommands + the
|
|
65
|
+
`putitoutthere-cli` GitHub App's device-flow plumbing + the keyring
|
|
66
|
+
(only purpose was powering `token list --secrets`).
|
|
67
|
+
- `src/release.ts` engine-side GitHub Release creation (duplicated by
|
|
68
|
+
the reusable workflow's `gh release create --generate-notes` step).
|
|
69
|
+
- `publish --preflight-check` flag (deep token-scope check for
|
|
70
|
+
long-lived tokens; OIDC-only renders it moot).
|
|
71
|
+
- Dead config fields: `cadence`, `agents_path`, `smoke`,
|
|
72
|
+
`wheels_artifact` — defined in the schema, never read.
|
|
73
|
+
|
|
74
|
+
Net: ~2,800 lines of source removed, ~17% of `src/`.
|
|
75
|
+
|
|
76
|
+
**Required changes.**
|
|
77
|
+
|
|
78
|
+
| Before | After |
|
|
79
|
+
|-----|-----|
|
|
80
|
+
| `[package.trust_policy] workflow = "release.yml"` | Delete the block. Workflow renames still produce HTTP 400 from registries — same UX every other tool gives you. |
|
|
81
|
+
| `putitoutthere doctor` / `preflight` / `token` / `auth` invocations in any consumer script | Remove. None of these are reachable through the reusable workflow; consumer-facing surface is the workflow itself. |
|
|
82
|
+
| `cadence`, `agents_path`, `smoke`, `wheels_artifact` fields in `putitoutthere.toml` | Delete. They were never consumed; configs declaring them now fail validation under `.strict()`. |
|
|
83
|
+
| `--preflight-check` flag passed to `publish` | Drop. Internal `requireAuth` still gates publish. |
|
|
84
|
+
|
|
85
|
+
**Deprecations removed.** Everything in the list above.
|
|
86
|
+
|
|
87
|
+
**Behavior changes without code changes.** Engine behavior on the
|
|
88
|
+
plan / publish path is unchanged. `requireAuth` (the gate that
|
|
89
|
+
catches missing OIDC env or missing token) still runs; the deep
|
|
90
|
+
scope check (which required a long-lived token to inspect) no
|
|
91
|
+
longer runs because there's no long-lived token to inspect. GitHub
|
|
92
|
+
Release creation moves entirely to the reusable workflow's
|
|
93
|
+
`gh release create` step — engines invoked outside that workflow
|
|
94
|
+
(local dry-runs, custom integrations) no longer create Releases.
|
|
95
|
+
|
|
96
|
+
**Verification.** A consumer who never used any of the removed
|
|
97
|
+
surfaces sees no observable change. Consumers who used `doctor` or
|
|
98
|
+
`token` subcommands see exit-1 + "unknown command"; switch to the
|
|
99
|
+
reusable workflow.
|
|
100
|
+
|
|
101
|
+
### Public surface collapsed to a reusable workflow
|
|
102
|
+
|
|
103
|
+
**Summary.** The consumer surface is now one line in a `release.yml`:
|
|
104
|
+
|
|
105
|
+
```yaml
|
|
106
|
+
on:
|
|
107
|
+
push: { branches: [main] }
|
|
108
|
+
|
|
109
|
+
jobs:
|
|
110
|
+
release:
|
|
111
|
+
uses: thekevinscott/putitoutthere/.github/workflows/release.yml@v0
|
|
112
|
+
permissions:
|
|
113
|
+
contents: write
|
|
114
|
+
id-token: write
|
|
115
|
+
```
|
|
116
|
+
|
|
117
|
+
Plus the consumer's existing `putitoutthere.toml`. Triggers live in
|
|
118
|
+
the consumer's file; everything below them — pinned action versions,
|
|
119
|
+
plan/build/publish orchestration, runner toolchain setup, artifact
|
|
120
|
+
upload/download, GitHub Release creation — lives in the reusable
|
|
121
|
+
workflow that piot ships. The CLI and the JS action are internal
|
|
122
|
+
seams the reusable workflow invokes; consumers do not call them.
|
|
123
|
+
Auth is OIDC trusted publishers only — long-lived registry tokens
|
|
124
|
+
are not reachable through the workflow. See [design
|
|
125
|
+
commitments](https://github.com/thekevinscott/putitoutthere/blob/main/notes/design-commitments.md)
|
|
126
|
+
for the authoritative non-goals.
|
|
127
|
+
|
|
128
|
+
**Required changes.**
|
|
129
|
+
|
|
130
|
+
| Before (hand-written `release.yml`) | After |
|
|
131
|
+
|-----|-----|
|
|
132
|
+
| ~100 lines of YAML: plan/build/publish jobs, twine install, git identity, GitHub Release backfill, hand-pinned action majors | `uses: thekevinscott/putitoutthere/.github/workflows/release.yml@v0` |
|
|
133
|
+
| `putitoutthere init` to scaffold the workflow | Subcommand removed; consumers add the snippet above by hand |
|
|
134
|
+
| `[[package]].build_workflow = "publish-foo.yml"` for unsupported shapes | Removed. Shapes that don't fit piot's named build modes write their own release workflow that doesn't use piot |
|
|
135
|
+
| Long-lived registry tokens (`NPM_TOKEN`, `PYPI_API_TOKEN`, `CARGO_REGISTRY_TOKEN`) passed to a hand-written publish step | Not reachable through the reusable workflow. Register an OIDC trusted publisher per registry once |
|
|
136
|
+
| Optional inputs `dry_run`, `working_directory`, `config` | Removed. Plan job already prints the matrix without side effects; config lives at `putitoutthere.toml` in the repo root, no override |
|
|
137
|
+
| Documentation site (`docs/`) | Removed. README is the single user-facing surface; `notes/internals/` holds the contracts the reusable workflow honors so consumers don't have to know them |
|
|
138
|
+
|
|
139
|
+
**Deprecations removed.** `build_workflow:` is no longer in the
|
|
140
|
+
config schema (`src/config.ts`); configs that declare it now fail
|
|
141
|
+
validation. `putitoutthere init`, `--cadence`, and `--force` flags
|
|
142
|
+
are removed from the CLI.
|
|
143
|
+
|
|
144
|
+
**Behavior changes without code changes.** Engine behavior (plan,
|
|
145
|
+
cascade, version bump, registry handlers, completeness check,
|
|
146
|
+
idempotency, OIDC trust-policy validation) is unchanged. The
|
|
147
|
+
reusable workflow internally pins:
|
|
148
|
+
|
|
149
|
+
- `actions/checkout@v4` (`fetch-depth: 0`)
|
|
150
|
+
- `actions/setup-node@v4`
|
|
151
|
+
- `actions/setup-python@v5`
|
|
152
|
+
- `actions/upload-artifact@v4`
|
|
153
|
+
- `actions/download-artifact@v4`
|
|
154
|
+
- `PyO3/maturin-action@v1`
|
|
155
|
+
|
|
156
|
+
If a consumer was running newer majors (e.g. coaxer hit
|
|
157
|
+
`download-artifact@v8` defaults that broke the artifact-naming
|
|
158
|
+
contract), the reusable workflow standardises everyone on the
|
|
159
|
+
known-tested versions.
|
|
160
|
+
|
|
161
|
+
**Verification.**
|
|
162
|
+
|
|
163
|
+
- `pnpm test:unit` passes in the main repo.
|
|
164
|
+
- A consumer's first cutover: drop in the 12-line `release.yml`
|
|
165
|
+
shown above, push a commit that touches a `[[package]].globs`
|
|
166
|
+
glob, and watch for a tag push + GitHub Release on the next
|
|
167
|
+
workflow run.
|
|
168
|
+
|
|
169
|
+
### Publish path works end-to-end for slash-containing `pkg.name`
|
|
170
|
+
|
|
171
|
+
**Summary.** Follow-up to the [`/`-encoding fix](#package-names-with--no-longer-need-an-encode-decode-workaround)
|
|
172
|
+
([#230](https://github.com/thekevinscott/putitoutthere/issues/230)).
|
|
173
|
+
Two bugs prevented slash-containing names from actually publishing
|
|
174
|
+
even after the planner started encoding `/` to `__`
|
|
175
|
+
([#237](https://github.com/thekevinscott/putitoutthere/issues/237)):
|
|
176
|
+
|
|
177
|
+
1. The pypi handler (`src/handlers/pypi.ts`) and the npm-platform
|
|
178
|
+
synthesizer (`src/handlers/npm-platform.ts`) both built artifact
|
|
179
|
+
directory lookups from the raw `pkg.name`, so a package called
|
|
180
|
+
`py/foo` couldn't match the encoded on-disk directory
|
|
181
|
+
`py__foo-sdist/`. Symptom: `pypi: no artifacts found for py/foo
|
|
182
|
+
under <root>` at publish time.
|
|
183
|
+
2. The planner emitted glob-shaped `artifact_path` values for crates
|
|
184
|
+
tarballs, pypi sdists, and pypi wheels (e.g.
|
|
185
|
+
`${pkg.path}/dist/*.tar.gz`). `actions/upload-artifact@v4` treats
|
|
186
|
+
a glob `path:` differently from a directory `path:` — it preserves
|
|
187
|
+
the workspace-relative path, so the sdist landed at
|
|
188
|
+
`artifacts/<name>/packages/python/dist/foo.tar.gz` instead of
|
|
189
|
+
`artifacts/<name>/foo.tar.gz`. Even after fix (1), the publish
|
|
190
|
+
handler couldn't find files inside that nested layout.
|
|
191
|
+
|
|
192
|
+
Both fixed:
|
|
193
|
+
|
|
194
|
+
- Handlers route directory lookups through `sanitizeArtifactName`,
|
|
195
|
+
matching whatever the planner emitted on the matrix row.
|
|
196
|
+
- Handlers walk the artifact directory recursively for the expected
|
|
197
|
+
file extensions (`.tar.gz` / `.whl` / `.crate`), so any layout
|
|
198
|
+
(flat or nested) works.
|
|
199
|
+
- Planner emits directory-shaped `artifact_path` values for the
|
|
200
|
+
three slots that used a glob:
|
|
201
|
+
|
|
202
|
+
| Slot | Before | After |
|
|
203
|
+
|---|---|---|
|
|
204
|
+
| crates tarball | `${pkg.path}/target/package/*.crate` | `${pkg.path}/target/package` |
|
|
205
|
+
| pypi maturin wheel | `${pkg.path}/dist/*.whl` | `${pkg.path}/dist` |
|
|
206
|
+
| pypi sdist | `${pkg.path}/dist/*.tar.gz` | `${pkg.path}/dist` |
|
|
207
|
+
|
|
208
|
+
**Required changes.**
|
|
209
|
+
|
|
210
|
+
- **None for repos that pass `matrix.artifact_path` straight through**
|
|
211
|
+
to `actions/upload-artifact@v4` (the canonical pattern shown in
|
|
212
|
+
`docs/guide/shapes/*`). The matrix field already carries the new
|
|
213
|
+
directory shape; on-disk artifact layout becomes flat
|
|
214
|
+
(`<name>/foo.tar.gz` instead of `<name>/packages/python/dist/foo.tar.gz`),
|
|
215
|
+
but consumer workflows see no observable change.
|
|
216
|
+
- **Repos that hand-coded a glob path** should switch to the
|
|
217
|
+
directory shape (or — better — replace the hard-coded value with
|
|
218
|
+
the matrix field):
|
|
219
|
+
|
|
220
|
+
```diff
|
|
221
|
+
- uses: actions/upload-artifact@v4
|
|
222
|
+
with:
|
|
223
|
+
name: ${{ matrix.artifact_name }}
|
|
224
|
+
- path: packages/python/dist/*.tar.gz
|
|
225
|
+
+ path: ${{ matrix.artifact_path }} # or "packages/python/dist"
|
|
226
|
+
```
|
|
227
|
+
|
|
228
|
+
The recursive reader keeps glob layouts working as a safety net,
|
|
229
|
+
but the directory shape is the canonical contract going forward.
|
|
230
|
+
|
|
231
|
+
**Deprecations removed.** None.
|
|
232
|
+
|
|
233
|
+
**Behavior changes without code changes.**
|
|
234
|
+
|
|
235
|
+
- Artifact directory layout is now flat: `artifacts/<name>/<file>`
|
|
236
|
+
instead of `artifacts/<name>/<workspace-relative-path>/<file>`.
|
|
237
|
+
Anything reading the artifact tree (the docs page, debugging
|
|
238
|
+
scripts, custom verification jobs) should expect files at the
|
|
239
|
+
artifact root.
|
|
240
|
+
- The publish-side handlers now walk subdirectories recursively
|
|
241
|
+
when looking for `.whl` / `.tar.gz` / `.crate` files. This is
|
|
242
|
+
defensive for consumers whose build steps write to a non-standard
|
|
243
|
+
location inside `<name>/`; the planner's directory `artifact_path`
|
|
244
|
+
remains the canonical contract.
|
|
245
|
+
|
|
246
|
+
**Verification.**
|
|
247
|
+
|
|
248
|
+
```sh
|
|
249
|
+
putitoutthere plan --json | jq '.[] | {name, artifact_name, artifact_path}'
|
|
250
|
+
```
|
|
251
|
+
|
|
252
|
+
Expect every `artifact_path` to be a plain directory (no `*`):
|
|
253
|
+
|
|
254
|
+
```json
|
|
255
|
+
{ "name": "py/cachetta", "artifact_name": "py__cachetta-sdist", "artifact_path": "py/cachetta/dist" }
|
|
256
|
+
```
|
|
257
|
+
|
|
258
|
+
After the next release run, the `actions/upload-artifact@v4` step
|
|
259
|
+
uploads `py/cachetta/dist/` contents flat under
|
|
260
|
+
`artifacts/py__cachetta-sdist/` (no nested `packages/python/dist/`
|
|
261
|
+
prefix), and the publish step finds the sdist immediately.
|
|
262
|
+
|
|
263
|
+
### Scaffolded `release.yml` now forwards `GITHUB_TOKEN`
|
|
264
|
+
|
|
265
|
+
**Summary.** piot has supported cutting a GitHub Release alongside each
|
|
266
|
+
tag push since #26, but the scaffolded `release.yml` template never
|
|
267
|
+
forwarded `GITHUB_TOKEN` to the publish step. GitHub Actions does not
|
|
268
|
+
auto-mount the runner token as an env var — `permissions: contents:
|
|
269
|
+
write` only grants the token *scope* to write Releases; the token still
|
|
270
|
+
has to be exposed via `env:` for piot's `release.ts` to read it from
|
|
271
|
+
`process.env.GITHUB_TOKEN`. Without it, piot silent-skipped Release
|
|
272
|
+
creation and consumers got tags but no Release entries on the repo's
|
|
273
|
+
Releases page. Fresh `piot init` runs now scaffold the env line.
|
|
274
|
+
|
|
275
|
+
**Required changes.** Existing repos that ran `piot init` before this
|
|
276
|
+
change need a one-line addition to `.github/workflows/release.yml`:
|
|
277
|
+
|
|
278
|
+
```diff
|
|
279
|
+
- uses: thekevinscott/putitoutthere@v0
|
|
280
|
+
with:
|
|
281
|
+
command: publish
|
|
282
|
+
dry_run: ${{ inputs.dry_run || 'false' }}
|
|
283
|
+
env:
|
|
284
|
+
NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }}
|
|
285
|
+
CARGO_REGISTRY_TOKEN: ${{ secrets.CARGO_TOKEN }}
|
|
286
|
+
PYPI_API_TOKEN: ${{ secrets.PYPI_API_TOKEN }}
|
|
287
|
+
+ GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
|
288
|
+
```
|
|
289
|
+
|
|
290
|
+
The publish job already declares `permissions: contents: write`, which
|
|
291
|
+
is the scope GitHub's runner-supplied `GITHUB_TOKEN` needs to create
|
|
292
|
+
Releases — no additional permission changes required.
|
|
293
|
+
|
|
294
|
+
**Deprecations removed.** None.
|
|
295
|
+
|
|
296
|
+
**Behavior changes without code changes.** Repos that adopt the new
|
|
297
|
+
template (or apply the diff above) start seeing GitHub Release entries
|
|
298
|
+
appear under the repo's `/releases` page after each publish. The
|
|
299
|
+
Release body is the output of:
|
|
300
|
+
|
|
301
|
+
```sh
|
|
302
|
+
git log <prev-tag>..<this-tag> --format='- %s' --no-merges
|
|
303
|
+
```
|
|
304
|
+
|
|
305
|
+
Tags suffixed with `-rc`, `-beta`, or `-alpha` are flagged
|
|
306
|
+
`prerelease: true`. Release creation is best-effort: a 4xx/5xx from
|
|
307
|
+
the GitHub API surfaces as a `publish: GitHub Release creation
|
|
308
|
+
failed` warning but does not fail the publish run — the registry
|
|
309
|
+
publish and tag push remain authoritative.
|
|
310
|
+
|
|
311
|
+
**Verification.** After the next release run on a repo that adopted the
|
|
312
|
+
fix:
|
|
313
|
+
|
|
314
|
+
```bash
|
|
315
|
+
# Inspect the publish job log:
|
|
316
|
+
# "publish: GitHub Release created at https://github.com/.../releases/tag/<name>-v<x.y.z>"
|
|
317
|
+
|
|
318
|
+
# Or hit the API directly:
|
|
319
|
+
gh release view <name>-v<x.y.z> --repo <owner>/<repo>
|
|
320
|
+
```
|
|
321
|
+
|
|
322
|
+
If you previously saw the warning `publish: GitHub Release creation
|
|
323
|
+
failed` in your publish logs, the warning should be gone and the
|
|
324
|
+
Releases page should populate.
|
|
325
|
+
|
|
326
|
+
### Package names with `/` no longer need an encode/decode workaround
|
|
327
|
+
|
|
328
|
+
**Summary.** Polyglot-monorepo repos that group packages by language
|
|
329
|
+
(e.g. `name = "py/foo"`, `"js/bar"`) used to fail at the build job
|
|
330
|
+
with:
|
|
331
|
+
|
|
332
|
+
```
|
|
333
|
+
The artifact name is not valid: py/foo-sdist.
|
|
334
|
+
Contains the following character: Forward slash /
|
|
335
|
+
```
|
|
336
|
+
|
|
337
|
+
…because `actions/upload-artifact@v4` forbids `/` in artifact names
|
|
338
|
+
and the planner emitted `artifact_name` verbatim from `pkg.name`
|
|
339
|
+
([#230](https://github.com/thekevinscott/putitoutthere/issues/230)).
|
|
340
|
+
The planner now encodes each `/` to `__`
|
|
341
|
+
(`py/foo` → `py__foo-sdist`), so the build job's
|
|
342
|
+
upload-artifact step works without modification — pass the matrix
|
|
343
|
+
`artifact_name` field through verbatim and the encoding happens
|
|
344
|
+
upstream.
|
|
345
|
+
|
|
346
|
+
**Required changes.**
|
|
347
|
+
|
|
348
|
+
- **None for repos with slash-free `pkg.name`** — `artifact_name`
|
|
349
|
+
is byte-identical to the previous version.
|
|
350
|
+
- **Repos that ran the [`cachetta#26`-style](https://github.com/thekevinscott/cachetta/pull/26)
|
|
351
|
+
encode/decode workaround should remove it.** The planner now
|
|
352
|
+
does the encoding natively; leaving the workaround in place
|
|
353
|
+
produces double-encoded names like `py____foo-sdist`, which the
|
|
354
|
+
publish-side reader will treat as a missing artifact.
|
|
355
|
+
|
|
356
|
+
```diff
|
|
357
|
+
- uses: actions/upload-artifact@v4
|
|
358
|
+
with:
|
|
359
|
+
- name: ${{ format('{0}', matrix.artifact_name) }} # any sed/format encode
|
|
360
|
+
- path: ${{ matrix.artifact_path }}
|
|
361
|
+
+ name: ${{ matrix.artifact_name }} # use the field as-is
|
|
362
|
+
+ path: ${{ matrix.artifact_path }}
|
|
363
|
+
```
|
|
364
|
+
|
|
365
|
+
```diff
|
|
366
|
+
- uses: actions/download-artifact@v4
|
|
367
|
+
with:
|
|
368
|
+
path: artifacts
|
|
369
|
+
- - name: Decode artifact dir names
|
|
370
|
+
- run: |
|
|
371
|
+
- # rename artifacts/py__foo-sdist back to artifacts/py/foo-sdist
|
|
372
|
+
- ...
|
|
373
|
+
```
|
|
374
|
+
|
|
375
|
+
**Deprecations removed.** None.
|
|
376
|
+
|
|
377
|
+
**Behavior changes without code changes.**
|
|
378
|
+
|
|
379
|
+
- `pkg.name` containing `__` (the new encoding sequence) is now
|
|
380
|
+
rejected at config load with: `package name must not contain "__"
|
|
381
|
+
(reserved: piot encodes "/" to "__" for artifact-name slots; pick
|
|
382
|
+
a different separator)`. If your config uses `__` in a package
|
|
383
|
+
name today, rename to use `-` or `_` and update any tags / consumer
|
|
384
|
+
references; piot can't safely sanitize it without ambiguity.
|
|
385
|
+
- `pkg.name` containing `\`, `:`, `<`, `>`, `|`, `*`, `?`, or `"`
|
|
386
|
+
is now rejected at config load. None of these are valid in npm,
|
|
387
|
+
PyPI, or crates.io names, so any config that previously contained
|
|
388
|
+
them was already broken at publish time — the change just moves
|
|
389
|
+
the failure earlier with a clearer message.
|
|
390
|
+
|
|
391
|
+
**Verification.**
|
|
392
|
+
|
|
393
|
+
```sh
|
|
394
|
+
putitoutthere plan --json | jq '.[].artifact_name'
|
|
395
|
+
```
|
|
396
|
+
|
|
397
|
+
Expect every emitted `artifact_name` to contain only ASCII letters,
|
|
398
|
+
digits, `-`, `_`, and `.` — no `/` and no other forbidden chars.
|
|
399
|
+
For a repo with `name = "py/cachetta"`:
|
|
400
|
+
|
|
401
|
+
```
|
|
402
|
+
"py__cachetta-sdist"
|
|
403
|
+
"py__cachetta-wheel-x86_64-unknown-linux-gnu"
|
|
404
|
+
```
|
|
405
|
+
|
|
406
|
+
After the next release, the build job's `actions/upload-artifact@v4`
|
|
407
|
+
step uploads under `py__cachetta-sdist/` (a single flat directory
|
|
408
|
+
under `artifacts/`), and piot's publish-side reader consumes the
|
|
409
|
+
same path.
|
|
410
|
+
|
|
411
|
+
### Documentation accuracy pass (#231)
|
|
412
|
+
|
|
413
|
+
**Summary.** A docs-vs-code audit found several places where reference
|
|
414
|
+
material lagged behind shipped behavior. Existing configs and workflows
|
|
415
|
+
keep working — the only consumer-observable change is that `putitoutthere
|
|
416
|
+
--help` no longer mislabels `--json` as "plan only".
|
|
417
|
+
|
|
418
|
+
**Required changes.** None.
|
|
419
|
+
|
|
420
|
+
**Deprecations removed.** None.
|
|
421
|
+
|
|
422
|
+
**Behavior changes without code changes.**
|
|
423
|
+
|
|
424
|
+
- `putitoutthere --help` output: the `--json` line now reads `emit
|
|
425
|
+
machine-readable output (most commands)` instead of `(plan only)`. The
|
|
426
|
+
flag has always been accepted on every command that emits a result;
|
|
427
|
+
only the help text was wrong.
|
|
428
|
+
- No other behavior changes. All other audit findings were addressed by
|
|
429
|
+
updating documentation (`docs/api/cli.md`, `docs/api/action.md`,
|
|
430
|
+
`docs/guide/configuration.md`, `docs/guide/trailer.md`, `README.md`,
|
|
431
|
+
`action.yml` description text, VitePress sidebar).
|
|
432
|
+
|
|
433
|
+
**Verification.**
|
|
434
|
+
|
|
435
|
+
```sh
|
|
436
|
+
putitoutthere --help | grep -- '--json'
|
|
437
|
+
# Expected: --json emit machine-readable output (most commands)
|
|
438
|
+
```
|
|
439
|
+
|
|
440
|
+
### Python shape examples now use `uv build`
|
|
441
|
+
|
|
442
|
+
**Summary.** Documentation examples for the Python library, Python
|
|
443
|
+
cibuildwheel, and dynamic-versions shapes switched the sdist-build
|
|
444
|
+
step from `python -m build --sdist` to `uv build --sdist`. piot's
|
|
445
|
+
contract is unchanged — backends, artifact names, the
|
|
446
|
+
`matrix.artifact_name` / `matrix.artifact_path` fields, and the
|
|
447
|
+
publish-side completeness check all work identically. The change
|
|
448
|
+
removes a `pip install build` round-trip and aligns the docs with
|
|
449
|
+
`uv` as the recommended Python toolchain.
|
|
450
|
+
|
|
451
|
+
**Required changes.** None. `python -m build` still works. To
|
|
452
|
+
follow the new examples in your own `release.yml`:
|
|
453
|
+
|
|
454
|
+
```diff
|
|
455
|
+
build:
|
|
456
|
+
...
|
|
457
|
+
steps:
|
|
458
|
+
- - uses: actions/setup-python@v5
|
|
459
|
+
- with: { python-version: '3.12' }
|
|
460
|
+
- name: Build sdist
|
|
461
|
+
- run: |
|
|
462
|
+
- cd ${{ matrix.path }}
|
|
463
|
+
- python -m pip install build
|
|
464
|
+
- python -m build --sdist --outdir dist
|
|
465
|
+
+ working-directory: ${{ matrix.path }}
|
|
466
|
+
+ run: uv build --sdist
|
|
467
|
+
+ # uv installs and manages Python itself; no setup-python step needed.
|
|
468
|
+
+ # Add this once at the top of the build job:
|
|
469
|
+
+ - uses: astral-sh/setup-uv@v3
|
|
470
|
+
```
|
|
471
|
+
|
|
472
|
+
`uv build --sdist` writes to `dist/` inside the working directory
|
|
473
|
+
(same as `python -m build --outdir dist`), so
|
|
474
|
+
`matrix.artifact_path` keeps pointing at the right place. The
|
|
475
|
+
publish job is unchanged — `setup-python` + `pip install twine` is
|
|
476
|
+
still the recommended path there because piot's PyPI handler shells
|
|
477
|
+
out to `twine`.
|
|
478
|
+
|
|
479
|
+
**When *not* to follow this example.** Stay on `python -m build`
|
|
480
|
+
if:
|
|
481
|
+
|
|
482
|
+
- Your CI image already has Python pre-installed and adding
|
|
483
|
+
`setup-uv` would slow the cold cache.
|
|
484
|
+
- Your `pyproject.toml` exercises a build backend feature that uv's
|
|
485
|
+
isolated build environment doesn't yet handle (rare; uv's build
|
|
486
|
+
isolation matches `python -m build`'s).
|
|
487
|
+
- Your team's runbook standardises on `python -m build` and the
|
|
488
|
+
consistency cost of switching outweighs the per-run speedup.
|
|
489
|
+
|
|
490
|
+
`python -m build` is not deprecated and will keep working.
|
|
491
|
+
|
|
492
|
+
**Deprecations removed.** None.
|
|
493
|
+
|
|
494
|
+
**Behavior changes without code changes.** None.
|
|
495
|
+
|
|
496
|
+
**Verification.**
|
|
497
|
+
|
|
498
|
+
```bash
|
|
499
|
+
# After the build job runs:
|
|
500
|
+
ls artifacts/<pkg.name>-sdist/
|
|
501
|
+
# Expected: <pypi-name>-X.Y.Z.tar.gz (no .devN suffix)
|
|
502
|
+
```
|
|
503
|
+
|
|
504
|
+
If you see the expected sdist, the switch worked. If you see a
|
|
505
|
+
`.devN` suffix, your project uses dynamic versioning — see
|
|
506
|
+
[dynamic versions](https://thekevinscott.github.io/putitoutthere/guide/dynamic-versions)
|
|
507
|
+
for the env-var handoff (unchanged by this migration).
|
|
508
|
+
|
|
509
|
+
### Repository renamed `put-it-out-there` → `putitoutthere`
|
|
510
|
+
|
|
511
|
+
**Summary.** The GitHub repository slug collapsed from `put-it-out-there`
|
|
512
|
+
to `putitoutthere`, matching the npm package and CLI binary name. The
|
|
513
|
+
human-readable name "Put It Out There" (with spaces) is unchanged. GitHub
|
|
514
|
+
auto-redirects the old URL, but any place a consumer has hard-coded the
|
|
515
|
+
old slug — npm/Cargo/pyproject `repository` URLs, GitHub Actions
|
|
516
|
+
references, OIDC trust policy `repository:` claims, docs links — should
|
|
517
|
+
be updated.
|
|
518
|
+
|
|
519
|
+
**Required changes.**
|
|
520
|
+
|
|
521
|
+
```diff
|
|
522
|
+
# package.json (or Cargo.toml / pyproject.toml)
|
|
523
|
+
-"repository": "https://github.com/<owner>/put-it-out-there"
|
|
524
|
+
+"repository": "https://github.com/<owner>/putitoutthere"
|
|
525
|
+
```
|
|
526
|
+
|
|
527
|
+
```diff
|
|
528
|
+
# .github/workflows/release.yml — if you reference the action by full repo path
|
|
529
|
+
-uses: thekevinscott/put-it-out-there/.github/actions/<...>
|
|
530
|
+
+uses: thekevinscott/putitoutthere/.github/actions/<...>
|
|
531
|
+
```
|
|
532
|
+
|
|
533
|
+
```diff
|
|
534
|
+
# OIDC trust policies (PyPI, npm) that gate on the source repo
|
|
535
|
+
-"repository": "<owner>/put-it-out-there"
|
|
536
|
+
+"repository": "<owner>/putitoutthere"
|
|
537
|
+
```
|
|
538
|
+
|
|
539
|
+
If you only ever invoked `putitoutthere` via the npm package
|
|
540
|
+
(`npx putitoutthere`, `pnpm add -D putitoutthere`) or the published
|
|
541
|
+
GitHub Action, no change is required — those references already used the
|
|
542
|
+
collapsed name.
|
|
543
|
+
|
|
544
|
+
**Deprecations removed.** None. The old slug continues to redirect at
|
|
545
|
+
the GitHub layer.
|
|
546
|
+
|
|
547
|
+
**Behavior changes without code changes.**
|
|
548
|
+
|
|
549
|
+
- Documentation site moved from
|
|
550
|
+
`https://thekevinscott.github.io/put-it-out-there/` to
|
|
551
|
+
`https://thekevinscott.github.io/putitoutthere/`. The old URL
|
|
552
|
+
redirects.
|
|
553
|
+
- `git remote -v` will still show the old URL until you `git remote
|
|
554
|
+
set-url origin https://github.com/thekevinscott/putitoutthere.git`.
|
|
555
|
+
Push and fetch keep working via redirect, but updating the remote
|
|
556
|
+
avoids surprise breakage if the redirect is ever retired.
|
|
557
|
+
|
|
558
|
+
**Verification.**
|
|
559
|
+
|
|
560
|
+
```sh
|
|
561
|
+
# Confirm no stale references in your repo
|
|
562
|
+
grep -r "put-it-out-there" .
|
|
563
|
+
```
|
|
564
|
+
|
|
565
|
+
Expect no hits outside historical changelog/migration entries.
|
|
566
|
+
|
|
567
|
+
### `[package.bundle_cli]` — stage a Rust CLI into every maturin wheel (#217)
|
|
568
|
+
|
|
569
|
+
**Summary.** New optional sub-table under `[[package]]` for pypi packages
|
|
570
|
+
that want the `ruff` / `uv` / `pydantic-core` wheel shape: a companion
|
|
571
|
+
Rust CLI binary, cross-compiled per target and staged into the Python
|
|
572
|
+
source tree before maturin runs, so each wheel ships the binary as
|
|
573
|
+
package data and `pip install <pkg>` gets a working CLI on `PATH` with
|
|
574
|
+
no Rust toolchain on the user's machine. Additive — existing
|
|
575
|
+
configurations are unchanged.
|
|
576
|
+
|
|
577
|
+
**Required changes.** None for existing configs. To opt in:
|
|
578
|
+
|
|
579
|
+
```diff
|
|
580
|
+
[[package]]
|
|
581
|
+
name = "my-py"
|
|
582
|
+
kind = "pypi"
|
|
583
|
+
build = "maturin"
|
|
584
|
+
path = "packages/python"
|
|
585
|
+
globs = ["packages/python/**"]
|
|
586
|
+
targets = ["x86_64-unknown-linux-gnu", "aarch64-apple-darwin"]
|
|
587
|
+
+
|
|
588
|
+
+[package.bundle_cli]
|
|
589
|
+
+bin = "my-cli"
|
|
590
|
+
+stage_to = "src/my_py/_binary"
|
|
591
|
+
+crate_path = "crates/my-rust" # defaults to "." (repo workspace root)
|
|
592
|
+
```
|
|
593
|
+
|
|
594
|
+
And in the Python package's `pyproject.toml`:
|
|
595
|
+
|
|
596
|
+
```diff
|
|
597
|
+
+[project.scripts]
|
|
598
|
+
+my-cli = "my_py._binary:entrypoint" # small os.execv launcher stub
|
|
599
|
+
+
|
|
600
|
+
[tool.maturin]
|
|
601
|
+
-include = ["..."]
|
|
602
|
+
+include = ["...", "src/my_py/_binary/**"] # ship the staged binary
|
|
603
|
+
```
|
|
604
|
+
|
|
605
|
+
See [README → Rust CLI inside a PyPI wheel](https://github.com/thekevinscott/putitoutthere/blob/main/README.md#rust-cli-inside-a-pypi-wheel)
|
|
606
|
+
for the full worked example including the launcher stub.
|
|
607
|
+
|
|
608
|
+
**Deprecations removed.** None.
|
|
609
|
+
|
|
610
|
+
**Behavior changes without code changes.** None for existing configs.
|
|
611
|
+
Packages that declare `[package.bundle_cli]` get two new steps emitted
|
|
612
|
+
in the scaffolded build job (`Setup Rust (if pypi bundle_cli)` +
|
|
613
|
+
`Build + stage bundled CLI`), both gated on
|
|
614
|
+
`matrix.kind == 'pypi' && matrix.bundle_cli.bin != '' && matrix.target != 'sdist'`
|
|
615
|
+
so packages without the block see no change.
|
|
616
|
+
|
|
617
|
+
**Verification.** For a repo that opts in:
|
|
618
|
+
|
|
619
|
+
```bash
|
|
620
|
+
# After piot's build job runs on one target:
|
|
621
|
+
ls packages/python/src/my_py/_binary/
|
|
622
|
+
# Expected: my-cli (or my-cli.exe on Windows targets)
|
|
623
|
+
|
|
624
|
+
# After the wheel is built:
|
|
625
|
+
python -m zipfile -l packages/python/dist/*.whl | grep _binary
|
|
626
|
+
# Expected: one entry per target listing the staged binary.
|
|
627
|
+
|
|
628
|
+
# End-to-end on a released wheel:
|
|
629
|
+
pip install my-py==<published-version>
|
|
630
|
+
which my-cli
|
|
631
|
+
my-cli --version
|
|
632
|
+
```
|