1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
# A complete .ctl/ver.yaml. Every section here is live, not commented out, so
# `tests/examples.rs` parses this exact file and plans from it — the examples
# cannot drift from the schema without failing the build. Copy it and delete
# what you do not need; only `packages` is required.
# `cargo` and `npm` are stock drivers and `cargo` and `bun` are stock
# publishers. Redeclaring one merges over the stock recipe rather than
# replacing it, so this pair changes nothing and is here to show the shape.
# Because it is a merge, an entry may carry only the field it overrides.
drivers:
cargo:
format: toml
keys:
npm:
format: json
keys:
# after: bun install # omit and verctl infers from the lockfile
# A manifest verctl neither parses nor knows: give it a read and a write
# command instead of format+keys. A string is a mise task, a list is execvp
# (no shell). Stdin is the file; stdout is the version (read) or the whole
# new file (write), and a write also gets VERCTL_VERSION.
plain:
read:
write:
# Any stack publishes through one list of argv. This adds uv alongside the
# stock cargo and bun recipes without a new verb.
publishers:
twine:
noun: package
argv:
packages:
- name: verctl
path: Cargo.toml
# driver: cargo # inferred from the file name
- name: "@org/pkg"
path: package.json
publisher: bun
registry: github
# changelog: docs/PKG.md # defaults to CHANGELOG.md beside the manifest
# first_minor: true # at 0.0.0, release 0.1.0 first instead of 0.0.1
# A file name that implies nothing, so both the driver and the publisher are
# named. This is what the `plain` driver and `publishers.twine` above are
# for — nothing here is declared without a package that uses it.
- name: py-tool
path: VERSION
driver: plain
publisher: twine
# How release tags are named. Omit for `v{version}` — one tag when every
# released package shares a version. Put `{name}` in the template for one
# tag (and one Release) per package; each Release body is that package's
# CHANGELOG section. A template with no `{name}` refuses a release that
# has more than one distinct version.
tags:
template: "v{version}"
# template: "{name}@{version}"
# One argv after every bump, on the Version PR. `stage` lists the extra paths
# it may write; any other dirty file fails the run.
prepare:
after:
stage:
# Machines are declared once. The key names one, `labels` is how GitHub finds
# it — every label at once, so `big` is a single machine carrying three labels,
# not three machines. verctl ships no built-in machine names: this section is
# yours, and only `labels` ever reaches runs-on.
runners:
big:
labels:
ns:
labels:
# Omit `assets` entirely for a library. `bin` is the recipe every target
# shares; prepare/build/binary override the stock rust recipe. Every other key
# is one host binary: the name is the job, the tarball, and the platform.
assets:
bin: verctl
# darwin-arm64 and linux-x64 are built in, so naming one is enough — and it
# keeps free hosted minutes.
darwin-arm64:
# Name a `runner` only when the built-in machine is wrong. One tarball is one
# machine, so this is one name and never a list.
linux-x64:
runner: big
# A name outside darwin-arm64 / linux-x64 describes a platform verctl knows
# nothing about, so it must describe all of it.
linux-arm64:
runner: ns
os: linux
arch: arm64
triple: aarch64-unknown-linux-gnu
# PR and push validation. Omit `ci` for one `verify` job on ubuntu-latest.
# A job may name several machines: one check each, `verify (ns)` and
# `verify (big)`. No os/arch/triple — validation does not cross-compile. A
# second job is a second key, and stays a separate check.
ci:
verify:
runners:
audit:
runners:
# Where served-file templates live. These are the defaults, so a repo that
# follows the convention writes no `templates` section at all. Each template
# declares its own target — see docs/served-files.md.
templates:
source: .ctl/templates
suffix: .jinja
# A version spelling, named once here and listed by every file that carries it,
# so two files that say the same thing say it in one place. `match` is literal
# text with `{version}` where the version goes; `occurrences` is how often a
# listing file must say it, and both too few and too many stop the release.
#
# "many" is for a document whose examples come and go, where only "still
# tracked" is worth asserting. "never" guards a spelling that was retired,
# `{exactly: 2}` and `{at_least: 2}` name counts.
# `whole_line` says the match owns its line instead of sitting inside one, for a
# frontmatter key or a shell assignment. It beats naming the neighbouring text,
# which breaks when the neighbour moves, and it makes `version: 0.1.1-rc1` fail
# to match rather than have its numeric head rewritten.
patterns:
install:
match: "github:victor-software-house/verctl@{version}"
occurrences: once
mention:
match: "verctl@{version}"
occurrences: many
frontmatter:
match: "version: {version}"
whole_line: true
occurrences: once
# Files that carry a version and cannot be generated: `prepare` rewrites them
# onto the Version PR commit, which is the commit the tag names, so the released
# tree carries them. A file that must name an already-published tarball — the
# pin a repo's own release lane installs — cannot be listed here either, because
# it can only move to a version that already exists.
pins:
# `tool` is the structural form: a mise `[tools]` entry, plus every `?ref=v…`
# include in the same file whose URL names this repo.
- file: examples/mise.toml
tool: "github:victor-software-house/verctl"
package: verctl
# `patterns` is the textual form, for a file with no format to speak of — a
# README sentence, a bash header. A file lists the patterns it carries by
# name; a name nothing declares, a name listed twice, and a pattern no file
# lists all fail the load.
- file: README.md
package: verctl
patterns:
# The same `install` spelling, in a second file. Naming it once is the point.
- file: docs/install.md
package: verctl
patterns:
# A bundled skill says which release its text describes, on its own line.
- file: skills/verctl/SKILL.md
package: verctl
patterns: