putitoutthere 0.1.42 → 0.1.44

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/README.md CHANGED
@@ -1,152 +1,429 @@
1
1
  # Put It Out There
2
2
 
3
- Polyglot release orchestrator for single-maintainer, LLM-authored projects
4
- that publish to crates.io, PyPI, and npm from one monorepo. One config file,
5
- one CLI, one trailer-driven signal — no per-package release plumbing.
3
+ A reusable GitHub Actions workflow that publishes packages to crates.io, PyPI,
4
+ and npm from one repo. OIDC-first, cascade-aware, polyglot. The consumer
5
+ surface is one config file plus ~10 lines of YAML calling
6
+ `uses: thekevinscott/putitoutthere/.github/workflows/release.yml@v1`.
6
7
 
7
- **[Documentation](https://thekevinscott.github.io/put-it-out-there/)**
8
+ ## Quickstart
8
9
 
9
- ## Install
10
+ ### 1. Drop in `.github/workflows/release.yml`
10
11
 
11
- ```sh
12
- npx putitoutthere init
12
+ ```yaml
13
+ name: Release
14
+
15
+ on:
16
+ push:
17
+ branches: [main]
18
+
19
+ jobs:
20
+ release:
21
+ uses: thekevinscott/putitoutthere/.github/workflows/release.yml@v1
22
+ permissions:
23
+ contents: write
24
+ id-token: write
13
25
  ```
14
26
 
15
- This scaffolds a `putitoutthere.toml`, a `release.yml` workflow, and an
16
- `AGENTS.md` file explaining the trailer convention to future
17
- contributors.
27
+ Pinned action versions, `plan → build → publish` orchestration, and GitHub
28
+ Release creation all live inside the reusable workflow.
18
29
 
19
- For one-off runs without scaffolding:
30
+ Optional inputs — `with:` block at the call site:
20
31
 
21
- ```sh
22
- pnpm add -D putitoutthere
23
- pnpm putitoutthere plan
24
- ```
32
+ | Input | Default | Use when |
33
+ |------------------|--------------|--------------------------------------------------------------------------|
34
+ | `environment` | `release` | Your GitHub deployment environment is named differently. |
35
+ | `node_version` | `24` | You need a specific Node version for `kind = "npm"` build steps. |
36
+ | `python_version` | `3.12` | You need a specific Python version for `kind = "pypi"` build steps. |
25
37
 
26
- ## Minimum config
38
+ ### 2. Drop in `putitoutthere.toml`
27
39
 
28
40
  ```toml
29
- # putitoutthere.toml
30
41
  [putitoutthere]
31
42
  version = 1
32
43
 
33
44
  [[package]]
34
45
  name = "my-lib"
35
- kind = "npm" # or "pypi" | "crates"
46
+ kind = "pypi" # or "npm" | "crates"
36
47
  path = "."
37
- paths = ["src/**", "package.json"]
48
+ paths = ["src/**", "pyproject.toml"]
49
+ build = "hatch" # required for kind = "pypi"
50
+ tag_format = "v{version}" # single-package repos often want this
38
51
  ```
39
52
 
40
- `paths` are the globs that trigger a release for this package. Any commit
41
- touching a matching file makes the package a candidate.
53
+ `paths` are the globs that trigger a release. Any commit touching a matching
54
+ file makes the package a candidate.
55
+
56
+ More config patterns are in [Configuration](#configuration) below.
57
+
58
+ ### 3. Register trusted publishers
42
59
 
43
- ## Releasing
60
+ Each registry needs a one-time external setup so OIDC publishes work. See
61
+ [Trusted publishers](#trusted-publishers) below — three short lists, one per
62
+ registry.
44
63
 
45
- Add a trailer to the commit that should ship:
64
+ ### 4. Push a release
65
+
66
+ Merge to `main`. Default behavior: any package whose `paths` matched changed
67
+ files cascades and ships at `patch`. To bump `minor` or `major`:
46
68
 
47
69
  ```
48
70
  fix: handle empty token lists
49
71
 
50
- release: patch
72
+ release: minor
73
+ ```
74
+
75
+ …in the merge commit body. See [Trailer](#trailer) below.
76
+
77
+ ## Configuration
78
+
79
+ `putitoutthere.toml` lives at the repo root.
80
+
81
+ ### `[putitoutthere]`
82
+
83
+ ```toml
84
+ [putitoutthere]
85
+ version = 1 # required; only 1 is valid today
51
86
  ```
52
87
 
53
- Valid bumps: `patch`, `minor`, `major`, `skip`. On push to `main`, the
54
- `release.yml` workflow runs `putitoutthere plan` against the trailer +
55
- changed paths, then `putitoutthere publish` per matching package. Tags are
56
- `{name}-v{version}`.
88
+ ### `[[package]]` (one per releasable unit)
89
+
90
+ | Field | Type | Required | Notes |
91
+ |-----------------|----------|----------|---------------------------------------------------|
92
+ | `name` | string | yes | Unique across the config. |
93
+ | `kind` | enum | yes | `crates` \| `pypi` \| `npm`. |
94
+ | `path` | string | yes | Package working dir (`Cargo.toml` / `pyproject.toml` / `package.json` location). |
95
+ | `paths` | string[] | yes | Globs that cascade this package. |
96
+ | `depends_on` | string[] | no | Package names this one cascades on top of. |
97
+ | `first_version` | string | no | Default `0.1.0`. |
98
+ | `tag_format` | string | no | Template for the git tag. Default `"{name}-v{version}"`. Single-package repos often want `"v{version}"`. |
99
+ | `trust_policy` | table | no | Declared OIDC trust-policy expectations. See [Trusted publishers](#trusted-publishers). |
100
+
101
+ ### `kind = "crates"`
102
+
103
+ | Field | Type | Notes |
104
+ |-----------------------|----------|------------------------------------------------------------|
105
+ | `crate` | string | Override `name` → crates.io name. |
106
+ | `features` | string[] | Pass through to `cargo publish --features`. |
107
+ | `no_default_features` | bool | Pass `--no-default-features` to `cargo publish` when true. |
108
+
109
+ ### `kind = "pypi"`
110
+
111
+ | Field | Type | Notes |
112
+ |--------------|------------------------|----------------------------------------------------|
113
+ | `pypi` | string | Override `name` → PyPI registered name. |
114
+ | `build` | enum | `maturin` \| `setuptools` \| `hatch`. Required. |
115
+ | `targets` | (string \| object)[] | Required when `build = "maturin"`. Triples or `{ triple, runner }` objects. |
116
+ | `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). |
117
+
118
+ ### `kind = "npm"`
57
119
 
58
- No trailer → no release, even if code changed. That's the point.
120
+ | Field | Type | Notes |
121
+ |-----------|------------------------|------------------------------------------------------|
122
+ | `npm` | string | Override `name` → npm name (for scoped packages). |
123
+ | `access` | enum | `public` \| `restricted`. Default `public`. |
124
+ | `tag` | string | dist-tag. Default `latest`. |
125
+ | `build` | enum | `napi` \| `bundled-cli`. Omitted = vanilla. See [Recipes → Bundled-CLI npm family](#bundled-cli-npm-family). |
126
+ | `targets` | (string \| object)[] | Required when `build ∈ {napi, bundled-cli}`. |
59
127
 
60
- ## Worked example: polyglot
128
+ ### Example: polyglot Rust library
61
129
 
62
- The reference fixture at [`test/fixtures/polyglot-everything/`](./test/fixtures/polyglot-everything/)
63
- mirrors a real polyglot shape: a Rust crate, a PyO3 Python wheel wrapping it,
64
- and an npm CLI bundling the Rust binary. One `putitoutthere.toml` declares
65
- all three:
130
+ One Rust crate feeds three artifacts:
66
131
 
67
132
  ```toml
68
133
  [[package]]
69
- name = "my-tool-rust"
134
+ name = "my-rust"
70
135
  kind = "crates"
71
- path = "packages/rust"
72
- paths = ["packages/rust/**"]
136
+ path = "crates/my-rust"
137
+ paths = ["crates/my-rust/**"]
73
138
 
74
139
  [[package]]
75
- name = "my-tool-python"
76
- kind = "pypi"
77
- path = "packages/python"
78
- paths = ["packages/python/**"]
79
- build = "maturin"
80
- depends_on = ["my-tool-rust"]
140
+ name = "my-py"
141
+ kind = "pypi"
142
+ path = "py/my-py"
143
+ paths = ["py/my-py/**"]
144
+ build = "maturin"
145
+ targets = ["x86_64-unknown-linux-gnu", "aarch64-apple-darwin"]
146
+ depends_on = ["my-rust"]
81
147
 
82
148
  [[package]]
83
- name = "my-tool-cli"
84
- kind = "npm"
85
- path = "packages/ts"
86
- paths = ["packages/ts/**"]
87
- build = "bundled-cli"
88
- depends_on = ["my-tool-rust"]
149
+ name = "my-cli"
150
+ kind = "npm"
151
+ path = "packages/ts"
152
+ paths = ["packages/ts/**"]
153
+ build = "bundled-cli"
154
+ targets = ["x86_64-unknown-linux-gnu", "aarch64-apple-darwin"]
155
+ depends_on = ["my-rust"]
89
156
  ```
90
157
 
91
- A change to `packages/rust/` cascades: the crate ships, and the Python + npm
92
- wrappers ship on top (bumped to match). A change to only the TS shim ships
93
- just the npm package.
158
+ A change to `crates/my-rust/` cascades: the crate ships, then the Python
159
+ wheels and npm family ship on top, version-bumped to match.
160
+
161
+ ### Example: multi-package workspace
162
+
163
+ ```toml
164
+ [[package]]
165
+ name = "@my/core"
166
+ kind = "npm"
167
+ path = "packages/core"
168
+ paths = ["packages/core/**"]
169
+
170
+ [[package]]
171
+ name = "@my/parser"
172
+ kind = "npm"
173
+ path = "packages/parser"
174
+ paths = ["packages/parser/**"]
175
+ depends_on = ["@my/core"]
176
+ ```
94
177
 
95
- ## Library shapes
178
+ ## Trailer
96
179
 
97
- End-to-end walkthroughs — config + `release.yml` + prerequisites + gotchas —
98
- for the common shapes. Pick the one that matches your repo:
180
+ The trailer is **optional**. Default behavior is `patch` whenever a package's
181
+ `paths` matched changed files.
99
182
 
100
- **Single-package**
183
+ Grammar:
101
184
 
102
- - [Python library](./docs/guide/shapes/python-library.md) — one `pyproject.toml` to PyPI
103
- - [npm library](./docs/guide/shapes/npm-library.md) — one `package.json` to npm
104
- - [Rust crate](./docs/guide/shapes/rust-crate.md) — one `Cargo.toml` to crates.io
185
+ ```
186
+ release: <bump> [pkg1, pkg2, ...]
187
+ ```
105
188
 
106
- **Multi-package workspaces**
189
+ `<bump>` is `patch` | `minor` | `major` | `skip`. The optional package list
190
+ scopes a non-default bump to specific packages.
107
191
 
108
- - [Rust workspace](./docs/guide/shapes/rust-workspace.md) — multiple crates with `depends_on` cascade
109
- - [npm workspace](./docs/guide/shapes/npm-workspace.md) — multiple npm packages, shared dependency graph
192
+ | Trailer | Effect |
193
+ |--------------------------|------------------------------------------------------------------------|
194
+ | *(none)* | Cascaded packages bump `patch`. |
195
+ | `release: minor` | Cascaded packages bump `minor`. |
196
+ | `release: major` | Cascaded packages bump `major`. |
197
+ | `release: skip` | No release this commit. Cascade ignored. |
198
+ | `release: minor [a, b]` | `a` and `b` bump `minor`; other cascaded packages stay at `patch`. |
110
199
 
111
- **Rust core, multi-registry**
200
+ The trailer matches anywhere in the commit body. If multiple `release:` lines
201
+ are present, the **last** one wins.
112
202
 
113
- - [Rust + PyO3 wheels](./docs/guide/shapes/rust-pyo3.md) — crate + PyPI (no napi)
114
- - [Rust + napi npm](./docs/guide/shapes/rust-napi.md) — crate + npm family (no PyPI)
115
- - [Polyglot Rust library](./docs/guide/shapes/polyglot-rust.md) — all three registries from one core
116
- - [Python wheels with C extensions](./docs/guide/shapes/python-cibuildwheel.md) — `cibuildwheel` for the `pillow`/`lxml`/`numpy` shape
203
+ ## Cascade
117
204
 
118
- **Distribution patterns**
205
+ A package cascades into the release plan when a commit changes any file
206
+ matching one of its `paths` globs since its last tag. If another package
207
+ declares `depends_on = ["this-package"]`, that package also cascades.
208
+ Transitively, DFS-ordered, with cycle detection at config-load.
119
209
 
120
- - [Bundled-CLI npm family](./docs/guide/shapes/bundled-cli.md) — compiled CLI shipped as an npm per-platform family
121
- - [Dual-family npm (CLI + napi)](./docs/guide/shapes/dual-family-npm.md) — one library with both an addon and a binary
210
+ Inside a single release, packages publish in topological order of their
211
+ `depends_on` graph. If your Python wrapper depends on a Rust crate, the
212
+ crate publishes first.
122
213
 
123
- Full index at [`docs/guide/shapes/`](./docs/guide/shapes/).
214
+ Each handler's first move on publish is `isPublished` — check the registry
215
+ for the target version. Already there → skip cleanly. Lets you re-run failed
216
+ releases without fighting registry-immutable-publish semantics.
124
217
 
125
218
  ## Trusted publishers
126
219
 
127
- Preferred over long-lived tokens. One-time setup per registry:
220
+ OIDC trusted publishers — the only auth path supported by the reusable
221
+ workflow. Long-lived registry tokens are not reachable through the workflow.
222
+
223
+ The fields each registry needs are the same: repository owner/name, workflow
224
+ filename (`release.yml`), and optionally a GitHub environment name.
225
+
226
+ ### crates.io
227
+
228
+ 1. Publish your crate once through the normal `cargo` flow so the crate
229
+ exists. (Trusted publishing needs a crate owner record.)
230
+ 2. Go to `https://crates.io/crates/<crate>/settings` → **Trusted Publishing**
231
+ → **Add**.
232
+ 3. Fill in: repository owner, repository name, workflow filename
233
+ (`release.yml`), environment (optional).
234
+
235
+ ### PyPI
236
+
237
+ 1. Go to `https://pypi.org/manage/project/<name>/settings/publishing/` (or
238
+ **Publishing** on the project page).
239
+ 2. Add a **GitHub** trusted publisher: owner, repo, workflow filename,
240
+ environment (optional).
241
+ 3. Brand-new project? Use a [pending publisher](https://docs.pypi.org/trusted-publishers/creating-a-project-through-oidc/)
242
+ to skip the bootstrap token.
243
+
244
+ ### npm
245
+
246
+ 1. Publish at least one version of your package with a classic
247
+ `NODE_AUTH_TOKEN` so the package exists on the registry. (npm's trusted
248
+ publisher requires an existing package.)
249
+ 2. Go to `https://www.npmjs.com/package/<name>/access` → **Require trusted
250
+ publisher**.
251
+ 3. Fill in: repository, workflow filename, environment (optional).
252
+ 4. Delete the bootstrap token.
253
+
254
+ ### Catching trust-policy drift before release
255
+
256
+ Renaming `release.yml` to anything else, or renaming the environment, breaks
257
+ publish with an opaque HTTP 400 from the registry. To catch the rename
258
+ before it ships, declare the expected values in `putitoutthere.toml`:
128
259
 
129
- - **npm:** [npm trusted publishing](https://docs.npmjs.com/trusted-publishers) — attach the package to this repo + workflow. `--provenance` is added automatically.
130
- - **PyPI:** [pending publisher](https://docs.pypi.org/trusted-publishers/) — register the project name pointing at this repo's `release.yml`.
131
- - **crates.io:** [OIDC via `rust-lang/crates-io-auth-action@v1`](https://github.com/rust-lang/crates-io-auth-action) — the crate needs one manual bootstrap publish first.
260
+ ```toml
261
+ [[package]]
262
+ name = "my-crate"
263
+ kind = "crates"
264
+ path = "crates/my-crate"
265
+ paths = ["crates/my-crate/**"]
266
+
267
+ [package.trust_policy]
268
+ workflow = "release.yml" # required; bare filename
269
+ environment = "release" # optional
270
+ repository = "my-org/my-crate" # optional
271
+ ```
272
+
273
+ The engine diffs the declaration against the local workflow file and (in CI)
274
+ against `GITHUB_WORKFLOW_REF` before any registry call. For `kind = "crates"`,
275
+ a registry cross-check runs when `CRATES_IO_DOCTOR_TOKEN` is set — calls the
276
+ trusted-publishing read API and fails on mismatch. PyPI and npm don't expose
277
+ read APIs for trust policy, so the declared diff is the full gate there.
278
+
279
+ ## Recipes
280
+
281
+ ### Bundled-CLI npm family
282
+
283
+ Ship a compiled CLI as an npm per-platform family — `npm install -g my-cli`
284
+ gives users a working binary on PATH. The `esbuild` / `biome` distribution
285
+ shape.
286
+
287
+ Config:
288
+
289
+ ```toml
290
+ [[package]]
291
+ name = "my-cli"
292
+ kind = "npm"
293
+ npm = "my-cli"
294
+ build = "bundled-cli"
295
+ path = "packages/ts-cli"
296
+ paths = ["packages/ts-cli/**", "crates/my-cli/**"]
297
+ targets = [
298
+ "x86_64-unknown-linux-gnu",
299
+ "aarch64-unknown-linux-gnu",
300
+ "x86_64-apple-darwin",
301
+ "aarch64-apple-darwin",
302
+ "x86_64-pc-windows-msvc",
303
+ ]
304
+ ```
305
+
306
+ The engine publishes a per-platform sub-package per target
307
+ (`my-cli-<triple>`) plus a top-level whose `optionalDependencies` pin them
308
+ at the published version. npm's resolver installs exactly one sub-package
309
+ at consumer install time.
310
+
311
+ You author the launcher script that picks the right per-platform binary
312
+ once. `package.json`:
313
+
314
+ ```json
315
+ {
316
+ "name": "my-cli",
317
+ "bin": { "my-cli": "bin/my-cli.js" }
318
+ }
319
+ ```
320
+
321
+ `bin/my-cli.js`:
322
+
323
+ ```js
324
+ #!/usr/bin/env node
325
+ const { spawnSync } = require('node:child_process');
326
+ const { platform, arch } = process;
327
+
328
+ const triples = {
329
+ 'linux-x64': 'x86_64-unknown-linux-gnu',
330
+ 'linux-arm64': 'aarch64-unknown-linux-gnu',
331
+ 'darwin-x64': 'x86_64-apple-darwin',
332
+ 'darwin-arm64': 'aarch64-apple-darwin',
333
+ 'win32-x64': 'x86_64-pc-windows-msvc',
334
+ };
335
+
336
+ const triple = triples[`${platform}-${arch}`];
337
+ if (!triple) {
338
+ console.error(`my-cli: unsupported platform ${platform}-${arch}`);
339
+ process.exit(1);
340
+ }
341
+
342
+ const pkg = `my-cli-${triple}`;
343
+ const binary = require.resolve(`${pkg}/bin/my-cli${platform === 'win32' ? '.exe' : ''}`);
344
+ const result = spawnSync(binary, process.argv.slice(2), { stdio: 'inherit' });
345
+ process.exit(result.status ?? 1);
346
+ ```
347
+
348
+ Each per-platform sub-package needs its own npm trusted-publisher
349
+ registration (a policy on `my-cli` does not cover
350
+ `my-cli-x86_64-unknown-linux-gnu`).
351
+
352
+ ### Rust CLI inside a PyPI wheel
353
+
354
+ `pip install my-lib` on any platform gets a working CLI on `PATH` without
355
+ the user installing a Rust toolchain. The `ruff` / `uv` / `pydantic-core`
356
+ pattern.
357
+
358
+ Config:
359
+
360
+ ```toml
361
+ [[package]]
362
+ name = "my-py"
363
+ kind = "pypi"
364
+ build = "maturin"
365
+ path = "packages/python"
366
+ paths = ["packages/python/**", "crates/my-rust/**"]
367
+ targets = [
368
+ "x86_64-unknown-linux-gnu",
369
+ "aarch64-unknown-linux-gnu",
370
+ "x86_64-apple-darwin",
371
+ "aarch64-apple-darwin",
372
+ "x86_64-pc-windows-msvc",
373
+ ]
374
+ depends_on = ["my-rust"]
375
+
376
+ [package.bundle_cli]
377
+ bin = "my-cli"
378
+ stage_to = "src/my_py/_binary"
379
+ crate_path = "crates/my-rust"
380
+ ```
381
+
382
+ The reusable workflow cross-compiles the binary per target and stages it
383
+ into the package source tree before maturin runs. Your `pyproject.toml`
384
+ ties the staged binary into a `console_scripts` entry:
385
+
386
+ ```toml
387
+ [project.scripts]
388
+ my-cli = "my_py._binary:entrypoint"
389
+
390
+ [tool.maturin]
391
+ include = ["src/my_py/_binary/**"]
392
+ ```
393
+
394
+ Launcher in `packages/python/src/my_py/_binary/__init__.py`:
395
+
396
+ ```python
397
+ import os, sys
398
+ from pathlib import Path
399
+
400
+ def entrypoint():
401
+ here = Path(__file__).parent
402
+ binary = here / ("my-cli.exe" if os.name == "nt" else "my-cli")
403
+ if not binary.exists():
404
+ sys.stderr.write(f"my-cli binary not found at {binary}\n")
405
+ sys.exit(1)
406
+ os.execv(binary, [str(binary), *sys.argv[1:]])
407
+ ```
132
408
 
133
- Token fallbacks (`NPM_TOKEN`, `PYPI_API_TOKEN`, `CARGO_REGISTRY_TOKEN`) are
134
- still read as env vars if OIDC isn't available. `putitoutthere doctor`
135
- reports which path is active.
409
+ ## Dynamic-version PyPI gotcha
136
410
 
137
- ## What it is not
411
+ If your `pyproject.toml` uses `[project].dynamic = ["version"]` with
412
+ `hatch-vcs` or `setuptools-scm`, the build backend derives the version from
413
+ the latest git tag at build time — which is still the **previous** release
414
+ when the build runs. Without a handoff, the sdist ships as
415
+ `<pkg>-X.Y.Z.devN.tar.gz` instead of `<pkg>-X.Y.Z.tar.gz`.
138
416
 
139
- - Not a changelog generator. Use `git log` + conventional commits if you
140
- want one.
141
- - Not a monorepo manager. Package boundaries are declared, not discovered.
142
- - Not a dependency resolver across ecosystems. `depends_on` is about
143
- *cascading releases*, not runtime version pinning.
144
- - Not a build system. Handlers shell out to `cargo`, `uv`/`maturin`/`hatch`,
145
- `npm` — standard toolchains only.
417
+ The reusable workflow sets `SETUPTOOLS_SCM_PRETEND_VERSION` to the planned
418
+ version on the build step, which both `setuptools-scm` and `hatch-vcs`
419
+ honor. Per-package variants like `SETUPTOOLS_SCM_PRETEND_VERSION_FOR_<PKG>`
420
+ are silently ignored by `hatch-vcs`; only the global form works.
146
421
 
147
- ## Docs
422
+ ## Project layout
148
423
 
149
- - [Design proposal](./notes/4-17-2026-initial-plan/plan/proposal.md) — why this tool exists.
150
- - [Implementation plan](./notes/4-17-2026-initial-plan/plan/plan.md) — exhaustive reference.
151
- - [Migration guides](./migrations/) — per-repo plans for adopting putitoutthere.
152
- - [v0 epic](https://github.com/thekevinscott/put-it-out-there/issues/2) — remaining work.
424
+ - [`CHANGELOG.md`](./CHANGELOG.md) — per-release changes.
425
+ - [`MIGRATIONS.md`](./MIGRATIONS.md) — per-version upgrade guide.
426
+ - [`notes/design-commitments.md`](./notes/design-commitments.md) — non-goals.
427
+ - [`notes/internals/`](./notes/internals/) — internal contracts (artifact
428
+ layout, runner setup) that the reusable workflow honors so consumers don't
429
+ have to.
package/action.yml CHANGED
@@ -1,10 +1,10 @@
1
- name: 'Put It Out There'
2
- description: 'Polyglot release orchestrator for crates.io, PyPI, and npm.'
1
+ name: 'Put It Out There (internal)'
2
+ description: 'Internal step-level wrapper around the putitoutthere CLI. Consumers compose with the reusable workflow at .github/workflows/release.yml@v1, not with this action directly. See notes/design-commitments.md non-goal #10.'
3
3
  author: 'Kevin Scott'
4
4
 
5
5
  inputs:
6
6
  command:
7
- description: 'Which putitoutthere command to run: plan | publish'
7
+ description: 'Which CLI subcommand to run. Canonical values are `plan` and `publish`.'
8
8
  required: false
9
9
  default: 'plan'
10
10
  dry_run:
@@ -12,13 +12,13 @@ inputs:
12
12
  required: false
13
13
  default: 'false'
14
14
  fail_on_error:
15
- description: 'If true, non-zero putitoutthere exit codes fail the step.'
15
+ description: 'If true, non-zero CLI exit codes fail the step.'
16
16
  required: false
17
17
  default: 'true'
18
18
 
19
19
  outputs:
20
20
  matrix:
21
- description: 'JSON matrix emitted by `putitoutthere plan`. Empty string for other commands.'
21
+ description: 'JSON matrix emitted by `plan`. Output key is omitted when the plan resolves to zero rows or when command is anything other than plan; downstream jobs should coalesce with `|| ''[]'''.'
22
22
 
23
23
  runs:
24
24
  using: 'node24'
package/dist/action.js CHANGED
@@ -13,7 +13,7 @@ export async function main() {
13
13
  const dryRun = (process.env.INPUT_DRY_RUN ?? 'false').toLowerCase() === 'true';
14
14
  const failOnError = (process.env.INPUT_FAIL_ON_ERROR ?? 'true').toLowerCase() !== 'false';
15
15
  if (!command) {
16
- process.stderr.write('put-it-out-there action: missing required input `command`\n');
16
+ process.stderr.write('putitoutthere action: missing required input `command`\n');
17
17
  process.exit(1);
18
18
  }
19
19
  const argv = ['node', 'putitoutthere', command];
@@ -22,7 +22,7 @@ export async function main() {
22
22
  argv.push('--json');
23
23
  const code = await run(argv);
24
24
  if (code !== 0 && !failOnError) {
25
- process.stderr.write(`put-it-out-there action: ignoring non-zero exit (fail_on_error=false): ${code}\n`);
25
+ process.stderr.write(`putitoutthere action: ignoring non-zero exit (fail_on_error=false): ${code}\n`);
26
26
  process.exit(0);
27
27
  }
28
28
  process.exit(code);
@@ -1 +1 @@
1
- {"version":3,"file":"action.js","sourceRoot":"","sources":["../src/action.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAEH,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,MAAM,GAAG,CAAC,OAAO,CAAC,GAAG,CAAC,aAAa,IAAI,OAAO,CAAC,CAAC,WAAW,EAAE,KAAK,MAAM,CAAC;IAC/E,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,6DAA6D,CAC9D,CAAC;QACF,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;IAClB,CAAC;IAED,MAAM,IAAI,GAAG,CAAC,MAAM,EAAE,eAAe,EAAE,OAAO,CAAC,CAAC;IAChD,IAAI,MAAM;QAAE,IAAI,CAAC,IAAI,CAAC,WAAW,CAAC,CAAC;IACnC,IAAI,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC;IAEpB,MAAM,IAAI,GAAG,MAAM,GAAG,CAAC,IAAI,CAAC,CAAC;IAC7B,IAAI,IAAI,KAAK,CAAC,IAAI,CAAC,WAAW,EAAE,CAAC;QAC/B,OAAO,CAAC,MAAM,CAAC,KAAK,CAClB,0EAA0E,IAAI,IAAI,CACnF,CAAC;QACF,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;IAClB,CAAC;IACD,OAAO,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;AACrB,CAAC;AAED,oFAAoF;AACpF,IAAI,MAAM,CAAC,IAAI,CAAC,GAAG,KAAK,UAAU,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC;IACpD,KAAK,IAAI,EAAE,CAAC;AACd,CAAC"}
1
+ {"version":3,"file":"action.js","sourceRoot":"","sources":["../src/action.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAEH,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,MAAM,GAAG,CAAC,OAAO,CAAC,GAAG,CAAC,aAAa,IAAI,OAAO,CAAC,CAAC,WAAW,EAAE,KAAK,MAAM,CAAC;IAC/E,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,MAAM,IAAI,GAAG,CAAC,MAAM,EAAE,eAAe,EAAE,OAAO,CAAC,CAAC;IAChD,IAAI,MAAM;QAAE,IAAI,CAAC,IAAI,CAAC,WAAW,CAAC,CAAC;IACnC,IAAI,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC;IAEpB,MAAM,IAAI,GAAG,MAAM,GAAG,CAAC,IAAI,CAAC,CAAC;IAC7B,IAAI,IAAI,KAAK,CAAC,IAAI,CAAC,WAAW,EAAE,CAAC;QAC/B,OAAO,CAAC,MAAM,CAAC,KAAK,CAClB,uEAAuE,IAAI,IAAI,CAChF,CAAC;QACF,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;IAClB,CAAC;IACD,OAAO,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;AACrB,CAAC;AAED,oFAAoF;AACpF,IAAI,MAAM,CAAC,IAAI,CAAC,GAAG,KAAK,UAAU,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC;IACpD,KAAK,IAAI,EAAE,CAAC;AACd,CAAC"}
package/dist/cli.d.ts CHANGED
@@ -1,16 +1,14 @@
1
1
  /**
2
- * `putitoutthere` CLI entry. Thin wrapper around the SDK.
3
- *
4
- * plan → src/plan.ts
5
- * publish → src/publish.ts
6
- * init → TODO #20
7
- * doctor → TODO #23
2
+ * `putitoutthere` CLI entry. Internal seam — the reusable workflow
3
+ * (`.github/workflows/release.yml`) invokes this. Not a consumer-
4
+ * facing surface; flags / help text are stable enough to test, but
5
+ * not promised externally. See `notes/design-commitments.md`.
8
6
  *
9
7
  * Global flags:
10
8
  * --cwd <path> working directory (default: process.cwd())
11
9
  * --config <path> path to putitoutthere.toml
12
10
  * --dry-run for publish; no side effects
13
- * --json for plan; emit JSON instead of a table
11
+ * --json machine-readable output
14
12
  */
15
13
  export declare function run(argv: readonly string[]): Promise<number>;
16
14
  //# sourceMappingURL=cli.d.ts.map
package/dist/cli.d.ts.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"cli.d.ts","sourceRoot":"","sources":["../src/cli.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;GAaG;AAyHH,wBAAsB,GAAG,CAAC,IAAI,EAAE,SAAS,MAAM,EAAE,GAAG,OAAO,CAAC,MAAM,CAAC,CA0UlE"}
1
+ {"version":3,"file":"cli.d.ts","sourceRoot":"","sources":["../src/cli.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AA6GH,wBAAsB,GAAG,CAAC,IAAI,EAAE,SAAS,MAAM,EAAE,GAAG,OAAO,CAAC,MAAM,CAAC,CAyTlE"}