@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.
- package/README.md +14 -256
- package/dist/bin.js +11 -0
- package/dist/sous.js +1094 -0
- package/docs/markdown/README.md +5 -4
- package/docs/markdown/_sidebar.md +21 -1
- package/docs/markdown/commands.md +294 -431
- package/docs/markdown/config-discovery.md +25 -14
- package/docs/markdown/config-inspection.md +53 -31
- package/docs/markdown/config-layers.md +168 -66
- package/docs/markdown/config-variables.md +44 -11
- package/docs/markdown/configuration.md +104 -17
- package/docs/markdown/design-principles.md +4 -1
- package/docs/markdown/repositories-authoring.md +30 -0
- package/docs/markdown/repositories-consuming.md +72 -14
- package/docs/markdown/repositories-file-formats.md +31 -13
- package/docs/markdown/repositories-providers.md +48 -57
- package/docs/markdown/repositories-quickstart.md +3 -2
- package/docs/markdown/repositories-troubleshooting.md +27 -1
- package/docs/markdown/repositories-variables.md +6 -1
- package/docs/markdown/repositories.md +18 -8
- package/package.json +48 -107
- package/recipes/core/sous-skills/skills/about-liquid-templates/SKILL.tpl.md +4 -6
- package/recipes/core/sous-skills/skills/about-liquid-templates/references/liquid-filters.tpl.md +2 -0
- package/recipes/core/sous-skills/skills/about-sous/SKILL.tpl.md +10 -9
- package/recipes/core/sous-skills/skills/about-sous-configuration/SKILL.tpl.md +35 -14
- package/recipes/core/sous-skills/skills/about-sous-recipes/SKILL.tpl.md +51 -20
- package/recipes/core/sous-skills/sous.recipe.yaml +1 -1
- package/sous.config.schema.json +351 -132
- package/bin/run.js +0 -51
- package/src/base-command.ts +0 -457
- package/src/commands/build.ts +0 -181
- package/src/commands/clear.ts +0 -79
- package/src/commands/compile.ts +0 -155
- package/src/commands/config/get.ts +0 -110
- package/src/commands/config/show.ts +0 -32
- package/src/commands/config/validate.ts +0 -53
- package/src/commands/help.ts +0 -46
- package/src/commands/init.ts +0 -289
- package/src/commands/launch.ts +0 -139
- package/src/commands/lock/rebuild.ts +0 -239
- package/src/commands/lock/show.ts +0 -115
- package/src/commands/namespace/list.ts +0 -138
- package/src/commands/namespace/show.ts +0 -130
- package/src/commands/prune.ts +0 -43
- package/src/commands/recipe/list.ts +0 -112
- package/src/commands/recipe/show.ts +0 -439
- package/src/commands/repo/add.ts +0 -145
- package/src/commands/repo/contribute.ts +0 -604
- package/src/commands/repo/gc.ts +0 -173
- package/src/commands/repo/init.ts +0 -136
- package/src/commands/repo/link.ts +0 -899
- package/src/commands/repo/list.ts +0 -236
- package/src/commands/repo/release.ts +0 -685
- package/src/commands/repo/remove.ts +0 -194
- package/src/commands/repo/search.ts +0 -243
- package/src/commands/repo/submit.ts +0 -341
- package/src/commands/repo/unlink.ts +0 -461
- package/src/commands/subscription/add.ts +0 -286
- package/src/commands/subscription/list.ts +0 -208
- package/src/commands/subscription/remove.ts +0 -182
- package/src/commands/subscription/update.ts +0 -216
- package/src/commands/update.ts +0 -178
- package/src/commands/vars/ask.ts +0 -417
- package/src/commands/vars/index.ts +0 -79
- package/src/commands/vars/list.ts +0 -67
- package/src/commands/vars/show.ts +0 -77
- package/src/config-command.ts +0 -30
- package/src/lib/build-preparation.ts +0 -128
- package/src/lib/build-service.ts +0 -580
- package/src/lib/config-discovery.ts +0 -378
- package/src/lib/config-inspect.ts +0 -145
- package/src/lib/config-kernel.mjs +0 -379
- package/src/lib/config-schema.ts +0 -401
- package/src/lib/env-file.ts +0 -328
- package/src/lib/env-local.ts +0 -212
- package/src/lib/errors.ts +0 -32
- package/src/lib/glob-pattern.ts +0 -24
- package/src/lib/include-resolver.ts +0 -523
- package/src/lib/interactive.ts +0 -165
- package/src/lib/markdown-compiler.ts +0 -947
- package/src/lib/package-info.ts +0 -25
- package/src/lib/pid-service.ts +0 -99
- package/src/lib/project-install.d.mts +0 -44
- package/src/lib/project-install.mjs +0 -234
- package/src/lib/project-scaffold/index.ts +0 -332
- package/src/lib/project-scaffold/templates.ts +0 -240
- package/src/lib/repos/catalog-display.ts +0 -215
- package/src/lib/repos/catalog-inputs.ts +0 -288
- package/src/lib/repos/catalog.ts +0 -842
- package/src/lib/repos/contribute.ts +0 -452
- package/src/lib/repos/core-recipe.ts +0 -105
- package/src/lib/repos/declarations.ts +0 -187
- package/src/lib/repos/defaults.ts +0 -175
- package/src/lib/repos/formats/common.ts +0 -440
- package/src/lib/repos/formats/index-file.ts +0 -253
- package/src/lib/repos/formats/links-map.ts +0 -98
- package/src/lib/repos/formats/lockfile.ts +0 -169
- package/src/lib/repos/formats/patterns.ts +0 -57
- package/src/lib/repos/formats/recipe-manifest.ts +0 -205
- package/src/lib/repos/formats/repo-manifest.ts +0 -97
- package/src/lib/repos/formats/store-entry.ts +0 -84
- package/src/lib/repos/formats/variable-definition.ts +0 -268
- package/src/lib/repos/freshness.ts +0 -264
- package/src/lib/repos/git-clone.ts +0 -850
- package/src/lib/repos/identity.ts +0 -89
- package/src/lib/repos/index.ts +0 -57
- package/src/lib/repos/links.ts +0 -356
- package/src/lib/repos/load-manifest.ts +0 -237
- package/src/lib/repos/lock-service.ts +0 -454
- package/src/lib/repos/locked-namespace-resolver.ts +0 -232
- package/src/lib/repos/locked-recipe-lookup.ts +0 -123
- package/src/lib/repos/locked-recipes.ts +0 -254
- package/src/lib/repos/managed-layer.ts +0 -422
- package/src/lib/repos/memories-hash-name.ts +0 -33
- package/src/lib/repos/namespace-resolver.ts +0 -527
- package/src/lib/repos/providers/base.ts +0 -268
- package/src/lib/repos/providers/git.ts +0 -245
- package/src/lib/repos/providers/github.ts +0 -589
- package/src/lib/repos/providers/gitlab.ts +0 -332
- package/src/lib/repos/providers/http.ts +0 -128
- package/src/lib/repos/providers/index-cache.ts +0 -421
- package/src/lib/repos/providers/index.ts +0 -106
- package/src/lib/repos/providers/local.ts +0 -410
- package/src/lib/repos/providers/provider.ts +0 -615
- package/src/lib/repos/recipe-config-layers.ts +0 -316
- package/src/lib/repos/recipe-key-matcher.ts +0 -62
- package/src/lib/repos/recipe-memories.ts +0 -178
- package/src/lib/repos/recipe-targets.ts +0 -236
- package/src/lib/repos/ref-search.ts +0 -46
- package/src/lib/repos/release/bump.ts +0 -161
- package/src/lib/repos/release/changelog.ts +0 -461
- package/src/lib/repos/release/git-state.ts +0 -460
- package/src/lib/repos/release/index-builder.ts +0 -1108
- package/src/lib/repos/release/index.ts +0 -19
- package/src/lib/repos/release/plan.ts +0 -521
- package/src/lib/repos/release/settle.ts +0 -154
- package/src/lib/repos/release/submissions.ts +0 -214
- package/src/lib/repos/release/submit-checkout.ts +0 -290
- package/src/lib/repos/release/submit-questions.ts +0 -153
- package/src/lib/repos/release/submit-service.ts +0 -898
- package/src/lib/repos/release/tags.ts +0 -239
- package/src/lib/repos/release/validate.ts +0 -463
- package/src/lib/repos/resolver.ts +0 -1028
- package/src/lib/repos/scaffold/index.ts +0 -245
- package/src/lib/repos/scaffold/templates.ts +0 -517
- package/src/lib/repos/seed.ts +0 -473
- package/src/lib/repos/store/contract.ts +0 -64
- package/src/lib/repos/store/hash.ts +0 -191
- package/src/lib/repos/store/recipe-store.ts +0 -599
- package/src/lib/repos/store/settings.ts +0 -58
- package/src/lib/repos/subscription-service.ts +0 -3859
- package/src/lib/repos/trust.ts +0 -447
- package/src/lib/repos/update-plan.ts +0 -235
- package/src/lib/self-update/index.ts +0 -14
- package/src/lib/self-update/installs.ts +0 -413
- package/src/lib/self-update/managers.ts +0 -126
- package/src/lib/self-update/plan.ts +0 -315
- package/src/lib/self-update/registry.ts +0 -99
- package/src/lib/self-update/run.ts +0 -506
- package/src/lib/self-update/versions.ts +0 -243
- package/src/lib/settings.ts +0 -1107
- package/src/lib/sous-home.ts +0 -104
- package/src/lib/state.ts +0 -167
- package/src/lib/vars/answers.ts +0 -192
- package/src/lib/vars/ask.ts +0 -1152
- package/src/lib/vars/definition-source.ts +0 -261
- package/src/lib/vars/display.ts +0 -231
- package/src/lib/vars/index.ts +0 -20
- package/src/lib/vars/ladder.ts +0 -282
- package/src/lib/vars/mappings.ts +0 -260
- package/src/lib/vars/names.ts +0 -94
- package/src/lib/vars/preanswers.ts +0 -395
- package/src/lib/vars/question-plan.ts +0 -241
- package/src/lib/vars/report.ts +0 -228
- package/src/lib/vars/safe-regex.ts +0 -235
- package/src/lib/vars/secrets.ts +0 -131
- package/src/lib/vars/validate.ts +0 -312
- package/src/lib/version-report.ts +0 -90
- package/src/lib/watch-loop.ts +0 -148
- package/src/lib/watch-service.ts +0 -116
- package/src/services/ref-resolver/container.ts +0 -107
- package/src/services/ref-resolver/dependency.ts +0 -60
- package/src/services/ref-resolver/format.ts +0 -197
- package/src/services/ref-resolver/glob.ts +0 -115
- package/src/services/ref-resolver/hash-names.ts +0 -155
- package/src/services/ref-resolver/index.ts +0 -55
- package/src/services/ref-resolver/injectable.ts +0 -36
- package/src/services/ref-resolver/location.ts +0 -62
- package/src/services/ref-resolver/lookups/cached-index-lookup.ts +0 -53
- package/src/services/ref-resolver/lookups/catalog-lookup.ts +0 -23
- package/src/services/ref-resolver/lookups/catalog-matcher.ts +0 -261
- package/src/services/ref-resolver/lookups/chained-lookup.ts +0 -27
- package/src/services/ref-resolver/lookups/env-var-lookup.ts +0 -65
- package/src/services/ref-resolver/lookups/fetched-index-lookup.ts +0 -150
- package/src/services/ref-resolver/lookups/recorded-dependency-lookup.ts +0 -62
- package/src/services/ref-resolver/lookups/ref-lookup.ts +0 -54
- package/src/services/ref-resolver/lookups/variable-lookup.ts +0 -123
- package/src/services/ref-resolver/named-refs.ts +0 -144
- package/src/services/ref-resolver/parser/env-var-splitter.ts +0 -27
- package/src/services/ref-resolver/parser/location-splitter.ts +0 -239
- package/src/services/ref-resolver/parser/name-path-splitter.ts +0 -200
- package/src/services/ref-resolver/parser/partial-ref.ts +0 -84
- package/src/services/ref-resolver/parser/query-splitter.ts +0 -61
- package/src/services/ref-resolver/parser/range-splitter.ts +0 -52
- package/src/services/ref-resolver/parser/ref-parser.ts +0 -115
- package/src/services/ref-resolver/parser/repo-qualifier-splitter.ts +0 -51
- package/src/services/ref-resolver/parts.ts +0 -115
- package/src/services/ref-resolver/pruners/command-line-pruner.ts +0 -47
- package/src/services/ref-resolver/pruners/include-pruner.ts +0 -66
- package/src/services/ref-resolver/pruners/manifest-pruner.ts +0 -130
- package/src/services/ref-resolver/pruners/ref-pruner.ts +0 -135
- package/src/services/ref-resolver/pruners/rule-helpers.ts +0 -44
- package/src/services/ref-resolver/pruners/stored-key-pruner.ts +0 -136
- package/src/services/ref-resolver/ref-helpers.ts +0 -62
- package/src/services/ref-resolver/ref-picker-service.ts +0 -204
- package/src/services/ref-resolver/ref-report.ts +0 -84
- package/src/services/ref-resolver/ref-resolve-arguments.ts +0 -47
- package/src/services/ref-resolver/ref-resolve-result.ts +0 -36
- package/src/services/ref-resolver/ref-resolver-service.ts +0 -323
- package/src/services/ref-resolver/source.ts +0 -48
- package/src/services/ref-resolver/tokens.ts +0 -25
- package/src/services/ref-resolver/types.ts +0 -155
- package/src/templating/filters/bullet-list.ts +0 -9
- package/src/templating/filters/index.ts +0 -8
- package/src/templating/init-liquid-engine.ts +0 -139
- package/src/templating/lib/glob-files.ts +0 -74
- package/src/templating/lib/import-export.ts +0 -32
- package/src/templating/lib/secret-scope.ts +0 -41
- package/src/templating/lib/tag-args.ts +0 -19
- package/src/templating/tags/exportScalarVarsJs.ts +0 -46
- package/src/templating/tags/getFiles.ts +0 -89
- package/src/templating/tags/index.ts +0 -14
- package/src/templating/tags/listFiles.ts +0 -54
- package/src/templating/tags/showVars.ts +0 -28
- package/src/utils/choice-prompt.ts +0 -143
- package/src/utils/command-errors.ts +0 -186
- package/src/utils/command-help.ts +0 -45
- package/src/utils/confirm-prompt.ts +0 -110
- package/src/utils/flags.ts +0 -177
- package/src/utils/formatting.ts +0 -849
- package/src/utils/prompts.ts +0 -53
- package/src/utils/sous-directory.ts +0 -245
- package/src/utils/table.ts +0 -603
- 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
|
-
}
|