Repository navigation
itos 2.0.0
itos 2.0.0. The release holds:
itos-2.0.0-<os>-<arch>.tar.gz, the Go binary, for linux and darwin on amd64 and arm64,
anditos-2.0.0-windows-amd64.zipfor windows; each holds the binary (itos, or
itos.exe),LICENSEandREADME.mdat its top level.itos.schema.json, the config's JSON Schema, which an editor checks anitos.yamlagainst
(# yaml-language-server: $schema=<its URL>atop the file).checksums.txt, the SHA-256 of every file above, insha256sum's format:
sha256sum --ignore-missing -c checksums.txtchecks the ones downloaded beside it.
Pin an asset's URL and its SHA-256 (README.md, "Install"), as checksums.txt lists them:
bd88d2b7829316d2051c21bd37696139fbecbf3ec50dafda85a769394a8f61d6 itos-2.0.0-linux-amd64.tar.gz
1de05e327ee52a9a396e3decd4c27488bb4b535beca7d0d80eb3de5602daec49 itos-2.0.0-linux-arm64.tar.gz
6e4f7d3cb154f742db61aa108c9a913d3fcb028e73d41fc70b75a3ccb3cd9d49 itos-2.0.0-darwin-amd64.tar.gz
692c676536017e2513cca77cddf6843f1861fa49a281642d817cedf1afde4d64 itos-2.0.0-darwin-arm64.tar.gz
95013218ff4959049aa5aac4cd40011fe5aac7efd61806ecd4ac6430635b6fb4 itos-2.0.0-windows-amd64.zip
74c7665ebce5c9ed06a07fa73206ee52e2c8ed4f92cb3046e175e017f8c630a9 itos.schema.json
itos 2.0.0
itos is Go only. The release publishes the Go binary, one file with no runtime, for linux and darwin
on amd64 and arm64 and for windows on amd64, beside the config's JSON Schema and checksums.txt;
the TypeScript tarball, itos-<version>.tgz, and with it Node 24 as itos's runtime, are gone. The
binary has run beside the tarball since v1.0.0, held to the same conformance corpus and every
feature on every push, and has judged every commit of itos's own repository since T-060, so a
project that ran the tarball gets the same answers from it.
itos also lints a commit's header itself, so a project can drop Node altogether. Up to v1.1.0 itos
judged the header through a delegate, in practice commitlint with @commitlint/config-conventional,
which is Node. Now commits.header_lint.use: builtin lints it in itos, by the rules
config-conventional holds, each problem under commitlint's rule id and in its words.
Nothing changes for a project that keeps its delegate: commits.header_lint.hook and stdin with
no use run it as before, and use: command says so explicitly. commits.header_lint.alongside is
still refused.
A commit can also say what it asks of a consumer, where the change is made: a footer whose source is
free text, required for the types a project names, with a since of its own so that a history
written before the rule still verifies, and itos commit footers to gather a range's footers into a
release's notes.
The keys added are commits.header_lint.use and commits.footers.<name>.since, and
commits.footers.<name>.source takes text beside ledger and { tests: <kind> }. No key is
removed or renamed and no default changes, so a config v1.1.0 accepts, v2.0.0 accepts and reads the
same. It is a major version because the pin moves: a project on the tarball moves to the binary's
install script (Upgrading, steps 1 and 2).
What is new
-
Go only (T-062, T-061). A release holds
itos-<version>-<os>-<arch>.tar.gzfor linux and
darwin on amd64 and arm64 anditos-<version>-windows-amd64.zip, each with the binary (itos,
oritos.exe),LICENSEandREADME.md;itos.schema.json; andchecksums.txt, the SHA-256 of
every one, which the release's description prints. There is noitos-2.0.0.tgz, so no
package.jsondependency and no Node: a project pins the binary with an install script
(Upgrading, step 1). Go developers install it with
go install github.com/donvargax/itos/v2/cmd/itos@v2.0.0: from v2 the module path ends in its
major version, as Go requires, so the path without/v2stays at v1.1.0. -
The built-in header lint (slice 25,
ID-HEADER-01toID-HEADER-06). With
commits.header_lint: { use: builtin }, the commit-msg hook,commit check-messageandverify
lint the header in itos as commitlint 21 does with config-conventional. A message fails on any of
config-conventional's errors:type-empty,type-enum,type-case,subject-empty,
subject-case(not sentence-, start-, pascal- or upper-case),subject-full-stop,
header-max-length(100),header-trim,body-max-line-lengthandfooter-max-line-length
(100, a line with a URL excepted). Its warnings,body-leading-blankandfooter-leading-blank,
are printed and never reject.type-enum's list iscommits.types, or config-conventional's own
when the config lists none. Each problem is printed as commitlint prints one, errors before
warnings, then itos's footer rules, which run beside it as they run beside a delegate:✖ subject may not be empty [subject-empty] ✖ type may not be empty [type-empty] ⚠ body must have leading blank line [body-leading-blank]Under
--json,commit check-messagelists each with itslevelandfix, the footer rules'
after them, as it lists a delegate's. The messages commitlint leaves alone pass: a merge's, a
revert's or a reapply's as git writes them, anamend!,fixup!orsquash!commit, and a
version alone. The commit-msg hook reads the message file ascommitlint --editdoes, git's
comment lines (core.commentChar,#by default) and everything below its scissors line left
out;commit check-messageandverifyread the message as given, as commitlint reads stdin.
A blank message file passes the hook, as it passescommitlint --edit, and git aborts the commit
for its empty message; a blank message on stdin is rejected, namingtype-emptyand
subject-empty, where commitlint refuses it as no input. itos's own repository runs it: before
commitlint left, the built-in lint gave commitlint's verdict on all 614 messages it was held to in
both readings (every commit of itos's history, its test corpus's messages and messages aimed at
each rule), and commitlint's words but for the known issue below. -
commits.header_lint.useis accepted,builtinorcommand.config checkrefused it as an
unknown key from v0.3.0, since nothing read it; a value other than those two is refused now
(config-enum, exit 2). -
A footer of free text (slice 26,
ID-UPGRADE-01toID-UPGRADE-03).
commits.footers.<name>.source: textis a footer with no IDs: each line<Key>: <text>is one
footer, its text whatever a consumer of the project must do, or the wordnone(alone, in lower
case) when a consumer changes nothing. Its rule is only that it is there, for the types
required_fornames, and not empty, for the typesvalidate_fornames:commits: footers: Upgrading: { source: text, required_for: [feat, fix, build] }
✖ feat commits need a "Upgrading: <what a consumer must do, or none>" footer [upgrading-footer]An empty
Upgrading:is no footer to a type that needs one, andthe Upgrading: footer is empty
where the type validates it.config checkrefusesstrip_prefix,must_be_liveandread_at
on a footer of free text (config-footer-text, exit 2), since they say how IDs are read, and its
problem for a source it does not know (config-footer-source) namestextamong the sources. -
A footer's own since (
ID-UPGRADE-04). Any footer may namesince, the full SHA of a
commit, ascommits.sincedoes:verifyleaves that commit and its ancestors out of the footer's
required_for, so adding a required footer to a project's rules does not fail every commit
written before it. The commit-msg hook andcommit check-messagejudge the commit being made and
always require it.config checkandverifycheck it as they checkcommits.since: a full SHA
(config-since), a commit the repository has (config-since-commit, exit 2), the shallow clone
named as the likely cause. -
itos commit footers <name> <from> <to>(ID-UPGRADE-05) lists the range's footers of a
free-text footer, one line each, oldest commit first, the commit's short SHA and the text, leaving
out those that saynoneand empty ones; a commit with several gives a line for each.--json
gives{"schema":1,"footer","range","footers":[{"sha","subject","text"}]}, each with the full
SHA. The range isfrom..toas git reads it, every commit up to<to>whenfromis empty or
all zeros, and is not narrowed bycommits.since. It exits 2 for a footer that is not free text. -
The help says which header lint runs.
itos commit check-message --helpand
itos hook commit-msg --helpsay the header lint is the built-in one withuse: builtin, or the
delegate; the hook's no longer names commitlint ("commitlint here"), since which lint a project
runs is its config's to say.itos verify --helpnames a footer's since, anditos --helplists
commit footers.
Known issues
v1.1.0's two known issues were the TypeScript's, and left with it: every pattern now matches as
RE2 matches it (Upgrading, step 5). Every known issue of v0.6.0 still stands
(docs/releases/v0.6.0.md, "Known issues"): config check accepts a ledger.group.label that
cannot be a flag (p1-group-label-flag-word) and any word in a range check's except_types
(p1-moves-except-types-checked); the commit-msg hook knows a merge by its message
(p1-moves-merge-by-git); itos work check does not name a registry left at the old default
(p1-registry-old-default-hint); ledger.id has no default (p1-ledger-id-default); wip_tag is
accepted on a command kind (p1-wip-tag-command-kind); the nightly stops at its first failure
(p1-nightly-past-first-failure, p1-ci-run-every-failure-json); a range start a shallow clone
lacks is not named as such (p1-range-start-shallow-clone); ci plan --data-at names a missing
ledger folder by a scratch path (p1-data-at-ledger-folder); a check two named tasks share runs
once per task (p1-ci-checks-once, p1-hook-checks-once); a ledger of <folder>/{group}.yaml
matches the registry (p1-ledger-matches-registry); the commit-time data check does not follow
every file (p1-people-checked-at-commit, p1-smoke-rule-at-commit, p1-verify-data-per-commit);
a pre-commit hook's early exit is hand-written (p1-pre-commit-asks-what-skips); and shell: [] is
accepted (p1-shell-not-empty). Three are new:
-
subject-casemay name more cases than commitlint for a subject with some accented letters
(p3-header-case-decomposition). commitlint finds start-case and pascal-case after decomposing
every precomposed letter (NFD); Go's standard library has no such tables, and the built-in lint
decomposes only the letters of Latin-1 and Latin Extended-A. For a subject such asǍb Cdit
sayssubject must not be sentence-case, start-casewhere commitlint says
subject must not be sentence-case. The verdict is the same: every casesubject-caserefuses
is sentence-case too. -
config checkacceptshookandstdinbesideuse: builtin
(p3-header-lint-unread-keys), though nothing reads them then. Take them out when you switch
(Upgrading, step 3). -
A free-text footer is one line (
p3-text-footer-lines). A footer line may not pass 100
characters (footer-max-line-length), so a longer step is written as severalUpgrading:lines,
anditos commit footerslists each as a footer of its own; an indented line below one is not
read as part of it.
Upgrading
Do these in order, in one session, then commit as your project's rules ask (the install script,
the shim and .gitignore are a build commit). Steps 1 to 4 move a project from the tarball to the
binary and, if it wants, off Node; a project that already runs the binary, from v1.0.0's Upgrading,
step 6, does step 1's version and hashes only, then steps 3 and 4 if it still has commitlint. A
project on v1.1.0 can skip step 5; one on v1.0.x or v0.6.0 does it. itos.yaml needs no edit to
keep working: no key is removed or renamed and no default changes.
-
Pin the binary. Commit this install script as
tools/bin/install-itos(executable). It
pins the version and each platform's SHA-256, so a replaced release cannot pass, installs into
an ignored.tools/bin/, and does nothing when it already installed that version and hash
there (.tools/bin/itos.pinrecords them; it never runs the binary to ask, sinceitos version
loadsitos.yamland stops on a config it refuses):#!/bin/sh # tools/bin/install-itos: the pinned itos, into .tools/bin/ set -eu version=2.0.0 cd "$(git rev-parse --show-toplevel)" case "$(uname -s)-$(uname -m)" in Linux-x86_64) platform=linux-amd64 sum=<its line's hash> ;; Linux-aarch64 | Linux-arm64) platform=linux-arm64 sum=<its line's hash> ;; Darwin-x86_64) platform=darwin-amd64 sum=<its line's hash> ;; Darwin-arm64) platform=darwin-arm64 sum=<its line's hash> ;; MINGW*-x86_64 | MSYS*-x86_64) platform=windows-amd64 sum=<its line's hash> ;; *) echo "install-itos: no itos archive for $(uname -s)-$(uname -m)" >&2 && exit 1 ;; esac case "$platform" in windows-*) archive="itos-$version-$platform.zip" bin=.tools/bin/itos.exe ;; *) archive="itos-$version-$platform.tar.gz" bin=.tools/bin/itos ;; esac # What was installed, so a call with nothing to do runs nothing. pin=".tools/bin/itos.pin" if [ -x "$bin" ] && [ "$(cat "$pin" 2>/dev/null)" = "$version $sum" ]; then exit 0; fi tmp=$(mktemp -d) trap 'rm -rf "$tmp"' EXIT curl -fsSL -o "$tmp/$archive" "https://github.com/donvargax/itos/releases/download/v$version/$archive" if command -v sha256sum >/dev/null; then check="sha256sum -c -"; else check="shasum -a 256 -c -"; fi (cd "$tmp" && echo "$sum $archive" | $check) mkdir -p .tools/bin case "$archive" in *.zip) unzip -o -q "$tmp/$archive" itos.exe -d .tools/bin ;; *) tar -xzf "$tmp/$archive" -C .tools/bin itos ;; esac echo "$version $sum" > "$pin" echo "install-itos: itos $version in $bin"
Fill each
<its line's hash>with that archive's hash from this release'schecksums.txt, one
<hash> <archive>line per asset: the release's description prints it above these notes
(https://github.com/donvargax/itos/releases/tag/v2.0.0), and
gh release download v2.0.0 -R donvargax/itos -p checksums.txtdownloads it. Add.tools/to
.gitignore. Then make the scripthooks.binnames (tools/bin/itosby default), which your
hooks and CI call, run the binary, installing it first when it is missing or another version:#!/bin/sh # tools/bin/itos: the pinned itos binary, installed on first use root=$(git rev-parse --show-toplevel) || exit 3 "$root/tools/bin/install-itos" >&2 || exit 3 case "$(uname -s)" in MINGW* | MSYS*) exe=.exe ;; *) exe= ;; esac exec "$root/.tools/bin/itos$exe" "$@"
A fresh CI checkout then installs it on the first call, so CI needs no step of its own; the
runner needscurl,tar(orunzip), andsha256sumorshasum. Go developers can instead
go install github.com/donvargax/itos/v2/cmd/itos@v2.0.0: note the/v2, without which Go
installs v1.1.0. A later version is the samebuildcommit:version=and each hash. -
Remove the tarball.
npm uninstall itos(orpnpm remove itos,yarn remove itos) takes
its URL out ofpackage.jsonand the lockfile. Anything that called the package's bin
(npx itos,node_modules/.bin/itos, apackage.jsonscript runningitos) calls
tools/bin/itosinstead. Node 24 is no longer needed for itos. -
To drop commitlint, switch to the built-in header lint. In
itos.yaml, replace
commits.header_lint'shookandstdinwithuse: builtin:commits: header_lint: { use: builtin }
Take
hookandstdinout:config checkaccepts them besideuse: builtin, but nothing
reads them (Known issues). Then take commitlint out: its config (commitlint.config.*, or the
commitlintkey ofpackage.json),@commitlint/cli,@commitlint/config-conventionaland
any@commitlint/*package beside them. Check it:
printf 'update things\n' | tools/bin/itos commit check-message -exits 1 naming
[type-empty], and a sound message of yours exits 0.The built-in lint holds config-conventional's rules and nothing else. If your commitlint config
changes them (a rule of its own, a plugin, another level or limit, its owntype-enum), keep the
delegate withuse: commandinstead, or accept config-conventional's rules. The built-in lint's
type list iscommits.types, or config-conventional's own when the config lists none, so make
it the list your commitlint config allowed. A script that reads the lint's report finds the same
lines,✖ <words> [<rule>], but on stderr where the delegate's went to stdout, without
commitlint's⧗ --- input ---heading and itsfound N problemssummary; under--json
nothing changes. To keep your delegate, change nothing:hookandstdinwith nouserun it
as before, anduse: commandbeside them says so. -
Drop Node, if nothing else needs it. With the tarball and commitlint gone, Node may be left
for nothing: then remove CI's Node setup and its install step,.nvmrcorengines, and
package.jsonand its lockfile if they hold nothing else. If your git hooks are installed by a
Node tool (husky, Vite+), keep it, or move them to one that is not:
tools/bin/itos hooks install --manager gitwrites plain git hooks, which are not committed,
so each clone runs it once; lefthook, pre-commit and prek are not Node either (set
hooks.managerto the one you use). -
If you come from v1.0.x or v0.6.0 on the tarball, check your patterns as RE2. The binary
compiles and matches the config's regular expressions as RE2:ledger.id,
ledger.group.pattern,tests.<kind>.id,ci.cost.staticandci.covers'matches. Run
tools/bin/itos config check. A pattern RE2 cannot compile is refused (exit 2, the rule
config-regexp) with<key> is not an RE2 regular expression: <pattern>, and every command that
loads the config stops on it: a lookaround ((?=…),(?!…),(?<=…),(?<!…)), a
backreference (\1,\k<name>), a letter escape RE2 has no reading for (\c,\e,\u, and
any letter but\A\b\B\d\D\p\P\Q\s\S\w\W\x\z\a\f
\n\r\t\v, or inside a class\b,\A,\B,\Qand\ztoo), the classes[]and
[^], a Unicode or POSIX class name RE2 lacks (\p{Foo},[[:foo:]]), a range bounded by a
class ([a-\d]),(?i-:…), and a repeat count above 1000. Rewrite it without them: a lookahead
that only guards what follows is usually the same pattern with that part matched
(^vp check(?= )as^vp check), a backreference spelled out for the cases it meets,\eas
theeJavaScript read it as,[\b]as\x08, and a long count split (\d{1,1001}as
\d{1,1000}\d?). A pattern both compile can still match differently:\smatches only ASCII
whitespace in RE2, and.matches\r. A script that read the old words ("is not a regular
expression", "compiles as a JavaScript regular expression") matches the rule instead. -
Check
itos.yaml'srequires.requiresis a list of comparators that must all hold, and
2.0.0 is past<2,<1.2or=1.1.0, soitos version --checkwould exit 1 with
itos 2.0.0 does not satisfy >=1.1.0 <2 (the config's requires); widen such a cap. A lower bound
alone (>=0.6.0) needs no change. If you useuse: builtin, a footer'ssinceor
source: text, raise it to>=2.0.0: an older itos refuses those as unknown keys, and
version --checkthen names the version instead. -
Point the schema line at v2.0.0's. If
itos.yaml's first line names a release's
itos.schema.json, make it
# yaml-language-server: $schema=https://github.com/donvargax/itos/releases/download/v2.0.0/itos.schema.json,
which knows the keys this release adds. -
To have each commit say what it asks of a consumer, add a footer of free text. Optional, and
new: name it, the types that need it, andsince, the full SHA of the commit you add it after
(git rev-parse HEAD), so thatverifydoes not require it of the commits written before:commits: footers: Upgrading: { source: text, required_for: [feat, fix], since: "<full SHA>" }
The commit-msg hook then rejects a
featorfixwithout anUpgrading:line, so each says
what its consumers must do, orUpgrading: none; the commit adding the rule needs one only if
its own type is listed.itos commit footers Upgrading <last release> HEADlists what your next
release's notes must tell your consumers. -
Check:
tools/bin/itos versionprintsitos 2.0.0,tools/bin/itos config checkpasses,
tools/bin/itos version --checkexits 0, and the commit of these changes goes through your hooks
on the binary. -
Report a problem itos causes. To report a problem itos caused in this repository, open an
issue on donvargax/itos with the exact command, its exact output and exit code, what
itos versionprints, the part ofitos.yamlthat matters, and what you expected, each under
its own heading (What was run / Its output / itos version / The config / What was expected):gh issue create -R donvargax/itos --label consumer-report --title "consumer report: <one line>" --body-file <report.md>
(or, in a browser, https://github.com/donvargax/itos/issues/new?template=consumer-report.yml).
gh issue create --templatedoes not fill in the form when nothing answers it, so a session
writes the body itself, one##heading per part, and passes the label. One problem per
issue; leave out anything private, since the repository is public.