@sous-io/sous 0.2.28 → 0.2.30
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/docs/markdown/_sidebar.md +2 -0
- package/docs/markdown/commands.md +10 -6
- package/docs/markdown/repositories-authoring.md +19 -11
- package/docs/markdown/repositories-consuming.md +34 -13
- package/docs/markdown/repositories-file-formats.md +88 -13
- package/docs/markdown/repositories-providers.md +8 -6
- package/docs/markdown/repositories-troubleshooting.md +6 -0
- package/docs/markdown/repositories-variables.md +13 -3
- package/docs/markdown/repositories.md +3 -3
- package/package.json +1 -1
- package/recipes/core/sous-skills/skills/about-sous-recipes/SKILL.tpl.md +11 -7
- package/recipes/core/sous-skills/skills/about-sous-repos/SKILL.tpl.md +7 -4
- package/recipes/core/sous-skills/sous.recipe.yaml +1 -1
- package/src/commands/lock/rebuild.ts +7 -6
- package/src/commands/recipe/show.ts +163 -64
- package/src/commands/repo/contribute.ts +1 -10
- package/src/commands/repo/gc.ts +3 -2
- package/src/commands/repo/release.ts +20 -3
- package/src/commands/repo/search.ts +2 -1
- package/src/commands/vars/ask.ts +32 -10
- package/src/lib/refs/find.ts +202 -21
- package/src/lib/refs/index.ts +2 -0
- package/src/lib/refs/parse.ts +844 -0
- package/src/lib/refs/scopes.ts +34 -0
- package/src/lib/refs/settle.ts +219 -0
- package/src/lib/repos/catalog.ts +87 -80
- package/src/lib/repos/declarations.ts +177 -0
- package/src/lib/repos/formats/common.ts +14 -0
- package/src/lib/repos/formats/index-file.ts +42 -2
- package/src/lib/repos/formats/lockfile.ts +5 -3
- package/src/lib/repos/formats/recipe-manifest.ts +16 -212
- package/src/lib/repos/formats/variable-definition.ts +268 -0
- package/src/lib/repos/index.ts +0 -1
- package/src/lib/repos/lock-service.ts +3 -2
- package/src/lib/repos/locked-namespace-resolver.ts +21 -15
- package/src/lib/repos/locked-recipes.ts +2 -2
- package/src/lib/repos/namespace-resolver.ts +40 -47
- package/src/lib/repos/providers/base.ts +30 -0
- package/src/lib/repos/providers/github.ts +22 -0
- package/src/lib/repos/providers/gitlab.ts +68 -0
- package/src/lib/repos/providers/provider.ts +96 -0
- package/src/lib/repos/release/index-builder.ts +221 -47
- package/src/lib/repos/release/index.ts +1 -0
- package/src/lib/repos/release/plan.ts +8 -6
- package/src/lib/repos/release/settle.ts +237 -0
- package/src/lib/repos/release/tags.ts +3 -7
- package/src/lib/repos/resolver.ts +251 -53
- package/src/lib/repos/subscription-service.ts +355 -49
- package/src/lib/repos/update-plan.ts +3 -2
- package/src/lib/vars/mappings.ts +17 -30
- package/src/lib/vars/question-plan.ts +44 -21
- package/src/lib/repos/ref.ts +0 -513
|
@@ -2,27 +2,41 @@
|
|
|
2
2
|
* `sous recipe show <ref>`.
|
|
3
3
|
*
|
|
4
4
|
* Shows one recipe in full: where it is published, every version it publishes,
|
|
5
|
-
* what the version this project would use depends on
|
|
6
|
-
*
|
|
7
|
-
*
|
|
8
|
-
*
|
|
9
|
-
*
|
|
10
|
-
*
|
|
5
|
+
* what the version this project would use depends on and how each dependency is
|
|
6
|
+
* declared, everything subscribing to it would install, every question that
|
|
7
|
+
* would ask, and where its files land in this project. It reads only what sous
|
|
8
|
+
* already has on disk: the repository's cached index, the project's lockfile,
|
|
9
|
+
* and the recipe's own files when they are in the store or a linked working
|
|
10
|
+
* copy. The index describes each version's dependencies and questions, so a
|
|
11
|
+
* recipe nothing has installed is described as fully as one in the store, its
|
|
12
|
+
* files excepted. `--latest` reads the repository's index from upstream
|
|
13
|
+
* instead, saving nothing, and `--installed` looks the ref up among installed
|
|
14
|
+
* recipes only.
|
|
11
15
|
*/
|
|
12
16
|
|
|
13
17
|
import { Args } from "@oclif/core";
|
|
18
|
+
import semver from "semver";
|
|
14
19
|
import { BaseCommand } from "../../base-command.js";
|
|
15
20
|
import { resolveRootScope } from "../../lib/settings.js";
|
|
16
|
-
import {
|
|
21
|
+
import {
|
|
22
|
+
subscriptionServiceFor,
|
|
23
|
+
type SubscriptionPreview,
|
|
24
|
+
type SubscriptionService,
|
|
25
|
+
} from "../../lib/repos/subscription-service.js";
|
|
17
26
|
import { loadCatalogContext } from "../../lib/repos/catalog-inputs.js";
|
|
18
27
|
import {
|
|
19
28
|
describeInstalled,
|
|
20
29
|
describeRecipe,
|
|
30
|
+
type CatalogInputs,
|
|
21
31
|
type RecipeContentListing,
|
|
22
32
|
type RecipeDependencyListing,
|
|
23
|
-
type
|
|
33
|
+
type RecipeDetail,
|
|
24
34
|
type RecipeVersionListing,
|
|
25
35
|
} from "../../lib/repos/catalog.js";
|
|
36
|
+
import { PROJECT_REQUESTER } from "../../lib/repos/resolver.js";
|
|
37
|
+
import { describeDeclaration, describeDependencyKind } from "../../lib/repos/declarations.js";
|
|
38
|
+
import { formatQuestionPlan } from "../../lib/vars/question-plan.js";
|
|
39
|
+
import { describeError } from "../../lib/repos/release/validate.js";
|
|
26
40
|
import {
|
|
27
41
|
INDENT,
|
|
28
42
|
describeIndexSource,
|
|
@@ -61,14 +75,19 @@ const DEPENDENCY_COLUMNS: TableColumn[] = [
|
|
|
61
75
|
{ key: "kind", header: "Kind", overflow: "wrap", priority: "medium", minWidth: 16 },
|
|
62
76
|
];
|
|
63
77
|
|
|
64
|
-
/** The columns the
|
|
65
|
-
const
|
|
66
|
-
{ key: "
|
|
67
|
-
{ key: "
|
|
68
|
-
{ key: "
|
|
69
|
-
{
|
|
70
|
-
|
|
71
|
-
|
|
78
|
+
/** The columns the listing of everything a subscription installs shows. */
|
|
79
|
+
const INSTALLS_COLUMNS: TableColumn[] = [
|
|
80
|
+
{ key: "key", header: "Recipe", kind: "path", overflow: "truncate", minWidth: 12 },
|
|
81
|
+
{ key: "version", header: "Version", overflow: "truncate", minWidth: 7 },
|
|
82
|
+
{ key: "kind", header: "Kind", overflow: "wrap", minWidth: 16 },
|
|
83
|
+
{
|
|
84
|
+
key: "neededBy",
|
|
85
|
+
header: "Needed by",
|
|
86
|
+
overflow: "wrap",
|
|
87
|
+
flex: 1,
|
|
88
|
+
priority: "medium",
|
|
89
|
+
minWidth: 12,
|
|
90
|
+
},
|
|
72
91
|
];
|
|
73
92
|
|
|
74
93
|
/** The columns the content listing shows. */
|
|
@@ -86,7 +105,8 @@ const CONTENT_COLUMNS: TableColumn[] = [
|
|
|
86
105
|
];
|
|
87
106
|
|
|
88
107
|
export default class RecipeShow extends BaseCommand {
|
|
89
|
-
static description =
|
|
108
|
+
static description =
|
|
109
|
+
"Show one recipe: its versions, dependencies, what subscribing installs and asks, and its files";
|
|
90
110
|
|
|
91
111
|
/**
|
|
92
112
|
* The other spelling of the topic. It lives under a hidden topic, so it is
|
|
@@ -165,24 +185,76 @@ export default class RecipeShow extends BaseCommand {
|
|
|
165
185
|
printBrowsingNotes({ notChecked: notChecked.filter((name) => name === detail.repo) });
|
|
166
186
|
|
|
167
187
|
this.printVersions(detail.versions);
|
|
168
|
-
this.printDependencies(
|
|
188
|
+
this.printDependencies(
|
|
189
|
+
detail.dependencies,
|
|
190
|
+
detail.manifestRead || detail.dependenciesRecorded
|
|
191
|
+
);
|
|
169
192
|
|
|
170
|
-
|
|
193
|
+
const preview = await this.preview(service, inputs, detail);
|
|
194
|
+
if (typeof preview === "string") {
|
|
195
|
+
blankLine();
|
|
196
|
+
paragraph(preview);
|
|
197
|
+
} else {
|
|
198
|
+
this.printInstalls(preview);
|
|
199
|
+
this.printQuestions(preview);
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
if (detail.manifestRead) {
|
|
203
|
+
this.printContents(detail.contents);
|
|
204
|
+
} else {
|
|
205
|
+
blankLine();
|
|
206
|
+
subheading("Where its files land in this project");
|
|
171
207
|
blankLine();
|
|
172
208
|
paragraph(
|
|
173
|
-
"The recipe's own files are not on this machine, so the
|
|
174
|
-
"
|
|
209
|
+
"The recipe's own files are not on this machine, so the files it publishes are " +
|
|
210
|
+
"not known here. Subscribing to it fetches them."
|
|
175
211
|
);
|
|
176
|
-
footer();
|
|
177
|
-
return;
|
|
178
212
|
}
|
|
179
213
|
|
|
180
|
-
this.printVariables(detail.variables);
|
|
181
|
-
this.printContents(detail.contents);
|
|
182
|
-
|
|
183
214
|
footer();
|
|
184
215
|
}
|
|
185
216
|
|
|
217
|
+
/**
|
|
218
|
+
* Works out what subscribing to the described version would install and ask,
|
|
219
|
+
* from the same indexes the rest of the page reads, fetching nothing. The
|
|
220
|
+
* answer is a sentence instead when it cannot be worked out.
|
|
221
|
+
*
|
|
222
|
+
* @param service - The project's subscription service.
|
|
223
|
+
* @param inputs - The catalog's inputs, whose indexes the preview resolves against.
|
|
224
|
+
* @param detail - The recipe being described.
|
|
225
|
+
*/
|
|
226
|
+
private async preview(
|
|
227
|
+
service: SubscriptionService,
|
|
228
|
+
inputs: CatalogInputs,
|
|
229
|
+
detail: RecipeDetail
|
|
230
|
+
): Promise<SubscriptionPreview | string> {
|
|
231
|
+
if (detail.describing === undefined) {
|
|
232
|
+
return (
|
|
233
|
+
"This recipe publishes no version a subscription would install, so nothing " +
|
|
234
|
+
"is described below."
|
|
235
|
+
);
|
|
236
|
+
}
|
|
237
|
+
try {
|
|
238
|
+
return await service.previewSubscription(
|
|
239
|
+
{
|
|
240
|
+
repo: detail.repo,
|
|
241
|
+
namespace: detail.namespace,
|
|
242
|
+
recipe: detail.name,
|
|
243
|
+
range: detail.describing,
|
|
244
|
+
},
|
|
245
|
+
{
|
|
246
|
+
indexes: new Map(inputs.repos.map((repo) => [repo.name, repo.index])),
|
|
247
|
+
...(semver.prerelease(detail.describing) === null ? {} : { prerelease: true }),
|
|
248
|
+
}
|
|
249
|
+
);
|
|
250
|
+
} catch (error) {
|
|
251
|
+
return (
|
|
252
|
+
`Sous could not work out what subscribing to version ${detail.describing} ` +
|
|
253
|
+
`would install. ${describeError(error)}`
|
|
254
|
+
);
|
|
255
|
+
}
|
|
256
|
+
}
|
|
257
|
+
|
|
186
258
|
/**
|
|
187
259
|
* Every published version, newest first, with what each one is to this
|
|
188
260
|
* project.
|
|
@@ -207,24 +279,21 @@ export default class RecipeShow extends BaseCommand {
|
|
|
207
279
|
}
|
|
208
280
|
|
|
209
281
|
/**
|
|
210
|
-
* What the described version depends on
|
|
211
|
-
*
|
|
212
|
-
* was released.
|
|
282
|
+
* What the described version depends on: the manifest entry that brings each
|
|
283
|
+
* dependency in and whether it is a co-subscription or a build dependency,
|
|
284
|
+
* and what its repository's index resolved it to when it was released.
|
|
213
285
|
*
|
|
214
286
|
* @param dependencies - The dependencies the catalog listed.
|
|
215
|
-
* @param
|
|
287
|
+
* @param known - Whether the manifest or the index says what the version declares.
|
|
216
288
|
*/
|
|
217
|
-
private printDependencies(
|
|
218
|
-
dependencies: RecipeDependencyListing[],
|
|
219
|
-
manifestRead: boolean
|
|
220
|
-
): void {
|
|
289
|
+
private printDependencies(dependencies: RecipeDependencyListing[], known: boolean): void {
|
|
221
290
|
blankLine();
|
|
222
291
|
subheading("What it depends on");
|
|
223
292
|
blankLine();
|
|
224
293
|
|
|
225
294
|
if (dependencies.length === 0) {
|
|
226
295
|
paragraph(
|
|
227
|
-
|
|
296
|
+
known
|
|
228
297
|
? "This recipe depends on nothing else."
|
|
229
298
|
: "The repository's index records no dependencies for this version."
|
|
230
299
|
);
|
|
@@ -233,7 +302,10 @@ export default class RecipeShow extends BaseCommand {
|
|
|
233
302
|
|
|
234
303
|
const rows = dependencies.map((entry) => ({
|
|
235
304
|
key: entry.key,
|
|
236
|
-
declared:
|
|
305
|
+
declared:
|
|
306
|
+
entry.declared === undefined
|
|
307
|
+
? "not recorded in the index"
|
|
308
|
+
: describeDeclaration(entry.declared),
|
|
237
309
|
resolved:
|
|
238
310
|
entry.resolvedVersion ??
|
|
239
311
|
(entry.resolvedRange === undefined
|
|
@@ -249,32 +321,69 @@ export default class RecipeShow extends BaseCommand {
|
|
|
249
321
|
}
|
|
250
322
|
|
|
251
323
|
/**
|
|
252
|
-
*
|
|
253
|
-
*
|
|
324
|
+
* Every recipe a subscription to the described version would install, each
|
|
325
|
+
* with whether its files land in the project and what brings it in, and the
|
|
326
|
+
* repositories it would need that the project does not trust yet.
|
|
254
327
|
*
|
|
255
|
-
* @param
|
|
328
|
+
* @param preview - What subscribing would install and ask.
|
|
256
329
|
*/
|
|
257
|
-
private
|
|
330
|
+
private printInstalls(preview: SubscriptionPreview): void {
|
|
258
331
|
blankLine();
|
|
259
|
-
subheading("What
|
|
332
|
+
subheading("What subscribing installs");
|
|
260
333
|
blankLine();
|
|
261
334
|
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
335
|
+
const rows = preview.resolved.map((recipe) => {
|
|
336
|
+
const subscribed = recipe.requestedBy.includes(PROJECT_REQUESTER);
|
|
337
|
+
const holders = recipe.requestedBy.filter((holder) => holder !== PROJECT_REQUESTER);
|
|
338
|
+
return {
|
|
339
|
+
key: recipe.key,
|
|
340
|
+
version: recipe.version,
|
|
341
|
+
kind: subscribed ? "the subscription itself" : describeDependencyKind(recipe.kind),
|
|
342
|
+
neededBy: holders.length === 0 ? "this subscription" : holders.join(", "),
|
|
343
|
+
};
|
|
344
|
+
});
|
|
345
|
+
|
|
346
|
+
for (const line of renderTable(INSTALLS_COLUMNS, rows, { indent: INDENT })) {
|
|
347
|
+
log(indent(line, INDENT));
|
|
265
348
|
}
|
|
266
349
|
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
350
|
+
if (preview.missingRepos.length > 0) {
|
|
351
|
+
blankLine();
|
|
352
|
+
paragraph(
|
|
353
|
+
`Subscribing also needs ${
|
|
354
|
+
preview.missingRepos.length === 1 ? "a repository" : "repositories"
|
|
355
|
+
} this project does not trust yet, so what ${
|
|
356
|
+
preview.missingRepos.length === 1 ? "it publishes" : "they publish"
|
|
357
|
+
} is not listed above: ` +
|
|
358
|
+
preview.missingRepos
|
|
359
|
+
.map(
|
|
360
|
+
(missing) =>
|
|
361
|
+
`${missing.name}${missing.url === undefined ? "" : ` (${missing.url})`}, ` +
|
|
362
|
+
`needed by ${missing.requiredBy.map((entry) => entry.requestedBy).join(", ")}`
|
|
363
|
+
)
|
|
364
|
+
.join("; ") +
|
|
365
|
+
"."
|
|
366
|
+
);
|
|
367
|
+
}
|
|
368
|
+
}
|
|
275
369
|
|
|
276
|
-
|
|
277
|
-
|
|
370
|
+
/**
|
|
371
|
+
* Every question a subscription to the described version would ask, laid out
|
|
372
|
+
* exactly as `sous subscription add --dry-run` lays them out, without the
|
|
373
|
+
* flags that answer them ahead of time.
|
|
374
|
+
*
|
|
375
|
+
* @param preview - What subscribing would install and ask.
|
|
376
|
+
*/
|
|
377
|
+
private printQuestions(preview: SubscriptionPreview): void {
|
|
378
|
+
blankLine();
|
|
379
|
+
subheading("What subscribing asks you");
|
|
380
|
+
blankLine();
|
|
381
|
+
|
|
382
|
+
for (const line of formatQuestionPlan(preview.questions, {
|
|
383
|
+
unreadable: preview.unreadable,
|
|
384
|
+
answerHints: false,
|
|
385
|
+
})) {
|
|
386
|
+
log(line === "" ? "" : indent(line));
|
|
278
387
|
}
|
|
279
388
|
}
|
|
280
389
|
|
|
@@ -311,13 +420,3 @@ export default class RecipeShow extends BaseCommand {
|
|
|
311
420
|
}
|
|
312
421
|
}
|
|
313
422
|
|
|
314
|
-
/**
|
|
315
|
-
* Plain-language wording for how a dependency was declared.
|
|
316
|
-
*
|
|
317
|
-
* @param kind - What the manifest declared it as, when the manifest was read.
|
|
318
|
-
*/
|
|
319
|
-
function describeDependencyKind(kind: "depends" | "subscribes" | undefined): string {
|
|
320
|
-
if (kind === "depends") return "build dependency";
|
|
321
|
-
if (kind === "subscribes") return "co-subscription";
|
|
322
|
-
return "unknown";
|
|
323
|
-
}
|
|
@@ -6,7 +6,6 @@ import {
|
|
|
6
6
|
SousScope,
|
|
7
7
|
findReference,
|
|
8
8
|
pickReference,
|
|
9
|
-
referenceReposFromIndexes,
|
|
10
9
|
type ReferenceRepo,
|
|
11
10
|
} from "../../lib/refs/index.js";
|
|
12
11
|
import {
|
|
@@ -374,15 +373,7 @@ export default class RepoContribute extends BaseCommand {
|
|
|
374
373
|
shellEnv: this.shellEnv,
|
|
375
374
|
});
|
|
376
375
|
|
|
377
|
-
const
|
|
378
|
-
const repos: ReferenceRepo[] = Object.keys(trusted).map((name) => {
|
|
379
|
-
const url = trusted[name]?.url;
|
|
380
|
-
const index = service.cachedIndex(name);
|
|
381
|
-
if (index === undefined) {
|
|
382
|
-
return { name, ...(url === undefined ? {} : { url }), namespaces: [], recipes: [] };
|
|
383
|
-
}
|
|
384
|
-
return referenceReposFromIndexes([name], new Map([[name, index]]), { [name]: url })[0]!;
|
|
385
|
-
});
|
|
376
|
+
const repos: ReferenceRepo[] = service.cachedReferenceRepos();
|
|
386
377
|
|
|
387
378
|
const matches = findReference(
|
|
388
379
|
ref,
|
package/src/commands/repo/gc.ts
CHANGED
|
@@ -17,6 +17,7 @@ import { subscriptionServiceFor } from "../../lib/repos/subscription-service.js"
|
|
|
17
17
|
import { resolveStoreSettings } from "../../lib/repos/store/settings.js";
|
|
18
18
|
import type { StoreKey } from "../../lib/repos/store/contract.js";
|
|
19
19
|
import { renderTable, type TableColumn } from "../../utils/table.js";
|
|
20
|
+
import { splitRecipeKey } from "../../lib/refs/parse.js";
|
|
20
21
|
import {
|
|
21
22
|
blankLine,
|
|
22
23
|
dryRunNotice,
|
|
@@ -120,12 +121,12 @@ export default class RepoGc extends BaseCommand {
|
|
|
120
121
|
const keep: StoreKey[] = Object.entries(lock.recipes).flatMap(([key, entry]) => {
|
|
121
122
|
const identity = lock.repos[entry.repo]?.identity;
|
|
122
123
|
if (identity === undefined) return [];
|
|
123
|
-
const namespace =
|
|
124
|
+
const { namespace, name } = splitRecipeKey(key);
|
|
124
125
|
return [
|
|
125
126
|
{
|
|
126
127
|
identity,
|
|
127
128
|
namespace,
|
|
128
|
-
name
|
|
129
|
+
name,
|
|
129
130
|
version: entry.version,
|
|
130
131
|
},
|
|
131
132
|
];
|
|
@@ -30,6 +30,7 @@ import {
|
|
|
30
30
|
releaseScope,
|
|
31
31
|
remoteUrl,
|
|
32
32
|
scopeProblems,
|
|
33
|
+
settleDependencyLocations,
|
|
33
34
|
uncommittedChanges,
|
|
34
35
|
validateRepo,
|
|
35
36
|
warningsIn,
|
|
@@ -38,6 +39,7 @@ import {
|
|
|
38
39
|
type ReleasePlan,
|
|
39
40
|
type ReleaseScope,
|
|
40
41
|
type RepoValidation,
|
|
42
|
+
type SettledDependency,
|
|
41
43
|
type ValidationProblem,
|
|
42
44
|
} from "../../lib/repos/release/index.js";
|
|
43
45
|
import {
|
|
@@ -195,7 +197,14 @@ export default class RepoRelease extends Command {
|
|
|
195
197
|
}
|
|
196
198
|
log(` Read ${describeCount(validation.recipes.length, "recipe")}.`);
|
|
197
199
|
|
|
198
|
-
|
|
200
|
+
// A dependency that reads more than one way is settled before anything is
|
|
201
|
+
// planned or written, so a repository that cannot be reached stops the run
|
|
202
|
+
// with nothing changed.
|
|
203
|
+
const settling = await settleDependencyLocations(validation);
|
|
204
|
+
reportProblems(settling.problems);
|
|
205
|
+
if (hasErrors(settling.problems)) return this.stopForErrors(settling.problems);
|
|
206
|
+
|
|
207
|
+
if (flags.check) return await this.runCheck(rootDir, validation, settling.settled);
|
|
199
208
|
|
|
200
209
|
// --- Plan -------------------------------------------------------------
|
|
201
210
|
|
|
@@ -284,6 +293,7 @@ export default class RepoRelease extends Command {
|
|
|
284
293
|
existing,
|
|
285
294
|
sousVersion: SOUS_VERSION,
|
|
286
295
|
publishing,
|
|
296
|
+
settled: settling.settled,
|
|
287
297
|
});
|
|
288
298
|
reportProblems(rebuilt.problems);
|
|
289
299
|
if (hasErrors(rebuilt.problems)) return this.stopForErrors(rebuilt.problems);
|
|
@@ -365,15 +375,22 @@ export default class RepoRelease extends Command {
|
|
|
365
375
|
*
|
|
366
376
|
* @param rootDir - The repository's root directory.
|
|
367
377
|
* @param validation - The validated repository.
|
|
378
|
+
* @param settled - The dependencies that read more than one way, as settled.
|
|
368
379
|
*/
|
|
369
380
|
private async runCheck(
|
|
370
381
|
rootDir: string,
|
|
371
|
-
validation: RepoValidation
|
|
382
|
+
validation: RepoValidation,
|
|
383
|
+
settled: Map<string, SettledDependency>
|
|
372
384
|
): Promise<void> {
|
|
373
385
|
section("Checking the index");
|
|
374
386
|
|
|
375
387
|
const existing = readExistingIndex(rootDir);
|
|
376
|
-
const result = await buildIndex({
|
|
388
|
+
const result = await buildIndex({
|
|
389
|
+
validation,
|
|
390
|
+
existing,
|
|
391
|
+
sousVersion: SOUS_VERSION,
|
|
392
|
+
settled,
|
|
393
|
+
});
|
|
377
394
|
reportProblems(result.problems);
|
|
378
395
|
if (hasErrors(result.problems)) return this.stopForErrors(result.problems);
|
|
379
396
|
|
|
@@ -21,6 +21,7 @@ import {
|
|
|
21
21
|
} from "../../lib/repos/catalog-display.js";
|
|
22
22
|
import { readEffectiveLinks } from "../../lib/repos/links.js";
|
|
23
23
|
import { renderTable, type TableColumn } from "../../utils/table.js";
|
|
24
|
+
import { namespaceOfKey } from "../../lib/refs/parse.js";
|
|
24
25
|
import { browsingFlags } from "../../utils/flags.js";
|
|
25
26
|
import {
|
|
26
27
|
blankLine,
|
|
@@ -142,7 +143,7 @@ export default class RepoSearch extends BaseCommand {
|
|
|
142
143
|
const installed = locked?.repo === repo ? locked.version : undefined;
|
|
143
144
|
if (flags.installed && installed === undefined) continue;
|
|
144
145
|
|
|
145
|
-
const namespace =
|
|
146
|
+
const namespace = namespaceOfKey(key);
|
|
146
147
|
const haystack = [
|
|
147
148
|
key,
|
|
148
149
|
recipe.description ?? "",
|
package/src/commands/vars/ask.ts
CHANGED
|
@@ -48,7 +48,9 @@ import {
|
|
|
48
48
|
referenceContextFromVariables,
|
|
49
49
|
variableReferenceKey,
|
|
50
50
|
type ReferenceMatch,
|
|
51
|
+
type ReferenceRepo,
|
|
51
52
|
} from "../../lib/refs/index.js";
|
|
53
|
+
import { subscriptionServiceFor } from "../../lib/repos/subscription-service.js";
|
|
52
54
|
import {
|
|
53
55
|
applyProvidedAnswers,
|
|
54
56
|
askForMissing,
|
|
@@ -212,7 +214,10 @@ export default class VarsAsk extends BaseCommand {
|
|
|
212
214
|
|
|
213
215
|
// What the reference and the flags name, as the fully qualified reference of
|
|
214
216
|
// every variable to ask. Undefined means everything the usual rules pick.
|
|
215
|
-
|
|
217
|
+
// A reference written as a location settles against the trusted
|
|
218
|
+
// repositories, read from the cached indexes; a definitions file has none.
|
|
219
|
+
const trusted = flags.file === undefined ? this.trustedRepos() : [];
|
|
220
|
+
const only = await this.resolveSelection(defined, context, selection, trusted);
|
|
216
221
|
|
|
217
222
|
const askOptions = {
|
|
218
223
|
sousDir: this.configContext.sousDir,
|
|
@@ -257,13 +262,17 @@ export default class VarsAsk extends BaseCommand {
|
|
|
257
262
|
* @param defined - Every variable definition in play.
|
|
258
263
|
* @param ladder - The environment layers, for resolving environment variable names.
|
|
259
264
|
* @param selection - The reference and the flags, as the caller wrote them.
|
|
265
|
+
* @param trusted - The trusted repositories, which settle a reference written as a location.
|
|
260
266
|
* @returns The variables to ask, or undefined when nothing narrowed anything.
|
|
261
267
|
*/
|
|
262
268
|
private async resolveSelection(
|
|
263
269
|
defined: DefinedVariable[],
|
|
264
270
|
ladder: LadderContext,
|
|
265
|
-
selection: Selection
|
|
271
|
+
selection: Selection,
|
|
272
|
+
trusted: ReferenceRepo[] = []
|
|
266
273
|
): Promise<string[] | undefined> {
|
|
274
|
+
const contextOf = (pool: DefinedVariable[]) =>
|
|
275
|
+
referenceContextFromVariables(pool, ladder, trusted);
|
|
267
276
|
const { name, repo, namespace, vars } = selection;
|
|
268
277
|
if (name === undefined && repo === undefined && namespace === undefined && vars === undefined) {
|
|
269
278
|
return undefined;
|
|
@@ -272,13 +281,13 @@ export default class VarsAsk extends BaseCommand {
|
|
|
272
281
|
let pool = defined;
|
|
273
282
|
|
|
274
283
|
if (repo !== undefined) {
|
|
275
|
-
const match = await this.pick(findRepository(repo, contextOf(pool
|
|
284
|
+
const match = await this.pick(findRepository(repo, contextOf(pool)), repo, selection);
|
|
276
285
|
pool = pool.filter((entry) => entry.recipe.repo === match.repo);
|
|
277
286
|
}
|
|
278
287
|
|
|
279
288
|
if (namespace !== undefined) {
|
|
280
289
|
const match = await this.pick(
|
|
281
|
-
findNamespace(namespace, contextOf(pool
|
|
290
|
+
findNamespace(namespace, contextOf(pool)),
|
|
282
291
|
namespace,
|
|
283
292
|
selection
|
|
284
293
|
);
|
|
@@ -287,7 +296,7 @@ export default class VarsAsk extends BaseCommand {
|
|
|
287
296
|
|
|
288
297
|
if (name !== undefined) {
|
|
289
298
|
const match = await this.pick(
|
|
290
|
-
findReference(name, ALL_SCOPES, contextOf(pool
|
|
299
|
+
findReference(name, ALL_SCOPES, contextOf(pool)),
|
|
291
300
|
name,
|
|
292
301
|
selection
|
|
293
302
|
);
|
|
@@ -298,7 +307,7 @@ export default class VarsAsk extends BaseCommand {
|
|
|
298
307
|
const chosen: DefinedVariable[] = [];
|
|
299
308
|
for (const wanted of vars) {
|
|
300
309
|
const match = await this.pick(
|
|
301
|
-
findVariable(wanted, contextOf(pool
|
|
310
|
+
findVariable(wanted, contextOf(pool)),
|
|
302
311
|
wanted,
|
|
303
312
|
selection
|
|
304
313
|
);
|
|
@@ -317,6 +326,23 @@ export default class VarsAsk extends BaseCommand {
|
|
|
317
326
|
return [...new Set(pool.map(variableReferenceKey))];
|
|
318
327
|
}
|
|
319
328
|
|
|
329
|
+
/**
|
|
330
|
+
* The trusted repositories as a reference searches them, from the cached
|
|
331
|
+
* indexes alone. A project whose repositories cannot be read this way still
|
|
332
|
+
* answers every other form of reference, so a failure here is an empty list.
|
|
333
|
+
*/
|
|
334
|
+
private trustedRepos(): ReferenceRepo[] {
|
|
335
|
+
try {
|
|
336
|
+
return subscriptionServiceFor({
|
|
337
|
+
configContext: this.configContext,
|
|
338
|
+
settings: this.settings,
|
|
339
|
+
shellEnv: this.shellEnv,
|
|
340
|
+
}).cachedReferenceRepos();
|
|
341
|
+
} catch {
|
|
342
|
+
return [];
|
|
343
|
+
}
|
|
344
|
+
}
|
|
345
|
+
|
|
320
346
|
/**
|
|
321
347
|
* Settles which of the things a reference could have meant this run proceeds
|
|
322
348
|
* with, through the rule every command shares.
|
|
@@ -356,10 +382,6 @@ function narrowTo(pool: DefinedVariable[], match: ReferenceMatch): DefinedVariab
|
|
|
356
382
|
});
|
|
357
383
|
}
|
|
358
384
|
|
|
359
|
-
/** The reference context for the variables still in the running. */
|
|
360
|
-
function contextOf(pool: DefinedVariable[], ladder: LadderContext) {
|
|
361
|
-
return referenceContextFromVariables(pool, ladder);
|
|
362
|
-
}
|
|
363
385
|
|
|
364
386
|
/** What this run is asking about, in the words the header shows. */
|
|
365
387
|
function describeSelection(selection: Selection, all: boolean): string {
|