@oakoss/oakum 0.1.4 → 0.3.0
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 +139 -1
- package/README.md +1 -1
- package/npm-shrinkwrap.json +2 -2
- package/package.json +2 -2
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,143 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## 0.3.0 (2026-09-12)
|
|
4
|
+
|
|
5
|
+
### Added
|
|
6
|
+
|
|
7
|
+
`migrate` derives `tag-format` from the tags a repository already carries, so a cutover from a tool that tags `name@version` no longer meets oakum's `name/v1.0.0` default at the first release. Derivation is structural rather than a list: a shape is written only when one template explains every tag, and only when oakum's own tag reader can read that shape back. Tags that disagree, or that no split explains, write nothing and are named among the remaining steps, because refusing beats cutting a tag the repository does not use; the shapes this repository could adopt are listed beside the ask, since which one is right depends on the tags it could not reconcile. That list drops any shape the workspace cannot use — above one tag-managed package the bare `v{{ version }}` is absent, because a release reads such a tag as leftover ambiguity, and the step that refuses a bare shape must not then offer it. A repository whose tags cannot be read is told so rather than treated as having none.
|
|
8
|
+
|
|
9
|
+
`migrate` also names the install pin among its remaining steps when the repository has none, quoting the version it just wrote to `tool-version` and the install command for the ecosystem it detected — the same one the workflow it prints below uses, rather than both ecosystems' commands regardless. Without a pin every later oakum command refuses, and a reader who installed globally would otherwise meet that refusal with the migration already applied.
|
|
10
|
+
|
|
11
|
+
A migration guide, at [docs/guide/migrating.md](https://github.com/oakoss/oakum/blob/main/docs/guide/migrating.md). It covers which source settings carry and which do not — and the three different reasons a setting does not carry, since "no counterpart, nothing lost" and "no counterpart, but your release commits change author" want different responses from a reader. It puts the remaining steps in dependency order rather than in the order they print, because pinning the binary before pasting the workflow is the difference between a clean cutover and meeting a refusal with the migration already applied.
|
|
12
|
+
|
|
13
|
+
It also names the one part of a cutover that cannot be ported: a pipeline built on a plan/act split, where one job computes a mode and downstream jobs run guarded by `--expect-mode`, has no equivalent here. oakum has no plan step — `ci version-pr` and `release` each derive their own precondition from repository state when they run — so there is no window between deciding and acting to guard. That is a different safety argument rather than a weaker one, but it means the workflow is rewritten, not translated.
|
|
14
|
+
|
|
15
|
+
The guide leads with the acceptance test, which is **`oakum status` names every package you expect to release** — not "`check --strict` exits 0". One real cutover was given the second and satisfied it immediately with a config that managed nothing. oakum has since closed the specific hole that cutover fell into: a config whose only packages are private now exits 2 and names the fix. One shape still passes deliberately — an `include`/`exclude` that selects nothing exits 0, because emptying a selection is a decision the config states and a gate must not refuse one. The argument is narrower for that, and it still holds: a plan is a positive statement about what will happen, a gate exiting 0 is the absence of a complaint, and reading the plan needs no guard to have been written in advance for the particular way your config is wrong.
|
|
16
|
+
|
|
17
|
+
`oakum migrate` now names what the repository can retire, instead of leaving it to be found in the schema. `extra-files` declares a JSON file that carries a version — a plugin manifest, a marketplace entry — and `oakum version` writes it in the same pass as the manifest and under the same rollback, which retires a sync script, its drift job and its tests. v1 writes JSON only, so a chart or a README badge is refused at config parse rather than written. The reporter who found this called it the strongest argument for the migration, and found it by noticing a phrase in `oakum version --help` and then reading `_schema.json`.
|
|
18
|
+
|
|
19
|
+
### Changed
|
|
20
|
+
|
|
21
|
+
The GitHub Actions guide says why the printed workflow carries no `concurrency:` block, and does not tell you to omit one. Whether a group is load-bearing depends on whether the pipeline carries a decision between jobs: one that does needs protecting from a cancellation window, and one that re-derives its preconditions per command does not. This repository's own workflow carries a group and the printed one does not, and both are correct for that reason. The earlier guidance would have generalised another tool's failure without checking whether the mechanism carries over.
|
|
22
|
+
|
|
23
|
+
`status` reports a changed package the config keeps but cannot version — a private package where `private-packages.version` is not set, so no bump file could ever cover it. It appears in the rendered summary and as an `unmanaged` array in `--json`, and it does not change the exit code. `check` stays silent on that state on purpose: `status` reports, `check` decides, and there is nothing here to decide.
|
|
24
|
+
|
|
25
|
+
`status --json` reports coverage for real. It emitted `uncovered: []` for every repository, whatever had changed, because the command never ran the coverage look — only `check` and `ci pr-status` did. `schema_version` is deliberately unchanged: the new fields are additive and an old parser sees the shape it always saw, but a reader that treated an empty `uncovered` as a clean result should now read `coverage`, which says what happened to the look — `ran`, `failed`, or `not-asked`. The third is not a hedge: a report rendered from a plan already in hand, such as the Version Packages body, never asks the question, and saying the look failed there would blame git for something nobody attempted. Where git cannot diff the tree, `status` says so in the rendered summary as well as the JSON, since stderr does not reach a step summary or a pull-request body.
|
|
26
|
+
|
|
27
|
+
`status` distinguishes a repository with nothing pending from one whose config manages no package on either axis — every selected package private, with the opt-in unset. Both printed `No packages planned.` and exited 0; the second now says so, and carries `manages_nothing` in its JSON. A selection that `include`/`exclude` empties is not covered by this and stays silent, in `status` as in `check`, because an empty selection is a decision someone wrote down. `ci pr-status` reports the same facts, and no longer stays silent on such a repository — it planned nothing and covered nothing, so the early return swallowed the one thing worth saying.
|
|
28
|
+
|
|
29
|
+
### Changed
|
|
30
|
+
|
|
31
|
+
`check` refuses when no selected package can produce work on either the version or the tag axis, where it exited 0 in silence. That state plans nothing and releases nothing while every gate reports green, and it is what a migration produces when the private-packages opt-in is lost. The refusal names both keys. A selection left empty by `include`/`exclude` stays silent, because that is a decision someone wrote down, while an absent `private-packages` key is the absence of one. `release` does not ask, so a release with nothing to do still says so in its own words.
|
|
32
|
+
|
|
33
|
+
`init` says the same thing where it writes such a config, rather than leaving it to the next command: an all-private workspace still gets its files and a zero exit, with the guidance on stderr.
|
|
34
|
+
|
|
35
|
+
`check` refuses a bump file that names a package it cannot version, matching what `status` and `version` already did. It asked a narrower question than the pipeline — it let an `include`/`exclude` exclusion through — so a repository could pass a green `check` and then have its version PR fail on the same file. If your `check` is green today on a bump file naming an excluded package, it will now fail, and so would the release that followed it. The refusal names every offending package and prints before returning, so a failure in an earlier look, such as a missing install pin, no longer erases it.
|
|
36
|
+
|
|
37
|
+
The coverage look refuses a shallow clone instead of reporting a clean result. At depth 1 the default base resolves to the checked-out commit itself, so the diff came back empty and every changed package looked covered. `actions/checkout` clones that way unless told otherwise, which made this the common CI shape rather than an edge case. `check` already refused such a clone through its tag look; `ci pr-status` did not, and now does, naming `fetch-depth: 0`. `status` reports it rather than refusing, because `status` is not a gate.
|
|
38
|
+
|
|
39
|
+
`check` stays silent on a changed package the config keeps but cannot version — an unpublishable package where `private-packages.version` is not set. That is a choice someone made, not a fault, and gating on it would turn every private-package change red until they wrote config; [ADR-0027](https://github.com/oakoss/oakum/blob/main/docs/decisions/0027-private-packages-version-opt-in.md) records that silence as something a changesets migratee keeps without a config change. `status` reports it instead — see the Added note.
|
|
40
|
+
|
|
41
|
+
`migrate` no longer calls an empty plan comparison a match. With nothing pending on either side it printed `0 package(s) planned by bumpy and by oakum; match`, which reads as verification of a transform that nothing exercised. That is the common case rather than the rare one, since a repository is usually migrated right after a release.
|
|
42
|
+
|
|
43
|
+
`oakum check` says what it covers, on every run. It exited 0 with no output at all, so a check that inspected a two-package workspace and a check that inspected nothing were the same observation — and a config selecting no package read as a clean repository. Every run now opens with two lines on **stdout**: how many of the workspace's packages the selection keeps, the ref the diff is taken from, the looks this run asks for, and — separately — the looks it does not, because a look nobody asked for must not read as a look that passed. The lines state scope and intent, never a verdict: they print before the first look so that a run which refuses describes itself too, and what became of those looks is the exit code's to say — nothing on stderr and exit 0 means every announced look finished and passed. **Breaking for anything that asserted `check` writes nothing to stdout**; the refusals themselves are unchanged and still land on stderr. Every look's refusal now reaches stderr, too: the looks are run together and then raised in order, so the second and third used to be discarded — a `--remote` run against a repository with no remotes lost that finding entirely whenever an earlier look refused first. And the base ref the report names is confirmed before it is quoted, so an explicit `--from` naming a ref git does not have is reported as that rather than announced and then failed on.
|
|
44
|
+
|
|
45
|
+
When a run refuses on more than one look, the refusal that sets the exit code is the one oakum measured, not the one written first. `check` runs its looks together and then raises one; it raised whichever came first in the source, so an unrelated leftover staging file turned a measured tag drift from `error` (exit 1) into `unverified` (exit 2) and erased the `error:` line from stderr — ADR-0034's split run backwards. A finding now outranks a look that did not happen ([ADR-0035](https://github.com/oakoss/oakum/blob/main/docs/decisions/0035-a-finding-outranks-an-unverified-look.md)), and every other refusal is still reported, prefixed `also`, so a reader can see which line the exit code came from. **Breaking for anything branching on `1` versus `2`**: a repository with both a finding and an unverified look now reports the finding. A base ref git does not have is `unverified`, not an error — a shallow CI checkout passing `--from` is the shape that hits it.
|
|
46
|
+
|
|
47
|
+
Neither a report nor a refusal that cannot be written replaces the outcome with a panic. `eprintln!` aborts when the write is refused, so a run whose stderr reader had gone away exited 101 and printed nothing at all, in place of the exit code ADR-0034 defines. Both channels now go through a write that reports failure rather than panicking — `oakum check | head -1` is an ordinary invocation, and the report this round added made it an exit-101 one.
|
|
48
|
+
|
|
49
|
+
An unverified outcome prints one outcome token on stderr, not two. Every failure was prefixed `error: `, and an unverified message already begins `unverified: `, so stderr read `error: unverified: …` — two words saying different things about one result. Both channels now read one classification of the variant, so the prose and the numeric channel (ADR-0034: `2` for unverified) cannot disagree — two exhaustive matches would have let a variant be filed one way for the shell and another for stderr. **Breaking for anything matching `error:` on an unverified failure**; it now matches `unverified:`.
|
|
50
|
+
|
|
51
|
+
### Fixed
|
|
52
|
+
|
|
53
|
+
An unknown configuration key is named, fully qualified as TOML resolved it. A scalar written below a table header is scoped into that table, so `commit-message` typed at what looks like the top level is read as `private-packages.commit-message` — and the error gave a line number with nothing on that line to look for. The name comes from a span-preserving parse rather than from reading the source text, so a header carrying a trailing comment is still that key's table, a key inside an inline table is named at its own depth, a bracketed line inside a string value is not mistaken for a header, and a header is named the way TOML resolved it — spacing dropped, but a name that genuinely needs quotes keeping them, so the path is one the document holds. A key the parse cannot place keeps the message it always had, and a document that omits a required key is reported as that rather than as `invalid TOML syntax`, which sent readers hunting a typo in TOML that parses perfectly. Values are still never echoed, and neither is the source line.
|
|
54
|
+
|
|
55
|
+
A template that reads an undefined variable names it, and names the variables in scope. `tag-format = "{{ name }}"` failed with `undefined value (in tag-format:1)`, which named neither the offending variable nor the correct one — `package`, findable only by reading oakum's source. Both halves are derived: the unsupplied names are the template's own undeclared variables minus what the context supplies and minus the renderer's globals, and the scope is the context's own keys. It is stated as a set — "this template reads `name`, which the context does not supply" — because that is what the analysis knows: it reports every name the template reads anywhere and cannot say which branch ran, so calling one of them the cause of this failure would claim more than was established. A nested read is resolved against the context rather than matched against its top-level keys, so `repo.url` is not accused of being missing and `repo.nope` still names itself. A context that is not a map says so rather than reporting no variables at all. This covers every rendered surface, not just `tag-format`. The four template keys in `_config.toml`'s schema now name the variables each surface receives, and a test ties each list to the context that surface actually renders with.
|
|
56
|
+
|
|
57
|
+
A git operation that failed for a non-ssh reason no longer reports an ssh configuration problem. oakum resolves the ssh transport once and puts it on every child, including local ones; when that probe failed, every operation was reported as needing `an ssh configuration oakum could not read`, with the real cause in parentheses and a remedy measured to move the failure rather than fix it — including when git was not installed. Four outcomes are now kept apart: a repository git will not open is reported as itself; an ssh configuration git would not give up keeps the skip-the-probe remedy; an ssh *variable* oakum cannot read is told to repair or unset that variable, rather than being answered with advice to set it; and a probe that never reached git at all claims no cause, because offering one would be a diagnosis nobody made. A child that died on a signal counts as the last of those: it establishes nothing about ssh or about the repository. The second probe is also asked only when the first one ran and refused, which halves the worst-case wait on a git that hangs.
|
|
58
|
+
|
|
59
|
+
`oakum status --help` says which render a bare `status` produces. It described the command as printing `JSON or a named render`, which reads as JSON being the default; it is the `summary` render, and the reporter ran both commands to find out.
|
|
60
|
+
|
|
61
|
+
`oakum migrate` names `gitUser` as a step rather than a forgettable dropped key. It decided who authored every release commit and tag; oakum has no counterpart, so a cutover reassigned authorship silently. Both the step and the printed workflow now say what that identity actually governs, because the two halves differ: annotated tags take the `git config user.name` / `user.email` the release job sets, while the version commit is written through the GitHub API and carries the token's own account, which no git config can change. A reader swapping in a GitHub App token — which a bot push needs to retrigger CI — changes the commit author whatever they configure.
|
|
62
|
+
|
|
63
|
+
A failed bump-file gate look states one fact. The printed step and the `unverified` refusal were two separately-authored sentences about one outcome, each free to drift from the other; they still read differently, because one is a bullet and one sets the exit code, but the fact they state now comes from one place. Which outcome refuses is a property of the look rather than a `match` repeated at each site.
|
|
64
|
+
|
|
65
|
+
An outcome oakum could not verify now exits `2`. Every failure exited `1`, which put "this repository failed the check" and "the check could not be run" behind the same number — the collapse the three-outcome rule exists to prevent, surviving in the one channel a caller reads without parsing prose. The three outcomes are now `0` ok, `2` unverified, `1` error, decided in one place and applied by every command that can report one. Every exit stays non-zero, so `set -e`, `if ! oakum check`, and a GitHub Actions step all behave as before; a script comparing the code against `1` exactly does not. See [ADR-0034](https://github.com/oakoss/oakum/blob/main/docs/decisions/0034-exit-two-for-unverified.md).
|
|
66
|
+
|
|
67
|
+
`migrate` is where this was costing the most. A cutover that wrote every file correctly and merely could not run the source tool to compare against reported `error: unverified: migrated files were kept` and exited `1` — indistinguishable from a migration that failed, on the path the install docs produce.
|
|
68
|
+
|
|
69
|
+
### Fixed
|
|
70
|
+
|
|
71
|
+
`migrate` carries a source tool's `privatePackages` into oakum's `private-packages`, where it dropped it. A repository whose packages are all private migrated to a config that managed nothing, so `check` exited 0, `status` planned nothing, and a release would have done nothing while reporting success. Changesets' bare-boolean form is read the way changesets reads it, as both axes. Both source files are read when present and their axes are unioned, so a repository migrating from one tool while a readable config from the other is still on disk carries both; the report names what each file contributed and the one line the write produces. A bumpy source also gets the "not carried over" report the changesets path already had. A source config oakum cannot open, read, or parse, or one whose axis is not a boolean, is reported and skipped rather than stopping the migration: both tools accept JSON their own loaders tolerate and `serde_json` does not, so a leftover config from an abandoned experiment no longer aborts an unrelated migration. A dangling symlink at either path is named as one rather than read as the file being absent.
|
|
72
|
+
|
|
73
|
+
`migrate` looks for the source tool in `node_modules/.bin` before giving up on it. `npm i -g` and Homebrew put `oakum` on `PATH` and a devDependency's binaries nowhere near it, so the install the docs recommend left the parity check unrunnable — on the one command whose whole job is comparing against that tool. The comparison then ran oakum against oakum, which cannot detect a mapping error, and said `match` about it. A source tool that really is absent now says where oakum looked, because installing globally and installing locally fix different halves of that. A source tool that ran and failed is no longer mistaken for one that answered: a crash whose output happens to parse is not an empty plan, a child the kernel killed is reported as such rather than as `exited -1`, and a difference measured against a plan read under a tool's exit-1 convention is unverified rather than proof the transform changed anything.
|
|
74
|
+
|
|
75
|
+
A tag that states no version — `v1`, `latest`, `nightly`, a date stamp — no longer cancels the whole tag-format derivation. `release` already classifies such a tag as someone else's and skips it, so one `v1` (the GitHub Actions convention) turned the derivation off on exactly the histories it was written for, and the two commands disagreed about the same tag. A history of nothing but moving tags is still reported, naming them: it has tags, and calling that "no tags" would say never released about a repository nobody managed to read.
|
|
76
|
+
|
|
77
|
+
`migrate` says which `versioning` mode it wrote and what settled it. `semver` is the non-default, and for a repository whose packages are all below 1.0.0 it is the difference between the next release being `0.18.0` and `1.0.0` — written silently until now. knope and release-plz imply `zero-major` instead; release-plz's own configuration documentation states the rule, and it had been grouped with the tools that take `0.1.3` to `1.0.0`.
|
|
78
|
+
|
|
79
|
+
A bump file copied out of the old tool's directory is described as a copy rather than a rewrite, and every original left behind is named among the remaining steps. oakum does not own that directory and will not delete from it, but the old tool goes on counting those files: measured in a clone, after `migrate` and `version` released a package 0.17.0 to 0.18.0, the old tool still showed three pending and would have released it again. A repository whose workflow still runs the old tool on every push to `main` gets that second release on the merge commit.
|
|
80
|
+
|
|
81
|
+
`migrate` names what in a repository refers to the old tool's bump-file directory, so a commit hook or CI step pointed at it can be repointed before it rejects the bump files oakum writes — which is what happened in the first repository to adopt oakum. oakum cannot tell a gate from a mention in prose, so the step asks the reader to look rather than asserting each match is a gate, and it says what it did not search: the look reads git's index outside `.changeset/`, so an untracked file, an unstaged edit, a submodule and `.git/hooks/` are all uncovered. A look that fails says so and exits unverified rather than passing for no gate. Only a bumpy cutover has an old directory to repoint anything at; changesets and knope already keep bump files in `.changeset/`, which oakum adopts in place.
|
|
82
|
+
|
|
83
|
+
The plan `migrate` prints is sorted. It came out in filesystem order, so the same repository could show a reader a differently ordered plan on a different machine.
|
|
84
|
+
|
|
85
|
+
A difference the parity check found outranks a look that could not happen. The first is a finding and the second is missing evidence, so a migration that proved the release plan changed reports that, at exit 1, even when the gate look also failed.
|
|
86
|
+
|
|
87
|
+
`oakum version` says what it wrote. The command that performs the irreversible part of a release — manifests, lockfile rows, changelogs, declared `extra-files`, and the bump files it consumes — exited 0 having said nothing at all, so a run that rewrote five files was indistinguishable from one that did nothing. It now names each package with its old and new version, says which package triggered a cascade, lists every file whose bytes changed, and names every bump file consumed. Printed after the writes land, so it reports rather than promises. `ci version-pr` plans the same writes through a different path and its output is unchanged.
|
|
88
|
+
|
|
89
|
+
The `.changeset/README.md` oakum writes no longer documents a bump-file form the parser refuses. It said oakum reads a quoted bare package name; `status`, `check` and `version` all refuse one, naming the file — `docs/specs/bump-files.md` has always listed quoted keys as not permitted. Since `init` and `migrate` write that README into the repository, the tool was teaching a rule it then rejected people for following. The corrected text also records that `migrate` rewrites the quoted form `@changesets/cli` writes, so a cutover needs no hand-editing.
|
|
90
|
+
|
|
91
|
+
`status` reports a config whose `include`/`exclude` leave no package selected. Such a config can never release anything, and both verbs said only `No packages planned.` — the same sentence a quiet repository prints. `check` still stays silent, because emptying a selection is a decision the config states and a gate must not refuse one; `status` is not a gate, so it says what the reader is looking at, and says that `check` will not refuse it. `status --json` carries `selection_empty` beside the existing `manages_nothing`; the two are exclusive, and a document asserting both, or asserting either beside a planned release, reads as neither.
|
|
92
|
+
|
|
93
|
+
`ReleaseState::manages_nothing` is no longer `const`, and the new `selection_empty` accessor is not `const` either: both read `packages.is_empty()`, which is not a const operation. A caller using either in a const context breaks; nothing in oakum does, and the library's `pub` surface is the seam between it and the binary rather than a curated API.
|
|
94
|
+
|
|
95
|
+
`migrate` carries `versionCommitMessage` into `commit-message`, an exact equivalent it previously left for the reader to restore by hand — and says so before writing it, beside the other settings it carries. A message equal to what oakum writes anyway is not written as a config line restating a default, and the run says that rather than reporting a loss that did not happen. A message oakum cannot carry is named with the reason: `commit-message` is rendered as a template, so a literal containing `{{ … }}` would fail the version PR or quietly become a different message, and a control character has no place in a commit headline; a newline ends the config's string form outright. `changelog` is named as a decision the reader owes rather than translated, because it means different things in each source tool and neither is oakum's `template`.
|
|
96
|
+
|
|
97
|
+
## 0.2.0 (2026-09-10)
|
|
98
|
+
|
|
99
|
+
### Added
|
|
100
|
+
|
|
101
|
+
`check` recognizes the npm channel: an exact `@oakoss/oakum` in `package.json`, a versioned `npm i` / `npm install` / `npm add` / `pnpm add` / `pnpm install` / `pnpm dlx` / `npx @oakoss/oakum@x` workflow line, and an `npm:@oakoss/oakum` mise pin count as install pins; a bare `@oakoss/oakum` is `unverified` as unversioned.
|
|
102
|
+
|
|
103
|
+
`plan::migrate_compare` compares two plan fingerprints, with typed empty discovery and migration resolve and load helpers beside it, so the migration proof can be exercised without going through the CLI. `migrate` keeps the I/O, the rewrites, and the banners, and no longer classifies knope fallout by matching a `Display` string.
|
|
104
|
+
|
|
105
|
+
`migrate` proves the migration against the source tool's own before-plan whenever one can be run (`bumpy status --json`, a local or `PATH` `changeset status --output` with no silent `npx`, a knope dry run), rather than comparing oakum against oakum with knope's feature remapped. When no source tool can be run it falls back to an oakum simulation for transform safety and exits `unverified` even where the two agree, so a missing binary no longer reads as agreement. An unexpected difference in the fingerprints is still a hard failure.
|
|
106
|
+
|
|
107
|
+
A bump-file note whose first line is a Keep a Changelog heading (`### Added`, `### Changed`, `### Deprecated`, `### Removed`, `### Fixed`, `### Security`) lands under that section, with the line dropped; the level still picks the section otherwise, and `add --section` writes the line. The changelog template sees `changes` (per note: section, file, and the adding commit's sha, pull request number, and author) and `repo` (the GitHub slug), read from git only when the template mentions them.
|
|
108
|
+
|
|
109
|
+
The workflow printed by `init` and `migrate` installs oakum with `npm i -g @oakoss/oakum@<tool-version>` in an npm workspace, since `cargo-binstall` is not on `ubuntu-latest` and npm is. `status --from` help names `check`, and the bundled README explains quoting and releaseless files without assuming knope.
|
|
110
|
+
|
|
111
|
+
### Changed
|
|
112
|
+
|
|
113
|
+
`check` and `release` exit `unverified` when `.changeset/_config.toml` is absent, naming `oakum init` and `oakum migrate`; `status` keeps defaults and says so. A malformed bump file fails `check`, `status`, `version`, and every other reader by name with its parse error instead of being skipped. `migrate` without a terminal requires `--yes` and otherwise prints the plan and refuses. The workflow printed by `init` and `migrate` runs `oakum check --strict`.
|
|
114
|
+
|
|
115
|
+
`check` names every `.<name>.oakum-write.*` staging file an unfinished oakum write left in `.changeset/`, at the repository root, beside a package manifest, or beside a declared extra file, and exits `unverified`; `init` and `migrate` name one in `.changeset/` on every run, the already-initialized and already-migrated paths included. Nothing removes it, since a run still in progress could own it.
|
|
116
|
+
|
|
117
|
+
`check` reports a `CHANGELOG.md` that `version` would refuse to append to (a changesets package-name title, or a UTF-8 BOM) as `unverified`, naming the fix, and `migrate` lists the same file under its remaining steps. Neither rewrites the file.
|
|
118
|
+
|
|
119
|
+
`add`, `generate`, `version`, and `ci version-pr` exit `unverified` when `.changeset/_config.toml` is absent, naming `oakum init` and `oakum migrate`, instead of writing on defaults.
|
|
120
|
+
|
|
121
|
+
### Fixed
|
|
122
|
+
|
|
123
|
+
The bundled `.changeset/README.md` is byte-for-byte what Prettier 3 prints (padded tables), so a repository that formats markdown keeps it recognizable as oakum's own; a real Prettier run over it and over `_schema.json` gates both.
|
|
124
|
+
|
|
125
|
+
`check` finds the tool-version pin in local composite actions, matrix cells, `with.*` inputs other than `tool`, `with.tool` arrays, and `workflow_call` input defaults. It previously read only workflow `run`, `tool`, and `with.tool` strings, so a pin drifting at any of those sites still verified `ok`. Invalid composite YAML and a non-object `with` fail closed; a step whose command comes only from `${{ matrix.* }}` is documented as unsupported.
|
|
126
|
+
|
|
127
|
+
A migration bump file identified by its path rather than an id is accepted, and `migrate` builds its after-plan through the same `load_migration_bump_files` seam the before-plan uses, refusing outright when any bump file is malformed.
|
|
128
|
+
|
|
129
|
+
`migrate` says what it left alone (`config.json` keys, an existing `README.md`), lists publishing and the version-PR branch among the remaining steps, says which tool planned each side of the plan comparison, reports `replaced` when it overwrites a stale `_schema.json`, checks its owned files before the prompt so nothing is rewritten and then refused, and restores missing owned files on a rerun after confirmation. Bump files accept single-quoted scoped package names, so a Prettier `singleQuote` rewrite no longer hides a release. The printed uninstall line names only the files oakum wrote.
|
|
130
|
+
|
|
131
|
+
Print repo-relative paths with forward slashes on Windows across version, write, inherited, changelog, and intent errors.
|
|
132
|
+
|
|
133
|
+
`init` and `migrate` say `unchanged` for a `_schema.json` that already holds the bundled schema, where they said `replaced`, and `migrate`'s pending line says it will leave such a file alone. After the prompt `migrate` looks at the owned files again and reports under `changed while waiting:` when a README appeared or stopped being oakum's in the meantime.
|
|
134
|
+
|
|
135
|
+
`release` fills the GitHub release body with the package's changelog section for that version, read at the tagged commit. When there is none, the body is the title and `release` says so on stderr; a changelog that cannot be read at that commit stops the run before any tag is written. `check`'s tag-drift line says it compared local tags and suggests `git fetch --tags`.
|
|
136
|
+
|
|
137
|
+
`add` writes the message under a blank line after the frontmatter, with a trailing newline (ADR-0031's envelope), and `generate --dry-run` previews the same shape; `init` emits `_schema.json` with short arrays on one line and pads the bundled README's table, so a repository's Prettier leaves freshly generated files alone. The printed workflow provisions pnpm in every job of an npm workspace and skips `check` on the version PR.
|
|
138
|
+
|
|
139
|
+
Windows containment treats a loopback admin UNC path as the same volume as the drive letter, so a localhost C$ prefix cannot smuggle a path past the repository check.
|
|
140
|
+
|
|
3
141
|
## 0.1.4 (2026-09-02)
|
|
4
142
|
|
|
5
143
|
### Fixed
|
|
@@ -28,4 +166,4 @@ Release workflow checks out the homebrew tap in a subdirectory so the composite
|
|
|
28
166
|
|
|
29
167
|
Self-host release CI uploads cargo-dist artifacts into the GitHub Release oakum already created, instead of calling create again.
|
|
30
168
|
|
|
31
|
-
Generated by oakum 0.
|
|
169
|
+
Generated by oakum 0.2.0.
|
package/README.md
CHANGED
|
@@ -30,7 +30,7 @@ Three install channels, one cargo-dist build behind all of them, so the versions
|
|
|
30
30
|
|
|
31
31
|
- `cargo install oakum` — builds from crates.io
|
|
32
32
|
- `brew install oakoss/tap/oakum`
|
|
33
|
-
- `pnpm add -D @oakoss/oakum
|
|
33
|
+
- `pnpm add -D @oakoss/oakum@<version>` — an exact version is what `oakum check` verifies against `tool-version`. The npm package is a fetcher, not a bundle: a small install script downloads the platform binary from the GitHub release. pnpm 10 and later run that script only for allowlisted packages. On pnpm 10 the allowlist is `pnpm.onlyBuiltDependencies` in `package.json`; without it pnpm warns `Ignored build scripts` and the first `pnpm exec oakum` fetches the binary instead. On pnpm 11 and later it is `allowBuilds: { '@oakoss/oakum': true }` in `pnpm-workspace.yaml` (pnpm 11 does not read the `pnpm` field of `package.json`), and without it `pnpm install` fails, so add it before the first install
|
|
34
34
|
|
|
35
35
|
None of these works offline, and the npm channel needs **two** origins at install time — the registry for the package, then `github.com` for the binary. An environment with an internal npm mirror but no route to GitHub installs the package and then fails in `postinstall`; there, `cargo install oakum` through a crates.io mirror is the only channel that avoids GitHub entirely, since the shell installer and Homebrew fetch from the GitHub release too.
|
|
36
36
|
|
package/npm-shrinkwrap.json
CHANGED
|
@@ -19,7 +19,7 @@
|
|
|
19
19
|
"hasInstallScript": true,
|
|
20
20
|
"license": "MIT",
|
|
21
21
|
"name": "@oakoss/oakum",
|
|
22
|
-
"version": "0.
|
|
22
|
+
"version": "0.3.0"
|
|
23
23
|
},
|
|
24
24
|
"node_modules/detect-libc": {
|
|
25
25
|
"engines": {
|
|
@@ -48,5 +48,5 @@
|
|
|
48
48
|
}
|
|
49
49
|
},
|
|
50
50
|
"requires": true,
|
|
51
|
-
"version": "0.
|
|
51
|
+
"version": "0.3.0"
|
|
52
52
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"artifactDownloadUrls": [
|
|
3
|
-
"https://github.com/oakoss/oakum/releases/download/v0.
|
|
3
|
+
"https://github.com/oakoss/oakum/releases/download/v0.3.0"
|
|
4
4
|
],
|
|
5
5
|
"author": "Jace Babin",
|
|
6
6
|
"bin": {
|
|
@@ -105,7 +105,7 @@
|
|
|
105
105
|
"zipExt": ".tar.xz"
|
|
106
106
|
}
|
|
107
107
|
},
|
|
108
|
-
"version": "0.
|
|
108
|
+
"version": "0.3.0",
|
|
109
109
|
"volta": {
|
|
110
110
|
"node": "18.14.1",
|
|
111
111
|
"npm": "9.5.0"
|