@sous-io/sous 0.2.1 → 0.2.2
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/docs/markdown/README.md +4 -0
- package/docs/markdown/_sidebar.md +4 -1
- package/docs/markdown/commands.md +293 -274
- package/docs/markdown/configuration.md +7 -0
- package/docs/markdown/repositories-authoring.md +210 -302
- package/docs/markdown/repositories-consuming.md +219 -468
- package/docs/markdown/repositories-file-formats.md +216 -980
- package/docs/markdown/repositories-providers.md +322 -0
- package/docs/markdown/repositories-quickstart.md +340 -0
- package/docs/markdown/repositories-troubleshooting.md +336 -0
- package/docs/markdown/repositories-variables.md +229 -296
- package/docs/markdown/repositories.md +262 -228
- package/package.json +1 -1
- package/recipes/core/sous-skills/sous.recipe.yaml +1 -1
|
@@ -1,18 +1,18 @@
|
|
|
1
1
|
# Authoring a Repository
|
|
2
2
|
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
these commands read and write; this page is about the workflow.
|
|
3
|
+
The guide to publishing recipes of your own: creating a repository, writing a recipe, declaring the variables it
|
|
4
|
+
needs, cutting a release, editing a published repository in place, and proposing a change to somebody else's.
|
|
5
|
+
[Repository file formats](repositories-file-formats.md) holds the schemas; this page is the workflow.
|
|
7
6
|
|
|
8
|
-
?> A recipe repository is not a sous project. It has no `.sous/` directory, and `sous repo init`,
|
|
9
|
-
|
|
10
|
-
repository itself.
|
|
7
|
+
?> A recipe repository is not a sous project. It has no `.sous/` directory, and `sous repo init`, `sous repo
|
|
8
|
+
release` and `sous repo submit` do not look for one. Run them from inside the repository itself.
|
|
11
9
|
|
|
12
10
|
## Create a repository
|
|
13
11
|
|
|
14
12
|
```term
|
|
15
13
|
$ sous repo init ./my-recipes --name my-recipes --namespace workflow
|
|
14
|
+
▶ Creating a recipe repository:
|
|
15
|
+
|
|
16
16
|
wrote sous.repo.yaml
|
|
17
17
|
wrote sous.index.json
|
|
18
18
|
wrote recipes/workflow/example/sous.recipe.yaml
|
|
@@ -22,52 +22,26 @@ $ sous repo init ./my-recipes --name my-recipes --namespace workflow
|
|
|
22
22
|
wrote .gitignore
|
|
23
23
|
```
|
|
24
24
|
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
Names are lowercase kebab-case, and a name given in any other case is lowercased for you, so a
|
|
33
|
-
directory called `My-Recipes` yields `my-recipes`. `repo init` refuses to write over a directory
|
|
34
|
-
that already holds a repo manifest unless you pass `--force`, and it reads every manifest back
|
|
35
|
-
after writing it, so the scaffold it leaves behind is one that validates.
|
|
36
|
-
|
|
37
|
-
?> `repo init --force` means overwrite, not "answer yes". It is unrelated to the shared
|
|
38
|
-
confirmation flag other commands spell `--yes`, `-y`, `--force` or `--trust`, and `repo init`
|
|
39
|
-
does not accept those other spellings.
|
|
40
|
-
|
|
41
|
-
## The layout
|
|
42
|
-
|
|
43
|
-
```text
|
|
44
|
-
my-recipes/
|
|
45
|
-
sous.repo.yaml what this repository publishes
|
|
46
|
-
sous.index.json the catalog, written by 'sous repo release'
|
|
47
|
-
recipes/
|
|
48
|
-
workflow/
|
|
49
|
-
example/
|
|
50
|
-
sous.recipe.yaml one recipe, with its own version
|
|
51
|
-
skills/
|
|
52
|
-
example-skill/
|
|
53
|
-
SKILL.md
|
|
54
|
-
.github/workflows/sous-release.yml
|
|
55
|
-
.gitignore
|
|
56
|
-
README.md
|
|
57
|
-
```
|
|
25
|
+
`--name` is the repository's short name (default: the directory's own name), `--namespace` the one namespace to
|
|
26
|
+
declare (default: the repository's name), `--force` overwrites a repository that already exists, and `--dry-run`
|
|
27
|
+
lists the files without writing them. The command asks nothing, and names are lowercase kebab-case, so `My-Recipes`
|
|
28
|
+
yields `my-recipes`. Every file is read back through the schemas sous uses on published repositories, so the
|
|
29
|
+
scaffold validates. The scaffold is files only: run `git init`, commit it, and add the remote you publish from
|
|
30
|
+
before releasing, since `sous repo release` reads and writes tags and fails in a directory git does not track.
|
|
58
31
|
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
path under `recipes`, and that the folder holds exactly one recipe manifest.
|
|
32
|
+
?> `repo init --force` means overwrite, not "answer yes". It is unrelated to the confirmation flag other
|
|
33
|
+
commands spell `--yes`, `-y`, `--force` or `--trust`.
|
|
62
34
|
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
35
|
+
`sous.repo.yaml` says what the repository publishes, `sous.index.json` is the catalog that `sous repo release`
|
|
36
|
+
writes, and each recipe folder carries one `sous.recipe.yaml` beside the files it contributes. Nothing else is
|
|
37
|
+
fixed: recipe folders may live anywhere, and what makes a folder a recipe is that `sous.repo.yaml` lists its
|
|
38
|
+
path under `recipes`. Both hand-written manifests are YAML or JSON and never JavaScript, because a repository's
|
|
39
|
+
trust story rests on being readable without running any of its code.
|
|
66
40
|
|
|
67
41
|
## Write a recipe
|
|
68
42
|
|
|
69
|
-
Copy the example folder, edit its manifest, and add
|
|
70
|
-
`
|
|
43
|
+
Copy the example folder, edit its manifest, and add its path to the `recipes` list in `sous.repo.yaml`, which is a
|
|
44
|
+
plain list of folder paths (`recipes:`, then `- recipes/workflow/task-files` under it):
|
|
71
45
|
|
|
72
46
|
```yaml
|
|
73
47
|
formatVersion: 1
|
|
@@ -76,173 +50,125 @@ name: task-files
|
|
|
76
50
|
version: 0.1.0
|
|
77
51
|
description: >-
|
|
78
52
|
Per-branch task files, with skills for starting and resuming work.
|
|
79
|
-
|
|
80
53
|
contents:
|
|
81
54
|
- kind: skills
|
|
82
55
|
include:
|
|
83
56
|
- skills/**/*.md
|
|
84
57
|
```
|
|
85
58
|
|
|
86
|
-
`contents` groups are what a
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
59
|
+
`contents` groups are what a subscribing project receives, one group per kind, each with `include` glob patterns
|
|
60
|
+
relative to the recipe folder (and optional `exclude` patterns). The four kinds are `skills`, `memories`,
|
|
61
|
+
`prompts` and `config`; the kind decides where files land, which the subscriber maps with
|
|
62
|
+
[`recipeOutputs`](repositories-file-formats.md#recipeoutputs-where-the-files-land). Field tables are in the
|
|
63
|
+
[recipe manifest](repositories-file-formats.md#sousrecipeyaml-the-recipe-manifest) reference, and recipe
|
|
64
|
+
metadata is the source of truth for the version: never edit `sous.index.json` by hand.
|
|
92
65
|
|
|
93
|
-
|
|
94
|
-
|
|
66
|
+
A recipe's files compile exactly the way a project's own `entryGlob` target does, so the
|
|
67
|
+
[`.tpl.` convention](configuration.md#templates-and-the-tpl-convention) applies unchanged inside a recipe. A
|
|
68
|
+
template may include a file from a recipe this one depends on through the reserved `~` sigil, resolved against
|
|
69
|
+
this recipe's own `depends` and `subscribes` at their pinned versions; see
|
|
70
|
+
[Including recipe files in your own templates](repositories.md#including-recipe-files-in-your-own-templates).
|
|
95
71
|
|
|
96
72
|
## Declare the variables a recipe needs
|
|
97
73
|
|
|
98
|
-
A recipe that needs a value from the project asks
|
|
99
|
-
|
|
100
|
-
variable and no valid answer is already in scope.
|
|
74
|
+
A recipe that needs a value from the project asks through a **definition**: a published specification, never a
|
|
75
|
+
value. Sous asks the question only when a subscribed recipe needs the variable and no valid answer is in scope.
|
|
101
76
|
|
|
102
77
|
```yaml
|
|
103
78
|
variables:
|
|
104
|
-
- name:
|
|
79
|
+
- name: qaTaskRoot
|
|
105
80
|
type: path
|
|
106
|
-
prompt: Where should
|
|
81
|
+
prompt: Where should the review notes be stored?
|
|
107
82
|
description: >-
|
|
108
|
-
This recipe
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
default: .sous/tasks
|
|
83
|
+
This recipe writes one review note per branch, and this setting is the
|
|
84
|
+
directory those notes are read from and written to. The default keeps them
|
|
85
|
+
inside the project's .sous directory, which git already partly ignores.
|
|
86
|
+
Any local path works, relative to the project root or absolute.
|
|
87
|
+
example: ~/qa-notes
|
|
88
|
+
default: .sous/qa-notes
|
|
115
89
|
required: true
|
|
116
90
|
scope: shared
|
|
117
91
|
|
|
118
|
-
- name:
|
|
92
|
+
- name: qaServiceToken
|
|
119
93
|
type: string
|
|
120
|
-
env:
|
|
121
|
-
prompt: What is this project's service token?
|
|
94
|
+
env: QA_SERVICE_TOKEN
|
|
95
|
+
prompt: What is this project's review service token?
|
|
122
96
|
description: >-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
is stored in the gitignored env file so it never reaches git.
|
|
128
|
-
example: svc_0123456789abcdef0123
|
|
97
|
+
The review service is called once per run and authenticates with a token
|
|
98
|
+
issued per project. There is no default, because a token belongs to one
|
|
99
|
+
person on one machine; create one under Settings, then Tokens.
|
|
100
|
+
example: qa_0123456789abcdef0123
|
|
129
101
|
secret: true
|
|
130
102
|
scope: local
|
|
131
103
|
validate:
|
|
132
104
|
minLength: 20
|
|
133
105
|
```
|
|
134
106
|
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
the
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
- **`x-intentional: true`** silences the release warning about claiming a well-known environment
|
|
167
|
-
variable name. `PATH`, `HOME`, `USER`, `SHELL`, `GITHUB_TOKEN`, `GITLAB_TOKEN`, `NPM_TOKEN` and
|
|
168
|
-
anything starting `AWS_` or `SOUS_` draw that warning; binding an existing token is legitimate,
|
|
169
|
-
it just deserves saying out loud.
|
|
170
|
-
|
|
171
|
-
Two definitions of the same name may share one environment variable, because that is exactly
|
|
172
|
-
what the shared rung of the resolution ladder is for. Two definitions of **different** names
|
|
173
|
-
claiming the same environment variable is an error. See
|
|
174
|
-
[Recipe variables](repositories-variables.md) for how an answer is found at build time.
|
|
175
|
-
|
|
176
|
-
!> Within a major version a schema may only LOOSEN. Tightening a constraint is a major bump, and
|
|
177
|
-
an upgrade re-validates stored answers, re-prompting only where an old answer no longer fits.
|
|
178
|
-
|
|
179
|
-
## Edit a repository in place
|
|
180
|
-
|
|
181
|
-
Edits happen in a real working copy, never in the machine-wide store. `sous repo link`, run
|
|
182
|
-
inside a project, points that project's resolution of one repository at a checkout:
|
|
183
|
-
|
|
184
|
-
```bash
|
|
185
|
-
sous repo link ~/Projects/my-recipes # link the checkout that is already there
|
|
186
|
-
sous repo link my-recipes # clone it into .sous/repos/<owner>/<name>
|
|
187
|
-
sous repo link my-recipes ~/Projects/my-recipes # link a checkout that already exists
|
|
188
|
-
sous repo link my-recipes --global # one checkout shared by every project
|
|
189
|
-
sous repo unlink my-recipes
|
|
190
|
-
```
|
|
191
|
-
|
|
192
|
-
The command is written three ways.
|
|
193
|
-
|
|
194
|
-
A **path on its own** links the checkout that is already at that path, where it is. Relative
|
|
195
|
-
paths and `~` work, because that is what people type. The repository is added to the project
|
|
196
|
-
first if it has not been added yet, which is the same trust ceremony `sous repo add` runs; its
|
|
197
|
-
short name is the one the checkout's own repo manifest suggests, falling back to the directory's
|
|
198
|
-
name. Nothing is cloned.
|
|
199
|
-
|
|
200
|
-
A **repository on its own** is cloned for you, into `.sous/repos/<owner>/<name>` or into
|
|
201
|
-
`$SOUS_HOME/repos/<owner>/<name>` with `--global`. A directory that is already a checkout of the
|
|
202
|
-
same remote is reused rather than cloned again, so running the command twice is harmless; one
|
|
203
|
-
holding a different remote is an error, because reading the wrong recipes silently would be
|
|
204
|
-
worse than stopping.
|
|
107
|
+
- **`name` is camelCase**, and it is how templates refer to the variable. `type` is `string`, `number`,
|
|
108
|
+
`boolean`, `enum`, `path` or `url`. `required` defaults to true.
|
|
109
|
+
- **A description explains, a prompt asks.** Both `description` and `example` are required, and a manifest missing
|
|
110
|
+
either is refused. Write the description in full sentences: what the setting is for, what the default does, and
|
|
111
|
+
what else is acceptable; write the prompt as one plain question. It is also what `sous vars show <name>` prints.
|
|
112
|
+
- **An example is documentation, a default is a value.** Sous never stores an example and never offers it as
|
|
113
|
+
the answer; it only shows it beside the question. Use `default` for a value a project should actually start
|
|
114
|
+
with, and put the same text in both only when the sample answer is the right starting value.
|
|
115
|
+
- **`env` is the environment variable** an answer binds to. Omit it and the answer binds to the shared rung of
|
|
116
|
+
the resolution ladder, derived from the name: `apiUrl` becomes `SOUS_VAR_API_URL`. Name it explicitly to reuse
|
|
117
|
+
a value the environment already carries, such as `GITHUB_TOKEN`; `x-intentional: true` then silences the
|
|
118
|
+
release warning about claiming a well-known name (`PATH`, `HOME`, `USER`, `SHELL`, `GITHUB_TOKEN`,
|
|
119
|
+
`GITLAB_TOKEN`, `NPM_TOKEN`, or anything starting `AWS_` or `SOUS_`, except the `SOUS_VAR_` names sous
|
|
120
|
+
derives itself). Two definitions of the same name may share one variable; two of **different** names
|
|
121
|
+
claiming the same one is an error.
|
|
122
|
+
- **`secret: true`** hides the value everywhere sous prints it, and **`scope`** picks the file the answer is
|
|
123
|
+
written to: `shared` for the committed `.sous/.env`, `local` for the gitignored `.sous/.env.local`. A secret
|
|
124
|
+
is always stored locally, so a secret declared `shared` is rejected.
|
|
125
|
+
- **`validate`** carries `pattern`, `minLength`, `maxLength`, `min`, `max` and `enum`. A published `pattern` runs on
|
|
126
|
+
a worker under a time budget, so one that backtracks forever fails validation rather than hanging the person
|
|
127
|
+
answering, and the message names your pattern. Within a major version a schema may only LOOSEN: tightening a
|
|
128
|
+
constraint is a major bump, and an upgrade re-validates stored answers, re-prompting only where an old one no
|
|
129
|
+
longer fits.
|
|
130
|
+
- **Further reading:** [Recipe variables](repositories-variables.md) covers how an answer is found at build
|
|
131
|
+
time; [Variable definitions](repositories-file-formats.md#variable-definitions) is the full field table.
|
|
132
|
+
|
|
133
|
+
## Declare dependencies and co-subscriptions
|
|
134
|
+
|
|
135
|
+
`depends` fetches and pins a recipe whose files stay OUT of a subscriber's output but are addressable from your
|
|
136
|
+
own templates. `subscribes` is a co-subscription: its questions run and its files DO enter the output, which is
|
|
137
|
+
how a curated bundle is built. Both hold plain strings naming their target by where it lives.
|
|
205
138
|
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
inside `.sous/.gitignore`. Only the lines between the markers are ever rewritten, and both files
|
|
215
|
-
are written only when their contents would change. `sous prune` and `sous clear` never reach
|
|
216
|
-
into `.sous/repos/`.
|
|
217
|
-
|
|
218
|
-
Because a link bypasses versions, the lockfile and freshness checks, and because those bypasses
|
|
219
|
-
belong to one person's machine rather than to the team, both the link command and every
|
|
220
|
-
subsequent build say so loudly:
|
|
221
|
-
|
|
222
|
-
```text
|
|
223
|
-
The repository 'my-recipes' is now LINKED.
|
|
224
|
-
Its recipes are read from the checkout above, so versions, the lockfile
|
|
225
|
-
and freshness checks no longer apply to it. Builds say so every time.
|
|
139
|
+
```yaml
|
|
140
|
+
depends:
|
|
141
|
+
- workflow/qa-helper # a sibling, released alongside me
|
|
142
|
+
- workflow/qa-helper@^1.1 # a sibling with a range; uncommon
|
|
143
|
+
- github://sous-io/sous-recipes/workflow/sat@^1.0 # another repository
|
|
144
|
+
- gitlab://gitlab.example.com/group/subgroup/proj/qa/lint # a self-hosted host, with subgroups
|
|
145
|
+
subscribes:
|
|
146
|
+
- quality/code-review # co-subscribed: its files land too
|
|
226
147
|
```
|
|
227
148
|
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
149
|
+
A **bare ref** is a sibling in this repository; with no range it means "the version released alongside me", since
|
|
150
|
+
one run cuts both tags and records the exact version in the index. A locator URL names another repository,
|
|
151
|
+
and its last two segments are always the namespace and the recipe, which is the recipe's published **identity and
|
|
152
|
+
never a path on disk**: a recipe stored at `recipes/shared/sat/` and published as `workflow/sat` is written
|
|
153
|
+
`github://owner/repo/workflow/sat`. Everything before them is the repository, a dotted first segment naming the
|
|
154
|
+
host. The optional range after `@` follows npm's rules. `local://` locators and `repo:` short names are refused:
|
|
155
|
+
a local repository is a consumer's convenience, and a short name is one project's private label. See
|
|
156
|
+
[Dependencies named by location](repositories-file-formats.md#dependencies-named-by-location).
|
|
232
157
|
|
|
233
|
-
##
|
|
158
|
+
## Cut a release
|
|
234
159
|
|
|
235
|
-
`sous repo release` publishes new versions
|
|
236
|
-
|
|
237
|
-
publish them. Run it from inside the repository, with your recipe changes already committed.
|
|
160
|
+
`sous repo release` publishes new versions: one run raises versions, regenerates `sous.index.json`, commits both,
|
|
161
|
+
and cuts the tags. Run it inside the repository, with your recipe changes committed.
|
|
238
162
|
|
|
239
163
|
```term
|
|
240
164
|
$ sous repo release
|
|
241
|
-
|
|
242
|
-
|
|
165
|
+
▶ The release this would make:
|
|
166
|
+
|
|
167
|
+
workflow/qa-helper: 0.1.0 becomes 0.1.1 (a patch step; the tag would be
|
|
168
|
+
workflow/qa-helper@0.1.1)
|
|
243
169
|
|
|
244
170
|
Left alone:
|
|
245
|
-
workflow/
|
|
171
|
+
workflow/qa-variables: its files have not changed since workflow/qa-variables@0.1.0.
|
|
246
172
|
|
|
247
173
|
This run would:
|
|
248
174
|
Raise the versions listed above, in the manifests that declare them.
|
|
@@ -252,158 +178,140 @@ $ sous repo release
|
|
|
252
178
|
Push nothing; pass '--push' to push what it makes.
|
|
253
179
|
|
|
254
180
|
Publish these versions? yes
|
|
255
|
-
workflow/task-files: 1.1.0 becomes 1.1.1.
|
|
256
|
-
Wrote sous.index.json.
|
|
257
|
-
Committed: Release workflow/task-files@1.1.1
|
|
258
|
-
Created the tag workflow/task-files@1.1.1.
|
|
259
|
-
```
|
|
260
181
|
|
|
261
|
-
|
|
262
|
-
that question ahead of time, and `--dry-run` prints the plan and stops.
|
|
263
|
-
|
|
264
|
-
### What a run decides
|
|
265
|
-
|
|
266
|
-
Three facts about each recipe decide everything, and nothing else does:
|
|
267
|
-
|
|
268
|
-
1. **Is it in scope?** Every recipe is, unless `--namespace` or `--recipe` narrows the run.
|
|
269
|
-
2. **Have its files changed since the tag that last published it?** A recipe nobody touched is
|
|
270
|
-
not re-released; a published version that says the same thing as the one before it is noise.
|
|
271
|
-
`--include-unchanged` releases everything in scope anyway.
|
|
272
|
-
3. **Has its version already been raised past that tag?** If so, the bump has been done and this
|
|
273
|
-
run only publishes it. That is what a merge looks like to the continuous integration run.
|
|
274
|
-
|
|
275
|
-
### The flags
|
|
276
|
-
|
|
277
|
-
| Invocation | What it does |
|
|
278
|
-
|------------|--------------|
|
|
279
|
-
| `sous repo release` | Plan, ask once, then bump, regenerate, commit and tag |
|
|
280
|
-
| `sous repo release --dry-run` | Print the plan and stop |
|
|
281
|
-
| `sous repo release --yes` | Skip the question; everything else is the same |
|
|
282
|
-
| `sous repo release --namespace <ns>` | Release only that namespace. Repeatable |
|
|
283
|
-
| `sous repo release --recipe <ns/name>` | Release only that recipe. Repeatable |
|
|
284
|
-
| `sous repo release --bump <level>` | `patch` (the default), `minor`, `major` or `prerelease` |
|
|
285
|
-
| `sous repo release --no-bump` | Raise nothing; a changed recipe nobody raised is an error |
|
|
286
|
-
| `sous repo release --include-unchanged` | Release everything in scope, changed or not |
|
|
287
|
-
| `sous repo release --tag` | Cut the tags even on a branch other than the default one |
|
|
288
|
-
| `sous repo release --push` | Push the commit, and the tags this run created, to `origin` |
|
|
289
|
-
| `sous repo release --check` | Read only: validate, and fail when the committed index is out of date |
|
|
290
|
-
| `sous repo release --ci` | The merge preset: never bump, accept the plan, never ask, fail on anything unbumped |
|
|
182
|
+
▶ Publishing:
|
|
291
183
|
|
|
292
|
-
|
|
184
|
+
workflow/qa-helper: 0.1.0 becomes 0.1.1.
|
|
185
|
+
Wrote sous.index.json.
|
|
186
|
+
Committed: Release workflow/qa-helper@0.1.1
|
|
187
|
+
Created the tag workflow/qa-helper@0.1.1.
|
|
188
|
+
```
|
|
293
189
|
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
190
|
+
That run leaves out the banner and the checking step, and prints how to push once it finishes. The plan comes first
|
|
191
|
+
and a run asks once before changing anything; `--yes` answers ahead of time and `--dry-run` prints the plan and
|
|
192
|
+
stops. Three facts about each recipe decide that plan and nothing else does: whether it is in scope, whether its
|
|
193
|
+
files changed since the tag that last published it, and whether its version was already raised past that tag. The
|
|
194
|
+
third case is what a merge looks like to continuous integration: the bump is done, so the run only publishes it.
|
|
297
195
|
|
|
298
|
-
|
|
196
|
+
`--namespace <ns>` and `--recipe <ns/name>` narrow the run and both repeat; `--bump <level>` is `patch` (the
|
|
197
|
+
default), `minor`, `major` or `prerelease`; `--no-bump` raises nothing; `--include-unchanged` releases every
|
|
198
|
+
recipe in scope, changed or not; `--check` only reads, validating and failing when the committed index is stale;
|
|
199
|
+
and `--ci` is the merge preset: never bump, accept the plan, never ask, fail on anything unbumped. Every flag
|
|
200
|
+
this command takes, `--tag`, `--push` and `--non-interactive` among them, is in the
|
|
201
|
+
[command reference](commands.md#sous-repo-release).
|
|
299
202
|
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
203
|
+
`--bump` and `--no-bump` contradict each other, and so do `--check` and `--ci`; sous refuses the combination and
|
|
204
|
+
says which one to keep. `--ci` implies `--no-bump` and `--yes`, accepting the plan it prints rather than asking,
|
|
205
|
+
but it deliberately does not imply `--push`, so what a workflow pushes stays visible in the workflow file. Under
|
|
206
|
+
either, a recipe whose files changed without a bump is an error naming its manifest and the colliding tag.
|
|
303
207
|
|
|
304
|
-
|
|
305
|
-
the tag that has to be cut.
|
|
306
|
-
- The sibling has been published, has changed since, and sits **outside this release's scope**.
|
|
307
|
-
That is fine: the release goes ahead depending on the last published version, and warns with
|
|
308
|
-
facts you can check.
|
|
208
|
+
### The branch rule
|
|
309
209
|
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
```
|
|
210
|
+
The default branch is whatever `origin/HEAD` points at. On any other branch a release bumps and commits but cuts
|
|
211
|
+
no tags, and says why: tags are cut on the default branch, by continuous integration after the merge. Pass
|
|
212
|
+
`--tag` to cut them anyway, which is what a repository with no automation wants; a clone that was never told its
|
|
213
|
+
default branch (one with no remote) treats every branch as the default.
|
|
315
214
|
|
|
316
|
-
|
|
317
|
-
version installs what it was published with rather than re-resolving its ranges months later.
|
|
215
|
+
### The sibling rule
|
|
318
216
|
|
|
319
|
-
|
|
217
|
+
Tags are cut **dependency-first**, so a recipe is never published before something it depends on, and every
|
|
218
|
+
sibling it depends on must be a version that exists once the run's own tags are counted. A sibling **never
|
|
219
|
+
published** stops the run, naming the tag to cut. One that was published, has changed since, and sits **outside
|
|
220
|
+
this release's scope** is fine: the release depends on the last published version, and warns.
|
|
320
221
|
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
- **The tags are the backstop.** A tagged version missing from the index is rebuilt from its tag,
|
|
328
|
-
so deleting `sous.index.json` and regenerating it restores the same catalog.
|
|
222
|
+
```text
|
|
223
|
+
WARNING:
|
|
224
|
+
recipes/workflow/qa-variables/sous.recipe.yaml:
|
|
225
|
+
'workflow/qa-helper' has changes since 'workflow/qa-helper@0.1.0' that are outside this
|
|
226
|
+
release's scope; 'workflow/qa-variables@0.1.1' will depend on 'workflow/qa-helper@0.1.0'.
|
|
227
|
+
```
|
|
329
228
|
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
229
|
+
Each version's resolved dependencies are written into the index, so a consumer installing that version gets what
|
|
230
|
+
it was published with rather than ranges re-resolved months later. A published version never changes: its content
|
|
231
|
+
hash is carried forward exactly as published, and a disagreement is an error telling you to bump rather than
|
|
232
|
+
republish. A version counts as published once its tag exists, and the tags are the backstop, so a tagged version
|
|
233
|
+
missing from the index is rebuilt from it whenever the index is regenerated, which only happens on a run that
|
|
234
|
+
publishes something. A bump edits the manifest in place, so comments, field order and layout survive; a folded
|
|
235
|
+
block of YAML prose may be re-wrapped and the space before a trailing comment collapsed to one.
|
|
335
236
|
|
|
336
|
-
A
|
|
337
|
-
|
|
338
|
-
|
|
237
|
+
!> A release commits the version bumps and the index, and nothing else. It refuses to run while anything else is
|
|
238
|
+
uncommitted, because a tag names one commit and the index records what each recipe folder holds right now. It also
|
|
239
|
+
refuses when git does not know who is committing: set `git config user.name` and `git config user.email` first, or
|
|
240
|
+
give that identity to the account a continuous integration job runs as (the scaffolded workflow already does).
|
|
339
241
|
|
|
340
242
|
### The scaffolded workflow
|
|
341
243
|
|
|
342
|
-
`sous repo init` writes `.github/workflows/sous-release.yml`, which runs the same command in its
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
versions, accepts the plan it prints, and asks no questions: the version bump belongs in the
|
|
351
|
-
change being merged, so a recipe that changed without one fails here rather than being given a
|
|
352
|
-
version nobody reviewed. `--ci` implies `--yes`; the workflow passes it as well so the same
|
|
353
|
-
file works with an older sous. Both checkouts use `fetch-depth: 0`, so existing tags are
|
|
354
|
-
visible and a published version is never cut a second time.
|
|
244
|
+
`sous repo init` writes `.github/workflows/sous-release.yml`, which runs the same command in its two presets,
|
|
245
|
+
calling the CLI from npm so nothing is installed into the repository. On a **pull request** it runs
|
|
246
|
+
`npx --yes @sous-io/sous repo release --check`, which only reads, so it is safe on an untrusted branch and
|
|
247
|
+
fails the pull request when a manifest is wrong or the committed index is stale. On a **push to `main`** it
|
|
248
|
+
runs `npx --yes @sous-io/sous repo release --ci --push --yes`; edit the workflow's `branches` list if your
|
|
249
|
+
default branch has another name. `--ci` accepts the plan on its own, the redundant `--yes` keeping the file
|
|
250
|
+
working with an older published sous. Both jobs check out with `fetch-depth: 0`, so existing tags are visible
|
|
251
|
+
and no version is cut twice; the release job also takes `contents: write` and sets a git identity for the commit.
|
|
355
252
|
|
|
356
|
-
##
|
|
253
|
+
## Edit a repository in place
|
|
357
254
|
|
|
358
|
-
|
|
359
|
-
|
|
255
|
+
This is also how a recipe you have not released yet is run: link the checkout into a project and build. Edits
|
|
256
|
+
happen in a real working copy, never in the machine-wide store. `sous repo link`, run inside a **project**,
|
|
257
|
+
points that project's resolution of one repository at a checkout:
|
|
360
258
|
|
|
361
259
|
```bash
|
|
362
|
-
sous repo
|
|
363
|
-
sous repo
|
|
364
|
-
sous repo
|
|
365
|
-
sous repo
|
|
260
|
+
sous repo link ~/Projects/my-recipes # link the checkout that is already there
|
|
261
|
+
sous repo link my-recipes # clone it into .sous/repos/<owner>/<name>
|
|
262
|
+
sous repo link my-recipes ~/Projects/my-recipes # link a checkout that already exists
|
|
263
|
+
sous repo link my-recipes --global # one checkout shared by every project
|
|
366
264
|
```
|
|
367
265
|
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
it onto your account when you cannot, pushes the branch, and asks the provider to open the
|
|
376
|
-
proposal. Every one of those is the provider's own business; sous only sequences them and
|
|
377
|
-
reports what came back. A change sitting on the default branch is moved to a branch named
|
|
378
|
-
`sous/submit-<date>-<time>` first.
|
|
266
|
+
A **path on its own** links the checkout already at that path; relative paths and `~` work, nothing is cloned, and
|
|
267
|
+
the repository is added to the project first if it has not been added yet, under the short name its own manifest
|
|
268
|
+
suggests. A **repository on its own** is cloned for you, into `.sous/repos/<owner>/<name>` or
|
|
269
|
+
`$SOUS_HOME/repos/<owner>/<name>` with `--global`; a directory already holding a checkout of the same remote is
|
|
270
|
+
reused, so running it twice is harmless, and one holding a different remote is an error. A **repository followed by
|
|
271
|
+
a path** links the checkout there and clones nothing; in every form the path must hold a repo manifest at its root,
|
|
272
|
+
and a path in both slots is an error.
|
|
379
273
|
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
274
|
+
Linking also maintains the ignore files that keep machine-local sous files out of your project's history: a
|
|
275
|
+
`.sous/repos/.gitignore` holding a single `*`, and a managed block inside `.sous/.gitignore` whose marked lines
|
|
276
|
+
are the only ones ever rewritten. A link bypasses versions, the lockfile and freshness checks, and those bypasses
|
|
277
|
+
belong to one machine rather than the team, so link and every later build say so loudly:
|
|
383
278
|
|
|
384
|
-
|
|
279
|
+
```text
|
|
280
|
+
The repository 'my-recipes' is now LINKED.
|
|
281
|
+
Its recipes are read from the checkout above, so versions, the lockfile
|
|
282
|
+
and freshness checks no longer apply to it. Builds say so every time.
|
|
385
283
|
|
|
386
|
-
|
|
284
|
+
Run 'sous repo unlink my-recipes' to go back to published versions.
|
|
285
|
+
```
|
|
387
286
|
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
| GitHub | `gh` | Read from GitHub, so a contributor without it is forked automatically | `gh repo fork`, with a `fork` remote added for you | Pull request |
|
|
391
|
-
| GitLab | `glab` | Sous cannot tell, so it pushes to `origin` and says so | Not done for you; fork the project yourself and push your branch there | Merge request |
|
|
392
|
-
| Local | none | Not applicable | Not applicable | Not applicable; a repository on your own disk is edited directly |
|
|
287
|
+
`sous repo unlink` removes the map entry and nothing else: the checkout stays where it is, and its path is
|
|
288
|
+
printed so you can delete it. Unlinking a name linked in the other scope says which scope holds it.
|
|
393
289
|
|
|
394
|
-
|
|
395
|
-
through. A local repository never submits at all: it declares no submit support, so sous points
|
|
396
|
-
you at the repository's `contribute` field instead.
|
|
290
|
+
## Contribute to someone else's repository
|
|
397
291
|
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
292
|
+
`sous repo submit` proposes your committed changes to a repository's maintainers; it never publishes and never
|
|
293
|
+
writes to a repository directly. It takes `--title`, `--body`, `--draft` and `--dry-run`, and runs three stages,
|
|
294
|
+
printing each step:
|
|
295
|
+
|
|
296
|
+
1. **Preflight.** An `origin` remote exists, sous recognizes its provider, that provider's command line tool
|
|
297
|
+
(`gh` or `glab`) is installed and signed in, and everything is committed.
|
|
298
|
+
2. **Validation.** The repository validates and the committed index is current, so a proposal never fails the
|
|
299
|
+
maintainer's own checks and wastes their review.
|
|
300
|
+
3. **Delegation.** Sous asks the provider whether you can push to the repository itself, forks it onto your
|
|
301
|
+
account when you cannot, pushes the branch, and asks the provider to open the proposal. A change sitting on
|
|
302
|
+
the default branch is moved to `sous/submit-<YYYYMMDD>-<HHMM>`.
|
|
303
|
+
|
|
304
|
+
Each step is the provider's own business, and what each one can do depends on the host; see
|
|
305
|
+
[Providers](repositories-providers.md#proposing-a-change). Sous sequences the steps and reports what came back. A
|
|
306
|
+
failure partway through says which steps completed, so a pushed branch with no proposal behind it is reported
|
|
307
|
+
rather than left for you to guess at, and a provider that cannot carry out a step says what to do by hand. When it
|
|
308
|
+
cannot open a proposal at all, sous prints the `contribute` pointer from the repository's `sous.repo.yaml`; set
|
|
309
|
+
that field in your own repository so a contributor is never left without a route.
|
|
401
310
|
|
|
402
311
|
## Where to go next
|
|
403
312
|
|
|
404
313
|
- [Repository file formats](repositories-file-formats.md): every manifest and index schema
|
|
405
|
-
- [
|
|
406
|
-
machine
|
|
407
|
-
- [Skill categories](skill-categories.md): the canonical categories, and how the official
|
|
408
|
-
repository uses them as namespaces
|
|
314
|
+
- [Consuming recipes](repositories-consuming.md): the other side, from `repo add` to `build`
|
|
409
315
|
- [Command reference](commands.md): every command and flag
|
|
316
|
+
- [Providers](repositories-providers.md): what `gh` and `glab` can each do for a release and a submission
|
|
317
|
+
- [Recipe variables](repositories-variables.md): how the definitions declared here are answered at build time
|