@sacho/sacho 0.1.0-dev.12 → 0.1.0-dev.8
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 +38 -67
- package/package.json +9 -10
- package/skills/sacho/SKILL.md +0 -317
package/README.md
CHANGED
|
@@ -18,7 +18,7 @@ towncrier assumes Python; changesets assumes an npm monorepo. Sacho is a single
|
|
|
18
18
|
static binary with no runtime dependencies, usable in any repository regardless
|
|
19
19
|
of language, and it does not even assume Git.
|
|
20
20
|
|
|
21
|
-
[
|
|
21
|
+
[Sacho is opinionated.](./PHILOSOPHY.md) It enforces one fragment format,
|
|
22
22
|
one output style, and one set of invariants. Configuration exists to describe
|
|
23
23
|
your repository, not to customize the philosophy.
|
|
24
24
|
|
|
@@ -55,51 +55,6 @@ Without mise, npm, or Cargo, download the archive for your platform from
|
|
|
55
55
|
[GitHub Releases]: https://github.com/dahlia/sacho/releases
|
|
56
56
|
|
|
57
57
|
|
|
58
|
-
Agent Skill
|
|
59
|
-
-----------
|
|
60
|
-
|
|
61
|
-
Sacho ships an [Agent Skill] that teaches AI coding agents, such as Claude Code,
|
|
62
|
-
how to adopt and operate Sacho: writing fragments for users, cutting releases,
|
|
63
|
-
and forward-porting fixes across maintenance branches.
|
|
64
|
-
|
|
65
|
-
In Claude Code, install it as a plugin. Add this repository as a plugin
|
|
66
|
-
marketplace, then install the `sacho` plugin:
|
|
67
|
-
|
|
68
|
-
~~~~ text
|
|
69
|
-
/plugin marketplace add dahlia/sacho
|
|
70
|
-
/plugin install sacho@sacho
|
|
71
|
-
~~~~
|
|
72
|
-
|
|
73
|
-
For other agents, or to install the skill without the plugin system, use the
|
|
74
|
-
[`skills`] CLI, which reads the skill straight from this repository:
|
|
75
|
-
|
|
76
|
-
~~~~ sh
|
|
77
|
-
npx skills add dahlia/sacho
|
|
78
|
-
~~~~
|
|
79
|
-
|
|
80
|
-
Add `-a claude-code` to target one agent, `-g` to install it globally, and
|
|
81
|
-
`--skill sacho` to select the skill by name.
|
|
82
|
-
|
|
83
|
-
The skill is also bundled inside the `@sacho/sacho` npm package, following the
|
|
84
|
-
[`skills-npm`] layout. In a project that depends on that package, run
|
|
85
|
-
`npx skills-npm setup` once to link bundled skills from *node\_modules* into
|
|
86
|
-
your agent on every install.
|
|
87
|
-
|
|
88
|
-
[Agent Skill]: https://agentskills.io/
|
|
89
|
-
[`skills`]: https://github.com/vercel-labs/skills
|
|
90
|
-
[`skills-npm`]: https://github.com/antfu/skills-npm
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
Documentation
|
|
94
|
-
-------------
|
|
95
|
-
|
|
96
|
-
The [*Sacho documentation*] walks through adoption, everyday use, releases,
|
|
97
|
-
CI, and version-control integration. It also explains Sacho's core concepts and
|
|
98
|
-
provides complete command and configuration references.
|
|
99
|
-
|
|
100
|
-
[*Sacho documentation*]: https://sacho.dev/guide/getting-started
|
|
101
|
-
|
|
102
|
-
|
|
103
58
|
How it works
|
|
104
59
|
------------
|
|
105
60
|
|
|
@@ -181,9 +136,7 @@ sacho release --next 1.3.0
|
|
|
181
136
|
|
|
182
137
|
If no next version has been set, pass the release version explicitly, for
|
|
183
138
|
example `sacho release 1.2.0`. Use `--date YYYY-MM-DD` when the release date
|
|
184
|
-
must be supplied rather than taken from the local clock.
|
|
185
|
-
released changelog sections can cut its first release without fragments. Later
|
|
186
|
-
empty releases require an explicit `--allow-empty`.
|
|
139
|
+
must be supplied rather than taken from the local clock.
|
|
187
140
|
|
|
188
141
|
After committing a release, print its frozen Markdown section with `show`:
|
|
189
142
|
|
|
@@ -208,8 +161,7 @@ Version-control integration
|
|
|
208
161
|
---------------------------
|
|
209
162
|
|
|
210
163
|
Sacho can enforce that commits changing configured source paths also carry a
|
|
211
|
-
changelog fragment. Select the repository's VCS
|
|
212
|
-
*sacho.toml*:
|
|
164
|
+
changelog fragment. Select the repository's VCS in *sacho.toml*:
|
|
213
165
|
|
|
214
166
|
~~~~ toml
|
|
215
167
|
[vcs]
|
|
@@ -224,6 +176,41 @@ Git also supports `sacho check --staged`. The `none` preset explicitly skips
|
|
|
224
176
|
VCS-backed checks while leaving fragment validation and changelog consistency
|
|
225
177
|
checks enabled.
|
|
226
178
|
|
|
179
|
+
Each preset supplies three subprocess commands. Repositories can override one
|
|
180
|
+
command without repeating the other two:
|
|
181
|
+
|
|
182
|
+
~~~~ toml
|
|
183
|
+
[vcs]
|
|
184
|
+
preset = "git"
|
|
185
|
+
|
|
186
|
+
[vcs.commands]
|
|
187
|
+
message = ["my-vcs-wrapper", "message", "${commit}"]
|
|
188
|
+
~~~~
|
|
189
|
+
|
|
190
|
+
The first array element is the executable and every remaining element is one
|
|
191
|
+
literal argument; Sacho never invokes a shell. The commits command uses
|
|
192
|
+
`${base}`, while changed-paths and message use `${commit}`. An overridden query
|
|
193
|
+
is the complete query and does not invoke commands from its preset behind the
|
|
194
|
+
scenes. The built-in Mercurial preset handles parent comparisons internally.
|
|
195
|
+
|
|
196
|
+
The query output contracts are:
|
|
197
|
+
|
|
198
|
+
- `commits` emits zero or more nonempty UTF-8 commit identifiers, oldest
|
|
199
|
+
first and one per line. The final line may end in LF; CRLF is also
|
|
200
|
+
accepted.
|
|
201
|
+
- `changed-paths` emits concatenated NUL-terminated name-status records.
|
|
202
|
+
Ordinary records are `A\0PATH\0`, `D\0PATH\0`, `M\0PATH\0`, or
|
|
203
|
+
`T\0PATH\0`, where `T` denotes a file type change. Copy and rename records
|
|
204
|
+
are `C[SIMILARITY]\0OLD_PATH\0NEW_PATH\0` and
|
|
205
|
+
`R[SIMILARITY]\0OLD_PATH\0NEW_PATH\0`, where the optional similarity is a
|
|
206
|
+
decimal percentage from 0 through 100. Status fields are UTF-8 and paths
|
|
207
|
+
are nonempty repository-relative platform paths; path bytes need not be
|
|
208
|
+
UTF-8 on Unix. Nonempty output ends in NUL. A copied or renamed fragment
|
|
209
|
+
counts as new content only when similarity is present and below 100;
|
|
210
|
+
omitting it does not satisfy fragment coverage.
|
|
211
|
+
- `message` emits the complete UTF-8 commit message verbatim. Sacho does not
|
|
212
|
+
trim it.
|
|
213
|
+
|
|
227
214
|
`sacho init` installs Git merge attributes and drivers in Git repositories. In
|
|
228
215
|
Mercurial repositories it installs an idempotent block in *.hg/hgrc* containing
|
|
229
216
|
a successful-merge update hook and, when changelog materialization is enabled,
|
|
@@ -232,12 +219,6 @@ the changelog merge driver. These integrations invoke the executable that ran
|
|
|
232
219
|
per-path merge-driver hook; after resolving a concurrent fragment merge, run
|
|
233
220
|
`sacho sync --force`.
|
|
234
221
|
|
|
235
|
-
See [*CI and hooks*] for fragment coverage and [*Version control*] for preset,
|
|
236
|
-
merge-driver, and custom-query details.
|
|
237
|
-
|
|
238
|
-
[*CI and hooks*]: https://sacho.dev/guide/ci-and-hooks
|
|
239
|
-
[*Version control*]: https://sacho.dev/guide/version-control
|
|
240
|
-
|
|
241
222
|
|
|
242
223
|
Etymology
|
|
243
224
|
---------
|
|
@@ -262,13 +243,3 @@ is the same insistence this tool makes against generating changelogs from
|
|
|
262
243
|
commit messages after the fact.
|
|
263
244
|
|
|
264
245
|
[1]: https://en.wikipedia.org/wiki/Veritable_Records_of_the_Joseon_Dynasty#Compilation_process
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
License
|
|
268
|
-
-------
|
|
269
|
-
|
|
270
|
-
Copyright (C) 2026 Hong Minhee.
|
|
271
|
-
|
|
272
|
-
Sacho is free software licensed under the GNU General Public License version 3
|
|
273
|
-
only. It comes with no warranty, to the extent permitted by law. See
|
|
274
|
-
[*LICENSE*](./LICENSE) for the full terms.
|
package/package.json
CHANGED
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@sacho/sacho",
|
|
3
|
-
"version": "0.1.0-dev.
|
|
3
|
+
"version": "0.1.0-dev.8+a3ec562",
|
|
4
4
|
"description": "An opinionated changelog manager",
|
|
5
5
|
"license": "GPL-3.0-only",
|
|
6
6
|
"author": "Hong Minhee <hong@minhee.org>",
|
|
7
|
-
"homepage": "https://
|
|
7
|
+
"homepage": "https://github.com/dahlia/sacho",
|
|
8
8
|
"repository": {
|
|
9
9
|
"type": "git",
|
|
10
10
|
"url": "git+https://github.com/dahlia/sacho.git"
|
|
@@ -17,19 +17,18 @@
|
|
|
17
17
|
},
|
|
18
18
|
"files": [
|
|
19
19
|
"bin",
|
|
20
|
-
"lib"
|
|
21
|
-
"skills"
|
|
20
|
+
"lib"
|
|
22
21
|
],
|
|
23
22
|
"engines": {
|
|
24
23
|
"node": ">=18"
|
|
25
24
|
},
|
|
26
25
|
"optionalDependencies": {
|
|
27
|
-
"@sacho/sacho-aarch64-apple-darwin": "0.1.0-dev.
|
|
28
|
-
"@sacho/sacho-aarch64-pc-windows-msvc": "0.1.0-dev.
|
|
29
|
-
"@sacho/sacho-aarch64-unknown-linux-musl": "0.1.0-dev.
|
|
30
|
-
"@sacho/sacho-x86_64-apple-darwin": "0.1.0-dev.
|
|
31
|
-
"@sacho/sacho-x86_64-pc-windows-msvc": "0.1.0-dev.
|
|
32
|
-
"@sacho/sacho-x86_64-unknown-linux-musl": "0.1.0-dev.
|
|
26
|
+
"@sacho/sacho-aarch64-apple-darwin": "0.1.0-dev.8+a3ec562",
|
|
27
|
+
"@sacho/sacho-aarch64-pc-windows-msvc": "0.1.0-dev.8+a3ec562",
|
|
28
|
+
"@sacho/sacho-aarch64-unknown-linux-musl": "0.1.0-dev.8+a3ec562",
|
|
29
|
+
"@sacho/sacho-x86_64-apple-darwin": "0.1.0-dev.8+a3ec562",
|
|
30
|
+
"@sacho/sacho-x86_64-pc-windows-msvc": "0.1.0-dev.8+a3ec562",
|
|
31
|
+
"@sacho/sacho-x86_64-unknown-linux-musl": "0.1.0-dev.8+a3ec562"
|
|
33
32
|
},
|
|
34
33
|
"publishConfig": {
|
|
35
34
|
"access": "public",
|
package/skills/sacho/SKILL.md
DELETED
|
@@ -1,317 +0,0 @@
|
|
|
1
|
-
---
|
|
2
|
-
name: sacho
|
|
3
|
-
description: >-
|
|
4
|
-
Help a maintainer adopt and operate Sacho, the opinionated changelog manager
|
|
5
|
-
that keeps user-facing release notes as one small Markdown fragment per change
|
|
6
|
-
under changes.d/ and compiles them into CHANGES.md at release time. Use this
|
|
7
|
-
skill whenever a repository has a sacho.toml, a changes.d/ directory, or a
|
|
8
|
-
CHANGES.md built from fragments; whenever the user wants to add Sacho to a
|
|
9
|
-
project, write or edit a changelog entry for a change they just made, cut a
|
|
10
|
-
release, publish release notes, or wire `sacho check` into CI or a pre-commit
|
|
11
|
-
hook; whenever forward-porting a bugfix release across maintenance branches or
|
|
12
|
-
resolving a changelog merge conflict; and whenever `sacho check` or the
|
|
13
|
-
changelog merge driver reports a problem. Also use it any time the user
|
|
14
|
-
mentions Sacho, changes.d fragments, or asks how to write a changelog entry
|
|
15
|
-
for users instead of generating one from commit messages.
|
|
16
|
-
metadata:
|
|
17
|
-
type: reference
|
|
18
|
-
---
|
|
19
|
-
|
|
20
|
-
Managing changelogs with Sacho
|
|
21
|
-
==============================
|
|
22
|
-
|
|
23
|
-
Sacho keeps a project's release notes as one small Markdown file per change,
|
|
24
|
-
called a fragment, under `changes.d/`. At release time it sorts and compiles the
|
|
25
|
-
fragments into a dated section of `CHANGES.md`, then deletes the consumed
|
|
26
|
-
fragments. It works in any repository regardless of language, ships as a single
|
|
27
|
-
static binary, and does not even assume Git.
|
|
28
|
-
|
|
29
|
-
The single most important thing to hold onto: Sacho never generates entries from
|
|
30
|
-
commit messages, and neither should you. A commit message explains to
|
|
31
|
-
collaborators why a change was made; a changelog entry tells users what changed
|
|
32
|
-
and what to do when they upgrade. Those are different documents for different
|
|
33
|
-
readers. When you draft an entry, do not transform a diff or a `git log` line
|
|
34
|
-
into prose. Read the change, work out its effect on someone who only sees the
|
|
35
|
-
public surface, and write that. When a change has no user-visible effect, the
|
|
36
|
-
honest entry is no entry.
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
Delegate the details to the live docs
|
|
40
|
-
-------------------------------------
|
|
41
|
-
|
|
42
|
-
The commands and flags below are a working map, not the source of truth. For the
|
|
43
|
-
exact syntax of the version installed in this repository, trust `sacho --help`
|
|
44
|
-
and `sacho <command> --help` over anything written here or online, because the
|
|
45
|
-
docs track the latest release while a project may pin an older binary. Use the
|
|
46
|
-
Markdown documentation for concepts, workflow, and reasoning; reach for `--help`
|
|
47
|
-
before you depend on a specific flag or output format.
|
|
48
|
-
|
|
49
|
-
- `https://sacho.dev/llms.txt` lists every page.
|
|
50
|
-
`https://sacho.dev/llms-full.txt` is the whole documentation in one file
|
|
51
|
-
when you want to read broadly.
|
|
52
|
-
- Every page has a `.md` twin. Fetch these directly:
|
|
53
|
-
- `https://sacho.dev/why-sacho.md` and `https://sacho.dev/philosophy.md`
|
|
54
|
-
for the reasoning, worth reading before you write entries for a project.
|
|
55
|
-
- `https://sacho.dev/guide/getting-started.md` to adopt Sacho.
|
|
56
|
-
- `https://sacho.dev/guide/everyday-workflow.md` for the per-change loop.
|
|
57
|
-
- `https://sacho.dev/guide/releases.md` for cutting releases and
|
|
58
|
-
forward-porting.
|
|
59
|
-
- `https://sacho.dev/guide/ci-and-hooks.md` for coverage enforcement.
|
|
60
|
-
- `https://sacho.dev/guide/version-control.md` for merge drivers and
|
|
61
|
-
presets.
|
|
62
|
-
- `https://sacho.dev/concepts/fragments.md`,
|
|
63
|
-
`https://sacho.dev/concepts/changelog-lifecycle.md`, and
|
|
64
|
-
`https://sacho.dev/concepts/sections.md` for the model.
|
|
65
|
-
- `https://sacho.dev/reference/commands.md` and
|
|
66
|
-
`https://sacho.dev/reference/configuration.md` for exhaustive
|
|
67
|
-
references.
|
|
68
|
-
- `https://sacho.dev/troubleshooting.md` when something misbehaves.
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
Orient yourself before acting
|
|
72
|
-
-----------------------------
|
|
73
|
-
|
|
74
|
-
Check what the repository already has. Is there a `sacho.toml` at the root, a
|
|
75
|
-
`changes.d/` directory, a `CHANGES.md`? Is `sacho` on the `PATH`
|
|
76
|
-
(`sacho --version`)? A repository with `sacho.toml` is already set up, so skip
|
|
77
|
-
adoption and go to the workflow that fits the request. A repository without it
|
|
78
|
-
needs `sacho init` first, but only after confirming the maintainer wants to
|
|
79
|
-
adopt Sacho rather than asking about it.
|
|
80
|
-
|
|
81
|
-
If the binary is missing, the recommended install is
|
|
82
|
-
`mise use -g github:dahlia/sacho`; `npm install -g @sacho/sacho` and
|
|
83
|
-
`cargo install sacho` work too. Read `getting-started.md` for the full list.
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
Adopting Sacho in a project
|
|
87
|
-
---------------------------
|
|
88
|
-
|
|
89
|
-
Run `sacho init` from the repository root. It creates `sacho.toml`,
|
|
90
|
-
`changes.d/`, and `CHANGES.md`, and in a Git repository registers the merge
|
|
91
|
-
drivers. Interactive setup infers issue-link templates from the remote and can
|
|
92
|
-
install commit hooks; `sacho init --interactive` forces the questions and
|
|
93
|
-
`--no-interactive` suppresses them.
|
|
94
|
-
|
|
95
|
-
Adoption needs no migration. Sacho never rewrites existing released sections, so
|
|
96
|
-
leave the old `CHANGES.md` history exactly as it is and start using fragments
|
|
97
|
-
for the next version. Set that version with `sacho next 1.2.0` so the unreleased
|
|
98
|
-
heading and `release` know what is being prepared.
|
|
99
|
-
|
|
100
|
-
Two configuration decisions matter early, both covered in
|
|
101
|
-
`reference/configuration.md`: whether to enforce fragment coverage
|
|
102
|
-
(`[check].paths`), and, for a monorepo, whether to define `[[sections]]` so each
|
|
103
|
-
package's changes require a fragment in that package's section.
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
Writing a fragment for a change
|
|
107
|
-
-------------------------------
|
|
108
|
-
|
|
109
|
-
The everyday loop is four commands. Create the fragment, write it, format, and
|
|
110
|
-
check:
|
|
111
|
-
|
|
112
|
-
~~~~ sh
|
|
113
|
-
sacho add clear-function # add --section <id> <name> in a sectioned repo
|
|
114
|
-
sacho fmt
|
|
115
|
-
sacho preview
|
|
116
|
-
sacho check
|
|
117
|
-
~~~~
|
|
118
|
-
|
|
119
|
-
`sacho add` creates `changes.d/<name>.md` holding an empty list item and prints
|
|
120
|
-
the path. Name the file after the change itself (`clear-function`), never after
|
|
121
|
-
an issue or PR number. Topic names make the right follow-up natural: when a
|
|
122
|
-
later commit reworks the same feature, you edit the existing fragment instead of
|
|
123
|
-
adding a second one that documents a state users never saw.
|
|
124
|
-
|
|
125
|
-
Keep the fragment in the same commit series as the implementation. Stored in the
|
|
126
|
-
repository, it travels with the change through merges, rebases, cherry-picks,
|
|
127
|
-
and reverts for free. That coupling is the whole point of fragments, so never
|
|
128
|
-
park changelog text in an issue, a wiki, or a draft release note.
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
Writing entries users can actually read
|
|
132
|
-
---------------------------------------
|
|
133
|
-
|
|
134
|
-
This is the part no tool can do for the maintainer, and where you earn your
|
|
135
|
-
keep. Fetch `philosophy.md` when you want the full argument; the working rules:
|
|
136
|
-
|
|
137
|
-
Address the person upgrading, in the vocabulary of the public surface. A
|
|
138
|
-
refactor that touches forty files but changes nothing users can observe is a
|
|
139
|
-
real commit and a nonexistent entry. A one-line dependency bump that closes a
|
|
140
|
-
vulnerability is a trivial commit and a critical entry.
|
|
141
|
-
|
|
142
|
-
Write one entry per user-visible change. If a release adds `remove_all()` and a
|
|
143
|
-
later commit in the same cycle renames it to `clear()`, the released changelog
|
|
144
|
-
contains a single line, `Added clear() function`, not the two-step history. Edit
|
|
145
|
-
the existing fragment; never narrate intermediate states that never shipped.
|
|
146
|
-
|
|
147
|
-
Be concise, and keep internal names out. Private type names, module paths, and
|
|
148
|
-
subsystem nicknames tell the reader this document is not for them. If a
|
|
149
|
-
mechanism must be mentioned, describe what it does rather than naming a thing
|
|
150
|
-
the reader cannot look up.
|
|
151
|
-
|
|
152
|
-
Start each entry with a past-tense verb: Added, Changed, Deprecated, Fixed,
|
|
153
|
-
Removed, Security. Sacho sorts by the entry text, so these verbs fall into the
|
|
154
|
-
familiar Keep a Changelog order without any category metadata.
|
|
155
|
-
|
|
156
|
-
Reference issues and PRs with shortcut links in trailing brackets, house style,
|
|
157
|
-
at the end of the first paragraph: `[[#842], [#848]]`. Attribution is inline
|
|
158
|
-
prose in the same bracket: `[[#857] by Lee Hoyeon]`. The compiler resolves the
|
|
159
|
-
numbers into link definitions using the templates in `[links]`, and `check`
|
|
160
|
-
fails on a reference no template can resolve.
|
|
161
|
-
|
|
162
|
-
Because Sacho asks a human to write, present your drafts to the maintainer for
|
|
163
|
-
approval rather than committing prose in their voice unreviewed. You are helping
|
|
164
|
-
them write for their users, not automating the writing away.
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
The fragment file format
|
|
168
|
-
------------------------
|
|
169
|
-
|
|
170
|
-
A fragment is a UTF-8 Markdown file whose body is exactly one top-level
|
|
171
|
-
unordered list, with optional YAML frontmatter. Nothing else at the top level:
|
|
172
|
-
no headings, no stray paragraphs, no second list. Inside a list item any block
|
|
173
|
-
content is fine, including nested lists and fenced code. One item is usual;
|
|
174
|
-
related changes from one pull request can share a fragment as several items.
|
|
175
|
-
|
|
176
|
-
~~~~ markdown
|
|
177
|
-
---
|
|
178
|
-
priority: -10
|
|
179
|
-
---
|
|
180
|
-
|
|
181
|
-
- Added `clear()` to remove every entry at once. The function accepts an
|
|
182
|
-
optional predicate to remove entries selectively. [[#842], [#848]]
|
|
183
|
-
~~~~
|
|
184
|
-
|
|
185
|
-
`priority` (integer, default `0`) is the only frontmatter key; lower sorts
|
|
186
|
-
earlier, and most fragments need no frontmatter at all. See
|
|
187
|
-
`concepts/fragments.md` for the exact constraints.
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
Cutting a release
|
|
191
|
-
-----------------
|
|
192
|
-
|
|
193
|
-
Read the compiled result as a user would before you freeze it:
|
|
194
|
-
|
|
195
|
-
~~~~ sh
|
|
196
|
-
sacho next 1.2.0
|
|
197
|
-
sacho preview
|
|
198
|
-
sacho check
|
|
199
|
-
~~~~
|
|
200
|
-
|
|
201
|
-
In the preview, merge entries that describe the same change, drop development
|
|
202
|
-
history, and confirm each entry names public behavior. Then compile:
|
|
203
|
-
|
|
204
|
-
~~~~ sh
|
|
205
|
-
sacho release --next 1.3.0 # reads the version from changes.d/next
|
|
206
|
-
sacho release 1.2.0 --next 1.3.0 # or state the version explicitly
|
|
207
|
-
sacho release 1.2.0 --date 2026-07-19 --next 1.3.0 # when the date must be fixed
|
|
208
|
-
~~~~
|
|
209
|
-
|
|
210
|
-
`release` stamps the dated section, deletes the consumed fragments, and sets the
|
|
211
|
-
next version. The first release may proceed without fragments when the changelog
|
|
212
|
-
has no released version sections. Later releases refuse to proceed when the
|
|
213
|
-
fragments compile to nothing unless `--allow-empty` is passed. Empty list items
|
|
214
|
-
and HTML comments are scaffolding, not release notes, and scaffold-only
|
|
215
|
-
fragments require the explicit option even on the first release. Commit the
|
|
216
|
-
changed `CHANGES.md`, the removed fragments, and the updated `changes.d/next`
|
|
217
|
-
together.
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
Publishing release notes
|
|
221
|
-
------------------------
|
|
222
|
-
|
|
223
|
-
After the release commit, `sacho show` prints one frozen section:
|
|
224
|
-
|
|
225
|
-
~~~~ sh
|
|
226
|
-
sacho show 1.2.0
|
|
227
|
-
sacho show 1.2.0 --skip-heading --output-file release-notes.md
|
|
228
|
-
~~~~
|
|
229
|
-
|
|
230
|
-
A tag-triggered GitHub Actions job can hand that file to the GitHub CLI:
|
|
231
|
-
|
|
232
|
-
~~~~ sh
|
|
233
|
-
version="${GITHUB_REF_NAME#v}"
|
|
234
|
-
sacho show "$version" --skip-heading --output-file release-notes.md
|
|
235
|
-
gh release create "$GITHUB_REF_NAME" --notes-file release-notes.md
|
|
236
|
-
~~~~
|
|
237
|
-
|
|
238
|
-
`show` reads only released sections; it never compiles current fragments.
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
Enforcing coverage in CI and hooks
|
|
242
|
-
----------------------------------
|
|
243
|
-
|
|
244
|
-
Configure the paths whose changes require a fragment, then let a machine catch
|
|
245
|
-
the omissions reviewers miss:
|
|
246
|
-
|
|
247
|
-
~~~~ toml
|
|
248
|
-
[check]
|
|
249
|
-
paths = ["src/**", "packages/**"]
|
|
250
|
-
~~~~
|
|
251
|
-
|
|
252
|
-
Locally, `sacho check --staged` inspects the Git index;
|
|
253
|
-
`sacho init --install-hook` installs a pre-commit hook chain. In CI, check a
|
|
254
|
-
branch commit-by-commit against its base:
|
|
255
|
-
|
|
256
|
-
~~~~ sh
|
|
257
|
-
sacho check --base origin/main
|
|
258
|
-
~~~~
|
|
259
|
-
|
|
260
|
-
Make sure the checkout history includes the base revision; a shallow clone that
|
|
261
|
-
omits it cannot support the comparison. A commit that genuinely needs no entry
|
|
262
|
-
opts out with a `Changelog: none` trailer in its message (or the
|
|
263
|
-
`[skip changelog]` family of substrings). The exemption applies only to the
|
|
264
|
-
commit that carries it. Details in `guide/ci-and-hooks.md`.
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
Forward-porting a bugfix release across maintenance branches
|
|
268
|
-
------------------------------------------------------------
|
|
269
|
-
|
|
270
|
-
The scenario: a fix lands on `1.1-maintenance`, `1.1.5` ships, and the fix needs
|
|
271
|
-
to reach `1.2-maintenance` and `main`. Two paths, and which one you are on
|
|
272
|
-
decides the work.
|
|
273
|
-
|
|
274
|
-
Merge before releasing, and there is nothing to do. If you merge
|
|
275
|
-
`1.1-maintenance` into `1.2-maintenance` while the fix's fragment still exists,
|
|
276
|
-
the VCS carries the fragment along, and each branch later consumes its own copy.
|
|
277
|
-
Prefer this ordering when you can; it needs no Sacho command.
|
|
278
|
-
|
|
279
|
-
Merge the release tag, and the fragment arrives already consumed. The incoming
|
|
280
|
-
`1.1.5` release commit deleted the fragment, so the merge brings only the frozen
|
|
281
|
-
`1.1.5` section. The merge driver inserts that section at its version position
|
|
282
|
-
in `CHANGES.md`, keeps the receiving branch's `changes.d/next`, and prints a
|
|
283
|
-
`sacho carry` hint on stderr. Now it is policy, not mechanics: if the receiving
|
|
284
|
-
branch's own next release should also list the fix, run `sacho carry 1.1.5` to
|
|
285
|
-
decompile that section back into `carried-from-1.1.5.md` fragments, edit them
|
|
286
|
-
if this branch presents the change differently, and treat them as ordinary
|
|
287
|
-
unreleased entries. If the imported `1.1.5` section is enough, do nothing.
|
|
288
|
-
|
|
289
|
-
`carry` is idempotent by design: it always writes the same
|
|
290
|
-
`carried-from-<version>.md` name and overwrites any file already there. That is
|
|
291
|
-
safe on a fresh carry, but if you have already edited those carried fragments,
|
|
292
|
-
re-running `carry` discards your edits, so check for existing
|
|
293
|
-
`carried-from-*.md` files before repeating it.
|
|
294
|
-
|
|
295
|
-
Under Jujutsu there is no per-path merge driver, so after resolving a concurrent
|
|
296
|
-
fragment merge run `sacho sync --force` and inspect the result.
|
|
297
|
-
`guide/releases.md` and `guide/version-control.md` cover both.
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
When something goes wrong
|
|
301
|
-
-------------------------
|
|
302
|
-
|
|
303
|
-
`sacho check` reports across three layers: fragment validity and formatting,
|
|
304
|
-
agreement between the materialized unreleased region and the fragments, and
|
|
305
|
-
fragment coverage for changed paths.
|
|
306
|
-
|
|
307
|
-
A formatting or drift failure is mechanically repairable. `sacho check --fix`
|
|
308
|
-
runs `fmt` and re-syncs the generated region; it will not write prose. If
|
|
309
|
-
`CHANGES.md` was hand-edited, remember that fragments are the source of truth:
|
|
310
|
-
move the edited text into a fragment, then `sacho sync`. Sacho asks before
|
|
311
|
-
discarding hand edits, and `sacho sync --force` skips that prompt, so read the
|
|
312
|
-
diff first.
|
|
313
|
-
|
|
314
|
-
A missing-fragment failure names the affected section and suggests `sacho add`.
|
|
315
|
-
Either write the entry or, if the commit truly has no user-visible effect, use
|
|
316
|
-
the `Changelog: none` exemption. For anything stranger, fetch
|
|
317
|
-
`troubleshooting.md`.
|