@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,452 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* What `sous repo contribute` chains together, and how it decides whether a
|
|
3
|
-
* branch still has something to propose.
|
|
4
|
-
*
|
|
5
|
-
* Contributing to a recipe repository is a lifecycle: start (link the
|
|
6
|
-
* repository on a fresh branch of an up-to-date checkout), edit, propose,
|
|
7
|
-
* revise, and finish (propose whatever is left, then go back to the published
|
|
8
|
-
* versions). Each part already has a command of its own (`sous repo link`,
|
|
9
|
-
* `sous repo submit`, `sous repo unlink`), and the contribution command adds
|
|
10
|
-
* nothing those commands lack: this module only works out which of them to run,
|
|
11
|
-
* with which flags, in which order.
|
|
12
|
-
*
|
|
13
|
-
* A step is described as the command it runs and the arguments it passes, so
|
|
14
|
-
* the command can run it in-process, print it for a dry run, and name it in a
|
|
15
|
-
* failure, all from one description. Everything here that reads the checkout
|
|
16
|
-
* goes through the injectable git runner, so no test needs a network.
|
|
17
|
-
*/
|
|
18
|
-
|
|
19
|
-
import { ConfigError } from "../errors.js";
|
|
20
|
-
import {
|
|
21
|
-
currentBranch,
|
|
22
|
-
defaultBranch,
|
|
23
|
-
runGit,
|
|
24
|
-
UPSTREAM_REMOTE,
|
|
25
|
-
type GitOptions,
|
|
26
|
-
} from "./git-clone.js";
|
|
27
|
-
import type { ProposalSummary } from "./providers/provider.js";
|
|
28
|
-
import { FORK_REMOTE } from "./release/submit-service.js";
|
|
29
|
-
|
|
30
|
-
/** The commands a contribution is made of, by their oclif ids. */
|
|
31
|
-
export type ContributionCommand = "repo:link" | "repo:submit" | "repo:unlink";
|
|
32
|
-
|
|
33
|
-
/** One command a contribution runs, and how to talk about it. */
|
|
34
|
-
export type ContributionStep = {
|
|
35
|
-
/** The command's oclif id. */
|
|
36
|
-
command: ContributionCommand;
|
|
37
|
-
/** The arguments and flags passed to it, exactly as a command line would carry them. */
|
|
38
|
-
argv: string[];
|
|
39
|
-
/** What the step does, as a heading printed when it starts. */
|
|
40
|
-
running: string;
|
|
41
|
-
/** What the step did, as a line in the list of completed steps. */
|
|
42
|
-
done: string;
|
|
43
|
-
};
|
|
44
|
-
|
|
45
|
-
/**
|
|
46
|
-
* Config-locating flags the contribution command was given, which every step
|
|
47
|
-
* that discovers a project config has to be given as well, or it could find a
|
|
48
|
-
* different project from the one the contribution started in.
|
|
49
|
-
*/
|
|
50
|
-
export type LocatorFlags = {
|
|
51
|
-
config?: string;
|
|
52
|
-
"sous-dir"?: string;
|
|
53
|
-
"sous-confd"?: string;
|
|
54
|
-
};
|
|
55
|
-
|
|
56
|
-
/** The flags that shape the start of a contribution. */
|
|
57
|
-
export type StartFlags = {
|
|
58
|
-
/** `--branch`: an existing branch to work on. */
|
|
59
|
-
branch?: string;
|
|
60
|
-
/** `--create-branch`: a new branch to create. */
|
|
61
|
-
createBranch?: string;
|
|
62
|
-
/** `--generate-branch`: a new branch with a generated name. */
|
|
63
|
-
generateBranch?: boolean;
|
|
64
|
-
/** `--from`: the branch a new branch starts from. */
|
|
65
|
-
from?: string;
|
|
66
|
-
/** `--global`: the machine-wide link. */
|
|
67
|
-
global?: boolean;
|
|
68
|
-
/** The confirmation flag. */
|
|
69
|
-
yes?: boolean;
|
|
70
|
-
};
|
|
71
|
-
|
|
72
|
-
/** The flags that shape the end of a contribution. */
|
|
73
|
-
export type FinishFlags = {
|
|
74
|
-
/** `--title`: the proposal's title. */
|
|
75
|
-
title?: string;
|
|
76
|
-
/** `--body`: the proposal's description. */
|
|
77
|
-
body?: string;
|
|
78
|
-
/** `--draft`: open a new proposal as a draft. */
|
|
79
|
-
draft?: boolean;
|
|
80
|
-
/** `--commit`: commit uncommitted changes as part of the submission. */
|
|
81
|
-
commit?: boolean;
|
|
82
|
-
/** `--branch`: the branch to submit, instead of the one checked out. */
|
|
83
|
-
branch?: string;
|
|
84
|
-
/** `--remove`: delete the checkout when unlinking. */
|
|
85
|
-
remove?: boolean;
|
|
86
|
-
/** `--global`: the machine-wide link. */
|
|
87
|
-
global?: boolean;
|
|
88
|
-
/** The confirmation flag. */
|
|
89
|
-
yes?: boolean;
|
|
90
|
-
};
|
|
91
|
-
|
|
92
|
-
/**
|
|
93
|
-
* The config-locating flags, as arguments.
|
|
94
|
-
*
|
|
95
|
-
* @param locator - The flags the contribution command was given.
|
|
96
|
-
*/
|
|
97
|
-
export function locatorArgs(locator: LocatorFlags): string[] {
|
|
98
|
-
const args: string[] = [];
|
|
99
|
-
if (locator.config !== undefined) args.push("--config", locator.config);
|
|
100
|
-
if (locator["sous-dir"] !== undefined) args.push("--sous-dir", locator["sous-dir"]);
|
|
101
|
-
if (locator["sous-confd"] !== undefined) args.push("--sous-confd", locator["sous-confd"]);
|
|
102
|
-
return args;
|
|
103
|
-
}
|
|
104
|
-
|
|
105
|
-
/**
|
|
106
|
-
* The step that starts a contribution: `sous repo link <repo> --latest` on a
|
|
107
|
-
* new branch. The branch is generated unless one was named, either as a branch
|
|
108
|
-
* to create or as an existing branch to work on.
|
|
109
|
-
*
|
|
110
|
-
* startStep("sous-recipes", {}, {}).argv
|
|
111
|
-
* // -> ["sous-recipes", "--latest", "--generate-branch"]
|
|
112
|
-
*
|
|
113
|
-
* @param repo - The repository's short name.
|
|
114
|
-
* @param flags - The start flags, passed through.
|
|
115
|
-
* @param locator - The config-locating flags, passed through.
|
|
116
|
-
*/
|
|
117
|
-
export function startStep(
|
|
118
|
-
repo: string,
|
|
119
|
-
flags: StartFlags,
|
|
120
|
-
locator: LocatorFlags = {}
|
|
121
|
-
): ContributionStep {
|
|
122
|
-
const argv = [repo, "--latest"];
|
|
123
|
-
let running: string;
|
|
124
|
-
let done: string;
|
|
125
|
-
|
|
126
|
-
if (flags.branch !== undefined) {
|
|
127
|
-
argv.push("--branch", flags.branch);
|
|
128
|
-
running = `Linking '${repo}' on the branch '${flags.branch}', made to match upstream's`;
|
|
129
|
-
done = `Linked '${repo}' on the branch '${flags.branch}'`;
|
|
130
|
-
} else if (flags.createBranch !== undefined) {
|
|
131
|
-
argv.push("--create-branch", flags.createBranch);
|
|
132
|
-
running = `Linking '${repo}' on a new branch named '${flags.createBranch}'`;
|
|
133
|
-
done = `Linked '${repo}' on the new branch '${flags.createBranch}'`;
|
|
134
|
-
} else {
|
|
135
|
-
argv.push("--generate-branch");
|
|
136
|
-
running = `Linking '${repo}' on a new branch with a generated name`;
|
|
137
|
-
done = `Linked '${repo}' on a new branch`;
|
|
138
|
-
}
|
|
139
|
-
|
|
140
|
-
if (flags.from !== undefined) argv.push("--from", flags.from);
|
|
141
|
-
if (flags.global === true) argv.push("--global");
|
|
142
|
-
if (flags.yes === true) argv.push("--yes");
|
|
143
|
-
argv.push(...locatorArgs(locator));
|
|
144
|
-
|
|
145
|
-
return { command: "repo:link", argv, running, done };
|
|
146
|
-
}
|
|
147
|
-
|
|
148
|
-
/**
|
|
149
|
-
* The step that proposes what the branch holds: `sous repo submit <repo>`,
|
|
150
|
-
* with the proposal flags passed through. The confirmation flag is passed only
|
|
151
|
-
* when it was given, so a question the contributor has not answered ahead of
|
|
152
|
-
* time is still asked.
|
|
153
|
-
*
|
|
154
|
-
* submitStep("sous-recipes", { title: "Fix a typo" }).argv
|
|
155
|
-
* // -> ["sous-recipes", "--title", "Fix a typo"]
|
|
156
|
-
*
|
|
157
|
-
* @param repo - The repository's short name.
|
|
158
|
-
* @param flags - The finish flags, passed through.
|
|
159
|
-
*/
|
|
160
|
-
export function submitStep(repo: string, flags: FinishFlags): ContributionStep {
|
|
161
|
-
const argv = [repo];
|
|
162
|
-
if (flags.title !== undefined) argv.push("--title", flags.title);
|
|
163
|
-
if (flags.body !== undefined) argv.push("--body", flags.body);
|
|
164
|
-
if (flags.branch !== undefined) argv.push("--branch", flags.branch);
|
|
165
|
-
if (flags.draft === true) argv.push("--draft");
|
|
166
|
-
if (flags.commit === true) argv.push("--commit");
|
|
167
|
-
if (flags.yes === true) argv.push("--yes");
|
|
168
|
-
|
|
169
|
-
return {
|
|
170
|
-
command: "repo:submit",
|
|
171
|
-
argv,
|
|
172
|
-
running: "Proposing what the branch holds",
|
|
173
|
-
done: "Proposed what the branch holds",
|
|
174
|
-
};
|
|
175
|
-
}
|
|
176
|
-
|
|
177
|
-
/**
|
|
178
|
-
* The step that ends a contribution: `sous repo unlink <repo> --update`, which
|
|
179
|
-
* goes back to the published versions and moves the pins to the newest ones
|
|
180
|
-
* their ranges allow, so a release carrying the change is picked up.
|
|
181
|
-
*
|
|
182
|
-
* unlinkStep("sous-recipes", { remove: true }).argv
|
|
183
|
-
* // -> ["sous-recipes", "--update", "--remove"]
|
|
184
|
-
*
|
|
185
|
-
* @param repo - The repository's short name.
|
|
186
|
-
* @param flags - The finish flags, passed through.
|
|
187
|
-
* @param locator - The config-locating flags, passed through.
|
|
188
|
-
*/
|
|
189
|
-
export function unlinkStep(
|
|
190
|
-
repo: string,
|
|
191
|
-
flags: FinishFlags,
|
|
192
|
-
locator: LocatorFlags = {}
|
|
193
|
-
): ContributionStep {
|
|
194
|
-
const argv = [repo, "--update"];
|
|
195
|
-
if (flags.remove === true) argv.push("--remove");
|
|
196
|
-
if (flags.global === true) argv.push("--global");
|
|
197
|
-
if (flags.yes === true) argv.push("--yes");
|
|
198
|
-
argv.push(...locatorArgs(locator));
|
|
199
|
-
|
|
200
|
-
return {
|
|
201
|
-
command: "repo:unlink",
|
|
202
|
-
argv,
|
|
203
|
-
running:
|
|
204
|
-
flags.remove === true
|
|
205
|
-
? `Unlinking '${repo}', updating its pins and deleting the checkout`
|
|
206
|
-
: `Unlinking '${repo}' and updating its pins`,
|
|
207
|
-
done:
|
|
208
|
-
flags.remove === true
|
|
209
|
-
? `Unlinked '${repo}', updated its pins and deleted the checkout`
|
|
210
|
-
: `Unlinked '${repo}' and updated its pins`,
|
|
211
|
-
};
|
|
212
|
-
}
|
|
213
|
-
|
|
214
|
-
/**
|
|
215
|
-
* A step written out as the command line that would run it, for a dry run and
|
|
216
|
-
* for anyone who wants to run the parts by hand.
|
|
217
|
-
*
|
|
218
|
-
* commandLine(startStep("sous-recipes", {}))
|
|
219
|
-
* // -> "sous repo link sous-recipes --latest --generate-branch"
|
|
220
|
-
*
|
|
221
|
-
* @param step - The step.
|
|
222
|
-
*/
|
|
223
|
-
export function commandLine(step: ContributionStep): string {
|
|
224
|
-
const words = ["sous", ...step.command.split(":"), ...step.argv];
|
|
225
|
-
return words.map(quoteArgument).join(" ");
|
|
226
|
-
}
|
|
227
|
-
|
|
228
|
-
/** An argument as a shell would need it written: quoted when it holds anything unusual. */
|
|
229
|
-
function quoteArgument(value: string): string {
|
|
230
|
-
if (/^[A-Za-z0-9_@%+=:,./~-]+$/.test(value)) return value;
|
|
231
|
-
return `'${value.replace(/'/g, `'\\''`)}'`;
|
|
232
|
-
}
|
|
233
|
-
|
|
234
|
-
// --- What the branch still has to propose ----------------------------------------------------------
|
|
235
|
-
|
|
236
|
-
/** What a checkout's branch holds that a proposal may not carry yet. */
|
|
237
|
-
export type PendingWork = {
|
|
238
|
-
/** The branch that was examined, or undefined when HEAD is detached. */
|
|
239
|
-
branch?: string;
|
|
240
|
-
/** The upstream default branch, when it could be worked out. */
|
|
241
|
-
baseBranch?: string;
|
|
242
|
-
/** Every uncommitted change, as `git status --porcelain` prints it. */
|
|
243
|
-
uncommitted: string[];
|
|
244
|
-
/** Commits on the branch that the upstream default branch lacks, one line each. */
|
|
245
|
-
ahead: string[];
|
|
246
|
-
/** Of those, the commits no pushed copy of the branch holds, one line each. */
|
|
247
|
-
unpushed: string[];
|
|
248
|
-
/** The pushed copies of the branch this checkout knows about, such as `origin/my-change`. */
|
|
249
|
-
pushedCopies: string[];
|
|
250
|
-
};
|
|
251
|
-
|
|
252
|
-
/**
|
|
253
|
-
* Reads what a checkout's branch holds that has not been proposed: its
|
|
254
|
-
* uncommitted changes, its commits beyond the upstream default branch, and
|
|
255
|
-
* which of those no pushed copy of the branch holds. Nothing is fetched; the
|
|
256
|
-
* pushed copies are the remote-tracking branches a push leaves behind.
|
|
257
|
-
*
|
|
258
|
-
* pendingWork("/path/to/checkout")
|
|
259
|
-
* // -> { branch: "sous/edit-20260927-1200", baseBranch: "main", uncommitted: [],
|
|
260
|
-
* // ahead: ["1a2b3c4 Fix a typo"], unpushed: ["1a2b3c4 Fix a typo"], pushedCopies: [] }
|
|
261
|
-
*
|
|
262
|
-
* @param directory - The checkout.
|
|
263
|
-
* @param branch - The branch to examine; the checked-out one when omitted.
|
|
264
|
-
* @param options - The git runner to use.
|
|
265
|
-
*/
|
|
266
|
-
export function pendingWork(
|
|
267
|
-
directory: string,
|
|
268
|
-
branch?: string,
|
|
269
|
-
options: GitOptions = {}
|
|
270
|
-
): PendingWork {
|
|
271
|
-
const runner = options.runner ?? runGit;
|
|
272
|
-
const git = (args: string[]) => runner(args, { cwd: directory });
|
|
273
|
-
const lines = (text: string): string[] =>
|
|
274
|
-
text
|
|
275
|
-
.split("\n")
|
|
276
|
-
.map((line) => line.trim())
|
|
277
|
-
.filter((line) => line.length > 0);
|
|
278
|
-
const refExists = (ref: string) => git(["rev-parse", "--verify", "--quiet", ref]).status === 0;
|
|
279
|
-
|
|
280
|
-
const status = git(["status", "--porcelain"]);
|
|
281
|
-
if (status.status !== 0) {
|
|
282
|
-
throw new ConfigError(
|
|
283
|
-
`Sous could not read the checkout at ${directory}.\n` +
|
|
284
|
-
(status.stderr.length > 0 ? ` git said: ${status.stderr}\n` : "") +
|
|
285
|
-
` Check that it is still a git checkout, then run the command again.`
|
|
286
|
-
);
|
|
287
|
-
}
|
|
288
|
-
|
|
289
|
-
const examined = branch ?? currentBranch(directory, options);
|
|
290
|
-
const tip = examined === undefined ? "HEAD" : `refs/heads/${examined}`;
|
|
291
|
-
if (!refExists(tip)) {
|
|
292
|
-
throw new ConfigError(
|
|
293
|
-
`The checkout at ${directory} has no branch named '${examined}'.\n` +
|
|
294
|
-
` Name a branch it has with '--branch', or leave the flag off to use the one ` +
|
|
295
|
-
`that is checked out.`
|
|
296
|
-
);
|
|
297
|
-
}
|
|
298
|
-
|
|
299
|
-
const baseBranch = defaultBranch(directory, options);
|
|
300
|
-
const baseRef =
|
|
301
|
-
baseBranch !== undefined && refExists(`refs/remotes/${UPSTREAM_REMOTE}/${baseBranch}`)
|
|
302
|
-
? `refs/remotes/${UPSTREAM_REMOTE}/${baseBranch}`
|
|
303
|
-
: undefined;
|
|
304
|
-
|
|
305
|
-
const pushedCopies =
|
|
306
|
-
examined === undefined
|
|
307
|
-
? []
|
|
308
|
-
: [UPSTREAM_REMOTE, FORK_REMOTE]
|
|
309
|
-
.map((remote) => `${remote}/${examined}`)
|
|
310
|
-
.filter((copy) => refExists(`refs/remotes/${copy}`));
|
|
311
|
-
|
|
312
|
-
const commitsNotIn = (exclude: string[]): string[] => {
|
|
313
|
-
const args = ["log", "--oneline", "--no-decorate", tip];
|
|
314
|
-
// With no upstream default branch to measure against, a commit counts as
|
|
315
|
-
// the branch's own when no remote holds it.
|
|
316
|
-
if (exclude.length === 0) args.push("--not", "--remotes");
|
|
317
|
-
else args.push("--not", ...exclude);
|
|
318
|
-
const result = git(args);
|
|
319
|
-
return result.status === 0 ? lines(result.stdout) : [];
|
|
320
|
-
};
|
|
321
|
-
|
|
322
|
-
const base = baseRef === undefined ? [] : [baseRef];
|
|
323
|
-
const ahead = commitsNotIn(base);
|
|
324
|
-
const unpushed =
|
|
325
|
-
pushedCopies.length === 0
|
|
326
|
-
? ahead
|
|
327
|
-
: commitsNotIn([...base, ...pushedCopies.map((copy) => `refs/remotes/${copy}`)]);
|
|
328
|
-
|
|
329
|
-
return {
|
|
330
|
-
...(examined === undefined ? {} : { branch: examined }),
|
|
331
|
-
...(baseBranch === undefined ? {} : { baseBranch }),
|
|
332
|
-
uncommitted: lines(status.stdout),
|
|
333
|
-
ahead,
|
|
334
|
-
unpushed,
|
|
335
|
-
pushedCopies,
|
|
336
|
-
};
|
|
337
|
-
}
|
|
338
|
-
|
|
339
|
-
/**
|
|
340
|
-
* Whether a branch has something to propose.
|
|
341
|
-
*
|
|
342
|
-
* - `nothing`: it holds nothing a proposal lacks, so there is nothing to submit.
|
|
343
|
-
* - `pending`: it holds work no proposal carries yet.
|
|
344
|
-
* - `lookup`: everything on it was pushed, so only the proposal itself can say
|
|
345
|
-
* whether that work is proposed; the caller has to look it up.
|
|
346
|
-
*/
|
|
347
|
-
export type PendingVerdict =
|
|
348
|
-
| { kind: "nothing"; reason: string }
|
|
349
|
-
| { kind: "pending"; reason: string }
|
|
350
|
-
| { kind: "lookup"; reason: string };
|
|
351
|
-
|
|
352
|
-
/**
|
|
353
|
-
* Judges what `pendingWork` found, without asking the repository host anything.
|
|
354
|
-
*
|
|
355
|
-
* assessPendingWork({ branch: "fix", baseBranch: "main", uncommitted: [], ahead: [],
|
|
356
|
-
* unpushed: [], pushedCopies: [] })
|
|
357
|
-
* // -> { kind: "nothing", reason: "The branch 'fix' holds no commits that origin/main lacks, ..." }
|
|
358
|
-
*
|
|
359
|
-
* @param work - What the checkout's branch holds.
|
|
360
|
-
*/
|
|
361
|
-
export function assessPendingWork(work: PendingWork): PendingVerdict {
|
|
362
|
-
const branch = work.branch === undefined ? "the checked-out commit" : `the branch '${work.branch}'`;
|
|
363
|
-
const Branch = capitalize(branch);
|
|
364
|
-
const base =
|
|
365
|
-
work.baseBranch === undefined ? "any remote" : `${UPSTREAM_REMOTE}/${work.baseBranch}`;
|
|
366
|
-
|
|
367
|
-
if (work.uncommitted.length > 0) {
|
|
368
|
-
return {
|
|
369
|
-
kind: "pending",
|
|
370
|
-
reason: `The checkout holds ${count(work.uncommitted.length, "uncommitted change")}.`,
|
|
371
|
-
};
|
|
372
|
-
}
|
|
373
|
-
if (work.ahead.length === 0) {
|
|
374
|
-
return {
|
|
375
|
-
kind: "nothing",
|
|
376
|
-
reason: `${Branch} holds no commits that ${base} lacks, so there is nothing to submit.`,
|
|
377
|
-
};
|
|
378
|
-
}
|
|
379
|
-
if (work.unpushed.length > 0) {
|
|
380
|
-
return {
|
|
381
|
-
kind: "pending",
|
|
382
|
-
reason:
|
|
383
|
-
`${Branch} holds ${count(work.unpushed.length, "commit")} that ` +
|
|
384
|
-
(work.pushedCopies.length === 0
|
|
385
|
-
? "has never been pushed"
|
|
386
|
-
: `${work.pushedCopies.join(" and ")} lacks`) +
|
|
387
|
-
`, so no proposal carries ${work.unpushed.length === 1 ? "it" : "them"} yet.`,
|
|
388
|
-
};
|
|
389
|
-
}
|
|
390
|
-
return {
|
|
391
|
-
kind: "lookup",
|
|
392
|
-
reason: `Every commit on ${branch} was pushed to ${work.pushedCopies.join(" and ")}.`,
|
|
393
|
-
};
|
|
394
|
-
}
|
|
395
|
-
|
|
396
|
-
/**
|
|
397
|
-
* Judges a pushed branch by its proposal: an open proposal already carries
|
|
398
|
-
* everything, and so does a merged one; with no proposal, or one closed
|
|
399
|
-
* without being merged, the pushed commits are not proposed.
|
|
400
|
-
*
|
|
401
|
-
* assessProposal("fix", undefined, "pull request")
|
|
402
|
-
* // -> { kind: "pending", reason: "Every commit on the branch 'fix' was pushed, but no pull request is open for it." }
|
|
403
|
-
*
|
|
404
|
-
* @param branch - The branch.
|
|
405
|
-
* @param proposal - The branch's proposal, when it has one.
|
|
406
|
-
* @param noun - What the provider calls a proposal.
|
|
407
|
-
*/
|
|
408
|
-
export function assessProposal(
|
|
409
|
-
branch: string,
|
|
410
|
-
proposal: ProposalSummary | undefined,
|
|
411
|
-
noun: string
|
|
412
|
-
): PendingVerdict {
|
|
413
|
-
if (proposal === undefined) {
|
|
414
|
-
return {
|
|
415
|
-
kind: "pending",
|
|
416
|
-
reason: `Every commit on the branch '${branch}' was pushed, but no ${noun} is open for it.`,
|
|
417
|
-
};
|
|
418
|
-
}
|
|
419
|
-
switch (proposal.state) {
|
|
420
|
-
case "open":
|
|
421
|
-
return {
|
|
422
|
-
kind: "nothing",
|
|
423
|
-
reason:
|
|
424
|
-
`Every commit on the branch '${branch}' is already in its open ${noun}` +
|
|
425
|
-
`${proposal.url === undefined ? "" : `, ${proposal.url}`}.`,
|
|
426
|
-
};
|
|
427
|
-
case "merged":
|
|
428
|
-
return {
|
|
429
|
-
kind: "nothing",
|
|
430
|
-
reason:
|
|
431
|
-
`The ${noun} for the branch '${branch}' was merged` +
|
|
432
|
-
`${proposal.url === undefined ? "" : `: ${proposal.url}`}.`,
|
|
433
|
-
};
|
|
434
|
-
default:
|
|
435
|
-
return {
|
|
436
|
-
kind: "pending",
|
|
437
|
-
reason:
|
|
438
|
-
`The ${noun} for the branch '${branch}' was closed without being merged, so no ` +
|
|
439
|
-
`open ${noun} carries its commits.`,
|
|
440
|
-
};
|
|
441
|
-
}
|
|
442
|
-
}
|
|
443
|
-
|
|
444
|
-
/** "1 commit", "3 commits". */
|
|
445
|
-
function count(value: number, noun: string): string {
|
|
446
|
-
return `${value} ${noun}${value === 1 ? "" : "s"}`;
|
|
447
|
-
}
|
|
448
|
-
|
|
449
|
-
/** The text with its first letter in capitals. */
|
|
450
|
-
function capitalize(text: string): string {
|
|
451
|
-
return text.length === 0 ? text : `${text[0]!.toUpperCase()}${text.slice(1)}`;
|
|
452
|
-
}
|
|
@@ -1,105 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* The core recipe that ships inside the sous package.
|
|
3
|
-
*
|
|
4
|
-
* One namespace is special: `core`, the skills that teach an agent what sous is
|
|
5
|
-
* and how it works. Every project gets it, and a project that has never seen a
|
|
6
|
-
* network must still get it, so a copy of the recipe lives in the package at
|
|
7
|
-
* `recipes/core/sous-skills/` and seeds the machine-wide store on first run.
|
|
8
|
-
*
|
|
9
|
-
* Two rules hold this arrangement together:
|
|
10
|
-
*
|
|
11
|
-
* - VERSION PARITY. The packaged recipe's version is always exactly the
|
|
12
|
-
* version of the sous package shipping it, and the implicit `core`
|
|
13
|
-
* subscription's range is exactly the running sous version. Core therefore
|
|
14
|
-
* always matches the CLI, and upgrading sous upgrades core with it.
|
|
15
|
-
* `core-recipe.spec.ts` enforces the first half of that rule, and
|
|
16
|
-
* `scripts/sync-core-version.mts` is what satisfies it; the release
|
|
17
|
-
* pipeline (`.github/workflows/publish.yml`) enforces it again at publish
|
|
18
|
-
* time.
|
|
19
|
-
* - NO DEPENDENCIES. The packaged copy is the offline seed, so nothing in it
|
|
20
|
-
* may point at a recipe that has to be fetched.
|
|
21
|
-
*
|
|
22
|
-
* The same recipe is also published by the official repository, so an online
|
|
23
|
-
* project resolves it exactly like any other recipe; the package copy is a seed,
|
|
24
|
-
* not a private fork.
|
|
25
|
-
*/
|
|
26
|
-
|
|
27
|
-
import path from "node:path";
|
|
28
|
-
import { CLI_ROOT, SOUS_VERSION } from "../package-info.js";
|
|
29
|
-
import { ConfigError } from "../errors.js";
|
|
30
|
-
import { findRecipeManifest, loadManifestFile } from "./load-manifest.js";
|
|
31
|
-
import { parseRecipeManifest, type RecipeManifest } from "./formats/recipe-manifest.js";
|
|
32
|
-
|
|
33
|
-
/** The directory inside the package that holds every recipe sous ships. */
|
|
34
|
-
export const PACKAGED_RECIPES_DIRNAME = "recipes";
|
|
35
|
-
|
|
36
|
-
/** The short name the official repository is recorded under in every project. */
|
|
37
|
-
export const OFFICIAL_REPO_NAME = "sous-recipes";
|
|
38
|
-
|
|
39
|
-
/** Where the official repository lives. */
|
|
40
|
-
export const OFFICIAL_REPO_URL = "https://github.com/sous-io/sous-recipes";
|
|
41
|
-
|
|
42
|
-
/**
|
|
43
|
-
* The official repository's canonical identity: what the machine-wide store and
|
|
44
|
-
* the index cache file it under, whatever a project happens to call it.
|
|
45
|
-
*/
|
|
46
|
-
export const OFFICIAL_REPO_IDENTITY = "github.com/sous-io/sous-recipes";
|
|
47
|
-
|
|
48
|
-
/** The provider that handles the official repository. */
|
|
49
|
-
export const OFFICIAL_REPO_PROVIDER = "github";
|
|
50
|
-
|
|
51
|
-
/** The namespace every project is subscribed to unless it opts out. */
|
|
52
|
-
export const CORE_NAMESPACE = "core";
|
|
53
|
-
|
|
54
|
-
/** The name of the recipe published in that namespace. */
|
|
55
|
-
export const CORE_RECIPE_NAME = "sous-skills";
|
|
56
|
-
|
|
57
|
-
/** The core recipe's key, `core/sous-skills`. */
|
|
58
|
-
export const CORE_RECIPE_KEY = `${CORE_NAMESPACE}/${CORE_RECIPE_NAME}`;
|
|
59
|
-
|
|
60
|
-
/** The core recipe's folder, relative to the repository root, as the index records it. */
|
|
61
|
-
export const CORE_RECIPE_PATH = `${PACKAGED_RECIPES_DIRNAME}/${CORE_NAMESPACE}/${CORE_RECIPE_NAME}`;
|
|
62
|
-
|
|
63
|
-
/**
|
|
64
|
-
* The directory holding the packaged copy of the core recipe.
|
|
65
|
-
*
|
|
66
|
-
* @param packageRoot - The installed package's root directory. Defaults to the
|
|
67
|
-
* running CLI's own root, which is what every caller outside a test wants.
|
|
68
|
-
*/
|
|
69
|
-
export function packagedCoreRecipeDir(packageRoot: string = CLI_ROOT): string {
|
|
70
|
-
return path.join(packageRoot, PACKAGED_RECIPES_DIRNAME, CORE_NAMESPACE, CORE_RECIPE_NAME);
|
|
71
|
-
}
|
|
72
|
-
|
|
73
|
-
/**
|
|
74
|
-
* Reads and validates the packaged core recipe's manifest.
|
|
75
|
-
*
|
|
76
|
-
* A missing or unreadable manifest is a broken installation rather than a user
|
|
77
|
-
* mistake, so the error says so plainly instead of suggesting a fix the user
|
|
78
|
-
* cannot make.
|
|
79
|
-
*
|
|
80
|
-
* @param packageRoot - The installed package's root directory.
|
|
81
|
-
*/
|
|
82
|
-
export function readPackagedCoreManifest(packageRoot: string = CLI_ROOT): RecipeManifest {
|
|
83
|
-
const dir = packagedCoreRecipeDir(packageRoot);
|
|
84
|
-
const manifestPath = findRecipeManifest(dir);
|
|
85
|
-
|
|
86
|
-
if (manifestPath === undefined) {
|
|
87
|
-
throw new ConfigError(
|
|
88
|
-
`This installation of sous is missing the core recipe it ships with.\n` +
|
|
89
|
-
` Sous expected to find a recipe manifest in ${dir}.\n` +
|
|
90
|
-
` Reinstalling the sous package will restore it.`
|
|
91
|
-
);
|
|
92
|
-
}
|
|
93
|
-
|
|
94
|
-
return parseRecipeManifest(loadManifestFile(manifestPath), manifestPath);
|
|
95
|
-
}
|
|
96
|
-
|
|
97
|
-
/**
|
|
98
|
-
* The version of the core recipe this installation seeds, which is by rule the
|
|
99
|
-
* version of sous itself. Read from the constant rather than from the manifest
|
|
100
|
-
* so a damaged package cannot quietly seed a version that does not match the
|
|
101
|
-
* CLI; the parity test proves the two agree.
|
|
102
|
-
*/
|
|
103
|
-
export function coreRecipeVersion(): string {
|
|
104
|
-
return SOUS_VERSION;
|
|
105
|
-
}
|