@sous-io/sous 0.2.28 → 0.2.29
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 +1 -0
- package/docs/markdown/repositories-authoring.md +12 -8
- package/docs/markdown/repositories-consuming.md +8 -1
- package/docs/markdown/repositories-file-formats.md +53 -8
- package/docs/markdown/repositories-providers.md +8 -6
- package/docs/markdown/repositories-troubleshooting.md +6 -0
- package/docs/markdown/repositories-variables.md +9 -2
- 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/sous.recipe.yaml +1 -1
- package/src/commands/lock/rebuild.ts +2 -2
- 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 +44 -25
- package/src/lib/repos/formats/index-file.ts +2 -1
- package/src/lib/repos/formats/recipe-manifest.ts +10 -8
- 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 +55 -17
- 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 +198 -41
- package/src/lib/repos/subscription-service.ts +262 -38
- package/src/lib/vars/mappings.ts +17 -30
- package/src/lib/repos/ref.ts +0 -513
|
@@ -26,4 +26,5 @@
|
|
|
26
26
|
- [0005: Follow-ups from the first project on the hand-off](adrs/0005-first-project-follow-ups.md)
|
|
27
27
|
- [0006: `sous repo init` follows `sous-recipes`](adrs/0006-repo-init-model-home.md)
|
|
28
28
|
- [0007: Frozen index entries and tolerant readers](adrs/0007-frozen-index-dependencies.md)
|
|
29
|
+
- [0008: One ref parser, told where the ref came from](adrs/0008-one-ref-parser.md)
|
|
29
30
|
- [0009: One whole build, everywhere](adrs/0009-whole-build-everywhere.md)
|
|
@@ -151,19 +151,23 @@ depends:
|
|
|
151
151
|
- workflow/qa-helper # a sibling, released alongside me
|
|
152
152
|
- workflow/qa-helper@^1.1 # a sibling with a range; uncommon
|
|
153
153
|
- github://sous-io/sous-recipes/workflow/sat@^1.0 # another repository
|
|
154
|
-
- gitlab://gitlab.example.com/group/subgroup/proj
|
|
154
|
+
- gitlab://gitlab.example.com/group/subgroup/proj/-/qa/lint # a self-hosted host, with subgroups
|
|
155
|
+
- https://github.com/acme/recipes/tree/main/recipes/lint # a URL copied from the browser
|
|
155
156
|
subscribes:
|
|
156
157
|
- quality/code-review # co-subscribed: its files land too
|
|
157
158
|
```
|
|
158
159
|
|
|
159
160
|
A **bare ref** is a sibling in this repository; with no range it means "the version released alongside me", since
|
|
160
|
-
one run cuts both tags and records the exact version in the index. A
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
161
|
+
one run cuts both tags and records the exact version in the index. A location names another repository: a
|
|
162
|
+
locator, an HTTPS or SSH URL, or a URL copied from the browser. A ref is stored and printed as its published
|
|
163
|
+
**identity** (namespace and recipe), never as a folder path. Any spelling that settles to one identity is
|
|
164
|
+
accepted, including a folder path or a pasted browser URL: a recipe stored at `recipes/shared/sat/` and
|
|
165
|
+
published as `workflow/sat` may be written `github://owner/repo/workflow/sat` or
|
|
166
|
+
`https://github.com/owner/repo/tree/main/recipes/shared/sat`, and the release settles the second through the
|
|
167
|
+
other repository's index. The optional range after `@` follows npm's rules. `local://` locators and `repo:`
|
|
168
|
+
short names are refused: a local repository is a consumer's convenience, and a short name is one project's
|
|
169
|
+
private label. See [Dependencies named by location](repositories-file-formats.md#dependencies-named-by-location)
|
|
170
|
+
and every accepted spelling in [Ref forms](repositories-file-formats.md#ref-forms).
|
|
167
171
|
|
|
168
172
|
## Cut a release
|
|
169
173
|
|
|
@@ -87,15 +87,22 @@ $ sous recipe show workflow/qa-variables
|
|
|
87
87
|
sous subscription add workflow/qa-variables
|
|
88
88
|
sous subscription add workflow/qa-variables@^1.2.0
|
|
89
89
|
sous subscription add workflow # a whole namespace
|
|
90
|
+
sous subscription add https://github.com/acme/recipes/tree/main/recipes/workflow/alpha
|
|
90
91
|
```
|
|
91
92
|
|
|
93
|
+
A ref may be written in any form sous reads ([Ref forms](repositories-file-formats.md#ref-forms)),
|
|
94
|
+
including a URL copied from the browser. A location is matched to the trusted repository at it,
|
|
95
|
+
whatever this project calls that repository; one the project does not trust yet goes through the
|
|
96
|
+
trust question first, and nothing is fetched from it before the answer. The subscription is
|
|
97
|
+
recorded in its canonical form, `namespace/recipe`.
|
|
98
|
+
|
|
92
99
|
The whole dependency closure resolves before anything is downloaded, and only then is anything
|
|
93
100
|
written; an install is whole or not at all.
|
|
94
101
|
|
|
95
102
|
| Flag | What it does |
|
|
96
103
|
|------|--------------|
|
|
97
104
|
| `-y, --yes` | Answer yes to both questions this command can ask: the subscribe confirmation, and the trust question for a repository it has to add |
|
|
98
|
-
| `--accept-first` | When a
|
|
105
|
+
| `--accept-first` | When a ref matches several things, take the first one listed |
|
|
99
106
|
| `--answer <name>=<value>` | Answer one question ahead of time. Repeat it per answer, or read a whole file of them with `--answers-file <path>` |
|
|
100
107
|
| `--always-pull` | Install a newer in-range version whenever one exists, rather than holding the locked one |
|
|
101
108
|
| `--dry-run` | Print what would be installed, writing and downloading nothing |
|
|
@@ -104,15 +104,60 @@ release itself is never restricted, so whatever publishes the recipe still relea
|
|
|
104
104
|
|
|
105
105
|
### Dependencies named by location
|
|
106
106
|
|
|
107
|
-
`depends` and `subscribes`
|
|
107
|
+
`depends` and `subscribes` name their targets by where they live: `workflow/qa-helper` is a sibling in this
|
|
108
108
|
same repository, and `github://sous-io/sous-recipes/workflow/sub-agent-delegation@^1.0` is a recipe in
|
|
109
|
-
another one.
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
109
|
+
another one. Every spelling in [Ref forms](#ref-forms) that names a namespace or a recipe is accepted here,
|
|
110
|
+
except a `repo:` qualifier (one project's private name for a repository), a local path or `local://`
|
|
111
|
+
locator (a convenience, not a published location), and a location naming a whole repository with nothing
|
|
112
|
+
inside it. Names are recorded lowercase.
|
|
113
|
+
|
|
114
|
+
A ref is stored and printed as its published identity (namespace and recipe), never as a folder path. Any
|
|
115
|
+
spelling that settles to one identity is accepted, including a folder path or a pasted browser URL. Two
|
|
116
|
+
spellings need the other repository's index to settle: a GitLab URL with nested groups, which does not say
|
|
117
|
+
where the project path ends (`gitlab.com/a/b/c/d` may be project `a/b` naming the recipe `c/d`, or project
|
|
118
|
+
`a/b/c` naming the namespace `d`), and a browser URL, which names a folder. `sous repo release` settles each
|
|
119
|
+
by fetching the index of every candidate repository and keeping the reading whose index publishes what was
|
|
120
|
+
named, then records the repository it settled on in the index, beside each key the dependency reached. A
|
|
121
|
+
consumer reads that record and never probes. A network failure fails the release rather than guessing, and
|
|
122
|
+
two readings that both publish what is named fail it too, naming the spellings that read one way:
|
|
123
|
+
`gitlab://a/b/c/-/d/*` for the namespace, `gitlab://a/b/-/c/d` for the recipe.
|
|
124
|
+
|
|
125
|
+
### Ref forms
|
|
126
|
+
|
|
127
|
+
Every place a ref is written reads it with one parser, told where the ref came from. These forms are
|
|
128
|
+
recognized everywhere; the table after them says which each place allows. A refused form is an error saying
|
|
129
|
+
what to write there instead.
|
|
130
|
+
|
|
131
|
+
| Form | Example | Names |
|
|
132
|
+
| --- | --- | --- |
|
|
133
|
+
| A bare name | `workflow` | a namespace (on the command line, also a recipe or anything else by that name) |
|
|
134
|
+
| Namespace and recipe | `workflow/alpha` | a recipe |
|
|
135
|
+
| Namespace, spelled out | `workflow/*` | a namespace |
|
|
136
|
+
| Repository-qualified | `sous-recipes:workflow/alpha` | a recipe in the repository this project calls `sous-recipes` |
|
|
137
|
+
| With a range | `workflow/alpha@^1.2` | a recipe, within an npm-style range |
|
|
138
|
+
| Provider-scheme locator | `github://owner/repo/workflow`, `.../workflow/*`, `.../workflow/alpha` | a namespace or a recipe in that repository |
|
|
139
|
+
| A host other than the provider's own | `gitlab://gitlab.example.com/group/proj/-/workflow/alpha` | the same, on a self-hosted instance |
|
|
140
|
+
| HTTPS URL | `https://github.com/owner/repo/workflow/alpha` | the same as the locator |
|
|
141
|
+
| URL with no scheme | `github.com/owner/repo/workflow/alpha` | the same |
|
|
142
|
+
| SSH remote | `git@github.com:owner/repo.git`, `ssh://git@github.com/owner/repo.git/workflow` | the repository, or something inside it |
|
|
143
|
+
| Browser URL | `https://github.com/owner/repo/tree/main/recipes/workflow/alpha` | the recipe whose index `path` holds that folder (or a file in it); a folder holding one namespace's recipes names the namespace |
|
|
144
|
+
| GitLab separator | `https://gitlab.com/group/sub/proj/-/tree/main/recipes/workflow/alpha` | the same, with `/-/` marking where the project path ends |
|
|
145
|
+
| GitLab nested group | `gitlab://group/sub/proj/workflow/alpha` | every reading the path allows, settled as described above |
|
|
146
|
+
|
|
147
|
+
A `.git` suffix on the repository is dropped in every URL form. GitLab's canonical locator always marks the
|
|
148
|
+
end of the project path with `/-/`, so it reads one way.
|
|
149
|
+
|
|
150
|
+
| Place | Allows |
|
|
151
|
+
| --- | --- |
|
|
152
|
+
| The command line | every form; names in any case |
|
|
153
|
+
| A config file's subscription keys | `namespace` or `namespace/recipe`, lowercase; the repository is recorded in the lockfile and a range in the entry's `range` field |
|
|
154
|
+
| A recipe manifest's `depends` and `subscribes` | every form naming a namespace or a recipe, except `repo:` and a local location |
|
|
155
|
+
| A key sous stores (the lockfile, the index, the store) | `namespace` or `namespace/recipe`, lowercase |
|
|
156
|
+
|
|
157
|
+
Matching tries the exact spelling first, then ignores case; a name that still matches several things is a
|
|
158
|
+
question, answered by `--accept-first` or by choosing. What sous writes (config layers, the lockfile, the
|
|
159
|
+
index) and prints is always the canonical form: `namespace/recipe`, qualified with the repository's short
|
|
160
|
+
name where one is needed.
|
|
116
161
|
|
|
117
162
|
### Variable definitions
|
|
118
163
|
|
|
@@ -128,12 +128,14 @@ Canonicalizing also produces the two clone URLs sous hands to git, `https://<hos
|
|
|
128
128
|
and `git@<host>:<owner>/<name>.git`. For the `local` provider the HTTPS slot carries the
|
|
129
129
|
absolute directory, because that is what git is given when a recipe is fetched.
|
|
130
130
|
|
|
131
|
-
?> A published manifest names a dependency in another repository by a locator
|
|
132
|
-
scheme is the provider's identifier: `github://sous-io/sous-recipes/workflow/sat@^1.1`. A
|
|
133
|
-
segment containing a dot is read as the host, so `gitlab://gitlab.example.com/group/project
|
|
134
|
-
addresses a self-hosted instance; without one, the provider's public host is assumed.
|
|
135
|
-
|
|
136
|
-
|
|
131
|
+
?> A published manifest names a dependency in another repository by location, such as a locator
|
|
132
|
+
whose scheme is the provider's identifier: `github://sous-io/sous-recipes/workflow/sat@^1.1`. A
|
|
133
|
+
first segment containing a dot is read as the host, so `gitlab://gitlab.example.com/group/project/-/ns/recipe`
|
|
134
|
+
addresses a self-hosted instance; without one, the provider's public host is assumed. Where a
|
|
135
|
+
repository path ends, and what a browser URL's path looks like, is each provider's own rule (its
|
|
136
|
+
`readLocation`), so a URL copied from either host's file view works too. `local://` is refused,
|
|
137
|
+
because a path on your disk is not a published location. Every form is in
|
|
138
|
+
[Ref forms](repositories-file-formats.md#ref-forms).
|
|
137
139
|
|
|
138
140
|
## Private repositories
|
|
139
141
|
|
|
@@ -108,8 +108,14 @@ providers sous ships named (see [Providers](repositories-providers.md)). The leg
|
|
|
108
108
|
depends:
|
|
109
109
|
- workflow/sat # a sibling in this repository
|
|
110
110
|
- github://acme/recipes/workflow/sat@^1.1 # a recipe in another repository
|
|
111
|
+
- https://github.com/acme/recipes/tree/main/sat # the same, copied from the browser
|
|
111
112
|
```
|
|
112
113
|
|
|
114
|
+
Every accepted spelling is in [Ref forms](repositories-file-formats.md#ref-forms). A GitLab URL
|
|
115
|
+
with nested groups, or a browser URL, is settled by `sous repo release` through the other
|
|
116
|
+
repository's index; when that index cannot be read, the release stops and names it, and when two
|
|
117
|
+
readings both publish what was named, it names the `/-/` and `/*` spellings that read one way.
|
|
118
|
+
|
|
113
119
|
While developing both sides, use
|
|
114
120
|
[`sous repo link`](repositories-authoring.md#edit-a-repository-in-place); it redirects resolution
|
|
115
121
|
at a working copy without changing what you publish.
|
|
@@ -241,10 +241,17 @@ sous vars ask SOUS_VAR_TASK_FILE_ROOT # the same one, by a name in use
|
|
|
241
241
|
sous vars ask workflow/task-files.taskFileRoot # the same one, spelled out in full
|
|
242
242
|
sous vars ask task-files # every question one recipe asks
|
|
243
243
|
sous vars ask sous-recipes # every question that repository's recipes ask
|
|
244
|
+
sous vars ask https://github.com/sous-io/sous-recipes/tree/main/recipes/workflow/task-files
|
|
245
|
+
# the same recipe, as a URL copied from the browser
|
|
244
246
|
```
|
|
245
247
|
|
|
246
|
-
|
|
247
|
-
|
|
248
|
+
A repository, namespace or recipe may be written in any form in
|
|
249
|
+
[Ref forms](repositories-file-formats.md#ref-forms), including a location: it is matched to the
|
|
250
|
+
trusted repository at it and settled through that repository's cached index, then narrowed to the
|
|
251
|
+
variables it covers. `--repo` and `--namespace` accept a location the same way.
|
|
252
|
+
|
|
253
|
+
Matching tries the exact spelling first, then ignores case; a name that still matches several things is a
|
|
254
|
+
question. An environment variable name resolves when a definition declares it or an env file sets it. `--repo`, `--namespace` and `--var` say which kind is meant, each narrowing what the one
|
|
248
255
|
before left, so `sous vars ask --namespace workflow apiUrl` asks about that namespace's `apiUrl` alone. When
|
|
249
256
|
a name matches several things, sous lists them and asks; `--accept-first` takes the first, and a run with no
|
|
250
257
|
terminal fails naming it.
|
|
@@ -101,9 +101,9 @@ subscribes:
|
|
|
101
101
|
- quality/code-review
|
|
102
102
|
```
|
|
103
103
|
|
|
104
|
-
Both name their targets by location, written as a **locator URL**
|
|
105
|
-
([the
|
|
106
|
-
|
|
104
|
+
Both name their targets by location, written as a **locator URL** or any other URL form
|
|
105
|
+
([the forms](repositories-file-formats.md#ref-forms)) when the target lives in another repository,
|
|
106
|
+
never by a short name a consuming project chose; a curated bundle is simply
|
|
107
107
|
a recipe made mostly of `subscribes` entries. Because both lists are
|
|
108
108
|
declarative YAML rather than code, sous can read the whole dependency closure before fetching any
|
|
109
109
|
of it, which is what makes the trust decision answerable up front.
|
package/package.json
CHANGED
|
@@ -138,13 +138,17 @@ Both lists name other recipes, and they do different things:
|
|
|
138
138
|
- `subscribes` is a **co-subscription**. The other recipe's files land and its questions are asked,
|
|
139
139
|
as if the project had subscribed to it too.
|
|
140
140
|
|
|
141
|
-
Each entry names its target by where it lives
|
|
142
|
-
|
|
143
|
-
- a bare `namespace/recipe` for a sibling in the same repository.
|
|
144
|
-
version released alongside this one; an `@` range (npm's rules) is
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
141
|
+
Each entry names its target by where it lives:
|
|
142
|
+
|
|
143
|
+
- a bare `namespace/recipe` (or `namespace` for all of it) for a sibling in the same repository.
|
|
144
|
+
With no range it means the version released alongside this one; an `@` range (npm's rules) is
|
|
145
|
+
allowed but uncommon.
|
|
146
|
+
- a location for a recipe in another repository, such as
|
|
147
|
+
`github://example-org/team-recipes/review/wording@^1.0`, an HTTPS or SSH URL, or a URL copied
|
|
148
|
+
from the browser. A ref is stored and printed as its published identity (namespace and recipe),
|
|
149
|
+
never as a folder path. Any spelling that settles to one identity is accepted, including a
|
|
150
|
+
folder path or a pasted browser URL; `sous repo release` settles one that needs the other
|
|
151
|
+
repository's index, and fails rather than guess.
|
|
148
152
|
|
|
149
153
|
`local://` locators and `repo:` short names are refused: both are one project's private view of a
|
|
150
154
|
repository, and a published manifest is read everywhere.
|
|
@@ -24,7 +24,7 @@ import { recipeFilesDirectory } from "../../lib/repos/catalog-inputs.js";
|
|
|
24
24
|
import { readRecipeManifestIn } from "../../lib/repos/locked-recipes.js";
|
|
25
25
|
import { createEmptyLockfile } from "../../lib/repos/formats/lockfile.js";
|
|
26
26
|
import type { LockRepoInput } from "../../lib/repos/lock-service.js";
|
|
27
|
-
import {
|
|
27
|
+
import { RefSource, parseShortRef } from "../../lib/refs/index.js";
|
|
28
28
|
import {
|
|
29
29
|
PROJECT_REQUESTER,
|
|
30
30
|
resolveRefs,
|
|
@@ -133,7 +133,7 @@ export default class LockRebuild extends BaseCommand {
|
|
|
133
133
|
.sort()
|
|
134
134
|
.map((key) => {
|
|
135
135
|
const entry = subscriptions[key]!;
|
|
136
|
-
const parsed =
|
|
136
|
+
const parsed = parseShortRef(key, RefSource.Config);
|
|
137
137
|
return {
|
|
138
138
|
ref: {
|
|
139
139
|
...parsed,
|
|
@@ -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 {
|