@sous-io/sous 0.2.0 → 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 -301
- 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
- package/src/commands/repo/release.ts +14 -8
- package/src/lib/repos/scaffold/templates.ts +9 -7
|
@@ -1,387 +1,320 @@
|
|
|
1
1
|
# Recipe Variables
|
|
2
2
|
|
|
3
|
-
A recipe that needs a value from your project publishes a **variable definition**: a
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
variable and nothing in scope answers it, or when the answer in scope no longer fits.
|
|
3
|
+
A recipe that needs a value from your project publishes a **variable definition**: a name, a type, a question
|
|
4
|
+
and a set of constraints. You supply an **answer**. A "question" is only the interactive moment: sous asks one
|
|
5
|
+
when a recipe needs a variable and nothing in scope answers it, or when what is there no longer fits.
|
|
7
6
|
|
|
8
|
-
?> This page is about variables that recipes publish. Your project's own `${var}` configuration
|
|
9
|
-
|
|
10
|
-
[
|
|
7
|
+
?> This page is about variables that recipes publish. Your project's own `${var}` configuration variables are
|
|
8
|
+
a separate, ceremony-free system; see [Variables](config-variables.md). The two meet in one place only, under
|
|
9
|
+
[How a template reads an answer](#how-a-template-reads-an-answer).
|
|
11
10
|
|
|
12
|
-
##
|
|
11
|
+
## What a definition is, and where it lives
|
|
13
12
|
|
|
14
|
-
|
|
13
|
+
A definition ships inside the recipe that needs it, in its manifest's `variables:` array, so it versions,
|
|
14
|
+
resolves, pins and trusts like the rest of the recipe; every field it can carry is listed in
|
|
15
|
+
[Variable definitions](repositories-file-formats.md#variable-definitions).
|
|
15
16
|
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
| `.sous/.env.local` | no, gitignored | Machine-specific answers (`scope: local`) and every secret |
|
|
20
|
-
|
|
21
|
-
Sous edits these files the way a careful person would. Exactly one value line is rewritten or
|
|
22
|
-
appended; comments, blank lines, ordering and quoting all survive untouched. A newly added entry
|
|
23
|
-
gets a short generated header comment above it saying where the value came from:
|
|
24
|
-
|
|
25
|
-
```bash
|
|
26
|
-
# Set by sous for workflow/task-files: Where should task files live?
|
|
27
|
-
# One file per git branch is written here.
|
|
28
|
-
# Edit freely; sous only rewrites the value line.
|
|
29
|
-
SOUS_VAR_TASK_FILE_ROOT=.sous/tasks
|
|
30
|
-
```
|
|
17
|
+
A definition is in play when your lockfile pins the recipe that declares it, whether you subscribed to that
|
|
18
|
+
recipe or a dependency pulled it in. A recipe the store does not hold yet contributes nothing rather than
|
|
19
|
+
failing, so a fresh clone lists what it can until `sous build` restores the rest.
|
|
31
20
|
|
|
32
|
-
|
|
33
|
-
|
|
21
|
+
A project can also ask questions no recipe publishes: write the same `variables:` array into a YAML or JSON
|
|
22
|
+
file of your own and point `--file` at it. Every `sous vars` command takes that flag, and the definitions in
|
|
23
|
+
it are attributed to a pseudo-recipe in the `local` namespace named after the file, so an `apiUrl` declared in
|
|
24
|
+
`questions.yaml` generates `SOUS_VAR_LOCAL_QUESTIONS_API_URL`, `SOUS_VAR_LOCAL_API_URL` and `SOUS_VAR_API_URL`.
|
|
34
25
|
|
|
35
|
-
|
|
26
|
+
!> A definition's `validate.pattern` is a regular expression published by someone else, so sous runs each
|
|
27
|
+
match on a worker under a fixed budget of 100 milliseconds, and a pattern that exceeds it fails validation
|
|
28
|
+
naming the pattern and the recipe rather than blaming your answer. See
|
|
29
|
+
[A variable pattern that runs out of time](repositories-troubleshooting.md#a-variable-pattern-that-runs-out-of-time).
|
|
36
30
|
|
|
37
|
-
|
|
38
|
-
most specific first. The first name that holds a value wins.
|
|
31
|
+
## Answer the questions
|
|
39
32
|
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
| 1. mapping record | whatever the record names | `TEAM_API_URL` | Only when a record exists for this variable |
|
|
43
|
-
| 2. recipe scope | `SOUS_VAR_<NAMESPACE>_<RECIPE>_<VARIABLE>` | `SOUS_VAR_WORKFLOW_TASK_FILES_API_URL` | Answers this one recipe's variable and nothing else |
|
|
44
|
-
| 3. namespace scope | `SOUS_VAR_<NAMESPACE>_<VARIABLE>` | `SOUS_VAR_WORKFLOW_API_URL` | Answers every recipe in the namespace at once |
|
|
45
|
-
| 4. shared scope | `SOUS_VAR_<VARIABLE>` | `SOUS_VAR_API_URL` | Answers every recipe that declares that variable name |
|
|
46
|
-
| 5. declared name | the definition's own `env` field | `GITHUB_TOKEN` | How a recipe binds a value the environment already carries |
|
|
47
|
-
|
|
48
|
-
Read from the bottom up, the ladder is a story about sharing. One `SOUS_VAR_API_URL` answers
|
|
49
|
-
every recipe that wants an `apiUrl`, which is what you want most of the time. When two recipes
|
|
50
|
-
want the same name and mean different things, move one answer up a rung to the namespace or the
|
|
51
|
-
recipe form, and the more specific name wins for that recipe alone. When even that is not enough,
|
|
52
|
-
a mapping record settles it.
|
|
33
|
+
Subscribing asks whatever is unanswered, so in normal use you rarely run `sous vars ask` yourself; reach for
|
|
34
|
+
it after editing an env file by hand, after an upgrade tightened a constraint, or to re-answer with `--all`.
|
|
53
35
|
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
five candidates it knows are correct and asks the environment about each one.
|
|
36
|
+
Every trust decision is settled first: a subscribe resolves the dependency closure and runs the trust
|
|
37
|
+
ceremony for each new repository before the first question prints. Questions then run one recipe at a time,
|
|
38
|
+
the one you asked for and then each recipe it depends on, with a lead-in and per-recipe counts:
|
|
58
39
|
|
|
59
|
-
|
|
40
|
+
```text
|
|
41
|
+
workflow/task-files needs 4 answers, and workflow/sub-agent-delegation, which it depends on, needs 2.
|
|
60
42
|
|
|
61
|
-
|
|
43
|
+
workflow/task-files needs 4 answers before it can be used.
|
|
44
|
+
```
|
|
62
45
|
|
|
63
|
-
|
|
64
|
-
2. **`.sous/.env.local`.** Gitignored; your machine, your secrets.
|
|
65
|
-
3. **`.sous/.env`.** Committed; the team's shared defaults.
|
|
46
|
+
Each question prints its header, the publisher's description wrapped to your terminal, and four labeled facts:
|
|
66
47
|
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
[Discovery and overrides](config-discovery.md).
|
|
48
|
+
```text
|
|
49
|
+
Question 1 of 4: taskFileRoot
|
|
70
50
|
|
|
71
|
-
|
|
51
|
+
This recipe mandates the creation of task files that are stored locally and, in
|
|
52
|
+
general, should not be committed. The default stores them in the project's .sous
|
|
53
|
+
directory; any local path works, relative to the project root or absolute.
|
|
72
54
|
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
55
|
+
default : .sous/tasks
|
|
56
|
+
example : ~/my-task-files
|
|
57
|
+
stored-as : SOUS_VAR_TASK_FILE_ROOT
|
|
58
|
+
storage-path: /home/you/project/.sous/.env
|
|
76
59
|
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
"varMappings": {
|
|
80
|
-
"TEAM_API_URL": "sous-recipes:misc/stuff/apiUrl"
|
|
81
|
-
}
|
|
82
|
-
}
|
|
60
|
+
? Where should task files be stored? (.sous/tasks):
|
|
61
|
+
⏎ accept default • ⇥ advanced
|
|
83
62
|
```
|
|
84
63
|
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
Sous writes the ones it creates into `.sous/conf.d/520-var-mappings.jsonc`, editing one record at
|
|
88
|
-
a time so each name has exactly one, and you may hand-write `varMappings` in your primary config
|
|
89
|
-
too; the two merge like any other config layer.
|
|
64
|
+
The legend under the input line names the keys that do anything: Enter accepts what is typed, or the default
|
|
65
|
+
when there is one; a list question names the arrow keys instead, and a yes-or-no question the two letters.
|
|
90
66
|
|
|
91
|
-
|
|
92
|
-
the name an answer would use already holds a value that does not fit the definition, you are
|
|
93
|
-
asked where the answer should go:
|
|
67
|
+
### Tab opens the advanced view
|
|
94
68
|
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
SOUS_VAR_TOOLING_DEPLOY_SERVICE_TOKEN, with a mapping record (recommended)
|
|
98
|
-
SERVICE_TOKEN, replacing what is there
|
|
99
|
-
```
|
|
69
|
+
Tab works at every kind of question. The advanced view repeats the header and description, prints every fact
|
|
70
|
+
about the variable through the renderer `sous vars show` uses, and offers a menu:
|
|
100
71
|
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
a value something else is already using would be the worse of the two guesses.
|
|
72
|
+
```text
|
|
73
|
+
[Advanced Variable Settings]
|
|
104
74
|
|
|
105
|
-
|
|
75
|
+
Question 1 of 4: taskFileRoot
|
|
106
76
|
|
|
107
|
-
|
|
108
|
-
|
|
77
|
+
default : .sous/tasks
|
|
78
|
+
example : ~/my-task-files
|
|
79
|
+
required-by : workflow/task-files https://example.com/owner/recipes/workflow/task-files
|
|
80
|
+
defined-by : workflow/task-files https://example.com/owner/recipes/workflow/task-files
|
|
81
|
+
storage-path: /home/you/project/.sous/.env
|
|
82
|
+
stored-as : SOUS_VAR_TASK_FILE_ROOT
|
|
83
|
+
constraints : • must be a value of the type path (type: path)
|
|
109
84
|
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
taskFileRoot workflow/task-files SOUS_VAR_TASK_... .sous/tasks shared scope, the .env file
|
|
115
|
-
serviceToken tooling/deploy nothing yet
|
|
85
|
+
? What would you like to do?
|
|
86
|
+
Return to value entry
|
|
87
|
+
Change the storage file
|
|
88
|
+
Change the stored variable name
|
|
116
89
|
```
|
|
117
90
|
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
91
|
+
`required-by` names the recipe whose closure pulled this variable in, and spells out the chain when it
|
|
92
|
+
arrived through a dependency; `defined-by` names the recipe that declares it. Both carry the location beside
|
|
93
|
+
the recipe key, in muted grey: a repository URL, or a filesystem path for a repository on this machine.
|
|
121
94
|
|
|
122
|
-
|
|
95
|
+
**Changing the stored variable name** offers every rung the ladder looks up, sous's choice preselected, plus
|
|
96
|
+
a name you type; one the ladder never looks at is bound with a [mapping record](#when-names-collide-mapping-records).
|
|
123
97
|
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
actually answered.
|
|
98
|
+
**Changing the storage file** offers the committed `.sous/.env` and the gitignored `.sous/.env.local`.
|
|
99
|
+
Pointing a secret, or a variable the publisher declared machine-specific, at the committed file is allowed;
|
|
100
|
+
sous says what that means and asks you to confirm, which is informed consent rather than a locked door:
|
|
128
101
|
|
|
129
|
-
```
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
the API, and this setting says which one. The default points at
|
|
134
|
-
the public production host, but any deployment you can reach works.
|
|
135
|
-
For example: https://api.example.com
|
|
136
|
-
Recipe : workflow/task-files version 1.2.0 from sous-recipes
|
|
137
|
-
Stored in : .env
|
|
138
|
-
Value : https://api.example.com
|
|
139
|
-
|
|
140
|
-
example : https://api.example.com
|
|
141
|
-
required-by : workflow/task-files https://example.com/owner/recipes/workflow/task-files
|
|
142
|
-
defined-by : workflow/task-files https://example.com/owner/recipes/workflow/task-files
|
|
143
|
-
storage-path: /home/you/project/.sous/.env
|
|
144
|
-
stored-as : SOUS_VAR_API_URL
|
|
145
|
-
constraints : • must be a value of the type url (type: url)
|
|
102
|
+
```text
|
|
103
|
+
workflow/task-files declared this variable a secret, and .env is committed to git. An answer stored
|
|
104
|
+
there enters your project's git history, is pushed with every clone, and is visible to everyone who
|
|
105
|
+
can read the repository.
|
|
146
106
|
|
|
147
|
-
|
|
148
|
-
SOUS_VAR_WORKFLOW_TASK_FILES_API_URL recipe scope not set
|
|
149
|
-
SOUS_VAR_WORKFLOW_API_URL namespace scope not set
|
|
150
|
-
SOUS_VAR_API_URL shared scope answered it, from the .env file
|
|
107
|
+
? Store this answer in .env anyway? (y/N)
|
|
151
108
|
```
|
|
152
109
|
|
|
153
|
-
|
|
154
|
-
|
|
110
|
+
Once anything has changed, the first menu item becomes "Save changes and return to value entry" and a
|
|
111
|
+
"Discard changes and return to value entry" item joins it; returning prints the question again with the
|
|
112
|
+
updated facts. When an answer is entered, two lines say what was stored and where; a secret is never printed:
|
|
155
113
|
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
because each of those is a subcommand. A variable with one of those names is reached the long
|
|
161
|
-
way, as `sous vars show ask`.
|
|
114
|
+
```text
|
|
115
|
+
Answer : SOUS_VAR_TASK_FILE_ROOT=.sous/tasks
|
|
116
|
+
Saved to: /home/you/project/.sous/.env
|
|
117
|
+
```
|
|
162
118
|
|
|
163
|
-
|
|
119
|
+
Every run ends with a three-part report: `Answers already in scope:`, `Answers stored:`, `Left unanswered:`.
|
|
164
120
|
|
|
165
|
-
|
|
121
|
+
## Where answers are stored
|
|
166
122
|
|
|
167
|
-
|
|
168
|
-
|------------|--------------|
|
|
169
|
-
| `sous vars ask` | Asks only what is unanswered, or what no longer fits its definition |
|
|
170
|
-
| `sous vars ask <name>` | Asks everything the name covers; see the forms below |
|
|
171
|
-
| `sous vars ask --repo <name>` | Asks every variable one repository publishes |
|
|
172
|
-
| `sous vars ask --namespace <name>` | Asks every variable one namespace publishes |
|
|
173
|
-
| `sous vars ask --var <name>` | Asks one variable; repeat it for each variable |
|
|
174
|
-
| `sous vars ask --accept-first` | When the name matches several things, takes the first one listed |
|
|
175
|
-
| `sous vars ask --all` | Asks every variable again, including the ones already answered |
|
|
176
|
-
| `sous vars ask --file <path>` | Reads definitions from a standalone definitions file |
|
|
177
|
-
| `sous vars ask --answer <name>=<value>` | Answers one question ahead of time; repeat it for each answer |
|
|
178
|
-
| `sous vars ask --answers-file <path>` | Reads answers from a YAML or JSON file of `name: value` pairs |
|
|
179
|
-
| `sous vars ask --dry-run` | Reports what would be asked and written, without writing anything |
|
|
123
|
+
Answers live in your project's env files, and nowhere else:
|
|
180
124
|
|
|
181
|
-
|
|
125
|
+
| File | Committed | Holds |
|
|
126
|
+
|------|-----------|-------|
|
|
127
|
+
| `.sous/.env` | yes | Shared answers (`scope: shared`), the team's defaults |
|
|
128
|
+
| `.sous/.env.local` | no, gitignored | Machine-specific answers (`scope: local`) and every secret |
|
|
182
129
|
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
anything larger than a variable asks every question it publishes:
|
|
130
|
+
Sous edits them the way a careful person would: exactly one value line is rewritten or appended, and
|
|
131
|
+
comments, blank lines, ordering and quoting survive untouched. A new entry gets a generated header comment:
|
|
186
132
|
|
|
187
|
-
```
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
$ sous vars ask workflow/task-files.taskFileRoot
|
|
193
|
-
// the same variable again, spelled out
|
|
194
|
-
$ sous vars ask task-files
|
|
195
|
-
// every question that one recipe asks
|
|
196
|
-
$ sous vars ask workflow
|
|
197
|
-
// every question every recipe in that namespace asks
|
|
198
|
-
$ sous vars ask sous-recipes
|
|
199
|
-
// every question that repository's recipes ask
|
|
133
|
+
```bash
|
|
134
|
+
# Set by sous for workflow/qa-variables: Where should a review keep its working files?
|
|
135
|
+
# A run writes partial output, logs and diffs somewhere before it assembles the note.
|
|
136
|
+
# Edit freely; sous only rewrites the value line.
|
|
137
|
+
SOUS_VAR_QA_SCRATCH_DIR=/var/tmp/qa-review
|
|
200
138
|
```
|
|
201
139
|
|
|
202
|
-
|
|
203
|
-
`repository:namespace/recipe.variable`, and matching is case-sensitive. An environment variable
|
|
204
|
-
name resolves when the definition declares it (a recipe may bind an existing variable such as
|
|
205
|
-
`GITHUB_TOKEN`) or when it is one of the generated ladder names that `.sous/.env` or
|
|
206
|
-
`.sous/.env.local` actually sets.
|
|
140
|
+
Comments are output only; sous never reads one back, and editing the value line changes the answer.
|
|
207
141
|
|
|
208
|
-
|
|
209
|
-
same way. Each one resolves against what the one before it left, so
|
|
210
|
-
`sous vars ask --namespace workflow apiUrl` asks about that namespace's `apiUrl` even when
|
|
211
|
-
another namespace publishes one too.
|
|
142
|
+
## How sous finds an answer: the ladder
|
|
212
143
|
|
|
213
|
-
|
|
214
|
-
you meant. `--accept-first` takes the first one listed, and a run with no terminal fails naming
|
|
215
|
-
that flag rather than guessing.
|
|
144
|
+
For each variable, sous tries these names in order; the first one holding a value wins.
|
|
216
145
|
|
|
217
|
-
|
|
146
|
+
| Rung | Name | Example | When it applies |
|
|
147
|
+
|------|------|---------|-----------------|
|
|
148
|
+
| 1. mapping record | whatever the record names | `TEAM_TOKEN` | Only when a record exists for this variable |
|
|
149
|
+
| 2. recipe scope | `SOUS_VAR_<NAMESPACE>_<RECIPE>_<VARIABLE>` | `SOUS_VAR_WORKFLOW_QA_VARIABLES_QA_SERVICE_TOKEN` | Answers this one recipe's variable and nothing else |
|
|
150
|
+
| 3. namespace scope | `SOUS_VAR_<NAMESPACE>_<VARIABLE>` | `SOUS_VAR_WORKFLOW_QA_SERVICE_TOKEN` | Answers every recipe in the namespace at once |
|
|
151
|
+
| 4. shared scope | `SOUS_VAR_<VARIABLE>` | `SOUS_VAR_QA_SERVICE_TOKEN` | Answers every recipe that declares that variable name |
|
|
152
|
+
| 5. declared name | the definition's own `env` field | `QA_SERVICE_TOKEN` | How a recipe binds a value the environment already carries |
|
|
218
153
|
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
154
|
+
Read from the bottom up, the ladder is a story about sharing. One `SOUS_VAR_API_URL` answers every recipe
|
|
155
|
+
that wants an `apiUrl`, which is what you want most of the time; when two recipes want the same name and
|
|
156
|
+
mean different things, move one answer up a rung and the more specific name wins for that recipe alone.
|
|
222
157
|
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
158
|
+
Each rung is looked up in three places, in this order: the real shell environment, then `.sous/.env.local`,
|
|
159
|
+
then `.sous/.env`. So `SOUS_VAR_API_URL=... sous build` beats both files, and a machine-specific answer beats
|
|
160
|
+
the team's committed default; see [Discovery and overrides](config-discovery.md).
|
|
226
161
|
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
```
|
|
162
|
+
!> Candidate names are only ever GENERATED and looked up, never parsed back into scopes. The underscore is
|
|
163
|
+
both the delimiter and a legal identifier character, so no parse would be trustworthy.
|
|
230
164
|
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
stored and under what name). The keys that do anything are named by the legend the question draws
|
|
234
|
-
under its own input line, in the style the stock prompts use. Those facts are drawn by the renderer
|
|
235
|
-
the advanced view uses, so the same label means the same thing and lines up the same way on both.
|
|
165
|
+
`sous vars show` prints the whole ladder, which is the fastest way to see why an answer did or did not take
|
|
166
|
+
effect. Here `qaServiceToken` declared `env: QA_SERVICE_TOKEN`, so the bottom rung is that name:
|
|
236
167
|
|
|
237
168
|
```term
|
|
238
|
-
|
|
169
|
+
$ sous vars show qaServiceToken
|
|
170
|
+
➔ Environment variables sous looks at, most specific first:
|
|
171
|
+
|
|
172
|
+
Environment variable Rung Status
|
|
173
|
+
----------------------------------------------- --------------- -------------
|
|
174
|
+
SOUS_VAR_WORKFLOW_QA_VARIABLES_QA_SERVICE_TOKEN recipe scope not set
|
|
175
|
+
SOUS_VAR_WORKFLOW_QA_SERVICE_TOKEN namespace scope not set
|
|
176
|
+
SOUS_VAR_QA_SERVICE_TOKEN shared scope not set
|
|
177
|
+
QA_SERVICE_TOKEN declared name not set
|
|
178
|
+
```
|
|
239
179
|
|
|
240
|
-
|
|
180
|
+
## When names collide: mapping records
|
|
241
181
|
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
will store and search for your task files. The default value stores task files in
|
|
245
|
-
the project's .sous directory, but you can specify any local path, either relative
|
|
246
|
-
to the project root or absolute.
|
|
182
|
+
A mapping record binds one environment variable, of any name at all, to one fully qualified variable. It is
|
|
183
|
+
the top rung and the universal conflict resolver: two recipes wanting the same name, or a name already in use.
|
|
247
184
|
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
stored-as : SOUS_VAR_TASK_FILE_ROOT
|
|
251
|
-
storage-path: /home/you/project/.sous/.env
|
|
185
|
+
You rarely write one by hand, because sous offers one when the conflict appears: when the name an answer
|
|
186
|
+
would use already holds a value that does not fit the definition, you are asked where the answer should go:
|
|
252
187
|
|
|
253
|
-
|
|
254
|
-
|
|
188
|
+
```text
|
|
189
|
+
? SERVICE_TOKEN already holds a value that does not fit serviceToken. Where should this answer go?
|
|
190
|
+
> SOUS_VAR_TOOLING_DEPLOY_SERVICE_TOKEN, with a mapping record (recommended)
|
|
191
|
+
SERVICE_TOKEN, replacing what is there
|
|
255
192
|
```
|
|
256
193
|
|
|
257
|
-
|
|
258
|
-
the
|
|
259
|
-
`⇥ advanced` alone. A question answered from a list rather than typed names the arrow keys
|
|
260
|
-
instead:
|
|
194
|
+
Choosing the record writes both the answer under the scoped name and the record binding it, and the run
|
|
195
|
+
reports the pair. Where there is no terminal the record is written, since overwriting a value in use is worse.
|
|
261
196
|
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
```
|
|
197
|
+
Sous writes the records it creates into `.sous/conf.d/520-var-mappings.jsonc`, one at a time, so each name
|
|
198
|
+
has exactly one. The record's shape and the rest of the managed layers are under
|
|
199
|
+
[Configuration keys](repositories-file-formats.md#configuration-keys) and
|
|
200
|
+
[Managed config layers](repositories-file-formats.md#managed-config-layers); hand-write one if you prefer.
|
|
201
|
+
|
|
202
|
+
## See and answer variables from the command line
|
|
269
203
|
|
|
270
|
-
|
|
271
|
-
yes-or-no confirmation alike. The word is always "Advanced". Once an answer is stored, two lines
|
|
272
|
-
say what was stored and where:
|
|
204
|
+
`sous vars list` prints every variable in play, what answered it, and where the value came from.
|
|
273
205
|
|
|
274
206
|
```term
|
|
275
|
-
|
|
276
|
-
|
|
207
|
+
$ sous vars list
|
|
208
|
+
Variable Recipe Answered by Value Source
|
|
209
|
+
-------------- --------------------- ----------------------- -------------- --------------------------------
|
|
210
|
+
qaScratchDir workflow/qa-variables SOUS_VAR_QA_SCRATCH_DIR /var/tmp/qa shared scope, the .env.local file
|
|
211
|
+
qaServiceToken workflow/qa-variables (unanswered) nothing yet
|
|
212
|
+
qaTaskRoot workflow/qa-variables SOUS_VAR_QA_TASK_ROOT .sous/qa-notes shared scope, the .env file
|
|
277
213
|
```
|
|
278
214
|
|
|
279
|
-
|
|
280
|
-
|
|
215
|
+
`sous vars show <name>` prints one variable in full: the question, the publisher's description and example,
|
|
216
|
+
the recipe and version that published it, the value in scope and whether it fits, the labeled facts the
|
|
217
|
+
advanced view draws, and the ladder table above. The name may be bare or the full `namespace/recipe.name`.
|
|
281
218
|
|
|
282
|
-
|
|
219
|
+
`sous vars ask [name]` asks the questions the definitions imply and stores the answers.
|
|
283
220
|
|
|
284
|
-
|
|
285
|
-
|
|
221
|
+
| Invocation | What it does |
|
|
222
|
+
|------------|--------------|
|
|
223
|
+
| `sous vars ask` | Asks only what is unanswered, or what no longer fits its definition |
|
|
224
|
+
| `sous vars ask <name>` | Asks everything the name covers; see the forms below |
|
|
225
|
+
| `sous vars ask --repo <name>` | Asks every variable one repository publishes |
|
|
226
|
+
| `sous vars ask --namespace <name>` | Asks every variable one namespace publishes |
|
|
227
|
+
| `sous vars ask --var <name>` | Asks one variable; repeat the flag for each one |
|
|
228
|
+
| `sous vars ask --all` | Asks every variable again, including the ones already answered |
|
|
229
|
+
| `sous vars ask --accept-first` | When the name matches several things, takes the first one listed |
|
|
230
|
+
| `sous vars ask --file <path>` | Reads definitions from a standalone definitions file |
|
|
231
|
+
| `sous vars ask --answer <name>=<value>` | Answers one question ahead of time; repeat it for each answer |
|
|
232
|
+
| `sous vars ask --answers-file <path>` | Reads answers from a YAML or JSON file of `name: value` pairs |
|
|
233
|
+
| `sous vars ask --dry-run` | Reports what would be asked and written, without writing anything |
|
|
234
|
+
| `sous vars ask --non-interactive` | Never asks; fails instead, naming the environment variable or flag that would answer each question |
|
|
286
235
|
|
|
287
|
-
|
|
288
|
-
[Advanced Variable Settings]
|
|
236
|
+
`<name>` is a reference, resolved like any sous ref; anything larger than a variable asks all its questions:
|
|
289
237
|
|
|
290
|
-
|
|
238
|
+
```bash
|
|
239
|
+
sous vars ask taskFileRoot # one variable, by the name its recipe gave it
|
|
240
|
+
sous vars ask SOUS_VAR_TASK_FILE_ROOT # the same one, by a name in use that answers it
|
|
241
|
+
sous vars ask workflow/task-files.taskFileRoot # the same one, spelled out in full
|
|
242
|
+
sous vars ask task-files # every question one recipe asks
|
|
243
|
+
sous vars ask sous-recipes # every question that repository's recipes ask
|
|
244
|
+
```
|
|
291
245
|
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
246
|
+
Matching is case-sensitive, and an environment variable name resolves when a definition declares it or an
|
|
247
|
+
env file sets it. `--repo`, `--namespace` and `--var` say which kind is meant, each narrowing what the one
|
|
248
|
+
before left, so `sous vars ask --namespace workflow apiUrl` asks about that namespace's `apiUrl` alone. When
|
|
249
|
+
a name matches several things, sous lists them and asks; `--accept-first` takes the first, and a run with no
|
|
250
|
+
terminal fails naming it.
|
|
297
251
|
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
storage-path: /home/you/project/.sous/.env
|
|
303
|
-
stored-as : SOUS_VAR_TASK_FILE_ROOT
|
|
304
|
-
constraints : • must be a value of the type path (type: path)
|
|
305
|
-
• must be at least 1 character long (minLength: 1)
|
|
252
|
+
?> Bare `sous vars` is shorthand for `vars list`, and `sous vars <name>` for `vars show <name>`, which prints
|
|
253
|
+
the same report (`vars show` spells the storage path out in full); `sous var list`, `sous var show` and
|
|
254
|
+
`sous var ask` work too. A variable named `list`, `show` or `ask` collides with the subcommand, so reach it
|
|
255
|
+
the long way: `sous vars show ask`.
|
|
306
256
|
|
|
307
|
-
|
|
308
|
-
Return to value entry
|
|
309
|
-
Change the storage file
|
|
310
|
-
Change the stored variable name
|
|
311
|
-
```
|
|
257
|
+
### Answering ahead of the questions
|
|
312
258
|
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
259
|
+
`--answer <name>=<value>` answers a question before it is asked and repeats as often as needed, and
|
|
260
|
+
`--answers-file <path>` reads the same pairs from YAML or JSON; an `--answer` wins over the same name in the
|
|
261
|
+
file. Pair either with `--dry-run` first, as in
|
|
262
|
+
`sous vars ask --var qaScratchDir --answer qaScratchDir=/var/tmp/qa-review --dry-run`. Both work the same way
|
|
263
|
+
on `sous subscription add`; see [Answer the questions](repositories-consuming.md#answer-the-questions).
|
|
317
264
|
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
`.sous/.env` and the gitignored `.sous/.env.local`. Once anything has changed, the first menu item
|
|
322
|
-
becomes "Save changes and return to value entry" and a "Discard changes" item joins it.
|
|
265
|
+
Every supplied answer is checked before anything is written, so a value that does not fit fails the run
|
|
266
|
+
naming the constraint, and an unknown name fails naming every variable in play. An answer replacing a stored
|
|
267
|
+
one is written where that answer lives, and a value the shell supplies is never rewritten:
|
|
323
268
|
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
269
|
+
```text
|
|
270
|
+
Answers stored:
|
|
271
|
+
qaParallelAgents: 4 SOUS_VAR_QA_PARALLEL_AGENTS in .env
|
|
272
|
+
replaced the answer already there: 99
|
|
273
|
+
SOUS_VAR_QA_PARALLEL_AGENTS is set in your shell environment and answers
|
|
274
|
+
this variable first; unset it for the stored answer to take effect
|
|
275
|
+
```
|
|
329
276
|
|
|
330
|
-
|
|
331
|
-
scope, what was stored and under which name in which file, and what was left unanswered and why.
|
|
277
|
+
## Answer without a terminal
|
|
332
278
|
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
279
|
+
Sous never hangs waiting on a prompt it cannot show; what counts as "no terminal" is listed under
|
|
280
|
+
[Run sous in CI, or from an agent](repositories-consuming.md#run-sous-in-ci-or-from-an-agent). Such a run
|
|
281
|
+
fails on the unanswered required variables, naming every environment variable that would satisfy each one:
|
|
336
282
|
|
|
337
|
-
|
|
283
|
+
```text
|
|
284
|
+
Error: 1 variable still needs an answer, and there is no terminal to ask on.
|
|
338
285
|
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
(comments allowed), and an `--answer` wins over the same name in the file. Both flags work the
|
|
342
|
-
same way on `sous subscription add`, which is where a run with no terminal usually meets them;
|
|
343
|
-
[Consuming recipes](repositories-consuming.md#answering-questions-ahead-of-time) walks through
|
|
344
|
-
that flow, dry run first.
|
|
286
|
+
Set one of the environment variables listed under each variable, or run
|
|
287
|
+
'sous vars ask' from a terminal.
|
|
345
288
|
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
289
|
+
qaTaskRoot (workflow/qa-variables): Where should the review notes be stored?
|
|
290
|
+
SOUS_VAR_WORKFLOW_QA_VARIABLES_QA_TASK_ROOT (recipe scope)
|
|
291
|
+
SOUS_VAR_WORKFLOW_QA_TASK_ROOT (namespace scope)
|
|
292
|
+
SOUS_VAR_QA_TASK_ROOT (shared scope)
|
|
349
293
|
```
|
|
350
294
|
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
is checked against its definition before anything is written, so a value that does not fit fails
|
|
354
|
-
the run naming the constraint and the publisher's example, and a name no recipe declares fails
|
|
355
|
-
naming every variable that is in play. An answer for a variable that already has one replaces it,
|
|
356
|
-
where that answer lives, and the report says what it replaced. Whatever is left over is asked for
|
|
357
|
-
as usual.
|
|
358
|
-
|
|
359
|
-
## Without a terminal
|
|
295
|
+
Set one of those names in your pipeline and the build proceeds. For an answer the whole team shares, and
|
|
296
|
+
nothing about it sensitive, committing it to `.sous/.env` is simpler: a fresh clone needs no pipeline setup.
|
|
360
297
|
|
|
361
|
-
|
|
362
|
-
required variable fails, and the failure names every environment variable that would satisfy it,
|
|
363
|
-
most specific first, which is the message a continuous integration log needs to be useful:
|
|
298
|
+
## How a template reads an answer
|
|
364
299
|
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
Set one of the environment variables listed under each variable, or run
|
|
369
|
-
'sous vars ask' from a terminal.
|
|
300
|
+
Answers are environment variables, and a template renders config variables. The bridge is one `_env` entry
|
|
301
|
+
in your config, naming the variable the answer is stored under (`sous vars show` prints it as `stored-as`):
|
|
370
302
|
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
SOUS_VAR_WORKFLOW_API_URL (namespace scope)
|
|
374
|
-
SOUS_VAR_API_URL (shared scope)
|
|
303
|
+
```json
|
|
304
|
+
{ "_env": { "taskFileRoot": "SOUS_VAR_TASK_FILE_ROOT" } }
|
|
375
305
|
```
|
|
376
306
|
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
307
|
+
With that line `{{ taskFileRoot }}` renders in any template this project compiles, and `${taskFileRoot}`
|
|
308
|
+
works in `_vars` and every other config value.
|
|
309
|
+
|
|
310
|
+
!> Answers are not injected into the template scope on their own, and `_env` names one exact environment
|
|
311
|
+
variable rather than walking the ladder. A recipe's own templates read the project's `_vars` and the
|
|
312
|
+
auto-injected `sous*` variables; they do not see the answers to their own questions unless your config maps
|
|
313
|
+
them in.
|
|
380
314
|
|
|
381
315
|
## Where to go next
|
|
382
316
|
|
|
383
317
|
- [Consuming recipes](repositories-consuming.md): subscribing, building, and what lands where
|
|
384
|
-
- [Authoring a repository](repositories-authoring.md): declaring the definitions this page
|
|
385
|
-
|
|
386
|
-
- [Variable definitions](repositories-file-formats.md#variable-definitions): the full field table
|
|
318
|
+
- [Authoring a repository](repositories-authoring.md): declaring the definitions this page resolves
|
|
319
|
+
- [Troubleshooting](repositories-troubleshooting.md): a question sous cannot ask, a pattern that runs out of time
|
|
387
320
|
- [Command reference](commands.md): every command and flag
|