@sous-io/sous 0.2.37 → 1.0.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.
Files changed (244) hide show
  1. package/README.md +14 -256
  2. package/dist/bin.js +11 -0
  3. package/dist/sous.js +1094 -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 -245
  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 -410
  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 -413
  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 -1107
  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,615 +0,0 @@
1
- /**
2
- * The provider interface.
3
- *
4
- * A provider knows how to talk to one kind of repository host. Version one
5
- * ships exactly two, GitHub and GitLab, and the interface stays INTERNAL: it is
6
- * not a published plugin API yet, so it can still change shape while the rest
7
- * of the Repositories system settles.
8
- *
9
- * A provider does only two things on the read path, and both are deliberately
10
- * small: hand back a repo's index file, and fetch one recipe's subtree at one
11
- * tag. Anything larger (a full clone) belongs to the authoring workflow, not to
12
- * installing recipes.
13
- *
14
- * The write path is just as small, and it is the ONLY place a host's own
15
- * mechanics are allowed to live. Everything host-specific (which command line
16
- * tool is driven, how a fork is made, what a proposal is called, how one is
17
- * opened) belongs to a provider; the services above it ask in order and report
18
- * what came back. A provider declares `submit` in its features once it answers
19
- * the write side; one that does not may leave those methods out entirely, and
20
- * `ProviderBase` answers them with a clear refusal naming the provider and the
21
- * feature.
22
- */
23
-
24
- import { ConfigError } from "../../errors.js";
25
- import type { CommandRunner } from "./git.js";
26
- import type { FetchLike } from "./http.js";
27
-
28
- /**
29
- * What a provider can do. `fetch` is the read path every provider implements;
30
- * `submit` is the propose-a-change path; `proposals` is looking a proposal up
31
- * again afterwards (finding the one a branch already has, reporting its status,
32
- * and replacing its title or body), which is what lets `sous repo submit`
33
- * handle a proposal's whole life rather than only its first day. A provider
34
- * declares a feature only once it genuinely supports it.
35
- */
36
- export type ProviderFeature = "fetch" | "submit" | "proposals";
37
-
38
- /**
39
- * The identifier a repo entry uses to name its provider explicitly. `local` is a
40
- * repository on this machine, for local development and for tests; its trust
41
- * semantics are identical to a hosted one.
42
- */
43
- export type ProviderId = "github" | "gitlab" | "local";
44
-
45
- /** A repository URL, taken apart into the pieces every provider needs. */
46
- export type CanonicalRepo = {
47
- /** The host the repository lives on, such as `github.com`. */
48
- host: string;
49
- /** The owning user, organization or group path. */
50
- owner: string;
51
- /** The repository's own name, with any `.git` suffix removed. */
52
- name: string;
53
- /** The canonical HTTPS clone URL. */
54
- httpsUrl: string;
55
- /** The canonical SSH clone URL. */
56
- sshUrl: string;
57
- };
58
-
59
- /**
60
- * A location as written in a ref, taken apart only as far as every host agrees:
61
- * the host, and the path segments after it. Which of those segments are the
62
- * repository and which name something inside it is a host's own rule, so a
63
- * provider answers that (see `RepoProvider.readLocation`).
64
- *
65
- * https://gitlab.com/group/sub/project/-/tree/main/recipes/workflow/alpha
66
- * // -> { host: "gitlab.com",
67
- * // segments: ["group", "sub", "project", "-", "tree", "main", ...] }
68
- */
69
- export type WrittenLocation = {
70
- /** The host, lowercased, such as `github.com`. */
71
- host: string;
72
- /** Every path segment after the host, in order, none of them empty. */
73
- segments: string[];
74
- };
75
-
76
- /**
77
- * One way a provider reads a written location. A location on a host where a
78
- * repository path can be any length (a GitLab group nests) may have several.
79
- */
80
- export type LocationReading = {
81
- /** The repository's path on the host, such as `sous-io/sous-recipes`, with no `.git`. */
82
- repoPath: string;
83
- /**
84
- * The segments after the repository that name something inside it: a
85
- * namespace, a namespace and a recipe, or a namespace and `*`. Empty when the
86
- * location names only the repository.
87
- */
88
- named?: string[];
89
- /**
90
- * A browser URL's path inside the repository, copied from a host's file view
91
- * (after `tree/` or `blob/`), with the branch still in front of it. It is
92
- * settled against the `path` each recipe's index entry records.
93
- */
94
- browsed?: string;
95
- };
96
-
97
- /** Options every provider call accepts, all of them for testing seams. */
98
- export type ProviderOptions = {
99
- /**
100
- * The directory subprocesses run in. A write-path call is made from inside
101
- * the contributor's checkout, so the host's own command line tool reads the
102
- * repository the contributor is standing in.
103
- */
104
- cwd?: string;
105
- /** The environment to read tokens from. Defaults to `process.env`. */
106
- env?: NodeJS.ProcessEnv;
107
- /** The fetch implementation to use. Defaults to the global `fetch`. */
108
- fetchImpl?: FetchLike;
109
- /** How subprocesses are run. Defaults to spawning a real process. */
110
- run?: CommandRunner;
111
- /** Cancels an index request, for a caller that will not wait past a deadline. */
112
- signal?: AbortSignal;
113
- };
114
-
115
- /** What an index fetch returns. */
116
- export type FetchedIndex = {
117
- /** The raw text of the index file, still to be parsed and validated. */
118
- text: string;
119
- /** The git ref the file was read at, for the record. */
120
- ref: string;
121
- /** The entity tag the host sent, when it sent one. */
122
- etag?: string;
123
- };
124
-
125
- /** What a provider's command line tool is called, and where to get it. */
126
- export type ProviderCli = {
127
- /** The executable, spelled the way it is typed. */
128
- command: string;
129
- /** The plain-language name used in messages, such as `the GitHub CLI`. */
130
- label: string;
131
- /** Where the tool is installed from, for the message that says it is missing. */
132
- install: string;
133
- };
134
-
135
- /** What an authentication check found. */
136
- export type AuthStatus = {
137
- /** True when sous can act on the contributor's behalf here. */
138
- ok: boolean;
139
- /**
140
- * One plain-language explanation, ready to print: what is signed in when the
141
- * check passed, and what to install or run when it did not.
142
- */
143
- detail: string;
144
- };
145
-
146
- /** Where a fork of a repository ended up. */
147
- export type ForkedRepo = {
148
- /** The account the fork lives under. */
149
- owner: string;
150
- /** The fork's repository name. */
151
- name: string;
152
- /** The fork's HTTPS clone URL. */
153
- httpsUrl: string;
154
- /** The fork's SSH clone URL. */
155
- sshUrl: string;
156
- };
157
-
158
- /** One change, described the way every provider needs to hear about it. */
159
- export type ChangeProposal = {
160
- /** The branch the change is on. */
161
- branch: string;
162
- /** The branch the proposal targets, when the caller knows it. */
163
- base?: string;
164
- /** The proposal's title. */
165
- title: string;
166
- /** The proposal's body. */
167
- body: string;
168
- /** True when the proposal should be opened as a draft. */
169
- draft: boolean;
170
- /**
171
- * The account the branch was pushed to, when that is not the repository
172
- * itself. Only a provider knows how a cross-repository proposal is spelled,
173
- * so it is handed the owner and composes the rest.
174
- */
175
- head?: { owner: string };
176
- };
177
-
178
- /** What proposing a change produced. */
179
- export type ProposedChange = {
180
- /** The proposal's address, when the provider gave one. */
181
- url?: string;
182
- /** One plain-language line about what happened, ready to print. */
183
- detail: string;
184
- };
185
-
186
- /** Where a proposal stands: still open, merged, or closed without merging. */
187
- export type ProposalState = "open" | "merged" | "closed";
188
-
189
- /** One proposal, as plain data every provider can describe. */
190
- export type ProposalSummary = {
191
- /** How the provider identifies it, such as a pull request number. */
192
- id: string;
193
- /** Its address, when the provider reported one. */
194
- url?: string;
195
- /** Where it stands. */
196
- state: ProposalState;
197
- /** Its current title. */
198
- title: string;
199
- /** True when it is a draft. */
200
- draft: boolean;
201
- /** The branch it targets, when the provider reported it. */
202
- base?: string;
203
- };
204
-
205
- /** Which proposal to look for: the one a branch was pushed for. */
206
- export type ProposalQuery = {
207
- /** The branch the change is on. */
208
- branch: string;
209
- /**
210
- * True when the branch lives on a fork rather than in the repository itself.
211
- * A proposal from a fork is found by the fork's owner as well as the branch,
212
- * so two contributors' branches of the same name are never confused.
213
- */
214
- fromFork: boolean;
215
- /**
216
- * The account the fork lives under, when the caller knows it. Left out, the
217
- * provider asks the host which account is signed in.
218
- */
219
- forkOwner?: string;
220
- };
221
-
222
- /** How a proposal's review is going, in words every host can be mapped onto. */
223
- export type ProposalReview = "approved" | "changes requested" | "review required";
224
-
225
- /** How the automated checks on a proposal stand, counted. */
226
- export type ProposalChecks = {
227
- passed: number;
228
- failed: number;
229
- pending: number;
230
- };
231
-
232
- /** Everything a status report says about one proposal. */
233
- export type ProposalStatus = {
234
- /** The proposal itself. */
235
- proposal: ProposalSummary;
236
- /** How its review is going, when the host reports it. */
237
- review?: ProposalReview;
238
- /** How its checks stand, when it has any. */
239
- checks?: ProposalChecks;
240
- /** Whether it can be merged as it stands; undefined when the host is still working it out. */
241
- mergeable?: boolean;
242
- };
243
-
244
- /** What to replace on an open proposal. A field left out is left as it is. */
245
- export type ProposalUpdate = {
246
- title?: string;
247
- body?: string;
248
- };
249
-
250
- /** One repository host sous knows how to read from. */
251
- export interface RepoProvider {
252
- /** The provider's stable identifier, as written in a repo config entry. */
253
- readonly id: ProviderId;
254
- /** What this provider can do; see ProviderFeature. */
255
- readonly features: ProviderFeature[];
256
- /**
257
- * The host a locator such as `github://owner/repo/...` means when it names
258
- * none. A provider without a public host of its own leaves it out.
259
- */
260
- readonly defaultHost?: string;
261
- /**
262
- * Every way this provider reads a location inside one of its repositories:
263
- * where the repository path ends, and whether the rest names a namespace, a
264
- * recipe or a browser path. An empty list means the provider reads no such
265
- * location.
266
- */
267
- readLocation(location: WrittenLocation): LocationReading[];
268
- /**
269
- * The canonical locator for something inside one of this provider's
270
- * repositories, which is how sous prints a located ref.
271
- *
272
- * @param host - The repository's host.
273
- * @param repoPath - The repository's path on that host.
274
- * @param rest - What is named inside it, such as `workflow/alpha`.
275
- */
276
- formatLocator(host: string, repoPath: string, rest: string): string;
277
- /** True when this provider handles the given repository URL. */
278
- matches(url: string): boolean;
279
- /** Takes a repository URL apart, raising a ConfigError when it does not fit. */
280
- canonicalize(url: string): CanonicalRepo;
281
- /** Fetches the repo's `sous.index.json` at its default branch. */
282
- fetchIndex(repo: CanonicalRepo, options?: ProviderOptions): Promise<FetchedIndex>;
283
- /**
284
- * Fetches ONE recipe's subtree at one tag into `destDir`, never the whole
285
- * repository.
286
- *
287
- * @param repo - The canonicalized repository.
288
- * @param recipePath - The recipe folder's path, relative to the repo root.
289
- * @param tag - The git tag carrying the version to fetch.
290
- * @param destDir - Where the recipe's files should end up.
291
- * @param options - Testing seams.
292
- */
293
- fetchRecipeTree(
294
- repo: CanonicalRepo,
295
- recipePath: string,
296
- tag: string,
297
- destDir: string,
298
- options?: ProviderOptions
299
- ): Promise<void>;
300
-
301
- // --- The write path, answered by a provider that declares `submit` --------
302
-
303
- /** The command line tool this provider drives, when it has one. */
304
- readonly cli?: ProviderCli;
305
- /**
306
- * What this provider calls a proposal, such as `pull request` or `merge
307
- * request`. It is what the submission prints as it goes.
308
- */
309
- readonly proposalNoun?: string;
310
- /** Whether sous can act on the contributor's behalf here, and why not. */
311
- authStatus?(options?: ProviderOptions): Promise<AuthStatus>;
312
- /**
313
- * Whether the contributor may push to the repository itself. Undefined means
314
- * the provider genuinely cannot tell, which is not the same as `false`.
315
- */
316
- canPush?(repo: CanonicalRepo, options?: ProviderOptions): Promise<boolean | undefined>;
317
- /** Forks the repository onto the contributor's own account. */
318
- fork?(repo: CanonicalRepo, options?: ProviderOptions): Promise<ForkedRepo>;
319
- /** Opens a proposal for a branch that has already been pushed. */
320
- proposeChange?(
321
- repo: CanonicalRepo,
322
- proposal: ChangeProposal,
323
- options?: ProviderOptions
324
- ): Promise<ProposedChange>;
325
-
326
- // --- Proposals after the fact, answered by a provider that declares `proposals`
327
-
328
- /**
329
- * The proposal a branch was pushed for, or undefined when it has none. When a
330
- * branch has had several, the open one wins, and otherwise the newest.
331
- */
332
- findProposal?(
333
- repo: CanonicalRepo,
334
- query: ProposalQuery,
335
- options?: ProviderOptions
336
- ): Promise<ProposalSummary | undefined>;
337
- /** Where one proposal stands: its state, its review and its checks. */
338
- proposalStatus?(
339
- repo: CanonicalRepo,
340
- id: string,
341
- options?: ProviderOptions
342
- ): Promise<ProposalStatus>;
343
- /** Replaces an open proposal's title, its body, or both. */
344
- updateProposal?(
345
- repo: CanonicalRepo,
346
- id: string,
347
- update: ProposalUpdate,
348
- options?: ProviderOptions
349
- ): Promise<ProposedChange>;
350
- }
351
-
352
- /**
353
- * A provider that can find a proposal again, report on it and change its text.
354
- * This is what declaring the `proposals` feature promises.
355
- */
356
- export type ProposalCapableProvider = RepoProvider &
357
- Required<Pick<RepoProvider, "findProposal" | "proposalStatus" | "updateProposal">>;
358
-
359
- /**
360
- * True when a provider declares the `proposals` feature and really does answer
361
- * all three calls behind it.
362
- *
363
- * @param provider - The provider to test.
364
- */
365
- export function supportsProposals(
366
- provider: RepoProvider
367
- ): provider is ProposalCapableProvider {
368
- return (
369
- provider.features.includes("proposals") &&
370
- typeof provider.findProposal === "function" &&
371
- typeof provider.proposalStatus === "function" &&
372
- typeof provider.updateProposal === "function"
373
- );
374
- }
375
-
376
- /**
377
- * A provider that answers the whole write path. This is what declaring the
378
- * `submit` feature promises, and `supportsSubmit` is how a caller gets from the
379
- * one to the other without ever naming a provider.
380
- */
381
- export type SubmitCapableProvider = RepoProvider &
382
- Required<Pick<RepoProvider, "authStatus" | "canPush" | "fork" | "proposeChange">>;
383
-
384
- /**
385
- * True when a provider declares the `submit` feature and really does answer
386
- * every write-path call. It narrows the type, so a caller that has asked once
387
- * never has to test a method for existence again.
388
- *
389
- * @param provider - The provider to test.
390
- */
391
- export function supportsSubmit(provider: RepoProvider): provider is SubmitCapableProvider {
392
- return (
393
- provider.features.includes("submit") &&
394
- typeof provider.authStatus === "function" &&
395
- typeof provider.canPush === "function" &&
396
- typeof provider.fork === "function" &&
397
- typeof provider.proposeChange === "function"
398
- );
399
- }
400
-
401
- /** The words a host's file view puts between a repository and a path: `tree/` and `blob/`. */
402
- const BROWSE_WORDS = new Set(["tree", "blob"]);
403
-
404
- /**
405
- * A repository's last path segment without a `.git` suffix.
406
- *
407
- * @param segment - The segment as written.
408
- */
409
- export function withoutGitSuffix(segment: string): string {
410
- return segment.replace(/\.git$/i, "");
411
- }
412
-
413
- /**
414
- * Every reading of the segments after a repository: what they name, and, when
415
- * they start with `tree/` or `blob/`, the browser path they could also be. Both
416
- * are returned when both are possible (a namespace could be called `tree`), and
417
- * the caller keeps the one the repository's index confirms. A leading `-`
418
- * segment (GitLab's separator between a project and its pages) is dropped
419
- * first, wherever it is written.
420
- *
421
- * readingsAfterRepository("o/r", ["tree", "main", "recipes", "workflow", "alpha"]);
422
- * // -> [{ repoPath: "o/r", browsed: "main/recipes/workflow/alpha" },
423
- * // { repoPath: "o/r", named: ["tree", "main", "recipes", "workflow", "alpha"] }]
424
- *
425
- * @param repoPath - The repository's path on its host.
426
- * @param rest - The segments after it.
427
- */
428
- export function readingsAfterRepository(repoPath: string, rest: string[]): LocationReading[] {
429
- const after = rest[0] === "-" ? rest.slice(1) : rest;
430
- const readings: LocationReading[] = [];
431
- if (after.length >= 2 && BROWSE_WORDS.has(after[0]!.toLowerCase())) {
432
- readings.push({ repoPath, browsed: after.slice(1).join("/") });
433
- }
434
- readings.push({ repoPath, named: after });
435
- return readings;
436
- }
437
-
438
- /**
439
- * Normalizes a repository URL for matching: trims it, drops a trailing slash
440
- * and a trailing `.git`, and lowercases the scheme and host only.
441
- *
442
- * @param url - The URL as configured.
443
- */
444
- export function normalizeRepoUrl(url: string): string {
445
- let value = url.trim();
446
- while (value.endsWith("/")) value = value.slice(0, -1);
447
- if (value.endsWith(".git")) value = value.slice(0, -4);
448
- return value;
449
- }
450
-
451
- /**
452
- * Takes a repository URL apart into host, owner and name. Accepts the HTTPS
453
- * form, the `scp`-style SSH form (`git@host:owner/name.git`) and the
454
- * `ssh://host/owner/name` form, because all three are what people paste.
455
- *
456
- * Returns undefined rather than throwing, so a provider's `matches` can use it.
457
- *
458
- * @param url - The URL as configured.
459
- */
460
- export function splitRepoUrl(
461
- url: string
462
- ): { host: string; owner: string; name: string } | undefined {
463
- const normalized = normalizeRepoUrl(url);
464
- if (normalized.length === 0) return undefined;
465
-
466
- let host: string;
467
- let repoPath: string;
468
-
469
- const scpMatch = /^(?:([^@\s/]+)@)?([^@\s/:]+):(.+)$/.exec(normalized);
470
- if (/^[a-z][a-z0-9+.-]*:\/\//i.test(normalized)) {
471
- let parsed: URL;
472
- try {
473
- parsed = new URL(normalized);
474
- } catch {
475
- return undefined;
476
- }
477
- host = parsed.host.toLowerCase();
478
- repoPath = parsed.pathname.replace(/^\/+/, "");
479
- } else if (scpMatch !== null) {
480
- host = scpMatch[2]!.toLowerCase();
481
- repoPath = scpMatch[3]!.replace(/^\/+/, "");
482
- } else {
483
- return undefined;
484
- }
485
-
486
- const segments = repoPath.split("/").filter((segment) => segment.length > 0);
487
- if (segments.length < 2 || host.length === 0) return undefined;
488
-
489
- const name = segments[segments.length - 1]!;
490
- const owner = segments.slice(0, -1).join("/");
491
- return { host, owner, name };
492
- }
493
-
494
- /**
495
- * Builds the canonical form of a repository URL for a given host style.
496
- *
497
- * @param host - The repository host.
498
- * @param owner - The owning user, organization or group path.
499
- * @param name - The repository name.
500
- */
501
- export function buildCanonicalRepo(
502
- host: string,
503
- owner: string,
504
- name: string
505
- ): CanonicalRepo {
506
- return {
507
- host,
508
- owner,
509
- name,
510
- httpsUrl: `https://${host}/${owner}/${name}.git`,
511
- sshUrl: `git@${host}:${owner}/${name}.git`,
512
- };
513
- }
514
-
515
- /**
516
- * Raises the standard ConfigError for a URL a provider cannot take apart.
517
- *
518
- * @param providerId - Which provider rejected it.
519
- * @param url - The offending URL.
520
- */
521
- export function invalidRepoUrl(providerId: ProviderId, url: string): ConfigError {
522
- return new ConfigError(
523
- `'${url}' is not a ${providerId} repository URL that sous can read.\n` +
524
- ` A repository URL names an owner and a repository, as in ` +
525
- `'https://${providerId}.com/owner/repository'.`
526
- );
527
- }
528
-
529
- /**
530
- * Finds the provider that handles a repository URL among the ones given, or
531
- * undefined when none does. `providers/index.ts` wraps this with the built-in
532
- * provider list, which is what callers normally use.
533
- *
534
- * @param url - The repository URL.
535
- * @param providers - The providers to consider.
536
- */
537
- export function detectProviderIn(
538
- url: string,
539
- providers: RepoProvider[]
540
- ): RepoProvider | undefined {
541
- return providers.find((provider) => provider.matches(url));
542
- }
543
-
544
- /**
545
- * Looks a provider up by its identifier among the ones given, or undefined when
546
- * there is none.
547
- *
548
- * @param id - The provider identifier from a repo config entry.
549
- * @param providers - The providers to consider.
550
- */
551
- export function providerByIdIn(
552
- id: string,
553
- providers: RepoProvider[]
554
- ): RepoProvider | undefined {
555
- return providers.find((provider) => provider.id === id);
556
- }
557
-
558
- /**
559
- * Finds the provider for a repo entry: the one it names, otherwise the one that
560
- * recognizes its URL. Raises a ConfigError naming the URL and listing the
561
- * providers sous knows when neither works, and another when the named provider
562
- * contradicts a URL a different provider plainly owns.
563
- *
564
- * @param url - The repository URL.
565
- * @param providerId - The provider named by the repo entry, when it named one.
566
- * @param providers - The providers to consider.
567
- */
568
- export function requireProviderIn(
569
- url: string,
570
- providerId: string | undefined,
571
- providers: RepoProvider[]
572
- ): RepoProvider {
573
- const known = providers.map((entry) => entry.id).join(", ");
574
-
575
- if (providerId !== undefined) {
576
- const named = providerByIdIn(providerId, providers);
577
- if (named === undefined) {
578
- throw new ConfigError(
579
- `The repository at ${url} names the provider '${providerId}', which sous does not ` +
580
- `have.\n Sous ships these providers: ${known}.`
581
- );
582
- }
583
-
584
- // A named provider is honoured for a host nobody recognizes, which is what a
585
- // self-hosted instance needs. It is refused only when a DIFFERENT provider
586
- // plainly owns the URL, because that is a contradiction rather than a hint.
587
- if (!named.matches(url)) {
588
- const owner = detectProviderIn(url, providers);
589
- if (owner !== undefined) {
590
- throw new ConfigError(
591
- `The ${named.id} provider does not handle ${url}; that is ` +
592
- `${describeRepoUrl(owner.id)}, which the ${owner.id} provider handles.\n` +
593
- ` Drop '--provider' and let sous work it out, or name '${owner.id}'.`
594
- );
595
- }
596
- }
597
-
598
- return named;
599
- }
600
-
601
- const detected = detectProviderIn(url, providers);
602
- if (detected !== undefined) return detected;
603
-
604
- throw new ConfigError(
605
- `Sous does not recognize the host in the repository URL ${url}.\n` +
606
- ` Sous ships these providers: ${known}. For a self-hosted instance, name the ` +
607
- `provider that host runs on the repository entry, as in ` +
608
- `'sous repo add ${url} --provider <provider>'.`
609
- );
610
- }
611
-
612
- /** How a URL a provider recognizes is described in a mismatch message. */
613
- function describeRepoUrl(providerId: ProviderId): string {
614
- return providerId === "local" ? "a local path" : `a ${providerId} URL`;
615
- }