@sous-io/sous 0.2.36 → 1.0.1

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.
Files changed (244) hide show
  1. package/README.md +14 -256
  2. package/dist/bin.js +11 -0
  3. package/dist/sous.js +1093 -0
  4. package/docs/markdown/README.md +5 -4
  5. package/docs/markdown/_sidebar.md +21 -1
  6. package/docs/markdown/commands.md +294 -431
  7. package/docs/markdown/config-discovery.md +25 -14
  8. package/docs/markdown/config-inspection.md +53 -31
  9. package/docs/markdown/config-layers.md +168 -66
  10. package/docs/markdown/config-variables.md +44 -11
  11. package/docs/markdown/configuration.md +104 -17
  12. package/docs/markdown/design-principles.md +4 -1
  13. package/docs/markdown/repositories-authoring.md +30 -0
  14. package/docs/markdown/repositories-consuming.md +72 -14
  15. package/docs/markdown/repositories-file-formats.md +31 -13
  16. package/docs/markdown/repositories-providers.md +48 -57
  17. package/docs/markdown/repositories-quickstart.md +3 -2
  18. package/docs/markdown/repositories-troubleshooting.md +27 -1
  19. package/docs/markdown/repositories-variables.md +6 -1
  20. package/docs/markdown/repositories.md +18 -8
  21. package/package.json +48 -107
  22. package/recipes/core/sous-skills/skills/about-liquid-templates/SKILL.tpl.md +4 -6
  23. package/recipes/core/sous-skills/skills/about-liquid-templates/references/liquid-filters.tpl.md +2 -0
  24. package/recipes/core/sous-skills/skills/about-sous/SKILL.tpl.md +10 -9
  25. package/recipes/core/sous-skills/skills/about-sous-configuration/SKILL.tpl.md +35 -14
  26. package/recipes/core/sous-skills/skills/about-sous-recipes/SKILL.tpl.md +51 -20
  27. package/recipes/core/sous-skills/sous.recipe.yaml +1 -1
  28. package/sous.config.schema.json +351 -132
  29. package/bin/run.js +0 -51
  30. package/src/base-command.ts +0 -457
  31. package/src/commands/build.ts +0 -181
  32. package/src/commands/clear.ts +0 -79
  33. package/src/commands/compile.ts +0 -155
  34. package/src/commands/config/get.ts +0 -110
  35. package/src/commands/config/show.ts +0 -32
  36. package/src/commands/config/validate.ts +0 -53
  37. package/src/commands/help.ts +0 -46
  38. package/src/commands/init.ts +0 -289
  39. package/src/commands/launch.ts +0 -139
  40. package/src/commands/lock/rebuild.ts +0 -239
  41. package/src/commands/lock/show.ts +0 -115
  42. package/src/commands/namespace/list.ts +0 -138
  43. package/src/commands/namespace/show.ts +0 -130
  44. package/src/commands/prune.ts +0 -43
  45. package/src/commands/recipe/list.ts +0 -112
  46. package/src/commands/recipe/show.ts +0 -439
  47. package/src/commands/repo/add.ts +0 -145
  48. package/src/commands/repo/contribute.ts +0 -604
  49. package/src/commands/repo/gc.ts +0 -173
  50. package/src/commands/repo/init.ts +0 -136
  51. package/src/commands/repo/link.ts +0 -899
  52. package/src/commands/repo/list.ts +0 -236
  53. package/src/commands/repo/release.ts +0 -685
  54. package/src/commands/repo/remove.ts +0 -194
  55. package/src/commands/repo/search.ts +0 -243
  56. package/src/commands/repo/submit.ts +0 -341
  57. package/src/commands/repo/unlink.ts +0 -461
  58. package/src/commands/subscription/add.ts +0 -286
  59. package/src/commands/subscription/list.ts +0 -208
  60. package/src/commands/subscription/remove.ts +0 -182
  61. package/src/commands/subscription/update.ts +0 -216
  62. package/src/commands/update.ts +0 -178
  63. package/src/commands/vars/ask.ts +0 -417
  64. package/src/commands/vars/index.ts +0 -79
  65. package/src/commands/vars/list.ts +0 -67
  66. package/src/commands/vars/show.ts +0 -77
  67. package/src/config-command.ts +0 -30
  68. package/src/lib/build-preparation.ts +0 -128
  69. package/src/lib/build-service.ts +0 -580
  70. package/src/lib/config-discovery.ts +0 -378
  71. package/src/lib/config-inspect.ts +0 -145
  72. package/src/lib/config-kernel.mjs +0 -379
  73. package/src/lib/config-schema.ts +0 -401
  74. package/src/lib/env-file.ts +0 -328
  75. package/src/lib/env-local.ts +0 -212
  76. package/src/lib/errors.ts +0 -32
  77. package/src/lib/glob-pattern.ts +0 -24
  78. package/src/lib/include-resolver.ts +0 -523
  79. package/src/lib/interactive.ts +0 -165
  80. package/src/lib/markdown-compiler.ts +0 -947
  81. package/src/lib/package-info.ts +0 -25
  82. package/src/lib/pid-service.ts +0 -99
  83. package/src/lib/project-install.d.mts +0 -44
  84. package/src/lib/project-install.mjs +0 -234
  85. package/src/lib/project-scaffold/index.ts +0 -332
  86. package/src/lib/project-scaffold/templates.ts +0 -240
  87. package/src/lib/repos/catalog-display.ts +0 -215
  88. package/src/lib/repos/catalog-inputs.ts +0 -288
  89. package/src/lib/repos/catalog.ts +0 -842
  90. package/src/lib/repos/contribute.ts +0 -452
  91. package/src/lib/repos/core-recipe.ts +0 -105
  92. package/src/lib/repos/declarations.ts +0 -187
  93. package/src/lib/repos/defaults.ts +0 -175
  94. package/src/lib/repos/formats/common.ts +0 -440
  95. package/src/lib/repos/formats/index-file.ts +0 -253
  96. package/src/lib/repos/formats/links-map.ts +0 -98
  97. package/src/lib/repos/formats/lockfile.ts +0 -169
  98. package/src/lib/repos/formats/patterns.ts +0 -57
  99. package/src/lib/repos/formats/recipe-manifest.ts +0 -205
  100. package/src/lib/repos/formats/repo-manifest.ts +0 -97
  101. package/src/lib/repos/formats/store-entry.ts +0 -84
  102. package/src/lib/repos/formats/variable-definition.ts +0 -268
  103. package/src/lib/repos/freshness.ts +0 -264
  104. package/src/lib/repos/git-clone.ts +0 -850
  105. package/src/lib/repos/identity.ts +0 -89
  106. package/src/lib/repos/index.ts +0 -57
  107. package/src/lib/repos/links.ts +0 -356
  108. package/src/lib/repos/load-manifest.ts +0 -237
  109. package/src/lib/repos/lock-service.ts +0 -454
  110. package/src/lib/repos/locked-namespace-resolver.ts +0 -232
  111. package/src/lib/repos/locked-recipe-lookup.ts +0 -123
  112. package/src/lib/repos/locked-recipes.ts +0 -254
  113. package/src/lib/repos/managed-layer.ts +0 -422
  114. package/src/lib/repos/memories-hash-name.ts +0 -33
  115. package/src/lib/repos/namespace-resolver.ts +0 -527
  116. package/src/lib/repos/providers/base.ts +0 -268
  117. package/src/lib/repos/providers/git.ts +0 -237
  118. package/src/lib/repos/providers/github.ts +0 -589
  119. package/src/lib/repos/providers/gitlab.ts +0 -332
  120. package/src/lib/repos/providers/http.ts +0 -128
  121. package/src/lib/repos/providers/index-cache.ts +0 -421
  122. package/src/lib/repos/providers/index.ts +0 -106
  123. package/src/lib/repos/providers/local.ts +0 -391
  124. package/src/lib/repos/providers/provider.ts +0 -615
  125. package/src/lib/repos/recipe-config-layers.ts +0 -316
  126. package/src/lib/repos/recipe-key-matcher.ts +0 -62
  127. package/src/lib/repos/recipe-memories.ts +0 -178
  128. package/src/lib/repos/recipe-targets.ts +0 -236
  129. package/src/lib/repos/ref-search.ts +0 -46
  130. package/src/lib/repos/release/bump.ts +0 -161
  131. package/src/lib/repos/release/changelog.ts +0 -461
  132. package/src/lib/repos/release/git-state.ts +0 -460
  133. package/src/lib/repos/release/index-builder.ts +0 -1108
  134. package/src/lib/repos/release/index.ts +0 -19
  135. package/src/lib/repos/release/plan.ts +0 -521
  136. package/src/lib/repos/release/settle.ts +0 -154
  137. package/src/lib/repos/release/submissions.ts +0 -214
  138. package/src/lib/repos/release/submit-checkout.ts +0 -290
  139. package/src/lib/repos/release/submit-questions.ts +0 -153
  140. package/src/lib/repos/release/submit-service.ts +0 -898
  141. package/src/lib/repos/release/tags.ts +0 -239
  142. package/src/lib/repos/release/validate.ts +0 -463
  143. package/src/lib/repos/resolver.ts +0 -1028
  144. package/src/lib/repos/scaffold/index.ts +0 -245
  145. package/src/lib/repos/scaffold/templates.ts +0 -517
  146. package/src/lib/repos/seed.ts +0 -473
  147. package/src/lib/repos/store/contract.ts +0 -64
  148. package/src/lib/repos/store/hash.ts +0 -191
  149. package/src/lib/repos/store/recipe-store.ts +0 -599
  150. package/src/lib/repos/store/settings.ts +0 -58
  151. package/src/lib/repos/subscription-service.ts +0 -3859
  152. package/src/lib/repos/trust.ts +0 -447
  153. package/src/lib/repos/update-plan.ts +0 -235
  154. package/src/lib/self-update/index.ts +0 -14
  155. package/src/lib/self-update/installs.ts +0 -412
  156. package/src/lib/self-update/managers.ts +0 -126
  157. package/src/lib/self-update/plan.ts +0 -315
  158. package/src/lib/self-update/registry.ts +0 -99
  159. package/src/lib/self-update/run.ts +0 -506
  160. package/src/lib/self-update/versions.ts +0 -243
  161. package/src/lib/settings.ts +0 -1105
  162. package/src/lib/sous-home.ts +0 -104
  163. package/src/lib/state.ts +0 -167
  164. package/src/lib/vars/answers.ts +0 -192
  165. package/src/lib/vars/ask.ts +0 -1152
  166. package/src/lib/vars/definition-source.ts +0 -261
  167. package/src/lib/vars/display.ts +0 -231
  168. package/src/lib/vars/index.ts +0 -20
  169. package/src/lib/vars/ladder.ts +0 -282
  170. package/src/lib/vars/mappings.ts +0 -260
  171. package/src/lib/vars/names.ts +0 -94
  172. package/src/lib/vars/preanswers.ts +0 -395
  173. package/src/lib/vars/question-plan.ts +0 -241
  174. package/src/lib/vars/report.ts +0 -228
  175. package/src/lib/vars/safe-regex.ts +0 -235
  176. package/src/lib/vars/secrets.ts +0 -131
  177. package/src/lib/vars/validate.ts +0 -312
  178. package/src/lib/version-report.ts +0 -90
  179. package/src/lib/watch-loop.ts +0 -148
  180. package/src/lib/watch-service.ts +0 -116
  181. package/src/services/ref-resolver/container.ts +0 -107
  182. package/src/services/ref-resolver/dependency.ts +0 -60
  183. package/src/services/ref-resolver/format.ts +0 -197
  184. package/src/services/ref-resolver/glob.ts +0 -115
  185. package/src/services/ref-resolver/hash-names.ts +0 -155
  186. package/src/services/ref-resolver/index.ts +0 -55
  187. package/src/services/ref-resolver/injectable.ts +0 -36
  188. package/src/services/ref-resolver/location.ts +0 -62
  189. package/src/services/ref-resolver/lookups/cached-index-lookup.ts +0 -53
  190. package/src/services/ref-resolver/lookups/catalog-lookup.ts +0 -23
  191. package/src/services/ref-resolver/lookups/catalog-matcher.ts +0 -261
  192. package/src/services/ref-resolver/lookups/chained-lookup.ts +0 -27
  193. package/src/services/ref-resolver/lookups/env-var-lookup.ts +0 -65
  194. package/src/services/ref-resolver/lookups/fetched-index-lookup.ts +0 -150
  195. package/src/services/ref-resolver/lookups/recorded-dependency-lookup.ts +0 -62
  196. package/src/services/ref-resolver/lookups/ref-lookup.ts +0 -54
  197. package/src/services/ref-resolver/lookups/variable-lookup.ts +0 -123
  198. package/src/services/ref-resolver/named-refs.ts +0 -144
  199. package/src/services/ref-resolver/parser/env-var-splitter.ts +0 -27
  200. package/src/services/ref-resolver/parser/location-splitter.ts +0 -239
  201. package/src/services/ref-resolver/parser/name-path-splitter.ts +0 -200
  202. package/src/services/ref-resolver/parser/partial-ref.ts +0 -84
  203. package/src/services/ref-resolver/parser/query-splitter.ts +0 -61
  204. package/src/services/ref-resolver/parser/range-splitter.ts +0 -52
  205. package/src/services/ref-resolver/parser/ref-parser.ts +0 -115
  206. package/src/services/ref-resolver/parser/repo-qualifier-splitter.ts +0 -51
  207. package/src/services/ref-resolver/parts.ts +0 -115
  208. package/src/services/ref-resolver/pruners/command-line-pruner.ts +0 -47
  209. package/src/services/ref-resolver/pruners/include-pruner.ts +0 -66
  210. package/src/services/ref-resolver/pruners/manifest-pruner.ts +0 -130
  211. package/src/services/ref-resolver/pruners/ref-pruner.ts +0 -135
  212. package/src/services/ref-resolver/pruners/rule-helpers.ts +0 -44
  213. package/src/services/ref-resolver/pruners/stored-key-pruner.ts +0 -136
  214. package/src/services/ref-resolver/ref-helpers.ts +0 -62
  215. package/src/services/ref-resolver/ref-picker-service.ts +0 -204
  216. package/src/services/ref-resolver/ref-report.ts +0 -84
  217. package/src/services/ref-resolver/ref-resolve-arguments.ts +0 -47
  218. package/src/services/ref-resolver/ref-resolve-result.ts +0 -36
  219. package/src/services/ref-resolver/ref-resolver-service.ts +0 -323
  220. package/src/services/ref-resolver/source.ts +0 -48
  221. package/src/services/ref-resolver/tokens.ts +0 -25
  222. package/src/services/ref-resolver/types.ts +0 -155
  223. package/src/templating/filters/bullet-list.ts +0 -9
  224. package/src/templating/filters/index.ts +0 -8
  225. package/src/templating/init-liquid-engine.ts +0 -139
  226. package/src/templating/lib/glob-files.ts +0 -74
  227. package/src/templating/lib/import-export.ts +0 -32
  228. package/src/templating/lib/secret-scope.ts +0 -41
  229. package/src/templating/lib/tag-args.ts +0 -19
  230. package/src/templating/tags/exportScalarVarsJs.ts +0 -46
  231. package/src/templating/tags/getFiles.ts +0 -89
  232. package/src/templating/tags/index.ts +0 -14
  233. package/src/templating/tags/listFiles.ts +0 -54
  234. package/src/templating/tags/showVars.ts +0 -28
  235. package/src/utils/choice-prompt.ts +0 -143
  236. package/src/utils/command-errors.ts +0 -186
  237. package/src/utils/command-help.ts +0 -45
  238. package/src/utils/confirm-prompt.ts +0 -110
  239. package/src/utils/flags.ts +0 -177
  240. package/src/utils/formatting.ts +0 -849
  241. package/src/utils/prompts.ts +0 -53
  242. package/src/utils/sous-directory.ts +0 -245
  243. package/src/utils/table.ts +0 -603
  244. package/src/utils/value-prompt.ts +0 -119
@@ -1,417 +0,0 @@
1
- /**
2
- * `sous vars ask [name]`.
3
- *
4
- * Asks the questions the project's variable definitions imply and stores the
5
- * answers in `.sous/.env` or `.sous/.env.local`. By default it asks only what
6
- * is unanswered or no longer fits; `--all` asks everything again.
7
- *
8
- * The optional name is a reference, resolved through `src/services/ref-resolver/` exactly as
9
- * every other command resolves one. It may name a variable, an environment
10
- * variable that answers one, a recipe, a namespace or a repository, at any
11
- * level of qualification; naming anything larger than a variable asks every
12
- * question that thing publishes:
13
- *
14
- * sous vars ask taskFileRoot one variable
15
- * sous vars ask SOUS_VAR_TASK_FILE_ROOT the variable that name answers
16
- * sous vars ask task-files every question one recipe asks
17
- * sous vars ask workflow every question one namespace asks
18
- * sous vars ask sous-recipes every question one repository asks
19
- *
20
- * `--repo`, `--namespace` and `--var` say outright which kind of thing is
21
- * meant, and narrow the same way. A reference that means more than one thing is
22
- * offered as a list to choose between; `--accept-first` takes the first, and a
23
- * run with no terminal fails naming that flag.
24
- *
25
- * `--file` reads a standalone definitions file instead of the project's
26
- * recipes, which is how a project asks questions no recipe publishes yet.
27
- *
28
- * Answers can be supplied ahead of the questions with `--answer name=value` or
29
- * `--answers-file <path>`; whatever they answer is stored before anything is
30
- * asked, and only what is left over is asked for.
31
- *
32
- * Without a terminal the command never hangs waiting on a prompt: it fails and
33
- * names the exact environment variables that would answer each question.
34
- */
35
-
36
- import path from "node:path";
37
- import { Args, Flags } from "@oclif/core";
38
- import { BaseCommand } from "../../base-command.js";
39
- import { ConfigError } from "../../lib/errors.js";
40
- import {
41
- ChainedLookup,
42
- RefPickArguments,
43
- RefResolveArguments,
44
- VariableLookup,
45
- namespaceOf,
46
- recipeOf,
47
- refKey,
48
- repoOf,
49
- sharedRefPicker,
50
- sharedRefResolver,
51
- variableRefOf,
52
- type CatalogRepo,
53
- type RefKind,
54
- type SousRef,
55
- } from "../../services/ref-resolver/index.js";
56
- import { EnvVarLookup } from "../../services/ref-resolver/lookups/env-var-lookup.js";
57
- import { subscriptionServiceFor } from "../../lib/repos/subscription-service.js";
58
- import {
59
- applyProvidedAnswers,
60
- askForMissing,
61
- collectProvidedAnswers,
62
- FileDefinitionSource,
63
- formatAskReport,
64
- loadLadderContext,
65
- loadProjectDefinitions,
66
- unknownAnswerError,
67
- type DefinedVariable,
68
- } from "../../lib/vars/index.js";
69
- import type { LadderContext } from "../../lib/vars/ladder.js";
70
- import {
71
- blankLine,
72
- dryRunNotice,
73
- footer,
74
- heading,
75
- indent,
76
- log,
77
- showCommandVars,
78
- } from "../../utils/formatting.js";
79
- import { answerFlags } from "../../utils/flags.js";
80
-
81
- /** What the caller said should be asked: the reference, and the three flags. */
82
- type Selection = {
83
- /** The reference argument, when one was given. */
84
- name?: string;
85
- /** The repository named by `--repo`. */
86
- repo?: string;
87
- /** The namespace named by `--namespace`. */
88
- namespace?: string;
89
- /** Every variable named by `--var`. */
90
- vars?: string[];
91
- /** Take the first match rather than asking which was meant. */
92
- acceptFirst: boolean;
93
- };
94
-
95
- /** The line every "this matched nothing" error ends with. */
96
- const SEE_ALL = " Run 'sous vars' to see every variable this project's recipes define.";
97
-
98
- export default class VarsAsk extends BaseCommand {
99
- static description =
100
- "Answer the variables this project's recipes define, storing the answers in the .sous env files";
101
-
102
- /**
103
- * The other spelling of the topic. It lives under a hidden topic, so it is
104
- * typable everywhere without ever reaching the top-level listing.
105
- */
106
- static aliases = ["var:ask"];
107
-
108
- static examples = [
109
- "<%= config.bin %> vars ask",
110
- "<%= config.bin %> vars ask apiUrl",
111
- "<%= config.bin %> vars ask SOUS_VAR_API_URL",
112
- "<%= config.bin %> vars ask workflow/task-files",
113
- "<%= config.bin %> vars ask workflow --accept-first",
114
- "<%= config.bin %> vars ask --namespace workflow --var apiUrl",
115
- "<%= config.bin %> vars ask --all",
116
- "<%= config.bin %> vars ask --file ./questions.yaml",
117
- "<%= config.bin %> vars ask --answer apiUrl=https://api.example.com",
118
- "<%= config.bin %> vars ask --answers-file ./answers.yaml",
119
- ];
120
-
121
- static args = {
122
- name: Args.string({
123
- description:
124
- "What to ask about: a variable, an environment variable name, a recipe, a namespace or a repository",
125
- required: false,
126
- }),
127
- };
128
-
129
- static flags = {
130
- ...BaseCommand.baseFlags,
131
- all: Flags.boolean({
132
- description: "Ask every variable again, including the ones already answered",
133
- default: false,
134
- }),
135
- repo: Flags.string({
136
- description: "Ask only the variables published by this repository",
137
- }),
138
- namespace: Flags.string({
139
- description: "Ask only the variables published in this namespace",
140
- }),
141
- var: Flags.string({
142
- description: "Ask only this variable. Repeat it for each variable",
143
- multiple: true,
144
- }),
145
- "accept-first": Flags.boolean({
146
- description: "When a name matches several things, take the first one listed",
147
- default: false,
148
- }),
149
- file: Flags.string({
150
- description:
151
- "Read the variable definitions from a standalone definitions file instead of the project's recipes",
152
- }),
153
- "dry-run": Flags.boolean({
154
- description: "Report what would be asked and written, without writing anything",
155
- default: false,
156
- }),
157
- ...answerFlags(),
158
- };
159
-
160
- async run(): Promise<void> {
161
- const { args, flags } = await this.parse(VarsAsk);
162
- const dryRun = flags["dry-run"];
163
-
164
- const selection: Selection = {
165
- ...(args.name === undefined ? {} : { name: args.name }),
166
- ...(flags.repo === undefined ? {} : { repo: flags.repo }),
167
- ...(flags.namespace === undefined ? {} : { namespace: flags.namespace }),
168
- ...(flags.var === undefined ? {} : { vars: flags.var }),
169
- acceptFirst: flags["accept-first"],
170
- };
171
-
172
- showCommandVars({
173
- Project: this.projectLabel,
174
- Config: this.configContext.configPath,
175
- Definitions: flags.file ?? "the project's subscribed recipes",
176
- Asking: describeSelection(selection, flags.all),
177
- });
178
-
179
- if (dryRun) dryRunNotice("No answers will be written.");
180
-
181
- const provided = collectProvidedAnswers({
182
- ...(flags.answer === undefined ? {} : { answer: flags.answer }),
183
- ...(flags["answers-file"] === undefined
184
- ? {}
185
- : { answersFile: flags["answers-file"] }),
186
- });
187
-
188
- const source =
189
- flags.file === undefined
190
- ? loadProjectDefinitions(this.settings, this.configContext.sousDir)
191
- : new FileDefinitionSource(path.resolve(process.cwd(), flags.file));
192
- const defined = await source.load();
193
-
194
- heading("Answering variables");
195
- blankLine();
196
-
197
- if (defined.length === 0) {
198
- // A supplied answer names a variable nothing declares, which is a typo
199
- // until proven otherwise; it fails rather than passing unnoticed.
200
- if (provided[0] !== undefined) throw unknownAnswerError(provided[0], defined);
201
-
202
- log(
203
- indent(
204
- "No recipe in this project defines any variables yet, so there is nothing " +
205
- "to ask. Subscribe to a recipe that publishes some, or read a definitions " +
206
- "file with --file."
207
- )
208
- );
209
- footer();
210
- return;
211
- }
212
-
213
- const context = loadLadderContext({
214
- sousDir: this.configContext.sousDir,
215
- settings: this.settings,
216
- shellEnv: this.shellEnv,
217
- });
218
-
219
- // What the reference and the flags name, as the fully qualified reference of
220
- // every variable to ask. Undefined means everything the usual rules pick.
221
- // A reference written as a location settles against the trusted
222
- // repositories, read from the cached indexes; a definitions file has none.
223
- const trusted = flags.file === undefined ? this.trustedRepos() : [];
224
- const only = await this.resolveSelection(defined, context, selection, trusted);
225
-
226
- const askOptions = {
227
- sousDir: this.configContext.sousDir,
228
- // A project whose conf.d directory has never existed still gets one when
229
- // a mapping record needs writing; the writer creates it.
230
- confDir:
231
- this.configContext.confDir ?? path.join(this.configContext.sousDir, "conf.d"),
232
- interactive: this.interactive,
233
- dryRun,
234
- };
235
-
236
- // Answers supplied on the command line are validated and stored before any
237
- // question is asked, so what is left is exactly what nobody answered.
238
- const supplied = applyProvidedAnswers(defined, provided, context, askOptions);
239
-
240
- const report = await askForMissing(defined, context, {
241
- ...askOptions,
242
- ...(only === undefined ? {} : { only }),
243
- skip: supplied.keys,
244
- reask: flags.all,
245
- });
246
- report.answered.unshift(...supplied.stored);
247
-
248
- blankLine();
249
- for (const line of formatAskReport(report, dryRun)) {
250
- log(line === "" ? "" : indent(line));
251
- }
252
-
253
- footer();
254
- }
255
-
256
- /**
257
- * Turns the reference and the three narrowing flags into the exact set of
258
- * variables to ask, as fully qualified references.
259
- *
260
- * Each step narrows the pool and the next step resolves against what is left,
261
- * so `--namespace workflow apiUrl` asks about the `apiUrl` of that namespace
262
- * even when another namespace declares one too. A step that narrows the pool
263
- * to nothing is an error naming what did it, rather than a run that silently
264
- * asks no questions.
265
- *
266
- * @param defined - Every variable definition in play.
267
- * @param ladder - The environment layers, for resolving environment variable names.
268
- * @param selection - The reference and the flags, as the caller wrote them.
269
- * @param trusted - The trusted repositories, which settle a reference written as a location.
270
- * @returns The variables to ask, or undefined when nothing narrowed anything.
271
- */
272
- private async resolveSelection(
273
- defined: DefinedVariable[],
274
- ladder: LadderContext,
275
- selection: Selection,
276
- trusted: CatalogRepo[] = []
277
- ): Promise<string[] | undefined> {
278
- const lookupOf = (pool: DefinedVariable[]) =>
279
- new ChainedLookup(new VariableLookup(pool, trusted), new EnvVarLookup(pool, ladder));
280
- const { name, repo, namespace, vars } = selection;
281
- if (name === undefined && repo === undefined && namespace === undefined && vars === undefined) {
282
- return undefined;
283
- }
284
-
285
- let pool = defined;
286
-
287
- if (repo !== undefined) {
288
- const match = await this.pick(repo, lookupOf(pool), ["repo"], selection);
289
- pool = pool.filter((entry) => entry.recipe.repo === repoOf(match)?.name);
290
- }
291
-
292
- if (namespace !== undefined) {
293
- const match = await this.pick(namespace, lookupOf(pool), ["namespace"], selection);
294
- pool = narrowTo(pool, match);
295
- }
296
-
297
- if (name !== undefined) {
298
- const match = await this.pick(name, lookupOf(pool), undefined, selection);
299
- pool = narrowTo(pool, match);
300
- }
301
-
302
- if (vars !== undefined) {
303
- const chosen: DefinedVariable[] = [];
304
- for (const wanted of vars) {
305
- const match = await this.pick(wanted, lookupOf(pool), ["variable", "envVar"], selection);
306
- chosen.push(...narrowTo(pool, match));
307
- }
308
- pool = chosen;
309
- }
310
-
311
- if (pool.length === 0) {
312
- throw new ConfigError(
313
- `Nothing is left to ask: ${describeSelection(selection, false)} names no ` +
314
- `variable this project holds.\n${SEE_ALL}`
315
- );
316
- }
317
-
318
- return [...new Set(pool.map((entry) => refKey(variableRefOf(entry))))];
319
- }
320
-
321
- /**
322
- * The trusted repositories as a reference searches them, from the cached
323
- * indexes alone. A project whose repositories cannot be read this way still
324
- * answers every other form of reference, so a failure here is an empty list.
325
- */
326
- private trustedRepos(): CatalogRepo[] {
327
- try {
328
- return subscriptionServiceFor({
329
- configContext: this.configContext,
330
- settings: this.settings,
331
- shellEnv: this.shellEnv,
332
- }).cachedReferenceRepos();
333
- } catch {
334
- return [];
335
- }
336
- }
337
-
338
- /**
339
- * Settles which of the things a reference could have meant this run proceeds
340
- * with, through the rule every command shares.
341
- *
342
- * @param search - The reference exactly as it was written.
343
- * @param lookup - What the reference is searched in.
344
- * @param kinds - The kinds of ref this step accepts; every kind when left out.
345
- * @param selection - The selection, read for `--accept-first`.
346
- */
347
- private async pick(
348
- search: string,
349
- lookup: ChainedLookup,
350
- kinds: readonly RefKind[] | undefined,
351
- selection: Selection
352
- ): Promise<SousRef> {
353
- const { refs } = await sharedRefResolver().resolve(
354
- new RefResolveArguments({
355
- input: search,
356
- lookup,
357
- refusedIsEmpty: true,
358
- ...(kinds === undefined ? {} : { kinds }),
359
- })
360
- );
361
- // A name that answers several variables is one candidate per variable, so
362
- // choosing between them is the same question as choosing between variables.
363
- const candidates = refs.flatMap((ref) =>
364
- ref.kind === "envVar" && (ref.variables?.length ?? 0) > 1
365
- ? ref.variables!.map((variable): SousRef => ({ ...ref, variables: [variable] }))
366
- : [ref]
367
- );
368
- return sharedRefPicker().pick(
369
- candidates,
370
- new RefPickArguments({
371
- search,
372
- interactive: this.interactive,
373
- acceptFirst: selection.acceptFirst,
374
- announce: false,
375
- details: [SEE_ALL],
376
- })
377
- );
378
- }
379
- }
380
-
381
- /**
382
- * The variables one ref covers: a whole repository, namespace, recipe, or one
383
- * variable, or the variable an environment variable name answers.
384
- */
385
- function narrowTo(pool: DefinedVariable[], match: SousRef): DefinedVariable[] {
386
- const target = match.kind === "envVar" ? match.variables?.[0] : match;
387
- if (target === undefined) return [];
388
- const repo = repoOf(target)?.name;
389
- const namespace = namespaceOf(target)?.name;
390
- const recipe = recipeOf(target)?.name;
391
-
392
- return pool.filter((entry) => {
393
- if (repo !== undefined && entry.recipe.repo !== repo) return false;
394
- if (target.kind === "repo") return true;
395
-
396
- if (namespace !== undefined && entry.recipe.namespace !== namespace) return false;
397
- if (target.kind === "namespace") return true;
398
-
399
- if (recipe !== undefined && entry.recipe.name !== recipe) return false;
400
- if (target.kind === "recipe") return true;
401
-
402
- return target.kind === "variable" && entry.definition.name === target.name;
403
- });
404
- }
405
-
406
-
407
- /** What this run is asking about, in the words the header shows. */
408
- function describeSelection(selection: Selection, all: boolean): string {
409
- const parts: string[] = [];
410
- if (selection.repo !== undefined) parts.push(`the repository '${selection.repo}'`);
411
- if (selection.namespace !== undefined) parts.push(`the namespace '${selection.namespace}'`);
412
- if (selection.name !== undefined) parts.push(`'${selection.name}'`);
413
- for (const wanted of selection.vars ?? []) parts.push(`the variable '${wanted}'`);
414
-
415
- if (parts.length === 0) return all ? "every variable" : "everything unanswered";
416
- return parts.join(", ");
417
- }
@@ -1,79 +0,0 @@
1
- /**
2
- * Bare `sous vars`, and `sous vars <name>`.
3
- *
4
- * The canonical commands are `sous vars list` and `sous vars show <name>`; this
5
- * is the shorthand that keeps working, so it prints the listing with no argument
6
- * and the detail with one. It is hidden from the command list so `sous --help`
7
- * names `vars` once, as a topic.
8
- *
9
- * The optional argument works because oclif collates leading space-separated
10
- * words into a command id only while they keep matching a real command:
11
- * `sous vars show` finds the `vars:show` command, and `sous vars apiUrl` does
12
- * not, so it lands here with `apiUrl` as the argument. The one consequence is
13
- * that a variable named `list`, `show` or `ask` cannot be reached this way;
14
- * `sous vars show list` reaches it.
15
- */
16
-
17
- import path from "node:path";
18
- import { Args, Flags } from "@oclif/core";
19
- import { BaseCommand } from "../../base-command.js";
20
- import {
21
- FileDefinitionSource,
22
- loadLadderContext,
23
- loadProjectDefinitions,
24
- printVariableDetail,
25
- printVariableList,
26
- } from "../../lib/vars/index.js";
27
- import { footer, showCommandVars } from "../../utils/formatting.js";
28
-
29
- export default class Vars extends BaseCommand {
30
- static description = "List every variable this project's recipes define, with its answer";
31
-
32
- /** Hidden so `sous --help` names `vars` once, as a topic. */
33
- static hidden = true;
34
-
35
- static examples = ["<%= config.bin %> vars", "<%= config.bin %> vars apiUrl"];
36
-
37
- static args = {
38
- name: Args.string({
39
- description:
40
- "A variable's name (or its namespace/recipe.name key) to show in full",
41
- required: false,
42
- }),
43
- };
44
-
45
- static flags = {
46
- ...BaseCommand.baseFlags,
47
- file: Flags.string({
48
- description:
49
- "Read the variable definitions from a standalone definitions file instead of the project's recipes",
50
- }),
51
- };
52
-
53
- async run(): Promise<void> {
54
- const { args, flags } = await this.parse(Vars);
55
-
56
- showCommandVars({
57
- Project: this.projectLabel,
58
- Config: this.configContext.configPath,
59
- Definitions: flags.file ?? "the project's subscribed recipes",
60
- });
61
-
62
- const source =
63
- flags.file === undefined
64
- ? loadProjectDefinitions(this.settings, this.configContext.sousDir)
65
- : new FileDefinitionSource(path.resolve(process.cwd(), flags.file));
66
- const defined = await source.load();
67
-
68
- const context = loadLadderContext({
69
- sousDir: this.configContext.sousDir,
70
- settings: this.settings,
71
- shellEnv: this.shellEnv,
72
- });
73
-
74
- if (args.name === undefined) printVariableList(defined, context);
75
- else printVariableDetail(defined, context, args.name);
76
-
77
- footer();
78
- }
79
- }
@@ -1,67 +0,0 @@
1
- /**
2
- * `sous vars list`.
3
- *
4
- * Prints every variable in play: what it is called, which recipe published it,
5
- * the environment variable that answered it, the value (hidden when the
6
- * definition says the variable is a secret), and where the value came from.
7
- */
8
-
9
- import path from "node:path";
10
- import { Flags } from "@oclif/core";
11
- import { BaseCommand } from "../../base-command.js";
12
- import {
13
- FileDefinitionSource,
14
- loadLadderContext,
15
- loadProjectDefinitions,
16
- printVariableList,
17
- } from "../../lib/vars/index.js";
18
- import { footer, showCommandVars } from "../../utils/formatting.js";
19
-
20
- export default class VarsList extends BaseCommand {
21
- static description = "List every variable this project's recipes define, with its answer";
22
-
23
- /**
24
- * The other spelling of the topic. It lives under a hidden topic, so it is
25
- * typable everywhere without ever reaching the top-level listing.
26
- */
27
- static aliases = ["var:list"];
28
-
29
- static examples = [
30
- "<%= config.bin %> vars list",
31
- "<%= config.bin %> vars list --file ./questions.yaml",
32
- ];
33
-
34
- static flags = {
35
- ...BaseCommand.baseFlags,
36
- file: Flags.string({
37
- description:
38
- "Read the variable definitions from a standalone definitions file instead of the project's recipes",
39
- }),
40
- };
41
-
42
- async run(): Promise<void> {
43
- const { flags } = await this.parse(VarsList);
44
-
45
- showCommandVars({
46
- Project: this.projectLabel,
47
- Config: this.configContext.configPath,
48
- Definitions: flags.file ?? "the project's subscribed recipes",
49
- });
50
-
51
- const source =
52
- flags.file === undefined
53
- ? loadProjectDefinitions(this.settings, this.configContext.sousDir)
54
- : new FileDefinitionSource(path.resolve(process.cwd(), flags.file));
55
-
56
- printVariableList(
57
- await source.load(),
58
- loadLadderContext({
59
- sousDir: this.configContext.sousDir,
60
- settings: this.settings,
61
- shellEnv: this.shellEnv,
62
- })
63
- );
64
-
65
- footer();
66
- }
67
- }
@@ -1,77 +0,0 @@
1
- /**
2
- * `sous vars show <name>`.
3
- *
4
- * Prints everything about one variable: the question it asks, its documentation
5
- * and constraints, the recipe that published it, where an answer would be
6
- * stored, every environment variable name on the resolution ladder, and which
7
- * rung actually answered.
8
- */
9
-
10
- import path from "node:path";
11
- import { Args, Flags } from "@oclif/core";
12
- import { BaseCommand } from "../../base-command.js";
13
- import {
14
- FileDefinitionSource,
15
- loadLadderContext,
16
- loadProjectDefinitions,
17
- printVariableDetail,
18
- } from "../../lib/vars/index.js";
19
- import { footer, showCommandVars } from "../../utils/formatting.js";
20
-
21
- export default class VarsShow extends BaseCommand {
22
- static description = "Show everything about one variable, including how it was answered";
23
-
24
- /**
25
- * The other spelling of the topic. It lives under a hidden topic, so it is
26
- * typable everywhere without ever reaching the top-level listing.
27
- */
28
- static aliases = ["var:show"];
29
-
30
- static examples = [
31
- "<%= config.bin %> vars show apiUrl",
32
- "<%= config.bin %> vars show workflow/task-files.apiUrl",
33
- ];
34
-
35
- static args = {
36
- name: Args.string({
37
- description: "A variable's name, or its namespace/recipe.name key",
38
- required: true,
39
- }),
40
- };
41
-
42
- static flags = {
43
- ...BaseCommand.baseFlags,
44
- file: Flags.string({
45
- description:
46
- "Read the variable definitions from a standalone definitions file instead of the project's recipes",
47
- }),
48
- };
49
-
50
- async run(): Promise<void> {
51
- const { args, flags } = await this.parse(VarsShow);
52
-
53
- showCommandVars({
54
- Project: this.projectLabel,
55
- Config: this.configContext.configPath,
56
- Definitions: flags.file ?? "the project's subscribed recipes",
57
- });
58
-
59
- const source =
60
- flags.file === undefined
61
- ? loadProjectDefinitions(this.settings, this.configContext.sousDir)
62
- : new FileDefinitionSource(path.resolve(process.cwd(), flags.file));
63
-
64
- printVariableDetail(
65
- await source.load(),
66
- loadLadderContext({
67
- sousDir: this.configContext.sousDir,
68
- settings: this.settings,
69
- shellEnv: this.shellEnv,
70
- }),
71
- args.name,
72
- { sousDir: this.configContext.sousDir }
73
- );
74
-
75
- footer();
76
- }
77
- }
@@ -1,30 +0,0 @@
1
- import { BaseCommand } from "./base-command.js";
2
- import { headerTo } from "./utils/formatting.js";
3
-
4
- /**
5
- * Base class for the `sous config *` inspection commands (`show`, `get`).
6
- *
7
- * These commands emit machine-readable output (JSON, or a raw scalar) to stdout
8
- * so `sous config show | jq` and friends work. The decorative CLI header would
9
- * otherwise land on stdout during BaseCommand.init() and corrupt that output, so
10
- * it is routed to stderr here instead. Discovery, env loading, flag parsing and
11
- * ConfigError rendering are all inherited unchanged from BaseCommand.
12
- */
13
- export abstract class ConfigCommand extends BaseCommand {
14
- private static writeStderr(line: string): void {
15
- process.stderr.write(`${line}\n`);
16
- }
17
-
18
- protected emitHeader(): void {
19
- headerTo(ConfigCommand.writeStderr);
20
- }
21
-
22
- /**
23
- * Route init()/catch() error rendering to stderr too. Otherwise a discovery or
24
- * config-load failure (a not-found config, an invalid merged config, or a
25
- * missing `config get` path) would print the red error block to stdout for the
26
- * very commands whose contract is clean, machine-readable stdout — corrupting
27
- * `sous config show | jq` and any script capturing stdout.
28
- */
29
- protected errorSink: (line: string) => void = ConfigCommand.writeStderr;
30
- }