@sous-io/sous 0.1.1 → 0.2.0
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 +115 -35
- package/bin/run.js +10 -1
- package/docs/markdown/README.md +27 -0
- package/docs/markdown/_sidebar.md +18 -0
- package/docs/markdown/commands.md +308 -0
- package/docs/markdown/config-discovery.md +74 -0
- package/docs/markdown/config-inspection.md +69 -0
- package/docs/markdown/config-layers.md +92 -0
- package/docs/markdown/config-variables.md +79 -0
- package/docs/markdown/configuration.md +71 -0
- package/docs/markdown/design-principles.md +59 -0
- package/docs/markdown/repositories-authoring.md +408 -0
- package/docs/markdown/repositories-consuming.md +580 -0
- package/docs/markdown/repositories-file-formats.md +1084 -0
- package/docs/markdown/repositories-variables.md +387 -0
- package/docs/markdown/repositories.md +303 -0
- package/docs/markdown/skill-categories.md +58 -0
- package/package.json +72 -8
- package/{shared-prompts/skills/sous-skills → recipes/core/sous-skills/skills}/about-agent-skills/SKILL.tpl.md +20 -20
- package/{shared-prompts/skills/sous-skills → recipes/core/sous-skills/skills}/about-agent-skills/examples/about-something.md +2 -2
- package/{shared-prompts/skills/sous-skills → recipes/core/sous-skills/skills}/about-agent-skills/examples/do-something.md +1 -1
- package/{shared-prompts/skills/sous-skills → recipes/core/sous-skills/skills}/about-agent-skills/references/advanced-patterns.md +6 -6
- package/{shared-prompts/skills/sous-skills → recipes/core/sous-skills/skills}/about-agent-skills/references/commands.md +5 -5
- package/{shared-prompts/skills/sous-skills → recipes/core/sous-skills/skills}/about-agent-skills/references/frontmatter.md +3 -3
- package/{shared-prompts/skills/sous-skills → recipes/core/sous-skills/skills}/about-liquid-templates/SKILL.tpl.md +40 -25
- package/recipes/core/sous-skills/skills/about-sous/SKILL.tpl.md +70 -0
- package/recipes/core/sous-skills/skills/about-sous-configuration/SKILL.tpl.md +75 -0
- package/{shared-prompts/skills/sous-skills → recipes/core/sous-skills/skills}/create-skill/SKILL.tpl.md +8 -9
- package/recipes/core/sous-skills/sous.recipe.yaml +45 -0
- package/sous.config.schema.json +337 -0
- package/src/base-command.ts +220 -67
- package/src/commands/build.ts +150 -73
- package/src/commands/clear.ts +23 -15
- package/src/commands/compile.ts +74 -16
- package/src/commands/config/get.ts +110 -0
- package/src/commands/config/show.ts +32 -0
- package/src/commands/config/validate.ts +53 -0
- package/src/commands/help.ts +46 -0
- package/src/commands/launch.ts +36 -14
- package/src/commands/lock/rebuild.ts +241 -0
- package/src/commands/lock/show.ts +115 -0
- package/src/commands/namespace/list.ts +117 -0
- package/src/commands/namespace/show.ts +110 -0
- package/src/commands/prune.ts +3 -11
- package/src/commands/recipe/list.ts +95 -0
- package/src/commands/recipe/show.ts +301 -0
- package/src/commands/repo/add.ts +145 -0
- package/src/commands/repo/gc.ts +172 -0
- package/src/commands/repo/init.ts +136 -0
- package/src/commands/repo/link.ts +500 -0
- package/src/commands/repo/list.ts +179 -0
- package/src/commands/repo/release.ts +619 -0
- package/src/commands/repo/remove.ts +193 -0
- package/src/commands/repo/search.ts +189 -0
- package/src/commands/repo/submit.ts +133 -0
- package/src/commands/repo/unlink.ts +147 -0
- package/src/commands/subscription/add.ts +285 -0
- package/src/commands/subscription/list.ts +129 -0
- package/src/commands/subscription/remove.ts +181 -0
- package/src/commands/vars/ask.ts +374 -0
- package/src/commands/vars/index.ts +79 -0
- package/src/commands/vars/list.ts +67 -0
- package/src/commands/vars/show.ts +77 -0
- package/src/config-command.ts +30 -0
- package/src/lib/build-service.ts +206 -54
- package/src/lib/config-discovery.ts +220 -27
- package/src/lib/config-inspect.ts +145 -0
- package/src/lib/config-kernel.mjs +377 -0
- package/src/lib/config-schema.ts +361 -0
- package/src/lib/env-file.ts +328 -0
- package/src/lib/env-local.ts +18 -1
- package/src/lib/errors.ts +32 -0
- package/src/lib/include-resolver.ts +108 -15
- package/src/lib/interactive.ts +165 -0
- package/src/lib/markdown-compiler.ts +118 -37
- package/src/lib/package-info.ts +25 -0
- package/src/lib/pid-service.ts +32 -21
- package/src/lib/refs/find.ts +589 -0
- package/src/lib/refs/index.ts +12 -0
- package/src/lib/refs/pick.ts +147 -0
- package/src/lib/refs/scopes.ts +61 -0
- package/src/lib/repos/catalog-display.ts +116 -0
- package/src/lib/repos/catalog-inputs.ts +160 -0
- package/src/lib/repos/catalog.ts +722 -0
- package/src/lib/repos/core-recipe.ts +105 -0
- package/src/lib/repos/defaults.ts +175 -0
- package/src/lib/repos/formats/common.ts +389 -0
- package/src/lib/repos/formats/index-file.ts +215 -0
- package/src/lib/repos/formats/links-map.ts +96 -0
- package/src/lib/repos/formats/lockfile.ts +167 -0
- package/src/lib/repos/formats/patterns.ts +57 -0
- package/src/lib/repos/formats/recipe-manifest.ts +395 -0
- package/src/lib/repos/formats/repo-manifest.ts +88 -0
- package/src/lib/repos/formats/store-entry.ts +84 -0
- package/src/lib/repos/freshness.ts +208 -0
- package/src/lib/repos/git-clone.ts +312 -0
- package/src/lib/repos/identity.ts +89 -0
- package/src/lib/repos/index.ts +58 -0
- package/src/lib/repos/links.ts +353 -0
- package/src/lib/repos/load-manifest.ts +236 -0
- package/src/lib/repos/lock-service.ts +453 -0
- package/src/lib/repos/locked-namespace-resolver.ts +90 -0
- package/src/lib/repos/locked-recipes.ts +254 -0
- package/src/lib/repos/managed-layer.ts +422 -0
- package/src/lib/repos/namespace-resolver.ts +370 -0
- package/src/lib/repos/providers/base.ts +206 -0
- package/src/lib/repos/providers/git.ts +233 -0
- package/src/lib/repos/providers/github.ts +294 -0
- package/src/lib/repos/providers/gitlab.ts +263 -0
- package/src/lib/repos/providers/http.ts +102 -0
- package/src/lib/repos/providers/index-cache.ts +382 -0
- package/src/lib/repos/providers/index.ts +106 -0
- package/src/lib/repos/providers/local.ts +391 -0
- package/src/lib/repos/providers/provider.ts +401 -0
- package/src/lib/repos/recipe-config-layers.ts +287 -0
- package/src/lib/repos/recipe-targets.ts +223 -0
- package/src/lib/repos/ref-search.ts +46 -0
- package/src/lib/repos/ref.ts +513 -0
- package/src/lib/repos/reference-report.ts +122 -0
- package/src/lib/repos/release/bump.ts +161 -0
- package/src/lib/repos/release/git-state.ts +305 -0
- package/src/lib/repos/release/index-builder.ts +635 -0
- package/src/lib/repos/release/index.ts +16 -0
- package/src/lib/repos/release/plan.ts +512 -0
- package/src/lib/repos/release/submit-service.ts +496 -0
- package/src/lib/repos/release/tags.ts +243 -0
- package/src/lib/repos/release/validate.ts +463 -0
- package/src/lib/repos/resolver.ts +789 -0
- package/src/lib/repos/scaffold/index.ts +238 -0
- package/src/lib/repos/scaffold/templates.ts +413 -0
- package/src/lib/repos/seed.ts +414 -0
- package/src/lib/repos/store/contract.ts +64 -0
- package/src/lib/repos/store/hash.ts +114 -0
- package/src/lib/repos/store/recipe-store.ts +599 -0
- package/src/lib/repos/store/settings.ts +58 -0
- package/src/lib/repos/subscription-service.ts +2678 -0
- package/src/lib/repos/trust.ts +447 -0
- package/src/lib/settings.ts +546 -189
- package/src/lib/sous-home.ts +104 -0
- package/src/lib/state.ts +52 -20
- package/src/lib/vars/ask.ts +1152 -0
- package/src/lib/vars/definition-source.ts +252 -0
- package/src/lib/vars/display.ts +233 -0
- package/src/lib/vars/index.ts +18 -0
- package/src/lib/vars/ladder.ts +282 -0
- package/src/lib/vars/mappings.ts +265 -0
- package/src/lib/vars/names.ts +94 -0
- package/src/lib/vars/preanswers.ts +395 -0
- package/src/lib/vars/question-plan.ts +218 -0
- package/src/lib/vars/report.ts +228 -0
- package/src/lib/vars/safe-regex.ts +235 -0
- package/src/lib/vars/validate.ts +312 -0
- package/src/lib/watch-loop.ts +148 -0
- package/src/templating/init-liquid-engine.ts +58 -16
- package/src/utils/choice-prompt.ts +143 -0
- package/src/utils/command-errors.ts +186 -0
- package/src/utils/command-help.ts +45 -0
- package/src/utils/confirm-prompt.ts +110 -0
- package/src/utils/flags.ts +153 -0
- package/src/utils/formatting.ts +540 -55
- package/src/utils/prompts.ts +35 -1
- package/src/utils/sous-directory.ts +245 -0
- package/src/utils/table.ts +603 -0
- package/src/utils/value-prompt.ts +119 -0
- package/shared-prompts/_partials/resume-task.md +0 -51
- package/shared-prompts/_partials/sub-agent-delegation.md +0 -32
- package/shared-prompts/_partials/update-task-file.md +0 -52
- package/shared-prompts/memories/automated-browser-tasks/INDEX.tpl.md +0 -52
- package/shared-prompts/skills/automated-browser-tasks/about-automated-browser-tasks/SKILL.tpl.md +0 -102
- package/shared-prompts/skills/automated-browser-tasks/about-automated-browser-tasks/examples/auth-failure-handling.mjs +0 -81
- package/shared-prompts/skills/automated-browser-tasks/about-automated-browser-tasks/examples/chained-workflow.mjs +0 -126
- package/shared-prompts/skills/automated-browser-tasks/about-automated-browser-tasks/examples/simple-fetch.mjs +0 -92
- package/shared-prompts/skills/automated-browser-tasks/about-automated-browser-tasks/references/architecture.md +0 -61
- package/shared-prompts/skills/automated-browser-tasks/about-automated-browser-tasks/references/auth-and-sessions.md +0 -65
- package/shared-prompts/skills/automated-browser-tasks/about-automated-browser-tasks/references/ctx-api.md +0 -96
- package/shared-prompts/skills/automated-browser-tasks/about-automated-browser-tasks/references/installation.md +0 -104
- package/shared-prompts/skills/automated-browser-tasks/about-automated-browser-tasks/references/script-conventions.md +0 -243
- package/shared-prompts/skills/automated-browser-tasks/about-automated-browser-tasks/scripts/chrome-state.mjs +0 -148
- package/shared-prompts/skills/automated-browser-tasks/about-automated-browser-tasks/scripts/debug.mjs +0 -383
- package/shared-prompts/skills/automated-browser-tasks/about-automated-browser-tasks/scripts/debug.spec.mjs +0 -267
- package/shared-prompts/skills/automated-browser-tasks/about-automated-browser-tasks/scripts/eslint.config.mjs +0 -56
- package/shared-prompts/skills/automated-browser-tasks/about-automated-browser-tasks/scripts/harness.mjs +0 -169
- package/shared-prompts/skills/automated-browser-tasks/about-automated-browser-tasks/scripts/keyring.mjs +0 -59
- package/shared-prompts/skills/automated-browser-tasks/about-automated-browser-tasks/scripts/logger.mjs +0 -25
- package/shared-prompts/skills/automated-browser-tasks/about-automated-browser-tasks/scripts/params.mjs +0 -140
- package/shared-prompts/skills/automated-browser-tasks/about-automated-browser-tasks/scripts/run.mjs +0 -140
- package/shared-prompts/skills/automated-browser-tasks/about-automated-browser-tasks/scripts/settings.tpl.mjs +0 -1
- package/shared-prompts/skills/automated-browser-tasks/about-automated-browser-tasks/scripts/utils.mjs +0 -185
- package/shared-prompts/skills/automated-browser-tasks/create-automated-browser-task/SKILL.tpl.md +0 -52
- package/shared-prompts/skills/automated-browser-tasks/running-automated-browser-tasks/SKILL.tpl.md +0 -59
- package/shared-prompts/skills/automated-browser-tasks/update-automated-browser-task/SKILL.tpl.md +0 -47
- package/shared-prompts/skills/control-flow/approve/SKILL.tpl.md +0 -26
- package/shared-prompts/skills/control-flow/opine/SKILL.tpl.md +0 -58
- package/shared-prompts/skills/control-flow/repeat/SKILL.tpl.md +0 -27
- package/shared-prompts/skills/control-flow/research/SKILL.tpl.md +0 -34
- package/shared-prompts/skills/sous-skills/about-sous/SKILL.tpl.md +0 -51
- package/shared-prompts/skills/task-files/about-task-files/SKILL.tpl.md +0 -122
- package/shared-prompts/skills/task-files/continue-task-in-new-branch/SKILL.tpl.md +0 -80
- package/shared-prompts/skills/task-files/go/SKILL.tpl.md +0 -14
- package/shared-prompts/skills/task-files/resume-task/SKILL.tpl.md +0 -13
- package/shared-prompts/skills/task-files/start-task/SKILL.tpl.md +0 -93
- package/shared-prompts/skills/task-files/update/SKILL.tpl.md +0 -14
- package/shared-prompts/skills/task-files/update-task-file/SKILL.tpl.md +0 -13
- /package/{shared-prompts/skills/sous-skills → recipes/core/sous-skills/skills}/about-agent-skills/references/substitutions.md +0 -0
- /package/{shared-prompts/skills/sous-skills → recipes/core/sous-skills/skills}/about-liquid-templates/references/liquid-filters.md +0 -0
|
@@ -0,0 +1,241 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `sous lock rebuild`.
|
|
3
|
+
*
|
|
4
|
+
* Recomputes the lockfile from scratch: every subscription this project
|
|
5
|
+
* declares is resolved against the cached repository indexes, the whole
|
|
6
|
+
* dependency closure beneath them is walked, and the result replaces the
|
|
7
|
+
* lockfile outright. An entry nothing holds any more is dropped, which is what
|
|
8
|
+
* makes this the repair for a lockfile that has drifted from the config: a
|
|
9
|
+
* hand-edited file, a bad merge, or a subscription removed from the config by
|
|
10
|
+
* hand rather than with `sous subscription remove`.
|
|
11
|
+
*
|
|
12
|
+
* It is an edge-case utility, so it is deliberately blunt. It never asks a
|
|
13
|
+
* question and never grants trust: a subscription whose repository this project
|
|
14
|
+
* has not added is an error naming the repository. It downloads nothing; a
|
|
15
|
+
* repository whose index has not been fetched is named, and nothing in it can
|
|
16
|
+
* be resolved.
|
|
17
|
+
*/
|
|
18
|
+
|
|
19
|
+
import { Flags } from "@oclif/core";
|
|
20
|
+
import { BaseCommand } from "../../base-command.js";
|
|
21
|
+
import { ConfigError } from "../../lib/errors.js";
|
|
22
|
+
import { subscriptionServiceFor } from "../../lib/repos/subscription-service.js";
|
|
23
|
+
import { recipeFilesDirectory } from "../../lib/repos/catalog-inputs.js";
|
|
24
|
+
import { readRecipeManifestIn } from "../../lib/repos/locked-recipes.js";
|
|
25
|
+
import { createEmptyLockfile } from "../../lib/repos/formats/lockfile.js";
|
|
26
|
+
import type { LockRepoInput } from "../../lib/repos/lock-service.js";
|
|
27
|
+
import { parseRef } from "../../lib/repos/ref.js";
|
|
28
|
+
import {
|
|
29
|
+
PROJECT_REQUESTER,
|
|
30
|
+
resolveRefs,
|
|
31
|
+
type RefRequest,
|
|
32
|
+
type ResolverRepo,
|
|
33
|
+
} from "../../lib/repos/resolver.js";
|
|
34
|
+
import type { IndexFile } from "../../lib/repos/formats/index-file.js";
|
|
35
|
+
import {
|
|
36
|
+
blankLine,
|
|
37
|
+
dryRunNotice,
|
|
38
|
+
footer,
|
|
39
|
+
heading,
|
|
40
|
+
indent,
|
|
41
|
+
log,
|
|
42
|
+
paragraph,
|
|
43
|
+
showCommandVars,
|
|
44
|
+
} from "../../utils/formatting.js";
|
|
45
|
+
|
|
46
|
+
/** How far every line of this command's output is indented. */
|
|
47
|
+
const INDENT = 2;
|
|
48
|
+
|
|
49
|
+
export default class LockRebuild extends BaseCommand {
|
|
50
|
+
static description =
|
|
51
|
+
"Rebuild this project's lockfile from its subscriptions and the cached repository indexes";
|
|
52
|
+
|
|
53
|
+
/**
|
|
54
|
+
* The other spelling of the topic. It lives under a hidden topic, so it is
|
|
55
|
+
* typable everywhere without ever reaching the top-level listing.
|
|
56
|
+
*/
|
|
57
|
+
static aliases = ["locks:rebuild"];
|
|
58
|
+
|
|
59
|
+
static examples = [
|
|
60
|
+
"<%= config.bin %> lock rebuild",
|
|
61
|
+
"<%= config.bin %> lock rebuild --dry-run",
|
|
62
|
+
];
|
|
63
|
+
|
|
64
|
+
static flags = {
|
|
65
|
+
...BaseCommand.baseFlags,
|
|
66
|
+
"dry-run": Flags.boolean({
|
|
67
|
+
description: "Print what would change without writing the lockfile",
|
|
68
|
+
default: false,
|
|
69
|
+
}),
|
|
70
|
+
};
|
|
71
|
+
|
|
72
|
+
async run(): Promise<void> {
|
|
73
|
+
const { flags } = await this.parse(LockRebuild);
|
|
74
|
+
const dryRun = flags["dry-run"];
|
|
75
|
+
|
|
76
|
+
showCommandVars({
|
|
77
|
+
Project: this.projectLabel,
|
|
78
|
+
Config: this.configContext.configPath,
|
|
79
|
+
"Dry Run": dryRun,
|
|
80
|
+
});
|
|
81
|
+
|
|
82
|
+
heading("Rebuilding the lockfile");
|
|
83
|
+
|
|
84
|
+
const service = subscriptionServiceFor({
|
|
85
|
+
configContext: this.configContext,
|
|
86
|
+
settings: this.settings,
|
|
87
|
+
shellEnv: this.shellEnv,
|
|
88
|
+
});
|
|
89
|
+
|
|
90
|
+
const sousDir = this.configContext.sousDir;
|
|
91
|
+
const trusted = service.currentRepos();
|
|
92
|
+
|
|
93
|
+
const repos: Record<string, ResolverRepo> = {};
|
|
94
|
+
const lockRepos: Record<string, LockRepoInput> = {};
|
|
95
|
+
const indexes = new Map<string, IndexFile>();
|
|
96
|
+
const notFetched: string[] = [];
|
|
97
|
+
|
|
98
|
+
const before = service.lockService.read();
|
|
99
|
+
|
|
100
|
+
for (const name of Object.keys(trusted).sort()) {
|
|
101
|
+
const entry = trusted[name]!;
|
|
102
|
+
const identity = service.identityForRepo(name);
|
|
103
|
+
if (identity === undefined) continue;
|
|
104
|
+
|
|
105
|
+
repos[name] = {
|
|
106
|
+
url: entry.url,
|
|
107
|
+
identity,
|
|
108
|
+
...(entry.provider === undefined ? {} : { provider: entry.provider }),
|
|
109
|
+
...(entry.alwaysPull === undefined ? {} : { alwaysPull: entry.alwaysPull }),
|
|
110
|
+
};
|
|
111
|
+
lockRepos[name] = {
|
|
112
|
+
url: entry.url,
|
|
113
|
+
identity,
|
|
114
|
+
...(entry.provider === undefined ? {} : { provider: entry.provider }),
|
|
115
|
+
// The index a resolution was made against is recorded per repository.
|
|
116
|
+
// A rebuild resolves against the same cached index the last one did, so
|
|
117
|
+
// whatever the lockfile already recorded still holds.
|
|
118
|
+
...(before.repos[name]?.indexHash === undefined
|
|
119
|
+
? {}
|
|
120
|
+
: { indexHash: before.repos[name]!.indexHash! }),
|
|
121
|
+
};
|
|
122
|
+
|
|
123
|
+
const index = service.cachedIndex(name);
|
|
124
|
+
if (index === undefined) {
|
|
125
|
+
notFetched.push(name);
|
|
126
|
+
continue;
|
|
127
|
+
}
|
|
128
|
+
indexes.set(name, index);
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
const subscriptions = service.allSubscriptions();
|
|
132
|
+
const requests: RefRequest[] = Object.keys(subscriptions)
|
|
133
|
+
.sort()
|
|
134
|
+
.map((key) => {
|
|
135
|
+
const entry = subscriptions[key]!;
|
|
136
|
+
const parsed = parseRef(key);
|
|
137
|
+
return {
|
|
138
|
+
ref: {
|
|
139
|
+
...parsed,
|
|
140
|
+
...(entry.range === undefined ? {} : { range: entry.range }),
|
|
141
|
+
},
|
|
142
|
+
requestedBy: PROJECT_REQUESTER,
|
|
143
|
+
kind: "subscribes" as const,
|
|
144
|
+
...(entry.prerelease === true ? { prerelease: true } : {}),
|
|
145
|
+
};
|
|
146
|
+
});
|
|
147
|
+
|
|
148
|
+
const result = await resolveRefs(requests, {
|
|
149
|
+
indexes,
|
|
150
|
+
repos,
|
|
151
|
+
// A recipe's own manifest is what names its dependencies, and it is read
|
|
152
|
+
// from the files this machine already holds. Nothing is fetched here, so a
|
|
153
|
+
// recipe that is not in the store yet comes back under `missingManifests`.
|
|
154
|
+
loadManifest: (recipe) => {
|
|
155
|
+
const directory = recipeFilesDirectory({
|
|
156
|
+
service,
|
|
157
|
+
sousDir,
|
|
158
|
+
repo: recipe.repo,
|
|
159
|
+
key: recipe.key,
|
|
160
|
+
namespace: recipe.namespace,
|
|
161
|
+
name: recipe.name,
|
|
162
|
+
version: recipe.version,
|
|
163
|
+
});
|
|
164
|
+
return directory === undefined ? undefined : readRecipeManifestIn(directory);
|
|
165
|
+
},
|
|
166
|
+
});
|
|
167
|
+
|
|
168
|
+
if (result.missingRepos.length > 0) {
|
|
169
|
+
throw new ConfigError(
|
|
170
|
+
`This project's subscriptions need ${
|
|
171
|
+
result.missingRepos.length === 1 ? "a repository" : "repositories"
|
|
172
|
+
} it does not trust, so the lockfile cannot be rebuilt:\n` +
|
|
173
|
+
result.missingRepos
|
|
174
|
+
.map(
|
|
175
|
+
(missing) =>
|
|
176
|
+
` ${missing.name}${missing.url === undefined ? "" : ` (${missing.url})`}, ` +
|
|
177
|
+
`needed by ${missing.requiredBy.map((entry) => entry.ref).join(", ")}`
|
|
178
|
+
)
|
|
179
|
+
.join("\n") +
|
|
180
|
+
`\n Add ${
|
|
181
|
+
result.missingRepos.length === 1 ? "it" : "them"
|
|
182
|
+
} with 'sous repo add <url>', which is how a repository is trusted, then run ` +
|
|
183
|
+
`this command again.`
|
|
184
|
+
);
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
// The rebuild starts from an empty lockfile rather than the one on disk, so
|
|
188
|
+
// an entry nothing in the resolution holds any more is gone rather than
|
|
189
|
+
// carried through.
|
|
190
|
+
const after = service.lockService.applyResolution(
|
|
191
|
+
createEmptyLockfile(),
|
|
192
|
+
result.resolved,
|
|
193
|
+
lockRepos
|
|
194
|
+
);
|
|
195
|
+
const diff = service.lockService.diff(before, after);
|
|
196
|
+
|
|
197
|
+
blankLine();
|
|
198
|
+
for (const line of diff.lines) log(indent(line, INDENT));
|
|
199
|
+
|
|
200
|
+
if (notFetched.length > 0) {
|
|
201
|
+
blankLine();
|
|
202
|
+
paragraph(
|
|
203
|
+
`These repositories are trusted and their index has not been fetched yet, so ` +
|
|
204
|
+
`nothing in them could be resolved: ${notFetched.join(", ")}.`
|
|
205
|
+
);
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
if (result.missingManifests.length > 0) {
|
|
209
|
+
blankLine();
|
|
210
|
+
paragraph(
|
|
211
|
+
`The files of these recipes are not on this machine, so their own ` +
|
|
212
|
+
`dependencies could not be read and are not in the rebuilt lockfile: ` +
|
|
213
|
+
`${result.missingManifests.join(", ")}.`
|
|
214
|
+
);
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
for (const cycle of result.cycles) {
|
|
218
|
+
blankLine();
|
|
219
|
+
paragraph(`These recipes depend on each other in a circle: ${cycle.join(" -> ")}.`);
|
|
220
|
+
}
|
|
221
|
+
|
|
222
|
+
blankLine();
|
|
223
|
+
|
|
224
|
+
if (dryRun) {
|
|
225
|
+
dryRunNotice(`Nothing was written. The lockfile is ${service.lockService.filePath}.`);
|
|
226
|
+
footer();
|
|
227
|
+
return;
|
|
228
|
+
}
|
|
229
|
+
|
|
230
|
+
const written = service.lockService.write(after);
|
|
231
|
+
paragraph(
|
|
232
|
+
`The lockfile was rebuilt from ${requests.length} ${
|
|
233
|
+
requests.length === 1 ? "subscription" : "subscriptions"
|
|
234
|
+
} and now pins ${Object.keys(after.recipes).length} ${
|
|
235
|
+
Object.keys(after.recipes).length === 1 ? "recipe" : "recipes"
|
|
236
|
+
}: ${written}.`
|
|
237
|
+
);
|
|
238
|
+
|
|
239
|
+
footer();
|
|
240
|
+
}
|
|
241
|
+
}
|
|
@@ -0,0 +1,115 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `sous lock show`.
|
|
3
|
+
*
|
|
4
|
+
* Prints the lockfile: every recipe this project pins, the version and the
|
|
5
|
+
* repository it is pinned to, and who holds it. It reads the file and nothing
|
|
6
|
+
* else, so it works offline and never writes.
|
|
7
|
+
*/
|
|
8
|
+
|
|
9
|
+
import { BaseCommand } from "../../base-command.js";
|
|
10
|
+
import { subscriptionServiceFor } from "../../lib/repos/subscription-service.js";
|
|
11
|
+
import { PROJECT_HOLDER } from "../../lib/repos/formats/lockfile.js";
|
|
12
|
+
import { renderTable, type TableColumn } from "../../utils/table.js";
|
|
13
|
+
import {
|
|
14
|
+
blankLine,
|
|
15
|
+
footer,
|
|
16
|
+
heading,
|
|
17
|
+
indent,
|
|
18
|
+
log,
|
|
19
|
+
paragraph,
|
|
20
|
+
showCommandVars,
|
|
21
|
+
} from "../../utils/formatting.js";
|
|
22
|
+
|
|
23
|
+
/** How far every line of this command's output is indented. */
|
|
24
|
+
const INDENT = 2;
|
|
25
|
+
|
|
26
|
+
/**
|
|
27
|
+
* The columns the listing shows. The recipe and the version it is pinned at are
|
|
28
|
+
* what a lockfile is for, so they stay whatever the terminal's width; the list
|
|
29
|
+
* of holders takes the room that is left and wraps rather than being cut.
|
|
30
|
+
*/
|
|
31
|
+
const COLUMNS: TableColumn[] = [
|
|
32
|
+
{ key: "key", header: "Recipe", kind: "path", overflow: "truncate", minWidth: 12 },
|
|
33
|
+
{ key: "version", header: "Version", overflow: "truncate", minWidth: 7 },
|
|
34
|
+
{ key: "repo", header: "Repository", overflow: "truncate", priority: "medium" },
|
|
35
|
+
{
|
|
36
|
+
key: "heldBy",
|
|
37
|
+
header: "Held by",
|
|
38
|
+
overflow: "wrap",
|
|
39
|
+
flex: 1,
|
|
40
|
+
priority: "low",
|
|
41
|
+
minWidth: 16,
|
|
42
|
+
},
|
|
43
|
+
];
|
|
44
|
+
|
|
45
|
+
export default class LockShow extends BaseCommand {
|
|
46
|
+
static description = "Show every recipe version this project's lockfile pins";
|
|
47
|
+
|
|
48
|
+
/**
|
|
49
|
+
* The other spelling of the topic. It lives under a hidden topic, so it is
|
|
50
|
+
* typable everywhere without ever reaching the top-level listing.
|
|
51
|
+
*/
|
|
52
|
+
static aliases = ["locks:show"];
|
|
53
|
+
|
|
54
|
+
static examples = ["<%= config.bin %> lock show"];
|
|
55
|
+
|
|
56
|
+
static flags = { ...BaseCommand.baseFlags };
|
|
57
|
+
|
|
58
|
+
async run(): Promise<void> {
|
|
59
|
+
await this.parse(LockShow);
|
|
60
|
+
|
|
61
|
+
showCommandVars({
|
|
62
|
+
Project: this.projectLabel,
|
|
63
|
+
Config: this.configContext.configPath,
|
|
64
|
+
});
|
|
65
|
+
|
|
66
|
+
const service = subscriptionServiceFor({
|
|
67
|
+
configContext: this.configContext,
|
|
68
|
+
settings: this.settings,
|
|
69
|
+
shellEnv: this.shellEnv,
|
|
70
|
+
});
|
|
71
|
+
|
|
72
|
+
const lock = service.lockService.read();
|
|
73
|
+
const keys = Object.keys(lock.recipes).sort();
|
|
74
|
+
|
|
75
|
+
heading("Recipe versions this project pins");
|
|
76
|
+
blankLine();
|
|
77
|
+
|
|
78
|
+
if (keys.length === 0) {
|
|
79
|
+
paragraph(
|
|
80
|
+
`This project pins nothing yet. Its lockfile is ${service.lockService.filePath}.`
|
|
81
|
+
);
|
|
82
|
+
footer();
|
|
83
|
+
return;
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
const rows = keys.map((key) => {
|
|
87
|
+
const entry = lock.recipes[key]!;
|
|
88
|
+
return {
|
|
89
|
+
key,
|
|
90
|
+
version: entry.version,
|
|
91
|
+
repo: entry.repo,
|
|
92
|
+
heldBy: entry.requestedBy.map(describeHolder).join(", "),
|
|
93
|
+
};
|
|
94
|
+
});
|
|
95
|
+
|
|
96
|
+
for (const line of renderTable(COLUMNS, rows, { indent: INDENT })) {
|
|
97
|
+
log(indent(line, INDENT));
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
blankLine();
|
|
101
|
+
paragraph(`This project's lockfile is ${service.lockService.filePath}.`);
|
|
102
|
+
|
|
103
|
+
footer();
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
/**
|
|
108
|
+
* Plain-language wording for one holder of a locked recipe: the project itself,
|
|
109
|
+
* or the recipe that requires it.
|
|
110
|
+
*
|
|
111
|
+
* @param holder - What the lockfile recorded.
|
|
112
|
+
*/
|
|
113
|
+
function describeHolder(holder: string): string {
|
|
114
|
+
return holder === PROJECT_HOLDER ? "this project" : holder;
|
|
115
|
+
}
|
|
@@ -0,0 +1,117 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `sous namespace list`.
|
|
3
|
+
*
|
|
4
|
+
* Shows every namespace published by the repositories this project trusts, how
|
|
5
|
+
* many recipes each one holds, and how much of it the project subscribes to. It
|
|
6
|
+
* reads only the indexes sous already has on disk, so it works offline; a
|
|
7
|
+
* repository whose index has never been fetched is named at the end rather than
|
|
8
|
+
* being silently left out.
|
|
9
|
+
*/
|
|
10
|
+
|
|
11
|
+
import { BaseCommand } from "../../base-command.js";
|
|
12
|
+
import { subscriptionServiceFor } from "../../lib/repos/subscription-service.js";
|
|
13
|
+
import { catalogContextFor } from "../../lib/repos/catalog-inputs.js";
|
|
14
|
+
import { listNamespaces } from "../../lib/repos/catalog.js";
|
|
15
|
+
import { INDENT, describeCoverage } from "../../lib/repos/catalog-display.js";
|
|
16
|
+
import { renderTable, type TableColumn } from "../../utils/table.js";
|
|
17
|
+
import {
|
|
18
|
+
blankLine,
|
|
19
|
+
footer,
|
|
20
|
+
heading,
|
|
21
|
+
indent,
|
|
22
|
+
log,
|
|
23
|
+
paragraph,
|
|
24
|
+
showCommandVars,
|
|
25
|
+
} from "../../utils/formatting.js";
|
|
26
|
+
|
|
27
|
+
/**
|
|
28
|
+
* The columns the listing shows. The namespace and what a project has of it are
|
|
29
|
+
* the point of the command, so they stay whatever the terminal's width; the
|
|
30
|
+
* description takes the room that is left and wraps rather than being cut.
|
|
31
|
+
*/
|
|
32
|
+
const COLUMNS: TableColumn[] = [
|
|
33
|
+
{ key: "namespace", header: "Namespace", overflow: "truncate", minWidth: 10 },
|
|
34
|
+
{ key: "repo", header: "Repository", overflow: "truncate", priority: "medium" },
|
|
35
|
+
{ key: "recipes", header: "Recipes", kind: "number", priority: "medium" },
|
|
36
|
+
{ key: "subscribed", header: "Subscribed", priority: "medium" },
|
|
37
|
+
{
|
|
38
|
+
key: "description",
|
|
39
|
+
header: "What it is",
|
|
40
|
+
overflow: "wrap",
|
|
41
|
+
flex: 1,
|
|
42
|
+
priority: "low",
|
|
43
|
+
minWidth: 16,
|
|
44
|
+
},
|
|
45
|
+
];
|
|
46
|
+
|
|
47
|
+
export default class NamespaceList extends BaseCommand {
|
|
48
|
+
static description = "List the namespaces the repositories this project trusts publish";
|
|
49
|
+
|
|
50
|
+
/**
|
|
51
|
+
* The other spelling of the topic. It lives under a hidden topic, so it is
|
|
52
|
+
* typable everywhere without ever reaching the top-level listing.
|
|
53
|
+
*/
|
|
54
|
+
static aliases = ["namespaces:list"];
|
|
55
|
+
|
|
56
|
+
static examples = ["<%= config.bin %> namespace list"];
|
|
57
|
+
|
|
58
|
+
static flags = { ...BaseCommand.baseFlags };
|
|
59
|
+
|
|
60
|
+
async run(): Promise<void> {
|
|
61
|
+
await this.parse(NamespaceList);
|
|
62
|
+
|
|
63
|
+
showCommandVars({
|
|
64
|
+
Project: this.projectLabel,
|
|
65
|
+
Config: this.configContext.configPath,
|
|
66
|
+
});
|
|
67
|
+
|
|
68
|
+
heading("Namespaces in the repositories this project trusts");
|
|
69
|
+
|
|
70
|
+
const service = subscriptionServiceFor({
|
|
71
|
+
configContext: this.configContext,
|
|
72
|
+
settings: this.settings,
|
|
73
|
+
shellEnv: this.shellEnv,
|
|
74
|
+
});
|
|
75
|
+
|
|
76
|
+
const { inputs, notFetched } = catalogContextFor({
|
|
77
|
+
service,
|
|
78
|
+
sousDir: this.configContext.sousDir,
|
|
79
|
+
settings: this.settings,
|
|
80
|
+
});
|
|
81
|
+
|
|
82
|
+
const listings = listNamespaces(inputs);
|
|
83
|
+
|
|
84
|
+
blankLine();
|
|
85
|
+
|
|
86
|
+
if (listings.length === 0) {
|
|
87
|
+
paragraph(
|
|
88
|
+
inputs.repos.length === 0
|
|
89
|
+
? "Sous has read no repository index for this project, so there are no " +
|
|
90
|
+
"namespaces to show."
|
|
91
|
+
: "The repositories this project trusts publish no namespaces."
|
|
92
|
+
);
|
|
93
|
+
} else {
|
|
94
|
+
const rows = listings.map((entry) => ({
|
|
95
|
+
namespace: entry.namespace,
|
|
96
|
+
repo: entry.repo,
|
|
97
|
+
recipes: String(entry.recipeCount),
|
|
98
|
+
subscribed: describeCoverage(entry.subscribed),
|
|
99
|
+
description: entry.description ?? "no description published",
|
|
100
|
+
}));
|
|
101
|
+
|
|
102
|
+
for (const line of renderTable(COLUMNS, rows, { indent: INDENT })) {
|
|
103
|
+
log(indent(line, INDENT));
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
if (notFetched.length > 0) {
|
|
108
|
+
blankLine();
|
|
109
|
+
paragraph(
|
|
110
|
+
`These repositories are trusted and their index has not been fetched yet, so ` +
|
|
111
|
+
`nothing in them is listed: ${notFetched.join(", ")}.`
|
|
112
|
+
);
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
footer();
|
|
116
|
+
}
|
|
117
|
+
}
|
|
@@ -0,0 +1,110 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `sous namespace show <ref>`.
|
|
3
|
+
*
|
|
4
|
+
* Shows one namespace in full: the repository publishing it, what that
|
|
5
|
+
* repository says it is for, how much of it this project subscribes to, and
|
|
6
|
+
* every recipe in it with its latest version, its pinned version and its own
|
|
7
|
+
* subscription state. It reads only the indexes sous already has on disk.
|
|
8
|
+
*/
|
|
9
|
+
|
|
10
|
+
import { Args } from "@oclif/core";
|
|
11
|
+
import { BaseCommand } from "../../base-command.js";
|
|
12
|
+
import { subscriptionServiceFor } from "../../lib/repos/subscription-service.js";
|
|
13
|
+
import { catalogContextFor } from "../../lib/repos/catalog-inputs.js";
|
|
14
|
+
import { describeNamespace } from "../../lib/repos/catalog.js";
|
|
15
|
+
import {
|
|
16
|
+
INDENT,
|
|
17
|
+
RECIPE_COLUMNS,
|
|
18
|
+
describeCoverage,
|
|
19
|
+
factIf,
|
|
20
|
+
printFacts,
|
|
21
|
+
recipeRows,
|
|
22
|
+
} from "../../lib/repos/catalog-display.js";
|
|
23
|
+
import { renderTable } from "../../utils/table.js";
|
|
24
|
+
import {
|
|
25
|
+
blankLine,
|
|
26
|
+
footer,
|
|
27
|
+
heading,
|
|
28
|
+
indent,
|
|
29
|
+
log,
|
|
30
|
+
paragraph,
|
|
31
|
+
showCommandVars,
|
|
32
|
+
subheading,
|
|
33
|
+
} from "../../utils/formatting.js";
|
|
34
|
+
|
|
35
|
+
export default class NamespaceShow extends BaseCommand {
|
|
36
|
+
static description = "Show one namespace and every recipe the repository publishes in it";
|
|
37
|
+
|
|
38
|
+
/**
|
|
39
|
+
* The other spelling of the topic. It lives under a hidden topic, so it is
|
|
40
|
+
* typable everywhere without ever reaching the top-level listing.
|
|
41
|
+
*/
|
|
42
|
+
static aliases = ["namespaces:show"];
|
|
43
|
+
|
|
44
|
+
static examples = [
|
|
45
|
+
"<%= config.bin %> namespace show workflow",
|
|
46
|
+
"<%= config.bin %> namespace show sous-recipes:core",
|
|
47
|
+
];
|
|
48
|
+
|
|
49
|
+
static args = {
|
|
50
|
+
ref: Args.string({
|
|
51
|
+
description: "A namespace, optionally written as 'repository:namespace'",
|
|
52
|
+
required: true,
|
|
53
|
+
}),
|
|
54
|
+
};
|
|
55
|
+
|
|
56
|
+
static flags = { ...BaseCommand.baseFlags };
|
|
57
|
+
|
|
58
|
+
async run(): Promise<void> {
|
|
59
|
+
const { args } = await this.parse(NamespaceShow);
|
|
60
|
+
|
|
61
|
+
showCommandVars({
|
|
62
|
+
Project: this.projectLabel,
|
|
63
|
+
Config: this.configContext.configPath,
|
|
64
|
+
Namespace: args.ref,
|
|
65
|
+
});
|
|
66
|
+
|
|
67
|
+
const service = subscriptionServiceFor({
|
|
68
|
+
configContext: this.configContext,
|
|
69
|
+
settings: this.settings,
|
|
70
|
+
shellEnv: this.shellEnv,
|
|
71
|
+
});
|
|
72
|
+
|
|
73
|
+
const { inputs } = catalogContextFor({
|
|
74
|
+
service,
|
|
75
|
+
sousDir: this.configContext.sousDir,
|
|
76
|
+
settings: this.settings,
|
|
77
|
+
});
|
|
78
|
+
|
|
79
|
+
const detail = describeNamespace(inputs, args.ref);
|
|
80
|
+
|
|
81
|
+
heading(`The namespace ${detail.namespace}`);
|
|
82
|
+
blankLine();
|
|
83
|
+
|
|
84
|
+
printFacts([
|
|
85
|
+
{ label: "Repository", lines: [detail.repo] },
|
|
86
|
+
...factIf("Location", detail.repoUrl),
|
|
87
|
+
...factIf("About", detail.description),
|
|
88
|
+
{ label: "Recipes", lines: [String(detail.recipes.length)] },
|
|
89
|
+
{ label: "Subscribed", lines: [describeCoverage(detail.subscribed)] },
|
|
90
|
+
]);
|
|
91
|
+
|
|
92
|
+
blankLine();
|
|
93
|
+
subheading(`Recipes in ${detail.namespace}`);
|
|
94
|
+
blankLine();
|
|
95
|
+
|
|
96
|
+
if (detail.recipes.length === 0) {
|
|
97
|
+
paragraph(`The repository ${detail.repo} publishes no recipes in this namespace.`);
|
|
98
|
+
footer();
|
|
99
|
+
return;
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
for (const line of renderTable(RECIPE_COLUMNS, recipeRows(detail.recipes), {
|
|
103
|
+
indent: INDENT,
|
|
104
|
+
})) {
|
|
105
|
+
log(indent(line, INDENT));
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
footer();
|
|
109
|
+
}
|
|
110
|
+
}
|
package/src/commands/prune.ts
CHANGED
|
@@ -9,7 +9,6 @@ export default class Prune extends BaseCommand {
|
|
|
9
9
|
static examples = [
|
|
10
10
|
"<%= config.bin %> prune",
|
|
11
11
|
"<%= config.bin %> prune --dry-run",
|
|
12
|
-
"<%= config.bin %> prune --project myproject",
|
|
13
12
|
];
|
|
14
13
|
|
|
15
14
|
static flags = {
|
|
@@ -23,11 +22,10 @@ export default class Prune extends BaseCommand {
|
|
|
23
22
|
async run(): Promise<void> {
|
|
24
23
|
const { flags } = await this.parse(Prune);
|
|
25
24
|
|
|
26
|
-
const
|
|
27
|
-
const stateFilePath = resolveStateFilePath(project.key, this.settings, this.configContext);
|
|
25
|
+
const stateFilePath = resolveStateFilePath(this.settings, this.configContext);
|
|
28
26
|
|
|
29
27
|
showCommandVars({
|
|
30
|
-
Project:
|
|
28
|
+
Project: this.projectLabel,
|
|
31
29
|
Config: this.configContext.configPath,
|
|
32
30
|
"Dry Run": flags["dry-run"],
|
|
33
31
|
});
|
|
@@ -35,13 +33,7 @@ export default class Prune extends BaseCommand {
|
|
|
35
33
|
heading("Pruning");
|
|
36
34
|
|
|
37
35
|
const buildService = new BuildService();
|
|
38
|
-
await buildService.prune(
|
|
39
|
-
project.key,
|
|
40
|
-
this.settings,
|
|
41
|
-
stateFilePath,
|
|
42
|
-
flags["dry-run"],
|
|
43
|
-
this.configContext
|
|
44
|
-
);
|
|
36
|
+
await buildService.prune(this.settings, stateFilePath, flags["dry-run"], this.configContext);
|
|
45
37
|
|
|
46
38
|
footer();
|
|
47
39
|
}
|