@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,215 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Shared display helpers for the browsing commands.
|
|
3
|
-
*
|
|
4
|
-
* `sous namespace show` and `sous recipe list` both print a table of recipes,
|
|
5
|
-
* and every one of the four commands turns the catalog's words into the ones a
|
|
6
|
-
* person reads. One module holds them so the four never drift apart in wording.
|
|
7
|
-
*
|
|
8
|
-
* The labeled facts block is the one `sous vars show` prints; it is rendered by
|
|
9
|
-
* `renderFacts`, which is already generic over a label and its lines.
|
|
10
|
-
*/
|
|
11
|
-
|
|
12
|
-
import { renderFacts, type LabeledFact } from "../vars/display.js";
|
|
13
|
-
import {
|
|
14
|
-
blankLine,
|
|
15
|
-
log,
|
|
16
|
-
note,
|
|
17
|
-
palette,
|
|
18
|
-
paragraph,
|
|
19
|
-
wrapColumns,
|
|
20
|
-
} from "../../utils/formatting.js";
|
|
21
|
-
import type { TableColumn } from "../../utils/table.js";
|
|
22
|
-
import type {
|
|
23
|
-
NamespaceCoverage,
|
|
24
|
-
RecipeListing,
|
|
25
|
-
VersionStatus,
|
|
26
|
-
} from "./catalog.js";
|
|
27
|
-
|
|
28
|
-
/** How far every line of a browsing command's output is indented. */
|
|
29
|
-
export const INDENT = 2;
|
|
30
|
-
|
|
31
|
-
/**
|
|
32
|
-
* The columns a recipe listing shows. The recipe and its versions are why
|
|
33
|
-
* anybody ran the command, so they stay however narrow the terminal is; the
|
|
34
|
-
* description takes the room that is left and wraps rather than being cut.
|
|
35
|
-
*/
|
|
36
|
-
export const RECIPE_COLUMNS: TableColumn[] = [
|
|
37
|
-
{ key: "key", header: "Recipe", kind: "path", overflow: "truncate", minWidth: 12 },
|
|
38
|
-
{ key: "repo", header: "Repository", overflow: "truncate", priority: "medium" },
|
|
39
|
-
{ key: "latest", header: "Latest", overflow: "truncate", minWidth: 6 },
|
|
40
|
-
{ key: "pinned", header: "Pinned", overflow: "truncate", minWidth: 6 },
|
|
41
|
-
{ key: "subscribed", header: "Subscribed", priority: "medium" },
|
|
42
|
-
{
|
|
43
|
-
key: "description",
|
|
44
|
-
header: "What it is",
|
|
45
|
-
overflow: "wrap",
|
|
46
|
-
flex: 1,
|
|
47
|
-
priority: "low",
|
|
48
|
-
minWidth: 16,
|
|
49
|
-
},
|
|
50
|
-
];
|
|
51
|
-
|
|
52
|
-
/**
|
|
53
|
-
* The recipe columns for one run. Narrowed to what the project has installed,
|
|
54
|
-
* the pinned column is the installed version, and is headed that way.
|
|
55
|
-
*
|
|
56
|
-
* @param options - Whether the listing is narrowed to installed recipes.
|
|
57
|
-
*/
|
|
58
|
-
export function recipeColumns(options: { installed?: boolean } = {}): TableColumn[] {
|
|
59
|
-
if (options.installed !== true) return RECIPE_COLUMNS;
|
|
60
|
-
return RECIPE_COLUMNS.map((column) =>
|
|
61
|
-
column.key === "pinned" ? { ...column, header: "Installed" } : column
|
|
62
|
-
);
|
|
63
|
-
}
|
|
64
|
-
|
|
65
|
-
/** One rendered recipe row, in the shape `RECIPE_COLUMNS` reads. */
|
|
66
|
-
export type RecipeRow = {
|
|
67
|
-
key: string;
|
|
68
|
-
repo: string;
|
|
69
|
-
latest: string;
|
|
70
|
-
pinned: string;
|
|
71
|
-
subscribed: string;
|
|
72
|
-
description: string;
|
|
73
|
-
};
|
|
74
|
-
|
|
75
|
-
/**
|
|
76
|
-
* Turns recipe listings into table rows. A recipe the project does not pin
|
|
77
|
-
* leaves the pinned column blank, because there is nothing to report there
|
|
78
|
-
* rather than something worth a word.
|
|
79
|
-
*
|
|
80
|
-
* @param listings - What the catalog produced.
|
|
81
|
-
*/
|
|
82
|
-
export function recipeRows(listings: RecipeListing[]): RecipeRow[] {
|
|
83
|
-
return listings.map((entry) => ({
|
|
84
|
-
key: entry.key,
|
|
85
|
-
repo: entry.repo,
|
|
86
|
-
latest: entry.latest ?? "none published",
|
|
87
|
-
pinned: pinnedCell(entry.pinned, entry.linkedPath),
|
|
88
|
-
subscribed: entry.subscribed ? "yes" : "no",
|
|
89
|
-
description: entry.description ?? "no description published",
|
|
90
|
-
}));
|
|
91
|
-
}
|
|
92
|
-
|
|
93
|
-
/**
|
|
94
|
-
* The pinned version as a cell: the version alone, or the version marked
|
|
95
|
-
* `linked` in muted grey when builds read the recipe from a linked checkout.
|
|
96
|
-
*
|
|
97
|
-
* @param pinned - The version the lockfile pins, when it pins one.
|
|
98
|
-
* @param linkedPath - The linked checkout, when the repository is linked.
|
|
99
|
-
*/
|
|
100
|
-
export function pinnedCell(pinned: string | undefined, linkedPath: string | undefined): string {
|
|
101
|
-
if (pinned === undefined) return "";
|
|
102
|
-
return linkedPath === undefined ? pinned : `${pinned} ${palette.muted("linked")}`;
|
|
103
|
-
}
|
|
104
|
-
|
|
105
|
-
/** What a listing says about the linked recipes it marked. */
|
|
106
|
-
export const LINKED_NOTE =
|
|
107
|
-
"A recipe marked linked is pinned at the version shown, but builds currently read it " +
|
|
108
|
-
"from the linked checkout of its repository.";
|
|
109
|
-
|
|
110
|
-
/** What a repository listing says when an installed repository is linked. */
|
|
111
|
-
export const LINKED_REPO_NOTE =
|
|
112
|
-
"A recipe installed from a linked repository is pinned at the version shown, but " +
|
|
113
|
-
"builds currently read it from the linked checkout.";
|
|
114
|
-
|
|
115
|
-
/**
|
|
116
|
-
* Where a browsing command read the indexes from, in the words its opening
|
|
117
|
-
* block shows.
|
|
118
|
-
*
|
|
119
|
-
* @param latest - Whether the latest was asked for.
|
|
120
|
-
*/
|
|
121
|
-
export function describeIndexSource(latest: boolean): string {
|
|
122
|
-
return latest ? "each repository, upstream" : "the cached indexes";
|
|
123
|
-
}
|
|
124
|
-
|
|
125
|
-
/** What a browsing command could not read, and whether any row is linked. */
|
|
126
|
-
export type BrowsingNotes = {
|
|
127
|
-
/** Trusted repositories with no index at all. */
|
|
128
|
-
notFetched?: string[];
|
|
129
|
-
/** Repositories upstream could not answer for, shown from the cache. */
|
|
130
|
-
notChecked?: string[];
|
|
131
|
-
/** True when some row was marked linked. */
|
|
132
|
-
anyLinked?: boolean;
|
|
133
|
-
};
|
|
134
|
-
|
|
135
|
-
/**
|
|
136
|
-
* Prints what a browsing command's rows could not say themselves: repositories
|
|
137
|
-
* that were not listed at all, repositories shown from the cache because
|
|
138
|
-
* upstream could not be reached, and what `linked` means when a row carries
|
|
139
|
-
* it. Prints nothing when there is nothing to say.
|
|
140
|
-
*
|
|
141
|
-
* @param notes - What the command could not read, and whether a row was linked.
|
|
142
|
-
*/
|
|
143
|
-
export function printBrowsingNotes(notes: BrowsingNotes): void {
|
|
144
|
-
const lines: Array<{ text: string; kind: "fact" | "note" }> = [];
|
|
145
|
-
|
|
146
|
-
if ((notes.notFetched ?? []).length > 0) {
|
|
147
|
-
lines.push({
|
|
148
|
-
kind: "fact",
|
|
149
|
-
text:
|
|
150
|
-
`These repositories are trusted and their index has not been fetched yet, so ` +
|
|
151
|
-
`nothing in them is listed: ${notes.notFetched!.join(", ")}.`,
|
|
152
|
-
});
|
|
153
|
-
}
|
|
154
|
-
if ((notes.notChecked ?? []).length > 0) {
|
|
155
|
-
lines.push({
|
|
156
|
-
kind: "fact",
|
|
157
|
-
text:
|
|
158
|
-
`These repositories could not be reached, so they are shown from the cache and ` +
|
|
159
|
-
`were not checked: ${notes.notChecked!.join(", ")}.`,
|
|
160
|
-
});
|
|
161
|
-
}
|
|
162
|
-
if (notes.anyLinked === true) lines.push({ kind: "note", text: LINKED_NOTE });
|
|
163
|
-
|
|
164
|
-
if (lines.length === 0) return;
|
|
165
|
-
blankLine();
|
|
166
|
-
for (const line of lines) {
|
|
167
|
-
if (line.kind === "note") note(line.text);
|
|
168
|
-
else paragraph(line.text);
|
|
169
|
-
}
|
|
170
|
-
}
|
|
171
|
-
|
|
172
|
-
/**
|
|
173
|
-
* Plain-language wording for how much of a namespace a project subscribes to.
|
|
174
|
-
*
|
|
175
|
-
* @param coverage - What the catalog worked out.
|
|
176
|
-
*/
|
|
177
|
-
export function describeCoverage(coverage: NamespaceCoverage): string {
|
|
178
|
-
if (coverage === "whole namespace") return "the whole namespace";
|
|
179
|
-
if (coverage === "some recipes") return "some recipes";
|
|
180
|
-
return "no";
|
|
181
|
-
}
|
|
182
|
-
|
|
183
|
-
/**
|
|
184
|
-
* Plain-language wording for what one published version is to this project.
|
|
185
|
-
*
|
|
186
|
-
* @param status - What the catalog worked out.
|
|
187
|
-
*/
|
|
188
|
-
export function describeVersionStatus(status: VersionStatus): string {
|
|
189
|
-
if (status === "latest and pinned") return "the latest version, and the one pinned here";
|
|
190
|
-
if (status === "latest") return "the latest version";
|
|
191
|
-
if (status === "pinned") return "the version pinned here";
|
|
192
|
-
return "an earlier version";
|
|
193
|
-
}
|
|
194
|
-
|
|
195
|
-
/**
|
|
196
|
-
* Prints a labeled facts block, wrapped to the terminal. The renderer indents
|
|
197
|
-
* the block itself, so every facts block in the CLI sits at the same depth
|
|
198
|
-
* whichever command printed it.
|
|
199
|
-
*
|
|
200
|
-
* @param facts - The facts to print.
|
|
201
|
-
*/
|
|
202
|
-
export function printFacts(facts: LabeledFact[]): void {
|
|
203
|
-
for (const line of renderFacts(facts, wrapColumns())) log(line);
|
|
204
|
-
}
|
|
205
|
-
|
|
206
|
-
/**
|
|
207
|
-
* One labeled fact, when there is anything to say. Used to keep an absent field
|
|
208
|
-
* out of a facts block rather than printing an empty line for it.
|
|
209
|
-
*
|
|
210
|
-
* @param label - The label to show.
|
|
211
|
-
* @param value - The text beside it, or undefined to leave the fact out.
|
|
212
|
-
*/
|
|
213
|
-
export function factIf(label: string, value: string | undefined): LabeledFact[] {
|
|
214
|
-
return value === undefined || value.length === 0 ? [] : [{ label, lines: [value] }];
|
|
215
|
-
}
|
|
@@ -1,288 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Wiring the catalog to a running command.
|
|
3
|
-
*
|
|
4
|
-
* `catalog.ts` is pure: it reads indexes, a lockfile and a list of subscription
|
|
5
|
-
* keys. This module is where those come from in a real project, and it is
|
|
6
|
-
* deliberately the only place that knows: the subscription service for the
|
|
7
|
-
* trusted repositories and their cached indexes, the lockfile service for what
|
|
8
|
-
* is pinned, the links map and the store for a recipe's own files, and
|
|
9
|
-
* `recipes` for where a content kind lands.
|
|
10
|
-
*
|
|
11
|
-
* By default nothing here downloads anything: a repository whose index has
|
|
12
|
-
* never been fetched is left out of the catalog and named separately, so a
|
|
13
|
-
* browsing command is safe offline. Asked for the latest, it reads each index
|
|
14
|
-
* from upstream instead and writes none of it to the cache; a repository that
|
|
15
|
-
* cannot be reached is read from the cache and named as not checked.
|
|
16
|
-
*/
|
|
17
|
-
|
|
18
|
-
import type { Settings, VarScope } from "../settings.js";
|
|
19
|
-
import type { SubscriptionService } from "./subscription-service.js";
|
|
20
|
-
import type { CatalogInputs, CatalogRepo } from "./catalog.js";
|
|
21
|
-
import type { IndexFile } from "./formats/index-file.js";
|
|
22
|
-
import { linkedPathFor, readEffectiveLinks } from "./links.js";
|
|
23
|
-
import { mapLinkedRecipes, readRecipeManifestIn } from "./locked-recipes.js";
|
|
24
|
-
import { WRITABLE_CONTENT_KINDS, destinationsFor } from "./recipe-targets.js";
|
|
25
|
-
import type { WritableContentKind } from "./recipe-targets.js";
|
|
26
|
-
|
|
27
|
-
/** What building the catalog's inputs needs. */
|
|
28
|
-
export type CatalogInputsOptions = {
|
|
29
|
-
/** The subscription service for this project. */
|
|
30
|
-
service: SubscriptionService;
|
|
31
|
-
/** The project's `.sous/` directory. */
|
|
32
|
-
sousDir: string;
|
|
33
|
-
/** The merged project config. */
|
|
34
|
-
settings: Settings;
|
|
35
|
-
/**
|
|
36
|
-
* The resolved settings scope, when the caller has one. Only the destinations
|
|
37
|
-
* a recipe's files land in need it, so a command that does not show them may
|
|
38
|
-
* leave it out.
|
|
39
|
-
*/
|
|
40
|
-
scope?: VarScope;
|
|
41
|
-
/** The environment to read; decides where the store and the links map are. */
|
|
42
|
-
env?: NodeJS.ProcessEnv;
|
|
43
|
-
/**
|
|
44
|
-
* The indexes to read, when the caller has already gathered them (with
|
|
45
|
-
* `readTrustedIndexes`, say, to read upstream). The cached indexes otherwise.
|
|
46
|
-
*/
|
|
47
|
-
indexes?: TrustedIndexes;
|
|
48
|
-
};
|
|
49
|
-
|
|
50
|
-
/** Where to read the trusted repositories' indexes from. */
|
|
51
|
-
export type TrustedIndexesOptions = {
|
|
52
|
-
/** Read each index from upstream rather than the cache, writing none of it. */
|
|
53
|
-
latest?: boolean;
|
|
54
|
-
};
|
|
55
|
-
|
|
56
|
-
/** One trusted repository's index, and where it was read from. */
|
|
57
|
-
export type TrustedIndex = {
|
|
58
|
-
/** The repository's short name. */
|
|
59
|
-
name: string;
|
|
60
|
-
/** Where it lives, as the project's config records it. */
|
|
61
|
-
url?: string;
|
|
62
|
-
/** The index that was read. */
|
|
63
|
-
index: IndexFile;
|
|
64
|
-
/** Whether it came from upstream just now or from the cache. */
|
|
65
|
-
source: "upstream" | "cache";
|
|
66
|
-
};
|
|
67
|
-
|
|
68
|
-
/** Every trusted repository's index sous could read, and what it could not. */
|
|
69
|
-
export type TrustedIndexes = {
|
|
70
|
-
/** The indexes, by repository short name, sorted. */
|
|
71
|
-
repos: TrustedIndex[];
|
|
72
|
-
/**
|
|
73
|
-
* Trusted repositories with no index at all: never fetched, and (when the
|
|
74
|
-
* latest was asked for) not reachable either. Sorted.
|
|
75
|
-
*/
|
|
76
|
-
notFetched: string[];
|
|
77
|
-
/**
|
|
78
|
-
* Repositories the latest was asked for that could not be reached, and were
|
|
79
|
-
* read from the cache instead. Sorted; always empty when reading the cache.
|
|
80
|
-
*/
|
|
81
|
-
notChecked: string[];
|
|
82
|
-
};
|
|
83
|
-
|
|
84
|
-
/**
|
|
85
|
-
* Reads the index of every repository this project trusts. From the cache by
|
|
86
|
-
* default, which downloads nothing. With `latest`, from upstream, all at once,
|
|
87
|
-
* and nothing fetched is written to the cache: only a command that resolves
|
|
88
|
-
* versions changes what the cache holds. A repository upstream cannot answer
|
|
89
|
-
* for is read from the cache and named in `notChecked`.
|
|
90
|
-
*
|
|
91
|
-
* @param service - The subscription service for this project.
|
|
92
|
-
* @param options - Whether to read upstream.
|
|
93
|
-
*/
|
|
94
|
-
export async function readTrustedIndexes(
|
|
95
|
-
service: SubscriptionService,
|
|
96
|
-
options: TrustedIndexesOptions = {}
|
|
97
|
-
): Promise<TrustedIndexes> {
|
|
98
|
-
if (options.latest !== true) return cachedTrustedIndexes(service);
|
|
99
|
-
|
|
100
|
-
const trusted = service.currentRepos();
|
|
101
|
-
const names = Object.keys(trusted).sort();
|
|
102
|
-
const answers = await Promise.allSettled(names.map((name) => service.upstreamIndex(name)));
|
|
103
|
-
|
|
104
|
-
const result: TrustedIndexes = { repos: [], notFetched: [], notChecked: [] };
|
|
105
|
-
names.forEach((name, position) => {
|
|
106
|
-
const url = trusted[name]?.url;
|
|
107
|
-
const answer = answers[position]!;
|
|
108
|
-
if (answer.status === "fulfilled") {
|
|
109
|
-
result.repos.push({
|
|
110
|
-
name,
|
|
111
|
-
...(url === undefined ? {} : { url }),
|
|
112
|
-
index: answer.value,
|
|
113
|
-
source: "upstream",
|
|
114
|
-
});
|
|
115
|
-
return;
|
|
116
|
-
}
|
|
117
|
-
|
|
118
|
-
// Upstream could not answer, so the cached copy stands in and says so. A
|
|
119
|
-
// repository with no cached copy either is named once, as never fetched.
|
|
120
|
-
const cached = service.cachedIndex(name);
|
|
121
|
-
if (cached === undefined) {
|
|
122
|
-
result.notFetched.push(name);
|
|
123
|
-
return;
|
|
124
|
-
}
|
|
125
|
-
result.notChecked.push(name);
|
|
126
|
-
result.repos.push({ name, ...(url === undefined ? {} : { url }), index: cached, source: "cache" });
|
|
127
|
-
});
|
|
128
|
-
return result;
|
|
129
|
-
}
|
|
130
|
-
|
|
131
|
-
/** The catalog's inputs, plus what could not be read. */
|
|
132
|
-
export type CatalogContext = {
|
|
133
|
-
/** What the catalog functions read. */
|
|
134
|
-
inputs: CatalogInputs;
|
|
135
|
-
/**
|
|
136
|
-
* Trusted repositories whose index has never been fetched, so nothing in them
|
|
137
|
-
* could be listed. Sorted.
|
|
138
|
-
*/
|
|
139
|
-
notFetched: string[];
|
|
140
|
-
/**
|
|
141
|
-
* Repositories the latest was asked for that could not be reached, listed
|
|
142
|
-
* from the cache instead. Sorted.
|
|
143
|
-
*/
|
|
144
|
-
notChecked: string[];
|
|
145
|
-
};
|
|
146
|
-
|
|
147
|
-
/**
|
|
148
|
-
* Builds the catalog's inputs for one project: every trusted repository whose
|
|
149
|
-
* index sous already has, the lockfile, and the subscription keys the project
|
|
150
|
-
* declares.
|
|
151
|
-
*
|
|
152
|
-
* @param options - The subscription service, the project's directory and config.
|
|
153
|
-
*/
|
|
154
|
-
export function catalogContextFor(options: CatalogInputsOptions): CatalogContext {
|
|
155
|
-
const { service } = options;
|
|
156
|
-
const env = options.env ?? process.env;
|
|
157
|
-
const indexes = options.indexes ?? cachedTrustedIndexes(service);
|
|
158
|
-
|
|
159
|
-
const repos: CatalogRepo[] = indexes.repos.map((entry) => ({
|
|
160
|
-
name: entry.name,
|
|
161
|
-
...(entry.url === undefined ? {} : { url: entry.url }),
|
|
162
|
-
index: entry.index,
|
|
163
|
-
}));
|
|
164
|
-
|
|
165
|
-
const links = readEffectiveLinks(options.sousDir, env);
|
|
166
|
-
const linked: Record<string, string> = {};
|
|
167
|
-
for (const [name, link] of Object.entries(links)) linked[name] = link.path;
|
|
168
|
-
|
|
169
|
-
const inputs: CatalogInputs = {
|
|
170
|
-
repos,
|
|
171
|
-
lock: service.lockService.read(),
|
|
172
|
-
subscriptions: Object.keys(service.allSubscriptions()).sort(),
|
|
173
|
-
linked,
|
|
174
|
-
readManifest: (recipe) => {
|
|
175
|
-
const directory = recipeFilesDirectory({
|
|
176
|
-
service,
|
|
177
|
-
sousDir: options.sousDir,
|
|
178
|
-
env,
|
|
179
|
-
repo: recipe.repo,
|
|
180
|
-
key: recipe.key,
|
|
181
|
-
namespace: recipe.namespace,
|
|
182
|
-
name: recipe.name,
|
|
183
|
-
version: recipe.version,
|
|
184
|
-
});
|
|
185
|
-
return directory === undefined ? undefined : readRecipeManifestIn(directory);
|
|
186
|
-
},
|
|
187
|
-
destinationsFor: (kind) => {
|
|
188
|
-
// Config layers are loaded, not written into the project, so they land
|
|
189
|
-
// nowhere a listing could name.
|
|
190
|
-
if (!isWritableKind(kind)) return [];
|
|
191
|
-
return destinationsFor(kind, {
|
|
192
|
-
sousDir: options.sousDir,
|
|
193
|
-
settings: options.settings,
|
|
194
|
-
...(options.scope === undefined ? {} : { scope: options.scope }),
|
|
195
|
-
env,
|
|
196
|
-
});
|
|
197
|
-
},
|
|
198
|
-
};
|
|
199
|
-
|
|
200
|
-
return { inputs, notFetched: indexes.notFetched, notChecked: indexes.notChecked };
|
|
201
|
-
}
|
|
202
|
-
|
|
203
|
-
/**
|
|
204
|
-
* The catalog's inputs, reading the indexes the way the options say: from the
|
|
205
|
-
* cache, or with `latest` from upstream without writing to the cache.
|
|
206
|
-
*
|
|
207
|
-
* @param options - The subscription service, the project's directory and config,
|
|
208
|
-
* and whether to read upstream.
|
|
209
|
-
*/
|
|
210
|
-
export async function loadCatalogContext(
|
|
211
|
-
options: Omit<CatalogInputsOptions, "indexes"> & TrustedIndexesOptions
|
|
212
|
-
): Promise<CatalogContext> {
|
|
213
|
-
const indexes = await readTrustedIndexes(options.service, {
|
|
214
|
-
...(options.latest === undefined ? {} : { latest: options.latest }),
|
|
215
|
-
});
|
|
216
|
-
return catalogContextFor({ ...options, indexes });
|
|
217
|
-
}
|
|
218
|
-
|
|
219
|
-
/**
|
|
220
|
-
* Every trusted repository's cached index, read synchronously.
|
|
221
|
-
*
|
|
222
|
-
* @param service - The subscription service for this project.
|
|
223
|
-
*/
|
|
224
|
-
function cachedTrustedIndexes(service: SubscriptionService): TrustedIndexes {
|
|
225
|
-
const trusted = service.currentRepos();
|
|
226
|
-
const result: TrustedIndexes = { repos: [], notFetched: [], notChecked: [] };
|
|
227
|
-
for (const name of Object.keys(trusted).sort()) {
|
|
228
|
-
const index = service.cachedIndex(name);
|
|
229
|
-
if (index === undefined) {
|
|
230
|
-
result.notFetched.push(name);
|
|
231
|
-
continue;
|
|
232
|
-
}
|
|
233
|
-
const url = trusted[name]?.url;
|
|
234
|
-
result.repos.push({ name, ...(url === undefined ? {} : { url }), index, source: "cache" });
|
|
235
|
-
}
|
|
236
|
-
return result;
|
|
237
|
-
}
|
|
238
|
-
|
|
239
|
-
/** Which recipe, at which version, in which of this project's repositories. */
|
|
240
|
-
export type RecipeFilesQuery = {
|
|
241
|
-
/** The subscription service, which knows the store and the repository identities. */
|
|
242
|
-
service: SubscriptionService;
|
|
243
|
-
/** The project's `.sous/` directory, which holds the links map. */
|
|
244
|
-
sousDir: string;
|
|
245
|
-
/** The environment to read; decides where the store is. Defaults to `process.env`. */
|
|
246
|
-
env?: NodeJS.ProcessEnv;
|
|
247
|
-
/** The short name of the repository publishing it. */
|
|
248
|
-
repo: string;
|
|
249
|
-
/** The recipe key, `namespace/recipe`. */
|
|
250
|
-
key: string;
|
|
251
|
-
namespace: string;
|
|
252
|
-
name: string;
|
|
253
|
-
/** The exact version wanted. */
|
|
254
|
-
version: string;
|
|
255
|
-
};
|
|
256
|
-
|
|
257
|
-
/**
|
|
258
|
-
* The directory one published recipe's files are read from: a linked working
|
|
259
|
-
* copy when the repository is linked, and the store entry for that exact
|
|
260
|
-
* version otherwise. Undefined when neither is on this machine.
|
|
261
|
-
*
|
|
262
|
-
* Nothing is fetched, and the directory is not checked for existence: the
|
|
263
|
-
* caller reads what is there, and an absent manifest is an ordinary answer.
|
|
264
|
-
*
|
|
265
|
-
* @param input - The recipe's identity, and where this project keeps its state.
|
|
266
|
-
*/
|
|
267
|
-
export function recipeFilesDirectory(input: RecipeFilesQuery): string | undefined {
|
|
268
|
-
const checkout = linkedPathFor(input.repo, input.sousDir, input.env ?? process.env);
|
|
269
|
-
if (checkout !== undefined) {
|
|
270
|
-
const linked = mapLinkedRecipes(checkout)[input.key];
|
|
271
|
-
if (linked !== undefined) return linked;
|
|
272
|
-
}
|
|
273
|
-
|
|
274
|
-
const identity = input.service.identityForRepo(input.repo);
|
|
275
|
-
if (identity === undefined) return undefined;
|
|
276
|
-
|
|
277
|
-
return input.service.store.entryDir({
|
|
278
|
-
identity,
|
|
279
|
-
namespace: input.namespace,
|
|
280
|
-
name: input.name,
|
|
281
|
-
version: input.version,
|
|
282
|
-
});
|
|
283
|
-
}
|
|
284
|
-
|
|
285
|
-
/** True when a content kind's files are written into the project. */
|
|
286
|
-
function isWritableKind(kind: string): kind is WritableContentKind {
|
|
287
|
-
return (WRITABLE_CONTENT_KINDS as readonly string[]).includes(kind);
|
|
288
|
-
}
|