@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
|
@@ -13,7 +13,10 @@
|
|
|
13
13
|
* project) and `subscribes` (co-subscribed with full semantics), and those refs
|
|
14
14
|
* resolve the same way. The manifest lives inside the recipe's own files, so
|
|
15
15
|
* the caller supplies a loader; that is also the seam where a caller decides
|
|
16
|
-
* whether it is willing to fetch anything at this point.
|
|
16
|
+
* whether it is willing to fetch anything at this point. When the loader has no
|
|
17
|
+
* manifest to give, the lists are read from the version's index entry instead,
|
|
18
|
+
* which records the entry that declared each dependency; only a version whose
|
|
19
|
+
* entry predates that record goes unwalked.
|
|
17
20
|
*
|
|
18
21
|
* Two things the resolver deliberately does NOT do: it never downloads
|
|
19
22
|
* anything, and it never resolves against a repository the project has not
|
|
@@ -25,16 +28,20 @@ import semver from "semver";
|
|
|
25
28
|
import { ConfigError } from "../errors.js";
|
|
26
29
|
import type { IndexFile } from "./formats/index-file.js";
|
|
27
30
|
import type { LockKind } from "./formats/lockfile.js";
|
|
28
|
-
import type { RecipeManifest } from "./formats/recipe-manifest.js";
|
|
31
|
+
import type { RecipeManifest, VariableDefinition } from "./formats/recipe-manifest.js";
|
|
32
|
+
import { indexDependencyLists, type DependencyLists } from "./declarations.js";
|
|
29
33
|
import {
|
|
30
|
-
dependencyRefKey,
|
|
31
|
-
dependencyRepoUrl,
|
|
32
34
|
formatRef,
|
|
33
|
-
|
|
35
|
+
isBrowsedReading,
|
|
36
|
+
isNamedReading,
|
|
37
|
+
parseRef,
|
|
34
38
|
refKey,
|
|
35
|
-
|
|
39
|
+
splitRecipeKey,
|
|
36
40
|
type ParsedRef,
|
|
37
|
-
|
|
41
|
+
type RefReading,
|
|
42
|
+
} from "../refs/parse.js";
|
|
43
|
+
import { settleBrowsed, settleDependency, settleInIndex } from "../refs/settle.js";
|
|
44
|
+
import { RefSource } from "../refs/scopes.js";
|
|
38
45
|
import { shortNameFromIdentity } from "./identity.js";
|
|
39
46
|
import type { IndexDependency } from "./formats/index-file.js";
|
|
40
47
|
|
|
@@ -78,8 +85,9 @@ export type ResolverRepo = {
|
|
|
78
85
|
|
|
79
86
|
/**
|
|
80
87
|
* Loads a resolved recipe's manifest, which is what names its dependencies.
|
|
81
|
-
* Returning undefined means "not available"
|
|
82
|
-
*
|
|
88
|
+
* Returning undefined means "not available": the recipe's dependencies are then
|
|
89
|
+
* read from its index entry, and when that does not record them either, it is
|
|
90
|
+
* reported under `missingManifests` rather than having its dependencies walked.
|
|
83
91
|
*/
|
|
84
92
|
export type RecipeManifestLoader = (
|
|
85
93
|
recipe: ResolvedRecipe
|
|
@@ -138,6 +146,17 @@ export type ResolvedRecipe = {
|
|
|
138
146
|
* published against rather than at whatever its range would reach today.
|
|
139
147
|
*/
|
|
140
148
|
dependencies?: Record<string, IndexDependency>;
|
|
149
|
+
/**
|
|
150
|
+
* The variable definitions the index records for this exact version, when it
|
|
151
|
+
* records them: the questions it asks, known before its files are fetched.
|
|
152
|
+
*/
|
|
153
|
+
variables?: VariableDefinition[];
|
|
154
|
+
/**
|
|
155
|
+
* The manifest `depends` and `subscribes` lists the index records for this
|
|
156
|
+
* exact version, when it records them, so the recipe can be walked before its
|
|
157
|
+
* files are fetched.
|
|
158
|
+
*/
|
|
159
|
+
declares?: DependencyLists;
|
|
141
160
|
};
|
|
142
161
|
|
|
143
162
|
/** A repository something needs that the project has not added. */
|
|
@@ -163,7 +182,10 @@ export type ResolveResult = {
|
|
|
163
182
|
resolved: ResolvedRecipe[];
|
|
164
183
|
/** Repositories a dependency needs that the project has not added. */
|
|
165
184
|
missingRepos: MissingRepo[];
|
|
166
|
-
/**
|
|
185
|
+
/**
|
|
186
|
+
* Resolved recipes whose dependencies could not be read: the loader produced
|
|
187
|
+
* no manifest, and the index entry does not record them.
|
|
188
|
+
*/
|
|
167
189
|
missingManifests: string[];
|
|
168
190
|
/** Any dependency cycle found, as the chain of recipe keys forming it. */
|
|
169
191
|
cycles: string[][];
|
|
@@ -186,6 +208,15 @@ type WorkItem = RefRequest & {
|
|
|
186
208
|
provider?: string;
|
|
187
209
|
/** The dependency exactly as the manifest wrote it, for messages. */
|
|
188
210
|
written: string;
|
|
211
|
+
/**
|
|
212
|
+
* A browser path the dependency named, settled through the repository's
|
|
213
|
+
* index once the project has it. Until then `ref` holds no namespace.
|
|
214
|
+
*/
|
|
215
|
+
browsed?: string;
|
|
216
|
+
/** The range the manifest wrote, applied once a browsed path is settled. */
|
|
217
|
+
range?: string;
|
|
218
|
+
/** What the declaring version's index records, for pins once a browsed path is settled. */
|
|
219
|
+
pins?: Record<string, IndexDependency>;
|
|
189
220
|
};
|
|
190
221
|
};
|
|
191
222
|
|
|
@@ -200,24 +231,27 @@ type WorkItem = RefRequest & {
|
|
|
200
231
|
* version is what gets asked for, rather than whatever the declared range would
|
|
201
232
|
* reach today.
|
|
202
233
|
*
|
|
234
|
+
* A dependency that reads more than one way (a GitLab URL with nested groups)
|
|
235
|
+
* is settled by what the release recorded in the index, and failing that by
|
|
236
|
+
* what the project's own cached indexes already publish; nothing is fetched to
|
|
237
|
+
* find out. One that still reads several ways is an error naming them.
|
|
238
|
+
*
|
|
203
239
|
* @param written - The dependency as the manifest wrote it.
|
|
204
240
|
* @param parent - The recipe whose manifest declared it.
|
|
205
241
|
* @param kind - Whether it was declared as a dependency or a co-subscription.
|
|
242
|
+
* @param context - The added repositories and their cached indexes.
|
|
206
243
|
*/
|
|
207
244
|
function dependencyRequest(
|
|
208
245
|
written: string,
|
|
209
246
|
parent: ResolvedRecipe,
|
|
210
|
-
kind: LockKind
|
|
247
|
+
kind: LockKind,
|
|
248
|
+
context: ResolveContext
|
|
211
249
|
): WorkItem {
|
|
212
|
-
const
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
if (
|
|
217
|
-
ref.recipe = parsed.recipe;
|
|
218
|
-
const range = pinned?.version ?? parsed.range ?? pinned?.range;
|
|
219
|
-
if (range !== undefined) ref.range = range;
|
|
220
|
-
}
|
|
250
|
+
const reading = settleDependency(written, {
|
|
251
|
+
...(parent.dependencies === undefined ? {} : { recorded: parent.dependencies }),
|
|
252
|
+
known: (candidate) => knownLocally(candidate, context),
|
|
253
|
+
});
|
|
254
|
+
if (reading === undefined) throw unsettledDependencyError(written, parent);
|
|
221
255
|
|
|
222
256
|
const base = {
|
|
223
257
|
requestedBy: parent.key,
|
|
@@ -225,22 +259,143 @@ function dependencyRequest(
|
|
|
225
259
|
...(parent.prerelease ? { prerelease: true } : {}),
|
|
226
260
|
};
|
|
227
261
|
|
|
228
|
-
if (
|
|
262
|
+
if (isBrowsedReading(reading)) {
|
|
263
|
+
return {
|
|
264
|
+
ref: { namespace: "" },
|
|
265
|
+
...base,
|
|
266
|
+
remote: {
|
|
267
|
+
...remoteOf(reading, written),
|
|
268
|
+
browsed: reading.browsed,
|
|
269
|
+
...(reading.range === undefined ? {} : { range: reading.range }),
|
|
270
|
+
...(parent.dependencies === undefined ? {} : { pins: parent.dependencies }),
|
|
271
|
+
},
|
|
272
|
+
};
|
|
273
|
+
}
|
|
274
|
+
/* c8 ignore next */
|
|
275
|
+
if (!isNamedReading(reading)) throw unsettledDependencyError(written, parent);
|
|
276
|
+
|
|
277
|
+
const pinned = parent.dependencies?.[refKey(reading)];
|
|
278
|
+
const ref = pinnedRef(reading, pinned);
|
|
279
|
+
|
|
280
|
+
if (reading.location === undefined) {
|
|
229
281
|
return { ref: { ...ref, repo: parent.repo }, ...base };
|
|
230
282
|
}
|
|
231
283
|
|
|
284
|
+
const remote = remoteOf(reading, written);
|
|
232
285
|
return {
|
|
233
286
|
ref,
|
|
234
287
|
...base,
|
|
235
|
-
remote: {
|
|
236
|
-
identity: pinned?.repo ?? parsed.canonicalRepo!,
|
|
237
|
-
url: dependencyRepoUrl(parsed)!,
|
|
238
|
-
...(parsed.provider === undefined ? {} : { provider: parsed.provider }),
|
|
239
|
-
written: written.trim(),
|
|
240
|
-
},
|
|
288
|
+
remote: { ...remote, identity: pinned?.repo ?? remote.identity },
|
|
241
289
|
};
|
|
242
290
|
}
|
|
243
291
|
|
|
292
|
+
/**
|
|
293
|
+
* A named reading as a request asks for it: at the exact version the index
|
|
294
|
+
* recorded when it recorded one, otherwise at the range the manifest wrote.
|
|
295
|
+
*
|
|
296
|
+
* @param reading - The reading.
|
|
297
|
+
* @param pinned - What the declaring version's index recorded for it.
|
|
298
|
+
*/
|
|
299
|
+
function pinnedRef(reading: ParsedRef, pinned: IndexDependency | undefined): ParsedRef {
|
|
300
|
+
const ref: ParsedRef = { namespace: reading.namespace };
|
|
301
|
+
if (reading.recipe !== undefined) {
|
|
302
|
+
ref.recipe = reading.recipe;
|
|
303
|
+
const range = pinned?.version ?? reading.range ?? pinned?.range;
|
|
304
|
+
if (range !== undefined) ref.range = range;
|
|
305
|
+
}
|
|
306
|
+
return ref;
|
|
307
|
+
}
|
|
308
|
+
|
|
309
|
+
/** Where a located reading's repository lives, as a request carries it. */
|
|
310
|
+
function remoteOf(
|
|
311
|
+
reading: RefReading,
|
|
312
|
+
written: string
|
|
313
|
+
): NonNullable<WorkItem["remote"]> {
|
|
314
|
+
const location = reading.location!;
|
|
315
|
+
return {
|
|
316
|
+
identity: location.identity,
|
|
317
|
+
url: location.url,
|
|
318
|
+
provider: location.provider,
|
|
319
|
+
written: written.trim(),
|
|
320
|
+
};
|
|
321
|
+
}
|
|
322
|
+
|
|
323
|
+
/**
|
|
324
|
+
* Whether a reading's repository is one the project added and its cached index
|
|
325
|
+
* publishes what the reading names. It reads only what is already here.
|
|
326
|
+
*
|
|
327
|
+
* @param reading - One reading of a dependency.
|
|
328
|
+
* @param context - The added repositories and their cached indexes.
|
|
329
|
+
*/
|
|
330
|
+
function knownLocally(reading: RefReading, context: ResolveContext): boolean {
|
|
331
|
+
if (reading.location === undefined) return false;
|
|
332
|
+
const added = repoNamedByIdentity(context.repos, reading.location.identity);
|
|
333
|
+
const index = added === undefined ? undefined : context.indexes.get(added);
|
|
334
|
+
if (index === undefined) return false;
|
|
335
|
+
return settleInIndex(reading, { namespaces: Object.keys(index.namespaces), recipes: index.recipes }).length > 0;
|
|
336
|
+
}
|
|
337
|
+
|
|
338
|
+
/**
|
|
339
|
+
* The error for a dependency that reads more than one way with nothing to
|
|
340
|
+
* settle it.
|
|
341
|
+
*
|
|
342
|
+
* @param written - The dependency as written.
|
|
343
|
+
* @param parent - The recipe that declared it.
|
|
344
|
+
*/
|
|
345
|
+
function unsettledDependencyError(written: string, parent: ResolvedRecipe): ConfigError {
|
|
346
|
+
const readings = parseRef(written, RefSource.Manifest);
|
|
347
|
+
return new ConfigError(
|
|
348
|
+
`The dependency '${written.trim()}' of the recipe '${parent.key}' can be read ` +
|
|
349
|
+
`${readings.length} ways, and nothing says which one it means:\n` +
|
|
350
|
+
readings.map((reading) => ` ${formatRef(reading)}`).join("\n") +
|
|
351
|
+
`\n A release records which one it settled on in the index; the index of ` +
|
|
352
|
+
`'${parent.repo}' records none for this version. Release the recipe again with this ` +
|
|
353
|
+
`version of sous, or write the dependency so it reads one way, as one of the forms above.`
|
|
354
|
+
);
|
|
355
|
+
}
|
|
356
|
+
|
|
357
|
+
/**
|
|
358
|
+
* Settles a browsed path, once the project has the repository it names.
|
|
359
|
+
*
|
|
360
|
+
* @param item - The request, whose `ref` is filled in.
|
|
361
|
+
* @param index - The repository's cached index.
|
|
362
|
+
*/
|
|
363
|
+
function settleBrowsedItem(item: WorkItem, index: IndexFile | undefined): void {
|
|
364
|
+
const remote = item.remote!;
|
|
365
|
+
const settled =
|
|
366
|
+
index === undefined
|
|
367
|
+
? []
|
|
368
|
+
: settleBrowsed(remote.browsed!, {
|
|
369
|
+
namespaces: Object.keys(index.namespaces),
|
|
370
|
+
recipes: index.recipes,
|
|
371
|
+
});
|
|
372
|
+
|
|
373
|
+
let found: ParsedRef | undefined = settled.length === 1 ? settled[0] : undefined;
|
|
374
|
+
if (found === undefined) {
|
|
375
|
+
// The folder may have moved since the release; the key it settled on then
|
|
376
|
+
// is recorded beside the repository it lives in.
|
|
377
|
+
const recorded = Object.keys(remote.pins ?? {}).filter(
|
|
378
|
+
(key) => remote.pins![key]!.repo === remote.identity
|
|
379
|
+
);
|
|
380
|
+
if (recorded.length === 1) {
|
|
381
|
+
const { namespace, name } = splitRecipeKey(recorded[0]!);
|
|
382
|
+
found = { namespace, recipe: name };
|
|
383
|
+
}
|
|
384
|
+
}
|
|
385
|
+
|
|
386
|
+
if (found === undefined) {
|
|
387
|
+
throw new ConfigError(
|
|
388
|
+
`The dependency '${remote.written}' names a folder, and the index of the repository at ` +
|
|
389
|
+
`${remote.url} ${settled.length === 0 ? "publishes no recipe there" : "publishes several recipes there"}.` +
|
|
390
|
+
`\n It was required by the recipe '${item.requestedBy}'. Write the dependency as the ` +
|
|
391
|
+
`recipe it means, such as '${remote.url}/<namespace>/<recipe>'.`
|
|
392
|
+
);
|
|
393
|
+
}
|
|
394
|
+
|
|
395
|
+
const withRange = { ...found, ...(remote.range === undefined ? {} : { range: remote.range }) };
|
|
396
|
+
item.ref = { ...pinnedRef(withRange, remote.pins?.[refKey(found)]), repo: item.ref.repo };
|
|
397
|
+
}
|
|
398
|
+
|
|
244
399
|
/**
|
|
245
400
|
* The short name of the added repository with this identity, or undefined when
|
|
246
401
|
* the project has added none. Short names are a project's own labels, so the
|
|
@@ -249,7 +404,7 @@ function dependencyRequest(
|
|
|
249
404
|
* @param repos - The repositories the project has added.
|
|
250
405
|
* @param identity - The canonical identity to look for.
|
|
251
406
|
*/
|
|
252
|
-
function repoNamedByIdentity(
|
|
407
|
+
export function repoNamedByIdentity(
|
|
253
408
|
repos: Record<string, ResolverRepo>,
|
|
254
409
|
identity: string
|
|
255
410
|
): string | undefined {
|
|
@@ -266,7 +421,7 @@ function repoNamedByIdentity(
|
|
|
266
421
|
* @param repos - The repositories the project has added.
|
|
267
422
|
* @param identity - The canonical identity of the repository being named.
|
|
268
423
|
*/
|
|
269
|
-
function proposeRepoName(
|
|
424
|
+
export function proposeRepoName(
|
|
270
425
|
repos: Record<string, ResolverRepo>,
|
|
271
426
|
identity: string
|
|
272
427
|
): string {
|
|
@@ -334,6 +489,7 @@ export async function resolveRefs(
|
|
|
334
489
|
continue;
|
|
335
490
|
}
|
|
336
491
|
item.ref = { ...item.ref, repo: added };
|
|
492
|
+
if (item.remote.browsed !== undefined) settleBrowsedItem(item, context.indexes.get(added));
|
|
337
493
|
}
|
|
338
494
|
|
|
339
495
|
if (item.ref.recipe === undefined) {
|
|
@@ -358,18 +514,18 @@ export async function resolveRefs(
|
|
|
358
514
|
}
|
|
359
515
|
walked.set(recipe.key, recipe.version);
|
|
360
516
|
|
|
361
|
-
const
|
|
362
|
-
if (
|
|
517
|
+
const lists = dependencyListsOf(recipe, await context.loadManifest(recipe));
|
|
518
|
+
if (lists === undefined) {
|
|
363
519
|
missingManifests.add(recipe.key);
|
|
364
520
|
continue;
|
|
365
521
|
}
|
|
366
522
|
|
|
367
523
|
for (const [kind, refs] of [
|
|
368
|
-
["depends",
|
|
369
|
-
["subscribes",
|
|
524
|
+
["depends", lists.depends],
|
|
525
|
+
["subscribes", lists.subscribes],
|
|
370
526
|
] as Array<[LockKind, string[]]>) {
|
|
371
527
|
for (const written of refs) {
|
|
372
|
-
queue.push(dependencyRequest(written, recipe, kind));
|
|
528
|
+
queue.push(dependencyRequest(written, recipe, kind, context));
|
|
373
529
|
}
|
|
374
530
|
}
|
|
375
531
|
}
|
|
@@ -456,8 +612,9 @@ async function keepOnlyReachable(
|
|
|
456
612
|
} catch {
|
|
457
613
|
manifest = undefined;
|
|
458
614
|
}
|
|
615
|
+
const lists = dependencyListsOf(recipe, manifest);
|
|
459
616
|
|
|
460
|
-
if (
|
|
617
|
+
if (lists === undefined) {
|
|
461
618
|
// An unreadable manifest is already reported as a missing manifest. Keep
|
|
462
619
|
// everything this recipe held rather than dropping a dependency over it.
|
|
463
620
|
for (const other of resolved.values()) {
|
|
@@ -469,24 +626,11 @@ async function keepOnlyReachable(
|
|
|
469
626
|
}
|
|
470
627
|
|
|
471
628
|
for (const [kind, refs] of [
|
|
472
|
-
["depends",
|
|
473
|
-
["subscribes",
|
|
629
|
+
["depends", lists.depends],
|
|
630
|
+
["subscribes", lists.subscribes],
|
|
474
631
|
] as Array<[LockKind, string[]]>) {
|
|
475
632
|
for (const written of refs) {
|
|
476
|
-
|
|
477
|
-
try {
|
|
478
|
-
parsed = parseDependencyRef(written);
|
|
479
|
-
} catch {
|
|
480
|
-
continue;
|
|
481
|
-
}
|
|
482
|
-
// A namespace ref means every recipe in it, exactly as the walk expanded it.
|
|
483
|
-
const targets =
|
|
484
|
-
parsed.recipe === undefined
|
|
485
|
-
? [...resolved.keys()].filter((entry) =>
|
|
486
|
-
entry.startsWith(`${parsed.namespace}/`)
|
|
487
|
-
)
|
|
488
|
-
: [dependencyRefKey(parsed)];
|
|
489
|
-
for (const target of targets) {
|
|
633
|
+
for (const target of dependencyTargets(written, recipe, context, resolved)) {
|
|
490
634
|
if (!resolved.has(target)) continue;
|
|
491
635
|
if (declare(key, target, kind)) queue.push(target);
|
|
492
636
|
}
|
|
@@ -506,6 +650,57 @@ async function keepOnlyReachable(
|
|
|
506
650
|
}
|
|
507
651
|
}
|
|
508
652
|
|
|
653
|
+
/**
|
|
654
|
+
* The resolved keys one dependency of a recipe reaches, the same way the walk
|
|
655
|
+
* reached them: a namespace means every recipe in it, and a browsed path means
|
|
656
|
+
* the recipe it settles on. A dependency that no longer settles reaches
|
|
657
|
+
* nothing, because the walk would have failed on it.
|
|
658
|
+
*
|
|
659
|
+
* @param written - The dependency as the manifest wrote it.
|
|
660
|
+
* @param holder - The recipe that declared it.
|
|
661
|
+
* @param context - The added repositories and their cached indexes.
|
|
662
|
+
* @param resolved - What the walk resolved.
|
|
663
|
+
*/
|
|
664
|
+
function dependencyTargets(
|
|
665
|
+
written: string,
|
|
666
|
+
holder: ResolvedRecipe,
|
|
667
|
+
context: ResolveContext,
|
|
668
|
+
resolved: Map<string, ResolvedRecipe>
|
|
669
|
+
): string[] {
|
|
670
|
+
let item: WorkItem;
|
|
671
|
+
try {
|
|
672
|
+
item = dependencyRequest(written, holder, "depends", context);
|
|
673
|
+
if (item.remote?.browsed !== undefined) {
|
|
674
|
+
const added = repoNamedByIdentity(context.repos, item.remote.identity);
|
|
675
|
+
settleBrowsedItem(item, added === undefined ? undefined : context.indexes.get(added));
|
|
676
|
+
}
|
|
677
|
+
} catch {
|
|
678
|
+
return [];
|
|
679
|
+
}
|
|
680
|
+
const { namespace } = item.ref;
|
|
681
|
+
return item.ref.recipe === undefined
|
|
682
|
+
? [...resolved.keys()].filter((entry) => entry.startsWith(`${namespace}/`))
|
|
683
|
+
: [refKey(item.ref)];
|
|
684
|
+
}
|
|
685
|
+
|
|
686
|
+
/**
|
|
687
|
+
* What a resolved recipe declares under `depends` and `subscribes`: its
|
|
688
|
+
* manifest's lists when the manifest could be read, and otherwise the lists its
|
|
689
|
+
* index entry records, or undefined when neither says.
|
|
690
|
+
*
|
|
691
|
+
* @param recipe - The resolved recipe, carrying the lists its index entry records.
|
|
692
|
+
* @param manifest - Its manifest, when the loader produced one.
|
|
693
|
+
*/
|
|
694
|
+
function dependencyListsOf(
|
|
695
|
+
recipe: ResolvedRecipe,
|
|
696
|
+
manifest: RecipeManifest | undefined
|
|
697
|
+
): DependencyLists | undefined {
|
|
698
|
+
if (manifest !== undefined) {
|
|
699
|
+
return { depends: manifest.depends ?? [], subscribes: manifest.subscribes ?? [] };
|
|
700
|
+
}
|
|
701
|
+
return recipe.declares;
|
|
702
|
+
}
|
|
703
|
+
|
|
509
704
|
/** Records one more reason a repository is needed. */
|
|
510
705
|
function recordMissingRepo(into: Map<string, MissingRepo>, missing: MissingRepo): void {
|
|
511
706
|
const existing = into.get(missing.name);
|
|
@@ -618,15 +813,16 @@ function resolveRecipeRef(
|
|
|
618
813
|
|
|
619
814
|
const version = pickVersion(key, entry, ranges, prerelease, context.keep?.[key]);
|
|
620
815
|
const versionEntry = entry.versions[version]!;
|
|
816
|
+
const declares = indexDependencyLists(versionEntry);
|
|
621
817
|
|
|
622
818
|
const requestedBy = [...(previous?.requestedBy ?? [])];
|
|
623
819
|
if (!requestedBy.includes(item.requestedBy)) requestedBy.push(item.requestedBy);
|
|
624
820
|
|
|
625
|
-
const namespace =
|
|
821
|
+
const { namespace, name } = splitRecipeKey(key);
|
|
626
822
|
return {
|
|
627
823
|
key,
|
|
628
824
|
namespace,
|
|
629
|
-
name
|
|
825
|
+
name,
|
|
630
826
|
repo: chosen.repo,
|
|
631
827
|
identity: context.repos[chosen.repo]?.identity ?? chosen.repo,
|
|
632
828
|
version,
|
|
@@ -636,6 +832,8 @@ function resolveRecipeRef(
|
|
|
636
832
|
...(versionEntry.dependencies === undefined
|
|
637
833
|
? {}
|
|
638
834
|
: { dependencies: versionEntry.dependencies }),
|
|
835
|
+
...(versionEntry.variables === undefined ? {} : { variables: versionEntry.variables }),
|
|
836
|
+
...(declares === undefined ? {} : { declares }),
|
|
639
837
|
// A recipe held as a co-subscription by anyone is a co-subscription; a
|
|
640
838
|
// build dependency only stays one while nothing subscribes to it.
|
|
641
839
|
kind: previous?.kind === "subscribes" || item.kind === "subscribes" ? "subscribes" : "depends",
|