@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,460 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* What git says about the working tree a release or a submission is standing in.
|
|
3
|
-
*
|
|
4
|
-
* Both `sous repo release` and `sous repo submit` need the same handful of
|
|
5
|
-
* facts before they are allowed to do anything: is anything uncommitted, which
|
|
6
|
-
* branch is checked out, which branch is the default one, and where does
|
|
7
|
-
* `origin` point. A release makes exactly one commit of its own (its version
|
|
8
|
-
* bumps and its index, through `commitPaths`) and refuses while anything else
|
|
9
|
-
* is uncommitted; a submission commits only when `--commit` asks it to
|
|
10
|
-
* (`commitEverything`). Everything else here is read to refuse politely rather
|
|
11
|
-
* than to fix anything.
|
|
12
|
-
*
|
|
13
|
-
* Every function takes the injectable command runner, so tests never spawn git
|
|
14
|
-
* unless they mean to.
|
|
15
|
-
*/
|
|
16
|
-
|
|
17
|
-
import { runGit, type RunOptions } from "../providers/git.js";
|
|
18
|
-
|
|
19
|
-
/** One path git reports as changed, with the two-letter status it reported. */
|
|
20
|
-
export type ChangedPath = {
|
|
21
|
-
/** The status code from `git status --porcelain`, such as `M `, `??` or `A `. */
|
|
22
|
-
status: string;
|
|
23
|
-
/** The path, relative to the repository root. */
|
|
24
|
-
path: string;
|
|
25
|
-
};
|
|
26
|
-
|
|
27
|
-
/**
|
|
28
|
-
* Everything `git status --porcelain` reports: staged changes, unstaged changes
|
|
29
|
-
* and untracked files alike. An empty list means the working tree is clean.
|
|
30
|
-
*
|
|
31
|
-
* @param rootDir - The repository's root directory.
|
|
32
|
-
* @param options - The command runner to use.
|
|
33
|
-
*/
|
|
34
|
-
export async function uncommittedChanges(
|
|
35
|
-
rootDir: string,
|
|
36
|
-
options: RunOptions = {}
|
|
37
|
-
): Promise<ChangedPath[]> {
|
|
38
|
-
const output = await runGit(["status", "--porcelain"], {
|
|
39
|
-
cwd: rootDir,
|
|
40
|
-
run: options.run,
|
|
41
|
-
});
|
|
42
|
-
if (output.length === 0) return [];
|
|
43
|
-
|
|
44
|
-
const changed: ChangedPath[] = [];
|
|
45
|
-
for (const line of output.split("\n")) {
|
|
46
|
-
// The status field is one or two characters, and the command runner trims
|
|
47
|
-
// its output, so a leading space (an unstaged edit) is already gone by the
|
|
48
|
-
// time the line arrives here. Split on the whitespace instead of counting
|
|
49
|
-
// columns. A rename is reported as 'old -> new'; the new name is the one
|
|
50
|
-
// that still exists, so that is the one reported.
|
|
51
|
-
const match = /^(\S{1,2})\s+(.*)$/.exec(line.trim());
|
|
52
|
-
if (match === null) continue;
|
|
53
|
-
const target = match[2]!;
|
|
54
|
-
const arrow = target.indexOf(" -> ");
|
|
55
|
-
changed.push({
|
|
56
|
-
status: match[1]!,
|
|
57
|
-
path: arrow === -1 ? target : target.slice(arrow + 4),
|
|
58
|
-
});
|
|
59
|
-
}
|
|
60
|
-
return changed;
|
|
61
|
-
}
|
|
62
|
-
|
|
63
|
-
/**
|
|
64
|
-
* The name of the checked-out branch, or undefined when HEAD is detached (which
|
|
65
|
-
* is what a CI checkout of a tag looks like).
|
|
66
|
-
*
|
|
67
|
-
* @param rootDir - The repository's root directory.
|
|
68
|
-
* @param options - The command runner to use.
|
|
69
|
-
*/
|
|
70
|
-
export async function currentBranch(
|
|
71
|
-
rootDir: string,
|
|
72
|
-
options: RunOptions = {}
|
|
73
|
-
): Promise<string | undefined> {
|
|
74
|
-
try {
|
|
75
|
-
const name = await runGit(["rev-parse", "--abbrev-ref", "HEAD"], {
|
|
76
|
-
cwd: rootDir,
|
|
77
|
-
run: options.run,
|
|
78
|
-
});
|
|
79
|
-
return name === "HEAD" ? undefined : name;
|
|
80
|
-
} catch {
|
|
81
|
-
return undefined;
|
|
82
|
-
}
|
|
83
|
-
}
|
|
84
|
-
|
|
85
|
-
/**
|
|
86
|
-
* The repository's default branch, taken from what `origin/HEAD` points at.
|
|
87
|
-
* Returns undefined when the remote never told this clone, which is normal for
|
|
88
|
-
* a repository cloned with `--depth 1` or created locally.
|
|
89
|
-
*
|
|
90
|
-
* @param rootDir - The repository's root directory.
|
|
91
|
-
* @param options - The command runner to use.
|
|
92
|
-
*/
|
|
93
|
-
export async function defaultBranch(
|
|
94
|
-
rootDir: string,
|
|
95
|
-
options: RunOptions = {}
|
|
96
|
-
): Promise<string | undefined> {
|
|
97
|
-
try {
|
|
98
|
-
const ref = await runGit(["symbolic-ref", "--short", "refs/remotes/origin/HEAD"], {
|
|
99
|
-
cwd: rootDir,
|
|
100
|
-
run: options.run,
|
|
101
|
-
});
|
|
102
|
-
const slash = ref.indexOf("/");
|
|
103
|
-
return slash === -1 ? ref : ref.slice(slash + 1);
|
|
104
|
-
} catch {
|
|
105
|
-
return undefined;
|
|
106
|
-
}
|
|
107
|
-
}
|
|
108
|
-
|
|
109
|
-
/**
|
|
110
|
-
* The commit where this checkout's own work starts: the point HEAD shares with
|
|
111
|
-
* the remote's copy of a branch. Returns undefined when this checkout holds no
|
|
112
|
-
* copy of that branch, or when git cannot find a commit the two share.
|
|
113
|
-
*
|
|
114
|
-
* A shallow clone answers this as long as HEAD was built on the branch it was
|
|
115
|
-
* cloned from, which is why a submission can rely on it where it cannot rely on
|
|
116
|
-
* tags.
|
|
117
|
-
*
|
|
118
|
-
* @param rootDir - The repository's root directory.
|
|
119
|
-
* @param remote - The remote whose branch is compared, normally `origin`.
|
|
120
|
-
* @param branch - The branch on that remote, normally the default one.
|
|
121
|
-
* @param options - The command runner to use.
|
|
122
|
-
*/
|
|
123
|
-
export async function forkPoint(
|
|
124
|
-
rootDir: string,
|
|
125
|
-
remote: string,
|
|
126
|
-
branch: string,
|
|
127
|
-
options: RunOptions = {}
|
|
128
|
-
): Promise<string | undefined> {
|
|
129
|
-
try {
|
|
130
|
-
const commit = await runGit(["merge-base", `refs/remotes/${remote}/${branch}`, "HEAD"], {
|
|
131
|
-
cwd: rootDir,
|
|
132
|
-
run: options.run,
|
|
133
|
-
});
|
|
134
|
-
return commit.length > 0 ? commit : undefined;
|
|
135
|
-
} catch {
|
|
136
|
-
return undefined;
|
|
137
|
-
}
|
|
138
|
-
}
|
|
139
|
-
|
|
140
|
-
/**
|
|
141
|
-
* True when the commits since `since` changed a path, comparing that commit
|
|
142
|
-
* with HEAD.
|
|
143
|
-
*
|
|
144
|
-
* @param rootDir - The repository's root directory.
|
|
145
|
-
* @param since - The commit to compare HEAD with.
|
|
146
|
-
* @param relativePath - The path to check, relative to the repository root.
|
|
147
|
-
* @param options - The command runner to use.
|
|
148
|
-
*/
|
|
149
|
-
export async function pathChangedSince(
|
|
150
|
-
rootDir: string,
|
|
151
|
-
since: string,
|
|
152
|
-
relativePath: string,
|
|
153
|
-
options: RunOptions = {}
|
|
154
|
-
): Promise<boolean> {
|
|
155
|
-
const changed = await runGit(["diff", "--name-only", since, "HEAD", "--", relativePath], {
|
|
156
|
-
cwd: rootDir,
|
|
157
|
-
run: options.run,
|
|
158
|
-
});
|
|
159
|
-
return changed.length > 0;
|
|
160
|
-
}
|
|
161
|
-
|
|
162
|
-
/**
|
|
163
|
-
* The URL of a remote, or undefined when the repository has no such remote.
|
|
164
|
-
*
|
|
165
|
-
* @param rootDir - The repository's root directory.
|
|
166
|
-
* @param remote - The remote's name, normally `origin`.
|
|
167
|
-
* @param options - The command runner to use.
|
|
168
|
-
*/
|
|
169
|
-
export async function remoteUrl(
|
|
170
|
-
rootDir: string,
|
|
171
|
-
remote: string,
|
|
172
|
-
options: RunOptions = {}
|
|
173
|
-
): Promise<string | undefined> {
|
|
174
|
-
try {
|
|
175
|
-
const url = await runGit(["remote", "get-url", remote], {
|
|
176
|
-
cwd: rootDir,
|
|
177
|
-
run: options.run,
|
|
178
|
-
});
|
|
179
|
-
return url.length > 0 ? url : undefined;
|
|
180
|
-
} catch {
|
|
181
|
-
return undefined;
|
|
182
|
-
}
|
|
183
|
-
}
|
|
184
|
-
|
|
185
|
-
/**
|
|
186
|
-
* True when a path is tracked by git and identical to what HEAD holds. It is how
|
|
187
|
-
* a caller confirms that a file it is about to act on is the one the repository
|
|
188
|
-
* actually committed.
|
|
189
|
-
*
|
|
190
|
-
* @param rootDir - The repository's root directory.
|
|
191
|
-
* @param relativePath - The path to check, relative to the repository root.
|
|
192
|
-
* @param options - The command runner to use.
|
|
193
|
-
*/
|
|
194
|
-
export async function isCommittedAndUnchanged(
|
|
195
|
-
rootDir: string,
|
|
196
|
-
relativePath: string,
|
|
197
|
-
options: RunOptions = {}
|
|
198
|
-
): Promise<boolean> {
|
|
199
|
-
const tracked = await runGit(["ls-files", "--", relativePath], {
|
|
200
|
-
cwd: rootDir,
|
|
201
|
-
run: options.run,
|
|
202
|
-
});
|
|
203
|
-
if (tracked.length === 0) return false;
|
|
204
|
-
|
|
205
|
-
const changed = await runGit(["status", "--porcelain", "--", relativePath], {
|
|
206
|
-
cwd: rootDir,
|
|
207
|
-
run: options.run,
|
|
208
|
-
});
|
|
209
|
-
return changed.length === 0;
|
|
210
|
-
}
|
|
211
|
-
|
|
212
|
-
/**
|
|
213
|
-
* True when git can work out who is committing, which is what it needs before
|
|
214
|
-
* it will make a commit or an annotated tag.
|
|
215
|
-
*
|
|
216
|
-
* `git var GIT_AUTHOR_IDENT` answers the exact question git asks itself: it
|
|
217
|
-
* honours the `user.name` and `user.email` settings, the `GIT_AUTHOR_*` and
|
|
218
|
-
* `GIT_COMMITTER_*` environment variables, and the strict rules that reject a
|
|
219
|
-
* guessed identity. It fails with the same "empty ident name" or "unable to
|
|
220
|
-
* auto-detect email address" that a commit would have failed with, which is
|
|
221
|
-
* why the check is made ahead of time rather than left to the commit.
|
|
222
|
-
*
|
|
223
|
-
* @param rootDir - The repository's root directory.
|
|
224
|
-
* @param options - The command runner to use.
|
|
225
|
-
*/
|
|
226
|
-
export async function hasCommitIdentity(
|
|
227
|
-
rootDir: string,
|
|
228
|
-
options: RunOptions = {}
|
|
229
|
-
): Promise<boolean> {
|
|
230
|
-
for (const name of ["GIT_AUTHOR_IDENT", "GIT_COMMITTER_IDENT"]) {
|
|
231
|
-
try {
|
|
232
|
-
const ident = await runGit(["var", name], { cwd: rootDir, run: options.run });
|
|
233
|
-
if (ident.length === 0) return false;
|
|
234
|
-
} catch {
|
|
235
|
-
return false;
|
|
236
|
-
}
|
|
237
|
-
}
|
|
238
|
-
return true;
|
|
239
|
-
}
|
|
240
|
-
|
|
241
|
-
/**
|
|
242
|
-
* Stages exactly the given paths and commits them.
|
|
243
|
-
*
|
|
244
|
-
* This is how `sous repo release` commits on an author's behalf, and it is
|
|
245
|
-
* deliberately narrow: a release writes version bumps and an index, and those
|
|
246
|
-
* are the only paths it stages. Anything else in the working tree is left
|
|
247
|
-
* exactly as it was. The only other commit sous makes is `commitEverything`,
|
|
248
|
-
* for `sous repo submit --commit`.
|
|
249
|
-
*
|
|
250
|
-
* @param rootDir - The repository's root directory.
|
|
251
|
-
* @param paths - The paths to stage, relative to the repository root.
|
|
252
|
-
* @param message - The commit message.
|
|
253
|
-
* @param options - The command runner to use.
|
|
254
|
-
*/
|
|
255
|
-
export async function commitPaths(
|
|
256
|
-
rootDir: string,
|
|
257
|
-
paths: ReadonlyArray<string>,
|
|
258
|
-
message: string,
|
|
259
|
-
options: RunOptions = {}
|
|
260
|
-
): Promise<void> {
|
|
261
|
-
if (paths.length === 0) return;
|
|
262
|
-
await runGit(["add", "--", ...paths], { cwd: rootDir, run: options.run });
|
|
263
|
-
await runGit(["commit", "--message", message, "--", ...paths], {
|
|
264
|
-
cwd: rootDir,
|
|
265
|
-
run: options.run,
|
|
266
|
-
});
|
|
267
|
-
}
|
|
268
|
-
|
|
269
|
-
/**
|
|
270
|
-
* True when any of the given paths differs from what HEAD holds, so a release
|
|
271
|
-
* knows whether it has anything to commit.
|
|
272
|
-
*
|
|
273
|
-
* @param rootDir - The repository's root directory.
|
|
274
|
-
* @param paths - The paths to check, relative to the repository root.
|
|
275
|
-
* @param options - The command runner to use.
|
|
276
|
-
*/
|
|
277
|
-
export async function anythingToCommit(
|
|
278
|
-
rootDir: string,
|
|
279
|
-
paths: ReadonlyArray<string>,
|
|
280
|
-
options: RunOptions = {}
|
|
281
|
-
): Promise<boolean> {
|
|
282
|
-
if (paths.length === 0) return false;
|
|
283
|
-
const changed = await runGit(["status", "--porcelain", "--", ...paths], {
|
|
284
|
-
cwd: rootDir,
|
|
285
|
-
run: options.run,
|
|
286
|
-
});
|
|
287
|
-
return changed.length > 0;
|
|
288
|
-
}
|
|
289
|
-
|
|
290
|
-
/**
|
|
291
|
-
* Creates a branch at HEAD and checks it out.
|
|
292
|
-
*
|
|
293
|
-
* @param rootDir - The repository's root directory.
|
|
294
|
-
* @param branch - The branch to create.
|
|
295
|
-
* @param options - The command runner to use.
|
|
296
|
-
*/
|
|
297
|
-
export async function createBranch(
|
|
298
|
-
rootDir: string,
|
|
299
|
-
branch: string,
|
|
300
|
-
options: RunOptions = {}
|
|
301
|
-
): Promise<void> {
|
|
302
|
-
await runGit(["checkout", "-b", branch], { cwd: rootDir, run: options.run });
|
|
303
|
-
}
|
|
304
|
-
|
|
305
|
-
/** What a push did: sent new commits, or found the remote already had them. */
|
|
306
|
-
export type PushOutcome = "updated" | "up-to-date";
|
|
307
|
-
|
|
308
|
-
/**
|
|
309
|
-
* Pushes one branch to a remote, setting it as the branch's upstream, and says
|
|
310
|
-
* whether anything was sent.
|
|
311
|
-
*
|
|
312
|
-
* The push is never forced. When the remote branch holds commits the local one
|
|
313
|
-
* lacks, git refuses, and its own explanation is what the caller receives: git
|
|
314
|
-
* is the authority on whether a push is safe, so nothing here second-guesses it.
|
|
315
|
-
* Whether anything was sent is read from git's machine-readable report, where a
|
|
316
|
-
* ref that was already current is flagged with `=`.
|
|
317
|
-
*
|
|
318
|
-
* @param rootDir - The repository's root directory.
|
|
319
|
-
* @param remote - The remote to push to.
|
|
320
|
-
* @param branch - The branch to push.
|
|
321
|
-
* @param options - The command runner to use.
|
|
322
|
-
*/
|
|
323
|
-
export async function pushBranch(
|
|
324
|
-
rootDir: string,
|
|
325
|
-
remote: string,
|
|
326
|
-
branch: string,
|
|
327
|
-
options: RunOptions = {}
|
|
328
|
-
): Promise<PushOutcome> {
|
|
329
|
-
const report = await runGit(["push", "--porcelain", "--set-upstream", remote, branch], {
|
|
330
|
-
cwd: rootDir,
|
|
331
|
-
run: options.run,
|
|
332
|
-
});
|
|
333
|
-
|
|
334
|
-
// Git records what was pushed in the remote-tracking branch only when the
|
|
335
|
-
// remote's fetch configuration covers the branch, and a clone `sous repo
|
|
336
|
-
// link` makes covers only the default branch. Recording it here keeps the
|
|
337
|
-
// checkout's own answer to "was this pushed?" true, which is what `sous repo
|
|
338
|
-
// unlink --remove` and `sous repo contribute --finish` read. It is a record,
|
|
339
|
-
// not a step: a failure to write it changes nothing about the push.
|
|
340
|
-
try {
|
|
341
|
-
await runGit(["update-ref", `refs/remotes/${remote}/${branch}`, `refs/heads/${branch}`], {
|
|
342
|
-
cwd: rootDir,
|
|
343
|
-
run: options.run,
|
|
344
|
-
});
|
|
345
|
-
} catch {
|
|
346
|
-
// The push succeeded; only the local record of it is missing.
|
|
347
|
-
}
|
|
348
|
-
|
|
349
|
-
return pushReportIsUpToDate(report) ? "up-to-date" : "updated";
|
|
350
|
-
}
|
|
351
|
-
|
|
352
|
-
/**
|
|
353
|
-
* True when git's machine-readable push report says every ref it pushed was
|
|
354
|
-
* already current on the remote.
|
|
355
|
-
*
|
|
356
|
-
* pushReportIsUpToDate("To origin\n=\trefs/heads/a:refs/heads/a\t[up to date]\nDone");
|
|
357
|
-
* // -> true
|
|
358
|
-
*
|
|
359
|
-
* @param report - What `git push --porcelain` printed.
|
|
360
|
-
*/
|
|
361
|
-
export function pushReportIsUpToDate(report: string): boolean {
|
|
362
|
-
const refLines = report.split("\n").filter((line) => /^[ +\-*!=]\t/.test(line));
|
|
363
|
-
return refLines.length > 0 && refLines.every((line) => line.startsWith("=\t"));
|
|
364
|
-
}
|
|
365
|
-
|
|
366
|
-
/**
|
|
367
|
-
* True when a local branch of that name exists.
|
|
368
|
-
*
|
|
369
|
-
* @param rootDir - The repository's root directory.
|
|
370
|
-
* @param branch - The branch name.
|
|
371
|
-
* @param options - The command runner to use.
|
|
372
|
-
*/
|
|
373
|
-
export async function branchExists(
|
|
374
|
-
rootDir: string,
|
|
375
|
-
branch: string,
|
|
376
|
-
options: RunOptions = {}
|
|
377
|
-
): Promise<boolean> {
|
|
378
|
-
try {
|
|
379
|
-
await runGit(["rev-parse", "--verify", "--quiet", `refs/heads/${branch}`], {
|
|
380
|
-
cwd: rootDir,
|
|
381
|
-
run: options.run,
|
|
382
|
-
});
|
|
383
|
-
return true;
|
|
384
|
-
} catch {
|
|
385
|
-
return false;
|
|
386
|
-
}
|
|
387
|
-
}
|
|
388
|
-
|
|
389
|
-
/**
|
|
390
|
-
* Checks out an existing branch. Git refuses when uncommitted changes would be
|
|
391
|
-
* overwritten, and that refusal reaches the caller unchanged.
|
|
392
|
-
*
|
|
393
|
-
* @param rootDir - The repository's root directory.
|
|
394
|
-
* @param branch - The branch to check out.
|
|
395
|
-
* @param options - The command runner to use.
|
|
396
|
-
*/
|
|
397
|
-
export async function switchBranch(
|
|
398
|
-
rootDir: string,
|
|
399
|
-
branch: string,
|
|
400
|
-
options: RunOptions = {}
|
|
401
|
-
): Promise<void> {
|
|
402
|
-
await runGit(["switch", branch], { cwd: rootDir, run: options.run });
|
|
403
|
-
}
|
|
404
|
-
|
|
405
|
-
/**
|
|
406
|
-
* Every path the commits since `since` changed, comparing that commit with HEAD.
|
|
407
|
-
*
|
|
408
|
-
* @param rootDir - The repository's root directory.
|
|
409
|
-
* @param since - The commit to compare HEAD with.
|
|
410
|
-
* @param options - The command runner to use.
|
|
411
|
-
*/
|
|
412
|
-
export async function pathsChangedSince(
|
|
413
|
-
rootDir: string,
|
|
414
|
-
since: string,
|
|
415
|
-
options: RunOptions = {}
|
|
416
|
-
): Promise<string[]> {
|
|
417
|
-
const changed = await runGit(["diff", "--name-only", since, "HEAD"], {
|
|
418
|
-
cwd: rootDir,
|
|
419
|
-
run: options.run,
|
|
420
|
-
});
|
|
421
|
-
return changed.length === 0 ? [] : changed.split("\n").filter((line) => line.length > 0);
|
|
422
|
-
}
|
|
423
|
-
|
|
424
|
-
/**
|
|
425
|
-
* Stages everything the working tree holds (edits, deletions and untracked
|
|
426
|
-
* files alike) and commits it with the given message.
|
|
427
|
-
*
|
|
428
|
-
* This is the second place sous commits on an author's behalf, and it runs only
|
|
429
|
-
* for `sous repo submit --commit`, after the contributor has seen every path it
|
|
430
|
-
* stages and agreed to it.
|
|
431
|
-
*
|
|
432
|
-
* @param rootDir - The repository's root directory.
|
|
433
|
-
* @param message - The commit message.
|
|
434
|
-
* @param options - The command runner to use.
|
|
435
|
-
*/
|
|
436
|
-
export async function commitEverything(
|
|
437
|
-
rootDir: string,
|
|
438
|
-
message: string,
|
|
439
|
-
options: RunOptions = {}
|
|
440
|
-
): Promise<void> {
|
|
441
|
-
await runGit(["add", "--all"], { cwd: rootDir, run: options.run });
|
|
442
|
-
await runGit(["commit", "--quiet", "--message", message], {
|
|
443
|
-
cwd: rootDir,
|
|
444
|
-
run: options.run,
|
|
445
|
-
});
|
|
446
|
-
}
|
|
447
|
-
|
|
448
|
-
/**
|
|
449
|
-
* The branch name sous proposes for a submission, stamped with the minute it
|
|
450
|
-
* was made so two submissions from one checkout never collide.
|
|
451
|
-
*
|
|
452
|
-
* @param now - The moment the branch is being created.
|
|
453
|
-
*/
|
|
454
|
-
export function submitBranchName(now: Date): string {
|
|
455
|
-
const pad = (value: number) => String(value).padStart(2, "0");
|
|
456
|
-
const stamp =
|
|
457
|
-
`${now.getFullYear()}${pad(now.getMonth() + 1)}${pad(now.getDate())}` +
|
|
458
|
-
`-${pad(now.getHours())}${pad(now.getMinutes())}`;
|
|
459
|
-
return `sous/submit-${stamp}`;
|
|
460
|
-
}
|