@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,19 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* The publish side of the Repositories layer: everything `sous repo release`
|
|
3
|
-
* and `sous repo submit` are built from.
|
|
4
|
-
*
|
|
5
|
-
* Import from here rather than from the individual modules, so a caller has one
|
|
6
|
-
* place to look for validation, tags, working-tree state, index regeneration
|
|
7
|
-
* and version bumping.
|
|
8
|
-
*/
|
|
9
|
-
|
|
10
|
-
export * from "./validate.js";
|
|
11
|
-
export * from "./tags.js";
|
|
12
|
-
export * from "./git-state.js";
|
|
13
|
-
export * from "./index-builder.js";
|
|
14
|
-
export * from "./settle.js";
|
|
15
|
-
export * from "./plan.js";
|
|
16
|
-
export * from "./bump.js";
|
|
17
|
-
export * from "./submit-service.js";
|
|
18
|
-
export * from "./changelog.js";
|
|
19
|
-
export * from "./submissions.js";
|
|
@@ -1,521 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* The release plan: what one `sous repo release` run would do, worked out
|
|
3
|
-
* before anything is written.
|
|
4
|
-
*
|
|
5
|
-
* A release is decided by three facts about each recipe, and nothing else:
|
|
6
|
-
*
|
|
7
|
-
* 1. Is it IN SCOPE? By default every recipe the repository publishes is;
|
|
8
|
-
* `--namespace` and `--recipe` narrow it.
|
|
9
|
-
* 2. Has its content CHANGED since the tag that last published it? A recipe
|
|
10
|
-
* nobody touched is not re-released, because a published version that says
|
|
11
|
-
* the same thing as the one before it is noise.
|
|
12
|
-
* 3. Has its version already been RAISED past that tag? If so the author (or
|
|
13
|
-
* a previous run) has already done the bump, and this run only publishes
|
|
14
|
-
* it.
|
|
15
|
-
*
|
|
16
|
-
* From those, the plan says which manifests get a version bump, which tags get
|
|
17
|
-
* cut, and in which order: dependency-first, so a recipe is never published
|
|
18
|
-
* before something it depends on.
|
|
19
|
-
*
|
|
20
|
-
* Nothing here writes anything. The command prints the plan, asks once, and
|
|
21
|
-
* then carries it out.
|
|
22
|
-
*/
|
|
23
|
-
|
|
24
|
-
import path from "node:path";
|
|
25
|
-
import semver from "semver";
|
|
26
|
-
import { hashDirectory } from "../store/hash.js";
|
|
27
|
-
import {
|
|
28
|
-
RefSource,
|
|
29
|
-
isNamedRef,
|
|
30
|
-
locationOf,
|
|
31
|
-
namespaceOf,
|
|
32
|
-
sharedRefResolver,
|
|
33
|
-
shortKey,
|
|
34
|
-
splitRecipeKey,
|
|
35
|
-
} from "../../../services/ref-resolver/index.js";
|
|
36
|
-
import type { RunOptions } from "../providers/git.js";
|
|
37
|
-
import { listRecipeTags, tagFor, withTaggedTree, type RecipeTag } from "./tags.js";
|
|
38
|
-
import { nextVersion, type BumpLevel } from "./bump.js";
|
|
39
|
-
import type { RepoValidation, ValidatedRecipe, ValidationProblem } from "./validate.js";
|
|
40
|
-
|
|
41
|
-
/** Which recipes a run is allowed to touch. */
|
|
42
|
-
export type ReleaseScope = {
|
|
43
|
-
/** Namespaces named with `--namespace`. Empty means every namespace. */
|
|
44
|
-
namespaces: string[];
|
|
45
|
-
/** Recipe keys named with `--recipe`. Empty means every recipe. */
|
|
46
|
-
recipes: string[];
|
|
47
|
-
};
|
|
48
|
-
|
|
49
|
-
/** What is known about one recipe before the plan is made. */
|
|
50
|
-
export type RecipeState = {
|
|
51
|
-
/** The validated recipe. */
|
|
52
|
-
recipe: ValidatedRecipe;
|
|
53
|
-
/** Its key, `namespace/name`. */
|
|
54
|
-
key: string;
|
|
55
|
-
/** The version its manifest declares right now. */
|
|
56
|
-
version: string;
|
|
57
|
-
/** Whether the scope covers it. */
|
|
58
|
-
inScope: boolean;
|
|
59
|
-
/** The highest version any tag publishes, when the recipe has ever been tagged. */
|
|
60
|
-
lastTagged?: string;
|
|
61
|
-
/** The tag carrying that version. */
|
|
62
|
-
lastTag?: string;
|
|
63
|
-
/** Whether the recipe's files differ from what that tag carries. */
|
|
64
|
-
changed: boolean;
|
|
65
|
-
/** Whether a tag already exists for the version the manifest declares. */
|
|
66
|
-
currentVersionTagged: boolean;
|
|
67
|
-
};
|
|
68
|
-
|
|
69
|
-
/** One recipe this run would publish. */
|
|
70
|
-
export type PlannedRelease = {
|
|
71
|
-
/** The recipe key, `namespace/name`. */
|
|
72
|
-
key: string;
|
|
73
|
-
/** The recipe folder, relative to the repository root. */
|
|
74
|
-
path: string;
|
|
75
|
-
/** Absolute path to the recipe's manifest, which a bump rewrites. */
|
|
76
|
-
manifestPath: string;
|
|
77
|
-
/** The version the manifest declares now. */
|
|
78
|
-
from: string;
|
|
79
|
-
/** The version this run publishes. */
|
|
80
|
-
to: string;
|
|
81
|
-
/** The bump this run applies, when it applies one. */
|
|
82
|
-
bump?: BumpLevel;
|
|
83
|
-
/** The tag this run would cut. */
|
|
84
|
-
tag: string;
|
|
85
|
-
};
|
|
86
|
-
|
|
87
|
-
/** One recipe the plan leaves alone, and why. */
|
|
88
|
-
export type SkippedRecipe = {
|
|
89
|
-
key: string;
|
|
90
|
-
/** A complete sentence, ready to print. */
|
|
91
|
-
reason: string;
|
|
92
|
-
};
|
|
93
|
-
|
|
94
|
-
/** What a plan came to. */
|
|
95
|
-
export type ReleasePlan = {
|
|
96
|
-
/** Everything this run would publish, dependency-first. */
|
|
97
|
-
releases: PlannedRelease[];
|
|
98
|
-
/** Recipes the run leaves alone, with the reason for each. */
|
|
99
|
-
skipped: SkippedRecipe[];
|
|
100
|
-
/** Everything wrong with the plan, errors and warnings together. */
|
|
101
|
-
problems: ValidationProblem[];
|
|
102
|
-
/** Every release tag the repository carries. */
|
|
103
|
-
tags: RecipeTag[];
|
|
104
|
-
/** What each recipe looked like when the plan was made. */
|
|
105
|
-
states: RecipeState[];
|
|
106
|
-
};
|
|
107
|
-
|
|
108
|
-
/** What `buildReleasePlan` needs to know. */
|
|
109
|
-
export type BuildReleasePlanOptions = {
|
|
110
|
-
/** The validated repository. */
|
|
111
|
-
validation: RepoValidation;
|
|
112
|
-
/** Which recipes the run may touch. */
|
|
113
|
-
scope: ReleaseScope;
|
|
114
|
-
/** How far to raise a changed recipe's version. Defaults to a patch step. */
|
|
115
|
-
bump?: BumpLevel;
|
|
116
|
-
/** When true, nothing is bumped and an unbumped change is an error. */
|
|
117
|
-
noBump?: boolean;
|
|
118
|
-
/** When true, every recipe in scope is released, changed or not. */
|
|
119
|
-
includeUnchanged?: boolean;
|
|
120
|
-
/** Every release tag, when the caller has already listed them. */
|
|
121
|
-
tags?: RecipeTag[];
|
|
122
|
-
/** The command runner git calls go through. */
|
|
123
|
-
run?: RunOptions["run"];
|
|
124
|
-
};
|
|
125
|
-
|
|
126
|
-
/**
|
|
127
|
-
* Builds the scope from the repeatable `--namespace` and `--recipe` flags.
|
|
128
|
-
*
|
|
129
|
-
* @param namespaces - Namespaces named on the command line.
|
|
130
|
-
* @param recipes - Recipe keys named on the command line.
|
|
131
|
-
*/
|
|
132
|
-
export function releaseScope(
|
|
133
|
-
namespaces: string[] = [],
|
|
134
|
-
recipes: string[] = []
|
|
135
|
-
): ReleaseScope {
|
|
136
|
-
return {
|
|
137
|
-
namespaces: [...new Set(namespaces.map((value) => value.trim()).filter(Boolean))],
|
|
138
|
-
recipes: [...new Set(recipes.map((value) => value.trim()).filter(Boolean))],
|
|
139
|
-
};
|
|
140
|
-
}
|
|
141
|
-
|
|
142
|
-
/** True when the scope covers every recipe the repository publishes. */
|
|
143
|
-
export function scopeIsWholeRepository(scope: ReleaseScope): boolean {
|
|
144
|
-
return scope.namespaces.length === 0 && scope.recipes.length === 0;
|
|
145
|
-
}
|
|
146
|
-
|
|
147
|
-
/**
|
|
148
|
-
* The scope in words, for the run's preamble.
|
|
149
|
-
*
|
|
150
|
-
* @param scope - The scope to describe.
|
|
151
|
-
*/
|
|
152
|
-
export function describeScope(scope: ReleaseScope): string {
|
|
153
|
-
if (scopeIsWholeRepository(scope)) return "The whole repository";
|
|
154
|
-
const parts: string[] = [];
|
|
155
|
-
if (scope.namespaces.length > 0) {
|
|
156
|
-
parts.push(
|
|
157
|
-
`${scope.namespaces.length === 1 ? "the namespace" : "the namespaces"} ` +
|
|
158
|
-
scope.namespaces.join(", ")
|
|
159
|
-
);
|
|
160
|
-
}
|
|
161
|
-
if (scope.recipes.length > 0) {
|
|
162
|
-
parts.push(
|
|
163
|
-
`${scope.recipes.length === 1 ? "the recipe" : "the recipes"} ` +
|
|
164
|
-
scope.recipes.join(", ")
|
|
165
|
-
);
|
|
166
|
-
}
|
|
167
|
-
return parts.join(", and ").replace(/^./, (first) => first.toUpperCase());
|
|
168
|
-
}
|
|
169
|
-
|
|
170
|
-
/**
|
|
171
|
-
* Raises a ConfigError-free list of problems for scope entries that name
|
|
172
|
-
* nothing this repository publishes, so a typo is caught before anything runs.
|
|
173
|
-
*
|
|
174
|
-
* @param validation - The validated repository.
|
|
175
|
-
* @param scope - The scope as given.
|
|
176
|
-
*/
|
|
177
|
-
export function scopeProblems(
|
|
178
|
-
validation: RepoValidation,
|
|
179
|
-
scope: ReleaseScope
|
|
180
|
-
): ValidationProblem[] {
|
|
181
|
-
const problems: ValidationProblem[] = [];
|
|
182
|
-
const keys = new Set(validation.recipes.map((recipe) => recipe.key));
|
|
183
|
-
const namespaces = new Set(
|
|
184
|
-
validation.recipes.map((recipe) => recipe.manifest.namespace)
|
|
185
|
-
);
|
|
186
|
-
|
|
187
|
-
for (const namespace of scope.namespaces) {
|
|
188
|
-
if (namespaces.has(namespace)) continue;
|
|
189
|
-
problems.push({
|
|
190
|
-
level: "error",
|
|
191
|
-
where: `--namespace ${namespace}`,
|
|
192
|
-
message:
|
|
193
|
-
`this repository publishes no namespace called '${namespace}'. It publishes: ` +
|
|
194
|
-
`${[...namespaces].sort().join(", ")}.`,
|
|
195
|
-
});
|
|
196
|
-
}
|
|
197
|
-
|
|
198
|
-
for (const key of scope.recipes) {
|
|
199
|
-
if (keys.has(key)) continue;
|
|
200
|
-
problems.push({
|
|
201
|
-
level: "error",
|
|
202
|
-
where: `--recipe ${key}`,
|
|
203
|
-
message:
|
|
204
|
-
`this repository publishes no recipe called '${key}'. It publishes: ` +
|
|
205
|
-
`${[...keys].sort().join(", ")}.`,
|
|
206
|
-
});
|
|
207
|
-
}
|
|
208
|
-
|
|
209
|
-
return problems;
|
|
210
|
-
}
|
|
211
|
-
|
|
212
|
-
/**
|
|
213
|
-
* Works out what one run would publish.
|
|
214
|
-
*
|
|
215
|
-
* @param options - The validated repository, the scope, and the bump rules.
|
|
216
|
-
*/
|
|
217
|
-
export async function buildReleasePlan(
|
|
218
|
-
options: BuildReleasePlanOptions
|
|
219
|
-
): Promise<ReleasePlan> {
|
|
220
|
-
const { validation, scope } = options;
|
|
221
|
-
const run = options.run;
|
|
222
|
-
const tags = options.tags ?? (await listRecipeTags(validation.rootDir, { run }));
|
|
223
|
-
const problems: ValidationProblem[] = [];
|
|
224
|
-
|
|
225
|
-
const states: RecipeState[] = [];
|
|
226
|
-
for (const recipe of validation.recipes) {
|
|
227
|
-
states.push(await readRecipeState(validation.rootDir, recipe, scope, tags, run));
|
|
228
|
-
}
|
|
229
|
-
|
|
230
|
-
const releases: PlannedRelease[] = [];
|
|
231
|
-
const skipped: SkippedRecipe[] = [];
|
|
232
|
-
|
|
233
|
-
for (const state of states) {
|
|
234
|
-
if (!state.inScope) {
|
|
235
|
-
skipped.push({
|
|
236
|
-
key: state.key,
|
|
237
|
-
reason: "it is outside this release's scope.",
|
|
238
|
-
});
|
|
239
|
-
continue;
|
|
240
|
-
}
|
|
241
|
-
|
|
242
|
-
const worthReleasing = state.changed || options.includeUnchanged === true;
|
|
243
|
-
if (!worthReleasing) {
|
|
244
|
-
skipped.push({
|
|
245
|
-
key: state.key,
|
|
246
|
-
reason: `its files have not changed since ${state.lastTag}.`,
|
|
247
|
-
});
|
|
248
|
-
continue;
|
|
249
|
-
}
|
|
250
|
-
|
|
251
|
-
// A version already raised past the last tag needs no bump; this run only
|
|
252
|
-
// publishes it. That is what a merge commit looks like to the CI run.
|
|
253
|
-
const alreadyRaised = state.lastTagged === undefined || !state.currentVersionTagged;
|
|
254
|
-
|
|
255
|
-
if (alreadyRaised) {
|
|
256
|
-
releases.push({
|
|
257
|
-
key: state.key,
|
|
258
|
-
path: state.recipe.path,
|
|
259
|
-
manifestPath: state.recipe.manifestPath,
|
|
260
|
-
from: state.version,
|
|
261
|
-
to: state.version,
|
|
262
|
-
tag: tagOf(state.key, state.version),
|
|
263
|
-
});
|
|
264
|
-
continue;
|
|
265
|
-
}
|
|
266
|
-
|
|
267
|
-
if (options.noBump === true) {
|
|
268
|
-
problems.push({
|
|
269
|
-
level: "error",
|
|
270
|
-
where: relativeTo(validation.rootDir, state.recipe.manifestPath),
|
|
271
|
-
message:
|
|
272
|
-
`version ${state.version} is already published as the tag '${state.lastTag}', ` +
|
|
273
|
-
`and this recipe's files have changed since it. Raise the version in this ` +
|
|
274
|
-
`manifest; a published version never changes.`,
|
|
275
|
-
});
|
|
276
|
-
continue;
|
|
277
|
-
}
|
|
278
|
-
|
|
279
|
-
const level = options.bump ?? "patch";
|
|
280
|
-
const to = nextVersion(state.version, level);
|
|
281
|
-
releases.push({
|
|
282
|
-
key: state.key,
|
|
283
|
-
path: state.recipe.path,
|
|
284
|
-
manifestPath: state.recipe.manifestPath,
|
|
285
|
-
from: state.version,
|
|
286
|
-
to,
|
|
287
|
-
bump: level,
|
|
288
|
-
tag: tagOf(state.key, to),
|
|
289
|
-
});
|
|
290
|
-
}
|
|
291
|
-
|
|
292
|
-
const ordered = orderByDependencies(releases, validation);
|
|
293
|
-
problems.push(...checkSiblings(validation, ordered, states));
|
|
294
|
-
|
|
295
|
-
return { releases: ordered, skipped, problems, tags, states };
|
|
296
|
-
}
|
|
297
|
-
|
|
298
|
-
/**
|
|
299
|
-
* Orders a set of releases dependency-first, so a tag is never cut before the
|
|
300
|
-
* tags it will depend on. Recipes that do not depend on each other keep their
|
|
301
|
-
* original order, which is the order the repo manifest lists them in.
|
|
302
|
-
*
|
|
303
|
-
* A cycle cannot be ordered; the entries in it keep their original order, and
|
|
304
|
-
* the closure check that follows reports it as the real problem.
|
|
305
|
-
*
|
|
306
|
-
* @param releases - The releases to order.
|
|
307
|
-
* @param validation - The validated repository, read for each manifest's siblings.
|
|
308
|
-
*/
|
|
309
|
-
export function orderByDependencies(
|
|
310
|
-
releases: PlannedRelease[],
|
|
311
|
-
validation: RepoValidation
|
|
312
|
-
): PlannedRelease[] {
|
|
313
|
-
const byKey = new Map(releases.map((entry) => [entry.key, entry]));
|
|
314
|
-
const ordered: PlannedRelease[] = [];
|
|
315
|
-
const placed = new Set<string>();
|
|
316
|
-
const visiting = new Set<string>();
|
|
317
|
-
|
|
318
|
-
const place = (key: string): void => {
|
|
319
|
-
if (placed.has(key) || visiting.has(key)) return;
|
|
320
|
-
const entry = byKey.get(key);
|
|
321
|
-
if (entry === undefined) return;
|
|
322
|
-
|
|
323
|
-
visiting.add(key);
|
|
324
|
-
for (const sibling of siblingKeysOf(validation, key)) place(sibling);
|
|
325
|
-
visiting.delete(key);
|
|
326
|
-
|
|
327
|
-
placed.add(key);
|
|
328
|
-
ordered.push(entry);
|
|
329
|
-
};
|
|
330
|
-
|
|
331
|
-
for (const entry of releases) place(entry.key);
|
|
332
|
-
return ordered;
|
|
333
|
-
}
|
|
334
|
-
|
|
335
|
-
/**
|
|
336
|
-
* The keys of the recipes in THIS repository that a recipe depends on. A
|
|
337
|
-
* dependency written as a locator URL lives somewhere else and has nothing to
|
|
338
|
-
* do with the order tags are cut in here.
|
|
339
|
-
*
|
|
340
|
-
* @param validation - The validated repository.
|
|
341
|
-
* @param key - The recipe whose dependencies are wanted.
|
|
342
|
-
*/
|
|
343
|
-
export function siblingKeysOf(validation: RepoValidation, key: string): string[] {
|
|
344
|
-
const recipe = validation.recipes.find((entry) => entry.key === key);
|
|
345
|
-
if (recipe === undefined) return [];
|
|
346
|
-
|
|
347
|
-
const declared = [
|
|
348
|
-
...(recipe.manifest.depends ?? []),
|
|
349
|
-
...(recipe.manifest.subscribes ?? []),
|
|
350
|
-
];
|
|
351
|
-
|
|
352
|
-
const keys: string[] = [];
|
|
353
|
-
for (const written of declared) {
|
|
354
|
-
let readings;
|
|
355
|
-
try {
|
|
356
|
-
readings = sharedRefResolver().parse(written, RefSource.Manifest).refs;
|
|
357
|
-
} catch {
|
|
358
|
-
continue;
|
|
359
|
-
}
|
|
360
|
-
const parsed = readings[0]!;
|
|
361
|
-
if (!isNamedRef(parsed) || locationOf(parsed) !== undefined) continue;
|
|
362
|
-
|
|
363
|
-
if (parsed.kind === "recipe") {
|
|
364
|
-
keys.push(shortKey(parsed));
|
|
365
|
-
continue;
|
|
366
|
-
}
|
|
367
|
-
// A whole-namespace dependency means every recipe in it.
|
|
368
|
-
for (const entry of validation.recipes) {
|
|
369
|
-
if (entry.manifest.namespace === namespaceOf(parsed)!.name && entry.key !== key) {
|
|
370
|
-
keys.push(entry.key);
|
|
371
|
-
}
|
|
372
|
-
}
|
|
373
|
-
}
|
|
374
|
-
|
|
375
|
-
return [...new Set(keys)];
|
|
376
|
-
}
|
|
377
|
-
|
|
378
|
-
/**
|
|
379
|
-
* Checks the sibling rule: everything a released recipe depends on inside this
|
|
380
|
-
* repository has to be a version that exists once this run's own tags are
|
|
381
|
-
* counted.
|
|
382
|
-
*
|
|
383
|
-
* There are exactly two ways that fails, and they are different kinds of thing:
|
|
384
|
-
*
|
|
385
|
-
* - The sibling has never been tagged at all. Nothing can depend on it, so
|
|
386
|
-
* this is an error naming the tag that has to be cut.
|
|
387
|
-
* - The sibling HAS been tagged, and has changed since, but is outside this
|
|
388
|
-
* release's scope. The release is still correct: it will depend on the last
|
|
389
|
-
* tagged version. That is worth saying, so it is a warning, and the warning
|
|
390
|
-
* states only what is verifiable.
|
|
391
|
-
*
|
|
392
|
-
* @param validation - The validated repository.
|
|
393
|
-
* @param releases - The releases this run would publish.
|
|
394
|
-
* @param states - What each recipe looked like when the plan was made.
|
|
395
|
-
*/
|
|
396
|
-
function checkSiblings(
|
|
397
|
-
validation: RepoValidation,
|
|
398
|
-
releases: PlannedRelease[],
|
|
399
|
-
states: RecipeState[]
|
|
400
|
-
): ValidationProblem[] {
|
|
401
|
-
const problems: ValidationProblem[] = [];
|
|
402
|
-
const releasing = new Map(releases.map((entry) => [entry.key, entry]));
|
|
403
|
-
const byKey = new Map(states.map((state) => [state.key, state]));
|
|
404
|
-
|
|
405
|
-
for (const release of releases) {
|
|
406
|
-
for (const siblingKey of siblingKeysOf(validation, release.key)) {
|
|
407
|
-
if (releasing.has(siblingKey)) continue;
|
|
408
|
-
|
|
409
|
-
const sibling = byKey.get(siblingKey);
|
|
410
|
-
const where = relativeTo(validation.rootDir, release.manifestPath);
|
|
411
|
-
|
|
412
|
-
if (sibling === undefined) {
|
|
413
|
-
problems.push({
|
|
414
|
-
level: "error",
|
|
415
|
-
where,
|
|
416
|
-
message:
|
|
417
|
-
`it depends on '${siblingKey}', which this repository does not publish. A ` +
|
|
418
|
-
`dependency written without a location names a recipe in this same ` +
|
|
419
|
-
`repository.`,
|
|
420
|
-
});
|
|
421
|
-
continue;
|
|
422
|
-
}
|
|
423
|
-
|
|
424
|
-
if (sibling.lastTagged === undefined) {
|
|
425
|
-
problems.push({
|
|
426
|
-
level: "error",
|
|
427
|
-
where,
|
|
428
|
-
message:
|
|
429
|
-
`it depends on '${siblingKey}', which has never been published: this ` +
|
|
430
|
-
`repository carries no tag for it. Release it first, which cuts the tag ` +
|
|
431
|
-
`'${tagOf(siblingKey, sibling.version)}'.`,
|
|
432
|
-
});
|
|
433
|
-
continue;
|
|
434
|
-
}
|
|
435
|
-
|
|
436
|
-
if (sibling.changed) {
|
|
437
|
-
problems.push({
|
|
438
|
-
level: "warning",
|
|
439
|
-
where,
|
|
440
|
-
message:
|
|
441
|
-
`'${siblingKey}' has changes since '${sibling.lastTag}' that are outside ` +
|
|
442
|
-
`this release's scope; '${release.tag}' will depend on ` +
|
|
443
|
-
`'${sibling.lastTag}'.`,
|
|
444
|
-
});
|
|
445
|
-
}
|
|
446
|
-
}
|
|
447
|
-
}
|
|
448
|
-
|
|
449
|
-
return problems;
|
|
450
|
-
}
|
|
451
|
-
|
|
452
|
-
/**
|
|
453
|
-
* Reads the three facts a plan is made of for one recipe: whether the scope
|
|
454
|
-
* covers it, what tag last published it, and whether its files have changed
|
|
455
|
-
* since that tag.
|
|
456
|
-
*
|
|
457
|
-
* @param rootDir - The repository's root directory.
|
|
458
|
-
* @param recipe - The validated recipe.
|
|
459
|
-
* @param scope - The run's scope.
|
|
460
|
-
* @param tags - Every release tag the repository carries.
|
|
461
|
-
* @param run - The command runner git calls go through.
|
|
462
|
-
*/
|
|
463
|
-
async function readRecipeState(
|
|
464
|
-
rootDir: string,
|
|
465
|
-
recipe: ValidatedRecipe,
|
|
466
|
-
scope: ReleaseScope,
|
|
467
|
-
tags: ReadonlyArray<RecipeTag>,
|
|
468
|
-
run: RunOptions["run"]
|
|
469
|
-
): Promise<RecipeState> {
|
|
470
|
-
const key = recipe.key;
|
|
471
|
-
const version = recipe.manifest.version;
|
|
472
|
-
const mine = tags.filter((tag) => `${tag.namespace}/${tag.name}` === key);
|
|
473
|
-
|
|
474
|
-
const highest = mine
|
|
475
|
-
.map((tag) => tag.version)
|
|
476
|
-
.filter((value) => semver.valid(value) !== null)
|
|
477
|
-
.sort((left, right) => semver.rcompare(left, right))[0];
|
|
478
|
-
|
|
479
|
-
const state: RecipeState = {
|
|
480
|
-
recipe,
|
|
481
|
-
key,
|
|
482
|
-
version,
|
|
483
|
-
inScope: coveredByScope(recipe, scope),
|
|
484
|
-
changed: true,
|
|
485
|
-
currentVersionTagged: mine.some((tag) => tag.version === version),
|
|
486
|
-
...(highest === undefined ? {} : { lastTagged: highest, lastTag: tagOf(key, highest) }),
|
|
487
|
-
};
|
|
488
|
-
|
|
489
|
-
if (highest === undefined) return state;
|
|
490
|
-
|
|
491
|
-
// "Changed" is a content question, so it is answered by hashing, exactly the
|
|
492
|
-
// way a consumer decides whether a cached copy is still the published one.
|
|
493
|
-
const workingHash = await hashDirectory(recipe.dir);
|
|
494
|
-
const taggedHash = await withTaggedTree(
|
|
495
|
-
rootDir,
|
|
496
|
-
tagOf(key, highest),
|
|
497
|
-
recipe.path,
|
|
498
|
-
(dir) => hashDirectory(dir),
|
|
499
|
-
{ run }
|
|
500
|
-
);
|
|
501
|
-
state.changed = workingHash !== taggedHash;
|
|
502
|
-
return state;
|
|
503
|
-
}
|
|
504
|
-
|
|
505
|
-
/** True when a run's scope covers a recipe. */
|
|
506
|
-
function coveredByScope(recipe: ValidatedRecipe, scope: ReleaseScope): boolean {
|
|
507
|
-
if (scopeIsWholeRepository(scope)) return true;
|
|
508
|
-
if (scope.namespaces.includes(recipe.manifest.namespace)) return true;
|
|
509
|
-
return scope.recipes.includes(recipe.key);
|
|
510
|
-
}
|
|
511
|
-
|
|
512
|
-
/** The tag that publishes one version of one recipe key. */
|
|
513
|
-
function tagOf(key: string, version: string): string {
|
|
514
|
-
const { namespace, name } = splitRecipeKey(key);
|
|
515
|
-
return tagFor(namespace, name, version);
|
|
516
|
-
}
|
|
517
|
-
|
|
518
|
-
/** Renders an absolute path as a repository-relative one, with forward slashes. */
|
|
519
|
-
function relativeTo(rootDir: string, target: string): string {
|
|
520
|
-
return path.relative(rootDir, target).split(path.sep).join("/");
|
|
521
|
-
}
|
|
@@ -1,154 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Settling, at release time, the dependencies that read more than one way.
|
|
3
|
-
*
|
|
4
|
-
* Most dependencies say exactly what they mean: `workflow/alpha`, or
|
|
5
|
-
* `github://owner/repo/workflow/alpha`. Two kinds do not:
|
|
6
|
-
*
|
|
7
|
-
* - a GitLab URL with nested groups, which does not say where the project path
|
|
8
|
-
* ends (`gitlab.com/a/b/c/d` may be project `a/b` with namespace `c` and
|
|
9
|
-
* recipe `d`, or project `a/b/c` with namespace `d`);
|
|
10
|
-
* - a browser URL copied from a host's file view, which names a folder whose
|
|
11
|
-
* recipe only that repository's index can say.
|
|
12
|
-
*
|
|
13
|
-
* `sous repo release` settles each of them once, by fetching the index of every
|
|
14
|
-
* candidate repository through the provider layer and keeping the reading whose
|
|
15
|
-
* index publishes what was named. What it settled on is recorded in the index
|
|
16
|
-
* beside the dependency's key, so a consumer reads the answer and never probes.
|
|
17
|
-
*
|
|
18
|
-
* A network failure fails the release: a release never falls through to the
|
|
19
|
-
* next reading, because the reading it fell through to could be the wrong one.
|
|
20
|
-
* Two readings that both publish what is named are a genuine tie, and an error
|
|
21
|
-
* naming the spellings that read one way (the `/*` form for the namespace).
|
|
22
|
-
*/
|
|
23
|
-
|
|
24
|
-
import type { ProviderOptions, RepoProvider } from "../providers/index.js";
|
|
25
|
-
import {
|
|
26
|
-
FetchedIndexLookup,
|
|
27
|
-
RefSource,
|
|
28
|
-
locationOf,
|
|
29
|
-
namespaceOf,
|
|
30
|
-
rangeOf,
|
|
31
|
-
sharedRefResolver,
|
|
32
|
-
shortKey,
|
|
33
|
-
type SousRef,
|
|
34
|
-
} from "../../../services/ref-resolver/index.js";
|
|
35
|
-
import { describeError, type RepoValidation, type ValidationProblem } from "./validate.js";
|
|
36
|
-
|
|
37
|
-
/** What one dependency settled on. */
|
|
38
|
-
export type SettledDependency = {
|
|
39
|
-
/** The canonical identity of the repository publishing it. */
|
|
40
|
-
identity: string;
|
|
41
|
-
/**
|
|
42
|
-
* The recipe keys it reaches in that repository: one for a recipe, and every
|
|
43
|
-
* recipe the namespace publishes for a namespace.
|
|
44
|
-
*/
|
|
45
|
-
keys: string[];
|
|
46
|
-
/** The range the manifest wrote, when it wrote one. */
|
|
47
|
-
range?: string;
|
|
48
|
-
};
|
|
49
|
-
|
|
50
|
-
/** What settling found. */
|
|
51
|
-
export type SettleResult = {
|
|
52
|
-
/** Each settled dependency, keyed by the dependency exactly as written (trimmed). */
|
|
53
|
-
settled: Map<string, SettledDependency>;
|
|
54
|
-
/** Everything that could not be settled; every one of them is an error. */
|
|
55
|
-
problems: ValidationProblem[];
|
|
56
|
-
};
|
|
57
|
-
|
|
58
|
-
/** How settling reaches the network. */
|
|
59
|
-
export type SettleOptions = {
|
|
60
|
-
/** The providers to fetch through. Defaults to the built-ins. */
|
|
61
|
-
providers?: RepoProvider[];
|
|
62
|
-
/** Testing seams handed to every index fetch. */
|
|
63
|
-
providerOptions?: ProviderOptions;
|
|
64
|
-
};
|
|
65
|
-
|
|
66
|
-
/**
|
|
67
|
-
* True when a dependency's readings need an index to settle: it reads more than
|
|
68
|
-
* one way, or it names a browser path.
|
|
69
|
-
*
|
|
70
|
-
* @param readings - Every reading of the dependency.
|
|
71
|
-
*/
|
|
72
|
-
export function needsSettling(readings: SousRef[]): boolean {
|
|
73
|
-
return (
|
|
74
|
-
readings.length > 1 ||
|
|
75
|
-
readings.some((reading) => reading.kind === "repo" && reading.browsed !== undefined)
|
|
76
|
-
);
|
|
77
|
-
}
|
|
78
|
-
|
|
79
|
-
/**
|
|
80
|
-
* Settles every dependency in a repository that reads more than one way.
|
|
81
|
-
* Dependencies that read one way are left alone, and nothing is fetched for
|
|
82
|
-
* them, so a repository that uses none of these forms releases offline as
|
|
83
|
-
* before.
|
|
84
|
-
*
|
|
85
|
-
* @param validation - The validated repository.
|
|
86
|
-
* @param options - The providers, and testing seams.
|
|
87
|
-
*/
|
|
88
|
-
export async function settleDependencyLocations(
|
|
89
|
-
validation: RepoValidation,
|
|
90
|
-
options: SettleOptions = {}
|
|
91
|
-
): Promise<SettleResult> {
|
|
92
|
-
const settled = new Map<string, SettledDependency>();
|
|
93
|
-
const problems: ValidationProblem[] = [];
|
|
94
|
-
const lookup = new FetchedIndexLookup({
|
|
95
|
-
subject: "dependency",
|
|
96
|
-
...(options.providers === undefined ? {} : { providers: options.providers }),
|
|
97
|
-
...(options.providerOptions === undefined ? {} : { providerOptions: options.providerOptions }),
|
|
98
|
-
});
|
|
99
|
-
|
|
100
|
-
for (const recipe of validation.recipes) {
|
|
101
|
-
const declared = [...(recipe.manifest.depends ?? []), ...(recipe.manifest.subscribes ?? [])];
|
|
102
|
-
for (const written of declared) {
|
|
103
|
-
const trimmed = written.trim();
|
|
104
|
-
if (settled.has(trimmed)) continue;
|
|
105
|
-
|
|
106
|
-
let readings: SousRef[];
|
|
107
|
-
try {
|
|
108
|
-
readings = sharedRefResolver().parse(trimmed, RefSource.Manifest).refs;
|
|
109
|
-
} catch {
|
|
110
|
-
// Validation already reported a dependency that does not parse.
|
|
111
|
-
continue;
|
|
112
|
-
}
|
|
113
|
-
if (!needsSettling(readings)) continue;
|
|
114
|
-
|
|
115
|
-
const where = `${recipe.path} ('${trimmed}')`;
|
|
116
|
-
try {
|
|
117
|
-
settled.set(trimmed, await settleOne(readings, lookup));
|
|
118
|
-
} catch (error) {
|
|
119
|
-
problems.push({ level: "error", where, message: describeError(error) });
|
|
120
|
-
}
|
|
121
|
-
}
|
|
122
|
-
}
|
|
123
|
-
|
|
124
|
-
return { settled, problems };
|
|
125
|
-
}
|
|
126
|
-
|
|
127
|
-
/**
|
|
128
|
-
* Settles one dependency: the one reading whose repository's index publishes
|
|
129
|
-
* what it names.
|
|
130
|
-
*
|
|
131
|
-
* @param readings - Every reading of the dependency.
|
|
132
|
-
* @param lookup - The fetched indexes.
|
|
133
|
-
*/
|
|
134
|
-
async function settleOne(
|
|
135
|
-
readings: SousRef[],
|
|
136
|
-
lookup: FetchedIndexLookup
|
|
137
|
-
): Promise<SettledDependency> {
|
|
138
|
-
const { ref: choice } = await lookup.settle(readings);
|
|
139
|
-
const location = locationOf(choice)!;
|
|
140
|
-
const index = await lookup.indexAt(location);
|
|
141
|
-
const namespace = namespaceOf(choice)?.name;
|
|
142
|
-
const keys =
|
|
143
|
-
choice.kind === "namespace"
|
|
144
|
-
? Object.keys(index.recipes)
|
|
145
|
-
.filter((key) => key.startsWith(`${namespace}/`))
|
|
146
|
-
.sort()
|
|
147
|
-
: [shortKey(choice)];
|
|
148
|
-
const range = readings.map(rangeOf).find((written) => written !== undefined);
|
|
149
|
-
return {
|
|
150
|
-
identity: location.identity,
|
|
151
|
-
keys,
|
|
152
|
-
...(range === undefined ? {} : { range }),
|
|
153
|
-
};
|
|
154
|
-
}
|