@sous-io/sous 0.1.1 → 0.2.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/README.md +115 -35
- package/bin/run.js +10 -1
- package/docs/markdown/README.md +27 -0
- package/docs/markdown/_sidebar.md +18 -0
- package/docs/markdown/commands.md +308 -0
- package/docs/markdown/config-discovery.md +74 -0
- package/docs/markdown/config-inspection.md +69 -0
- package/docs/markdown/config-layers.md +92 -0
- package/docs/markdown/config-variables.md +79 -0
- package/docs/markdown/configuration.md +71 -0
- package/docs/markdown/design-principles.md +59 -0
- package/docs/markdown/repositories-authoring.md +408 -0
- package/docs/markdown/repositories-consuming.md +580 -0
- package/docs/markdown/repositories-file-formats.md +1084 -0
- package/docs/markdown/repositories-variables.md +387 -0
- package/docs/markdown/repositories.md +303 -0
- package/docs/markdown/skill-categories.md +58 -0
- package/package.json +72 -8
- package/{shared-prompts/skills/sous-skills → recipes/core/sous-skills/skills}/about-agent-skills/SKILL.tpl.md +20 -20
- package/{shared-prompts/skills/sous-skills → recipes/core/sous-skills/skills}/about-agent-skills/examples/about-something.md +2 -2
- package/{shared-prompts/skills/sous-skills → recipes/core/sous-skills/skills}/about-agent-skills/examples/do-something.md +1 -1
- package/{shared-prompts/skills/sous-skills → recipes/core/sous-skills/skills}/about-agent-skills/references/advanced-patterns.md +6 -6
- package/{shared-prompts/skills/sous-skills → recipes/core/sous-skills/skills}/about-agent-skills/references/commands.md +5 -5
- package/{shared-prompts/skills/sous-skills → recipes/core/sous-skills/skills}/about-agent-skills/references/frontmatter.md +3 -3
- package/{shared-prompts/skills/sous-skills → recipes/core/sous-skills/skills}/about-liquid-templates/SKILL.tpl.md +40 -25
- package/recipes/core/sous-skills/skills/about-sous/SKILL.tpl.md +70 -0
- package/recipes/core/sous-skills/skills/about-sous-configuration/SKILL.tpl.md +75 -0
- package/{shared-prompts/skills/sous-skills → recipes/core/sous-skills/skills}/create-skill/SKILL.tpl.md +8 -9
- package/recipes/core/sous-skills/sous.recipe.yaml +45 -0
- package/sous.config.schema.json +337 -0
- package/src/base-command.ts +220 -67
- package/src/commands/build.ts +150 -73
- package/src/commands/clear.ts +23 -15
- package/src/commands/compile.ts +74 -16
- package/src/commands/config/get.ts +110 -0
- package/src/commands/config/show.ts +32 -0
- package/src/commands/config/validate.ts +53 -0
- package/src/commands/help.ts +46 -0
- package/src/commands/launch.ts +36 -14
- package/src/commands/lock/rebuild.ts +241 -0
- package/src/commands/lock/show.ts +115 -0
- package/src/commands/namespace/list.ts +117 -0
- package/src/commands/namespace/show.ts +110 -0
- package/src/commands/prune.ts +3 -11
- package/src/commands/recipe/list.ts +95 -0
- package/src/commands/recipe/show.ts +301 -0
- package/src/commands/repo/add.ts +145 -0
- package/src/commands/repo/gc.ts +172 -0
- package/src/commands/repo/init.ts +136 -0
- package/src/commands/repo/link.ts +500 -0
- package/src/commands/repo/list.ts +179 -0
- package/src/commands/repo/release.ts +619 -0
- package/src/commands/repo/remove.ts +193 -0
- package/src/commands/repo/search.ts +189 -0
- package/src/commands/repo/submit.ts +133 -0
- package/src/commands/repo/unlink.ts +147 -0
- package/src/commands/subscription/add.ts +285 -0
- package/src/commands/subscription/list.ts +129 -0
- package/src/commands/subscription/remove.ts +181 -0
- package/src/commands/vars/ask.ts +374 -0
- package/src/commands/vars/index.ts +79 -0
- package/src/commands/vars/list.ts +67 -0
- package/src/commands/vars/show.ts +77 -0
- package/src/config-command.ts +30 -0
- package/src/lib/build-service.ts +206 -54
- package/src/lib/config-discovery.ts +220 -27
- package/src/lib/config-inspect.ts +145 -0
- package/src/lib/config-kernel.mjs +377 -0
- package/src/lib/config-schema.ts +361 -0
- package/src/lib/env-file.ts +328 -0
- package/src/lib/env-local.ts +18 -1
- package/src/lib/errors.ts +32 -0
- package/src/lib/include-resolver.ts +108 -15
- package/src/lib/interactive.ts +165 -0
- package/src/lib/markdown-compiler.ts +118 -37
- package/src/lib/package-info.ts +25 -0
- package/src/lib/pid-service.ts +32 -21
- package/src/lib/refs/find.ts +589 -0
- package/src/lib/refs/index.ts +12 -0
- package/src/lib/refs/pick.ts +147 -0
- package/src/lib/refs/scopes.ts +61 -0
- package/src/lib/repos/catalog-display.ts +116 -0
- package/src/lib/repos/catalog-inputs.ts +160 -0
- package/src/lib/repos/catalog.ts +722 -0
- package/src/lib/repos/core-recipe.ts +105 -0
- package/src/lib/repos/defaults.ts +175 -0
- package/src/lib/repos/formats/common.ts +389 -0
- package/src/lib/repos/formats/index-file.ts +215 -0
- package/src/lib/repos/formats/links-map.ts +96 -0
- package/src/lib/repos/formats/lockfile.ts +167 -0
- package/src/lib/repos/formats/patterns.ts +57 -0
- package/src/lib/repos/formats/recipe-manifest.ts +395 -0
- package/src/lib/repos/formats/repo-manifest.ts +88 -0
- package/src/lib/repos/formats/store-entry.ts +84 -0
- package/src/lib/repos/freshness.ts +208 -0
- package/src/lib/repos/git-clone.ts +312 -0
- package/src/lib/repos/identity.ts +89 -0
- package/src/lib/repos/index.ts +58 -0
- package/src/lib/repos/links.ts +353 -0
- package/src/lib/repos/load-manifest.ts +236 -0
- package/src/lib/repos/lock-service.ts +453 -0
- package/src/lib/repos/locked-namespace-resolver.ts +90 -0
- package/src/lib/repos/locked-recipes.ts +254 -0
- package/src/lib/repos/managed-layer.ts +422 -0
- package/src/lib/repos/namespace-resolver.ts +370 -0
- package/src/lib/repos/providers/base.ts +206 -0
- package/src/lib/repos/providers/git.ts +233 -0
- package/src/lib/repos/providers/github.ts +294 -0
- package/src/lib/repos/providers/gitlab.ts +263 -0
- package/src/lib/repos/providers/http.ts +102 -0
- package/src/lib/repos/providers/index-cache.ts +382 -0
- package/src/lib/repos/providers/index.ts +106 -0
- package/src/lib/repos/providers/local.ts +391 -0
- package/src/lib/repos/providers/provider.ts +401 -0
- package/src/lib/repos/recipe-config-layers.ts +287 -0
- package/src/lib/repos/recipe-targets.ts +223 -0
- package/src/lib/repos/ref-search.ts +46 -0
- package/src/lib/repos/ref.ts +513 -0
- package/src/lib/repos/reference-report.ts +122 -0
- package/src/lib/repos/release/bump.ts +161 -0
- package/src/lib/repos/release/git-state.ts +305 -0
- package/src/lib/repos/release/index-builder.ts +635 -0
- package/src/lib/repos/release/index.ts +16 -0
- package/src/lib/repos/release/plan.ts +512 -0
- package/src/lib/repos/release/submit-service.ts +496 -0
- package/src/lib/repos/release/tags.ts +243 -0
- package/src/lib/repos/release/validate.ts +463 -0
- package/src/lib/repos/resolver.ts +789 -0
- package/src/lib/repos/scaffold/index.ts +238 -0
- package/src/lib/repos/scaffold/templates.ts +413 -0
- package/src/lib/repos/seed.ts +414 -0
- package/src/lib/repos/store/contract.ts +64 -0
- package/src/lib/repos/store/hash.ts +114 -0
- package/src/lib/repos/store/recipe-store.ts +599 -0
- package/src/lib/repos/store/settings.ts +58 -0
- package/src/lib/repos/subscription-service.ts +2678 -0
- package/src/lib/repos/trust.ts +447 -0
- package/src/lib/settings.ts +546 -189
- package/src/lib/sous-home.ts +104 -0
- package/src/lib/state.ts +52 -20
- package/src/lib/vars/ask.ts +1152 -0
- package/src/lib/vars/definition-source.ts +252 -0
- package/src/lib/vars/display.ts +233 -0
- package/src/lib/vars/index.ts +18 -0
- package/src/lib/vars/ladder.ts +282 -0
- package/src/lib/vars/mappings.ts +265 -0
- package/src/lib/vars/names.ts +94 -0
- package/src/lib/vars/preanswers.ts +395 -0
- package/src/lib/vars/question-plan.ts +218 -0
- package/src/lib/vars/report.ts +228 -0
- package/src/lib/vars/safe-regex.ts +235 -0
- package/src/lib/vars/validate.ts +312 -0
- package/src/lib/watch-loop.ts +148 -0
- package/src/templating/init-liquid-engine.ts +58 -16
- package/src/utils/choice-prompt.ts +143 -0
- package/src/utils/command-errors.ts +186 -0
- package/src/utils/command-help.ts +45 -0
- package/src/utils/confirm-prompt.ts +110 -0
- package/src/utils/flags.ts +153 -0
- package/src/utils/formatting.ts +540 -55
- package/src/utils/prompts.ts +35 -1
- package/src/utils/sous-directory.ts +245 -0
- package/src/utils/table.ts +603 -0
- package/src/utils/value-prompt.ts +119 -0
- package/shared-prompts/_partials/resume-task.md +0 -51
- package/shared-prompts/_partials/sub-agent-delegation.md +0 -32
- package/shared-prompts/_partials/update-task-file.md +0 -52
- package/shared-prompts/memories/automated-browser-tasks/INDEX.tpl.md +0 -52
- package/shared-prompts/skills/automated-browser-tasks/about-automated-browser-tasks/SKILL.tpl.md +0 -102
- package/shared-prompts/skills/automated-browser-tasks/about-automated-browser-tasks/examples/auth-failure-handling.mjs +0 -81
- package/shared-prompts/skills/automated-browser-tasks/about-automated-browser-tasks/examples/chained-workflow.mjs +0 -126
- package/shared-prompts/skills/automated-browser-tasks/about-automated-browser-tasks/examples/simple-fetch.mjs +0 -92
- package/shared-prompts/skills/automated-browser-tasks/about-automated-browser-tasks/references/architecture.md +0 -61
- package/shared-prompts/skills/automated-browser-tasks/about-automated-browser-tasks/references/auth-and-sessions.md +0 -65
- package/shared-prompts/skills/automated-browser-tasks/about-automated-browser-tasks/references/ctx-api.md +0 -96
- package/shared-prompts/skills/automated-browser-tasks/about-automated-browser-tasks/references/installation.md +0 -104
- package/shared-prompts/skills/automated-browser-tasks/about-automated-browser-tasks/references/script-conventions.md +0 -243
- package/shared-prompts/skills/automated-browser-tasks/about-automated-browser-tasks/scripts/chrome-state.mjs +0 -148
- package/shared-prompts/skills/automated-browser-tasks/about-automated-browser-tasks/scripts/debug.mjs +0 -383
- package/shared-prompts/skills/automated-browser-tasks/about-automated-browser-tasks/scripts/debug.spec.mjs +0 -267
- package/shared-prompts/skills/automated-browser-tasks/about-automated-browser-tasks/scripts/eslint.config.mjs +0 -56
- package/shared-prompts/skills/automated-browser-tasks/about-automated-browser-tasks/scripts/harness.mjs +0 -169
- package/shared-prompts/skills/automated-browser-tasks/about-automated-browser-tasks/scripts/keyring.mjs +0 -59
- package/shared-prompts/skills/automated-browser-tasks/about-automated-browser-tasks/scripts/logger.mjs +0 -25
- package/shared-prompts/skills/automated-browser-tasks/about-automated-browser-tasks/scripts/params.mjs +0 -140
- package/shared-prompts/skills/automated-browser-tasks/about-automated-browser-tasks/scripts/run.mjs +0 -140
- package/shared-prompts/skills/automated-browser-tasks/about-automated-browser-tasks/scripts/settings.tpl.mjs +0 -1
- package/shared-prompts/skills/automated-browser-tasks/about-automated-browser-tasks/scripts/utils.mjs +0 -185
- package/shared-prompts/skills/automated-browser-tasks/create-automated-browser-task/SKILL.tpl.md +0 -52
- package/shared-prompts/skills/automated-browser-tasks/running-automated-browser-tasks/SKILL.tpl.md +0 -59
- package/shared-prompts/skills/automated-browser-tasks/update-automated-browser-task/SKILL.tpl.md +0 -47
- package/shared-prompts/skills/control-flow/approve/SKILL.tpl.md +0 -26
- package/shared-prompts/skills/control-flow/opine/SKILL.tpl.md +0 -58
- package/shared-prompts/skills/control-flow/repeat/SKILL.tpl.md +0 -27
- package/shared-prompts/skills/control-flow/research/SKILL.tpl.md +0 -34
- package/shared-prompts/skills/sous-skills/about-sous/SKILL.tpl.md +0 -51
- package/shared-prompts/skills/task-files/about-task-files/SKILL.tpl.md +0 -122
- package/shared-prompts/skills/task-files/continue-task-in-new-branch/SKILL.tpl.md +0 -80
- package/shared-prompts/skills/task-files/go/SKILL.tpl.md +0 -14
- package/shared-prompts/skills/task-files/resume-task/SKILL.tpl.md +0 -13
- package/shared-prompts/skills/task-files/start-task/SKILL.tpl.md +0 -93
- package/shared-prompts/skills/task-files/update/SKILL.tpl.md +0 -14
- package/shared-prompts/skills/task-files/update-task-file/SKILL.tpl.md +0 -13
- /package/{shared-prompts/skills/sous-skills → recipes/core/sous-skills/skills}/about-agent-skills/references/substitutions.md +0 -0
- /package/{shared-prompts/skills/sous-skills → recipes/core/sous-skills/skills}/about-liquid-templates/references/liquid-filters.md +0 -0
|
@@ -0,0 +1,408 @@
|
|
|
1
|
+
# Authoring a Repository
|
|
2
|
+
|
|
3
|
+
This is the guide to publishing recipes of your own: creating a repository, writing a recipe,
|
|
4
|
+
editing one that is already published, cutting a release, and proposing a change to someone
|
|
5
|
+
else's repository. [Repository file formats](repositories-file-formats.md) holds every schema
|
|
6
|
+
these commands read and write; this page is about the workflow.
|
|
7
|
+
|
|
8
|
+
?> A recipe repository is not a sous project. It has no `.sous/` directory, and `sous repo init`,
|
|
9
|
+
`sous repo release` and `sous repo submit` do not look for one. Run them from inside the
|
|
10
|
+
repository itself.
|
|
11
|
+
|
|
12
|
+
## Create a repository
|
|
13
|
+
|
|
14
|
+
```term
|
|
15
|
+
$ sous repo init ./my-recipes --name my-recipes --namespace workflow
|
|
16
|
+
wrote sous.repo.yaml
|
|
17
|
+
wrote sous.index.json
|
|
18
|
+
wrote recipes/workflow/example/sous.recipe.yaml
|
|
19
|
+
wrote recipes/workflow/example/skills/example-skill/SKILL.md
|
|
20
|
+
wrote README.md
|
|
21
|
+
wrote .github/workflows/sous-release.yml
|
|
22
|
+
wrote .gitignore
|
|
23
|
+
```
|
|
24
|
+
|
|
25
|
+
| Flag | What it does |
|
|
26
|
+
|------|--------------|
|
|
27
|
+
| `--name <name>` | Short name for the repository. Defaults to the directory's own name |
|
|
28
|
+
| `--namespace <name>` | The one namespace to declare. Defaults to the repository's name |
|
|
29
|
+
| `--force` | Write the scaffold over a repository that already exists |
|
|
30
|
+
| `--dry-run` | Print the files that would be written without writing them |
|
|
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
|
+
```
|
|
58
|
+
|
|
59
|
+
Nothing in that tree is fixed except the two manifest filenames and the index at the root.
|
|
60
|
+
Recipe folders may live anywhere; what makes a folder a recipe is that `sous.repo.yaml` lists its
|
|
61
|
+
path under `recipes`, and that the folder holds exactly one recipe manifest.
|
|
62
|
+
|
|
63
|
+
Both hand-written manifests are YAML or JSON and never JavaScript. A repository's whole trust
|
|
64
|
+
story rests on being readable without running any of its code, and a manifest that could execute
|
|
65
|
+
would break that guarantee.
|
|
66
|
+
|
|
67
|
+
## Write a recipe
|
|
68
|
+
|
|
69
|
+
Copy the example folder, edit its manifest, and add the new path to the `recipes` list in
|
|
70
|
+
`sous.repo.yaml`. A minimal recipe:
|
|
71
|
+
|
|
72
|
+
```yaml
|
|
73
|
+
formatVersion: 1
|
|
74
|
+
namespace: workflow
|
|
75
|
+
name: task-files
|
|
76
|
+
version: 0.1.0
|
|
77
|
+
description: >-
|
|
78
|
+
Per-branch task files, with skills for starting and resuming work.
|
|
79
|
+
|
|
80
|
+
contents:
|
|
81
|
+
- kind: skills
|
|
82
|
+
include:
|
|
83
|
+
- skills/**/*.md
|
|
84
|
+
```
|
|
85
|
+
|
|
86
|
+
`contents` groups are what a subscriber's project actually receives, one group per kind
|
|
87
|
+
(`skills`, `memories`, `prompts` or `config`), each with `include` glob patterns relative to the
|
|
88
|
+
recipe folder. Two more optional lists say what else the recipe needs: `depends` for build
|
|
89
|
+
dependencies whose files stay out of a subscriber's output, and `subscribes` for co-subscriptions
|
|
90
|
+
whose files go in. Full field tables are in
|
|
91
|
+
[`sous.recipe.yaml`](repositories-file-formats.md#sousrecipeyaml-the-recipe-manifest).
|
|
92
|
+
|
|
93
|
+
Recipe metadata is the source of truth for the version. Never edit `sous.index.json` by hand;
|
|
94
|
+
`sous repo release` regenerates it.
|
|
95
|
+
|
|
96
|
+
## Declare the variables a recipe needs
|
|
97
|
+
|
|
98
|
+
A recipe that needs a value from the project asks for it through a **definition**: a published
|
|
99
|
+
specification, never a value. Sous asks the question only when a subscribed recipe needs the
|
|
100
|
+
variable and no valid answer is already in scope.
|
|
101
|
+
|
|
102
|
+
```yaml
|
|
103
|
+
variables:
|
|
104
|
+
- name: taskFileRoot
|
|
105
|
+
type: path
|
|
106
|
+
prompt: Where should task files be stored?
|
|
107
|
+
description: >-
|
|
108
|
+
This recipe mandates the creation of task files that are stored locally
|
|
109
|
+
and, in general, should not be committed. This setting dictates the path in
|
|
110
|
+
which agents will store and search for your task files. The default value
|
|
111
|
+
stores task files in the project's .sous directory, but you can specify any
|
|
112
|
+
local path, either relative to the project root or absolute.
|
|
113
|
+
example: ~/my-task-files
|
|
114
|
+
default: .sous/tasks
|
|
115
|
+
required: true
|
|
116
|
+
scope: shared
|
|
117
|
+
|
|
118
|
+
- name: serviceToken
|
|
119
|
+
type: string
|
|
120
|
+
env: SERVICE_TOKEN
|
|
121
|
+
prompt: What is this project's service token?
|
|
122
|
+
description: >-
|
|
123
|
+
This recipe authenticates every call it makes with a service token, which
|
|
124
|
+
is issued per project and is not shared between them. Create one under
|
|
125
|
+
Settings, then Tokens, and give it read access to the project you are
|
|
126
|
+
configuring. There is no default; a token is always specific to you, and it
|
|
127
|
+
is stored in the gitignored env file so it never reaches git.
|
|
128
|
+
example: svc_0123456789abcdef0123
|
|
129
|
+
secret: true
|
|
130
|
+
scope: local
|
|
131
|
+
validate:
|
|
132
|
+
minLength: 20
|
|
133
|
+
```
|
|
134
|
+
|
|
135
|
+
The rules worth knowing while you write one:
|
|
136
|
+
|
|
137
|
+
- **`name` is camelCase**, and it is how templates refer to the variable.
|
|
138
|
+
- **`description` and `example` are both required.** The one-line `prompt` is rarely enough on its
|
|
139
|
+
own, and the person answering it cannot read your mind. The description is the paragraph shown
|
|
140
|
+
above the question and by `sous vars show <name>`; the example is a realistic sample answer,
|
|
141
|
+
shown with the question.
|
|
142
|
+
- **A description explains, a prompt asks.** Write the description in full sentences, and cover
|
|
143
|
+
three things: what the setting is for, what the default does, and what else is acceptable. Write
|
|
144
|
+
the prompt as one plain question and nothing else. The pair above is the model:
|
|
145
|
+
"This recipe mandates the creation of task files that are stored locally and, in general, should
|
|
146
|
+
not be committed. This setting dictates the path in which agents will store and search for your
|
|
147
|
+
task files. The default value stores task files in the project's .sous directory, but you can
|
|
148
|
+
specify any local path, either relative to the project root or absolute." asked as
|
|
149
|
+
"Where should task files be stored?". A description that only restates the prompt, or a prompt
|
|
150
|
+
that tries to carry the explanation, both make the question harder to answer.
|
|
151
|
+
- **An example is documentation, a default is a value.** Sous never stores an example and never
|
|
152
|
+
offers it as the answer; it only ever shows it. Use `default` for a value a project should
|
|
153
|
+
actually start with. The same text may appear in both when the sample answer really is the right
|
|
154
|
+
starting value.
|
|
155
|
+
- **`env` names the environment variable** an answer binds to. Omit it and `sous repo release`
|
|
156
|
+
derives one from the name: `apiUrl` becomes `SOUS_VAR_API_URL`. Naming it explicitly is how a
|
|
157
|
+
recipe reuses a value the environment already carries, such as `GITHUB_TOKEN`.
|
|
158
|
+
- **`secret: true`** stores the answer in the gitignored `.sous/.env.local` and hides the value
|
|
159
|
+
everywhere sous prints it.
|
|
160
|
+
- **`scope`** picks the file the answer is written to: `shared` for the committed `.sous/.env`,
|
|
161
|
+
`local` for the gitignored `.sous/.env.local`. A secret declared as `shared` is rejected,
|
|
162
|
+
because that combination would commit the secret.
|
|
163
|
+
- **`validate.pattern` runs under a time budget.** Sous runs a published pattern on a worker and
|
|
164
|
+
stops waiting after a fixed budget, so a pattern that backtracks forever cannot hang the person
|
|
165
|
+
answering; it fails validation instead, and the message names your pattern. Keep patterns simple.
|
|
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.
|
|
205
|
+
|
|
206
|
+
A **repository followed by a path** links the checkout at that path to that repository, and
|
|
207
|
+
clones nothing.
|
|
208
|
+
|
|
209
|
+
In every form the path must hold a repo manifest at its root, and a path in both slots is an
|
|
210
|
+
error: the first one already says which checkout to link.
|
|
211
|
+
|
|
212
|
+
Linking also maintains the two ignore files that keep machine-local sous files out of your
|
|
213
|
+
project's history: a `.sous/repos/.gitignore` holding a single `*`, and a delimited managed block
|
|
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.
|
|
226
|
+
```
|
|
227
|
+
|
|
228
|
+
`sous repo unlink` removes the map entry and nothing else. The checkout stays exactly where it
|
|
229
|
+
is, and its path is printed so you can delete it yourself if you want to. Getting the scope wrong
|
|
230
|
+
is the easy mistake here, so unlinking a name that is linked in the other scope tells you which
|
|
231
|
+
scope holds it and which flag removes it.
|
|
232
|
+
|
|
233
|
+
## Release
|
|
234
|
+
|
|
235
|
+
`sous repo release` publishes new versions of this repository's recipes. One run does the whole
|
|
236
|
+
job: it raises versions, regenerates `sous.index.json`, commits both, and cuts the tags that
|
|
237
|
+
publish them. Run it from inside the repository, with your recipe changes already committed.
|
|
238
|
+
|
|
239
|
+
```term
|
|
240
|
+
$ sous repo release
|
|
241
|
+
The release this would make:
|
|
242
|
+
workflow/task-files : 1.1.0 becomes 1.1.1 (a patch step; the tag would be workflow/task-files@1.1.1)
|
|
243
|
+
|
|
244
|
+
Left alone:
|
|
245
|
+
workflow/sat: its files have not changed since workflow/sat@1.4.0.
|
|
246
|
+
|
|
247
|
+
This run would:
|
|
248
|
+
Raise the versions listed above, in the manifests that declare them.
|
|
249
|
+
Regenerate sous.index.json, with each version's dependencies resolved.
|
|
250
|
+
Commit the manifests and the index together.
|
|
251
|
+
Cut one annotated tag per version, dependency-first.
|
|
252
|
+
Push nothing; pass '--push' to push what it makes.
|
|
253
|
+
|
|
254
|
+
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
|
+
|
|
261
|
+
The plan is always printed first, and a run asks once before it changes anything. `--yes` answers
|
|
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, never ask, fail on anything unbumped |
|
|
291
|
+
|
|
292
|
+
### The branch rule
|
|
293
|
+
|
|
294
|
+
On a branch other than the default one, a release bumps and commits but cuts no tags, and says
|
|
295
|
+
why: tags are cut on the default branch, by continuous integration after the merge. Pass `--tag`
|
|
296
|
+
to cut them anyway, which is what a repository with no automation wants.
|
|
297
|
+
|
|
298
|
+
### Dependencies and the sibling rule
|
|
299
|
+
|
|
300
|
+
Tags are cut **dependency-first**, so a recipe is never published before something it depends on.
|
|
301
|
+
Everything a released recipe depends on inside this repository has to be a version that exists
|
|
302
|
+
once the run's own tags are counted, and there are exactly two ways that fails:
|
|
303
|
+
|
|
304
|
+
- The sibling has **never been published**. Nothing can depend on it, so the run stops and names
|
|
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.
|
|
309
|
+
|
|
310
|
+
```term
|
|
311
|
+
recipes/workflow/task-files/sous.recipe.yaml:
|
|
312
|
+
'workflow/sat' has changes since 'workflow/sat@1.4.0' that are outside this release's scope;
|
|
313
|
+
'workflow/task-files@1.1.1' will depend on 'workflow/sat@1.4.0'.
|
|
314
|
+
```
|
|
315
|
+
|
|
316
|
+
Each version's resolved dependencies are written into the index, so a consumer installing that
|
|
317
|
+
version installs what it was published with rather than re-resolving its ranges months later.
|
|
318
|
+
|
|
319
|
+
### Three rules that keep metadata, tags and the index in step
|
|
320
|
+
|
|
321
|
+
- **A published version never changes.** Its content hash is carried forward exactly as
|
|
322
|
+
published, and a disagreement is an error telling you to bump the version rather than
|
|
323
|
+
republish it.
|
|
324
|
+
- **A version is published when its tag exists.** The one moment an index records a version
|
|
325
|
+
without a tag is the release commit itself: the index is committed and the tag is cut on that
|
|
326
|
+
commit. Any older version missing its tag is an error.
|
|
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.
|
|
329
|
+
|
|
330
|
+
!> A release commits the version bumps and the index, and nothing else. It refuses to run while
|
|
331
|
+
anything else is uncommitted, because a tag names one commit and the index it writes records
|
|
332
|
+
what each recipe folder holds right now. It also refuses, before writing anything, when git has
|
|
333
|
+
no author identity to commit under; set `user.name` and `user.email` in the repository, which the
|
|
334
|
+
scaffolded workflow does for you.
|
|
335
|
+
|
|
336
|
+
A bump edits the manifest in place, so its comments, its field order and its layout all survive.
|
|
337
|
+
Two small normalizations happen in a YAML manifest: a folded block of prose may be re-wrapped,
|
|
338
|
+
and the spacing before a trailing comment is collapsed to one space.
|
|
339
|
+
|
|
340
|
+
### The scaffolded workflow
|
|
341
|
+
|
|
342
|
+
`sous repo init` writes `.github/workflows/sous-release.yml`, which runs the same command in its
|
|
343
|
+
two presets. It calls the sous CLI straight from npm, so nothing has to be installed into the
|
|
344
|
+
repository:
|
|
345
|
+
|
|
346
|
+
- On a **pull request**, `sous repo release --check`. It only reads, so it is safe on an
|
|
347
|
+
untrusted branch, and it fails the pull request when a manifest is wrong or the committed index
|
|
348
|
+
(including the dependencies it records) is stale.
|
|
349
|
+
- On a **push to the default branch**, `sous repo release --ci --push`. `--ci` raises no versions
|
|
350
|
+
and asks no questions: the version bump belongs in the change being merged, so a recipe that
|
|
351
|
+
changed without one fails here rather than being given a version nobody reviewed. Both
|
|
352
|
+
checkouts use `fetch-depth: 0`, so existing tags are visible and a published version is never
|
|
353
|
+
cut a second time.
|
|
354
|
+
|
|
355
|
+
## Contribute to someone else's repository
|
|
356
|
+
|
|
357
|
+
`sous repo submit` proposes your committed changes to a repository's maintainers. It never
|
|
358
|
+
publishes and never writes to a repository directly.
|
|
359
|
+
|
|
360
|
+
```bash
|
|
361
|
+
sous repo submit
|
|
362
|
+
sous repo submit --title "Add a linting recipe"
|
|
363
|
+
sous repo submit --draft
|
|
364
|
+
sous repo submit --dry-run
|
|
365
|
+
```
|
|
366
|
+
|
|
367
|
+
It runs in three stages, printing each step before it runs:
|
|
368
|
+
|
|
369
|
+
1. **Preflight.** An `origin` remote exists, sous recognizes its provider, that provider's
|
|
370
|
+
command line tool (`gh` or `glab`) is installed and signed in, and everything is committed.
|
|
371
|
+
2. **Validation.** The repository validates and the committed index is current, so a proposal
|
|
372
|
+
never fails the maintainer's own checks and wastes their review.
|
|
373
|
+
3. **Delegation.** Sous asks the provider whether you can push to the repository itself, forks
|
|
374
|
+
it onto your account when you cannot, pushes the branch, and asks the provider to open the
|
|
375
|
+
proposal. Every one of those is the provider's own business; sous only sequences them and
|
|
376
|
+
reports what came back. A change sitting on the default branch is moved to a branch named
|
|
377
|
+
`sous/submit-<date>-<time>` first.
|
|
378
|
+
|
|
379
|
+
`--title` defaults to your last commit's subject and `--body` to a summary sous writes. A failure
|
|
380
|
+
partway through says exactly which steps completed: a pushed branch with no proposal behind it is
|
|
381
|
+
a normal outcome of a network failure, and you are told about it rather than left guessing.
|
|
382
|
+
|
|
383
|
+
### What each provider supports
|
|
384
|
+
|
|
385
|
+
Providers differ, and sous says so rather than pretending otherwise:
|
|
386
|
+
|
|
387
|
+
| Provider | Command line tool | Push permission | Forking | Proposal |
|
|
388
|
+
|---|---|---|---|---|
|
|
389
|
+
| 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 |
|
|
390
|
+
| 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 |
|
|
391
|
+
| Local | none | Not applicable | Not applicable | Not applicable; a repository on your own disk is edited directly |
|
|
392
|
+
|
|
393
|
+
When a provider cannot carry out a step, it says what to do by hand instead of stopping halfway
|
|
394
|
+
through. A local repository never submits at all: it declares no submit support, so sous points
|
|
395
|
+
you at the repository's `contribute` field instead.
|
|
396
|
+
|
|
397
|
+
?> When a repository's provider cannot open a proposal for you, sous prints the `contribute`
|
|
398
|
+
pointer from its `sous.repo.yaml` instead, so you are never left without a route. Set that field
|
|
399
|
+
in your own repository for the same reason.
|
|
400
|
+
|
|
401
|
+
## Where to go next
|
|
402
|
+
|
|
403
|
+
- [Repository file formats](repositories-file-formats.md): every manifest and index schema
|
|
404
|
+
- [Recipe variables](repositories-variables.md): what a definition turns into on a subscriber's
|
|
405
|
+
machine
|
|
406
|
+
- [Skill categories](skill-categories.md): the canonical categories, and how the official
|
|
407
|
+
repository uses them as namespaces
|
|
408
|
+
- [Command reference](commands.md): every command and flag
|