@sous-io/sous 0.2.25 → 0.2.27
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/docs/markdown/_sidebar.md +2 -0
- package/docs/markdown/commands.md +24 -8
- package/docs/markdown/repositories-authoring.md +5 -4
- package/docs/markdown/repositories-file-formats.md +16 -0
- package/docs/markdown/repositories.md +20 -3
- package/package.json +1 -1
- package/recipes/core/sous-skills/skills/about-liquid-templates/SKILL.tpl.md +3 -2
- package/recipes/core/sous-skills/sous.recipe.yaml +1 -1
- package/src/commands/build.ts +28 -43
- package/src/commands/compile.ts +2 -2
- package/src/commands/init.ts +9 -17
- package/src/commands/launch.ts +13 -7
- package/src/commands/prune.ts +13 -10
- package/src/commands/repo/remove.ts +7 -6
- package/src/commands/repo/unlink.ts +7 -6
- package/src/commands/subscription/add.ts +7 -6
- package/src/commands/subscription/remove.ts +7 -6
- package/src/commands/subscription/update.ts +7 -6
- package/src/lib/build-preparation.ts +3 -4
- package/src/lib/build-service.ts +60 -16
- package/src/lib/markdown-compiler.ts +75 -15
- package/src/lib/repos/formats/common.ts +18 -1
- package/src/lib/repos/formats/index-file.ts +79 -81
- package/src/lib/repos/locked-namespace-resolver.ts +132 -4
- package/src/lib/repos/locked-recipes.ts +0 -22
- package/src/lib/repos/namespace-resolver.ts +104 -19
- package/src/lib/repos/release/index-builder.ts +371 -80
|
@@ -25,3 +25,5 @@
|
|
|
25
25
|
- [0004: A global sous defers to the project's install](adrs/0004-project-install-handoff.md)
|
|
26
26
|
- [0005: Follow-ups from the first project on the hand-off](adrs/0005-first-project-follow-ups.md)
|
|
27
27
|
- [0006: `sous repo init` follows `sous-recipes`](adrs/0006-repo-init-model-home.md)
|
|
28
|
+
- [0007: Frozen index entries and tolerant readers](adrs/0007-frozen-index-dependencies.md)
|
|
29
|
+
- [0009: One whole build, everywhere](adrs/0009-whole-build-everywhere.md)
|
|
@@ -93,10 +93,21 @@ and the command you want almost always. Takes `--dry-run`.
|
|
|
93
93
|
|
|
94
94
|
- `--no-prune`, `--no-compile`: skip one half of the run.
|
|
95
95
|
- `--rebuild`: ignore cached hashes and reprocess every output.
|
|
96
|
-
- `--strict`:
|
|
96
|
+
- `--strict`: treat compile warnings as errors (a `.tpl.` file copied without being rendered, a git branch the
|
|
97
|
+
runtime context could not read).
|
|
97
98
|
- `-w, --watch`: rebuild on every change to a source file, a config layer or a linked checkout.
|
|
98
99
|
|
|
99
|
-
Before it compiles, a build
|
|
100
|
+
Before it compiles, a build prepares the project's recipes: it seeds the core recipe, pins any subscription the
|
|
101
|
+
lockfile does not pin yet, restores whatever the store on this machine is missing, and checks upstream for the
|
|
102
|
+
repositories that ask for newer versions. Every command that builds (`init`, `build`, `launch`, `prune`,
|
|
103
|
+
`repo remove`, `repo unlink`, `subscription add`, `subscription remove`, `subscription update`) runs this same
|
|
104
|
+
build, so each one prints the same things.
|
|
105
|
+
|
|
106
|
+
A compile error (a missing or circular include, a template that fails to render, a file sous cannot read) fails
|
|
107
|
+
the build. Every target is still compiled and every error is listed, then the build exits `1`. An output whose
|
|
108
|
+
target had an error is not written, so the copy from the last good build stays in place.
|
|
109
|
+
|
|
110
|
+
A build also lists each recipe this project uses that has a newer version within the range
|
|
100
111
|
declared for it, beside the version pinned. It moves no pin; only always-pull moves one. The build reads upstream
|
|
101
112
|
for this at most once per freshness window (`store.freshnessSeconds`, five minutes by default), gives a
|
|
102
113
|
repository three seconds to answer, and otherwise answers from the cached index without a word about the
|
|
@@ -106,18 +117,22 @@ Example: `sous build --rebuild`
|
|
|
106
117
|
|
|
107
118
|
### `sous compile`
|
|
108
119
|
Compiles markdown templates into output files, and prunes nothing. Takes `--rebuild`, `--strict`, `--dry-run`
|
|
109
|
-
and `-w, --watch`, each meaning what it means on `build
|
|
120
|
+
and `-w, --watch`, each meaning what it means on `build`, and exits `1` after a compile error the way `build`
|
|
121
|
+
does. It does not prepare the recipes first. Example: `sous compile --strict`
|
|
110
122
|
|
|
111
123
|
### `sous prune`
|
|
112
|
-
Removes output files no longer in the current config.
|
|
124
|
+
Removes output files no longer in the current config. It prepares the recipes first, as `build` does, because
|
|
125
|
+
what counts as current depends on the recipes the lockfile pins. Takes `--dry-run` only. Example:
|
|
126
|
+
`sous prune --dry-run`
|
|
113
127
|
|
|
114
128
|
### `sous clear`
|
|
115
129
|
Deletes every file and directory sous has written for the project, and asks first; `-f, --force` answers that
|
|
116
130
|
confirmation ahead of time. Example: `sous clear --force`
|
|
117
131
|
|
|
118
132
|
### `sous launch TOOL...`
|
|
119
|
-
Builds this project's outputs, then starts a coding agent configured under `tools` in its config.
|
|
120
|
-
launches without building; `--continuous` restarts the agent
|
|
133
|
+
Builds this project's outputs, then starts a coding agent configured under `tools` in its config. A build that
|
|
134
|
+
fails starts nothing and exits `1`. `--no-build` launches without building; `--continuous` restarts the agent
|
|
135
|
+
whenever it exits.
|
|
121
136
|
|
|
122
137
|
Any argument `launch` does not recognize is forwarded to the tool; a flag that collides with one of sous's own
|
|
123
138
|
goes after a bare `--`: `sous launch claude --resume`, `sous launch claude -- -c`.
|
|
@@ -447,8 +462,9 @@ Example: `sous vars ask --namespace workflow --var apiUrl`
|
|
|
447
462
|
|
|
448
463
|
A command that succeeds exits `0`. A command line sous could not parse (a missing argument, an unknown flag, a
|
|
449
464
|
value outside a flag's options) exits `2`. Every other failure exits `1`. A broken config halts sous rather than
|
|
450
|
-
producing output built on a guess.
|
|
451
|
-
|
|
465
|
+
producing output built on a guess. A build with a compile error carries on long enough to compile every target
|
|
466
|
+
and list every error, then exits `1`, leaving the last good copy of each output that had an error;
|
|
467
|
+
`--strict` on `build` and `compile` makes compile warnings fail the run too.
|
|
452
468
|
|
|
453
469
|
A failure prints one error block, in plain language, and nothing else; a usage mistake gets the command's own
|
|
454
470
|
help under it, on standard error. An error sous raises names the cause and the cure:
|
|
@@ -143,7 +143,7 @@ variables:
|
|
|
143
143
|
## Declare dependencies and co-subscriptions
|
|
144
144
|
|
|
145
145
|
`depends` fetches and pins a recipe whose files stay OUT of a subscriber's output but are addressable from your
|
|
146
|
-
own templates. `subscribes` is a co-subscription: its questions run and its files DO enter the output, which is
|
|
146
|
+
own templates, and from the templates of any project whose lockfile pins it. `subscribes` is a co-subscription: its questions run and its files DO enter the output, which is
|
|
147
147
|
how a curated bundle is built. Both hold plain strings naming their target by where it lives.
|
|
148
148
|
|
|
149
149
|
```yaml
|
|
@@ -240,9 +240,10 @@ this release's scope** is fine: the release depends on the last published versio
|
|
|
240
240
|
|
|
241
241
|
Each version's resolved dependencies are written into the index, so a consumer installing that version gets what
|
|
242
242
|
it was published with rather than ranges re-resolved months later. A published version never changes: its content
|
|
243
|
-
hash
|
|
244
|
-
|
|
245
|
-
|
|
243
|
+
hash and its dependencies are carried forward exactly as published, and a disagreement is an error telling you
|
|
244
|
+
to bump rather than republish ([what is checked](repositories-file-formats.md#sousindexjson)). A version counts
|
|
245
|
+
as published once its tag exists, and the tags are the backstop, so a tagged version missing from the index is
|
|
246
|
+
rebuilt from it, dependencies included, whenever the index is regenerated, which only happens on a run that
|
|
246
247
|
publishes something. A bump edits the manifest in place, so comments, field order and layout survive; a folded
|
|
247
248
|
block of YAML prose may be re-wrapped and the space before a trailing comment collapsed to one.
|
|
248
249
|
|
|
@@ -177,6 +177,22 @@ sibling, resolved exactly) or `range` plus `repo` (the identity of the repositor
|
|
|
177
177
|
index resolves the range); at least one is required. Installing a version installs these rather than
|
|
178
178
|
re-resolving the manifest's ranges.
|
|
179
179
|
|
|
180
|
+
**A published version's entry is frozen.** Its dependencies are resolved once, when the version is first
|
|
181
|
+
recorded, and every later release carries the entry forward exactly as published, like its hash; releasing
|
|
182
|
+
other recipes, adding a recipe to a namespace it subscribes to, or raising a sibling it names changes nothing
|
|
183
|
+
in it. A release still checks the recorded list against the version's manifest: a recipe the manifest names
|
|
184
|
+
that the list lacks, a recipe in the list the manifest does not declare, a sibling outside its declared
|
|
185
|
+
range, or another repository's entry with a different repository or range is an error naming the recipe, the
|
|
186
|
+
version and each difference; restore the entry if the index was edited, or bump the version to publish
|
|
187
|
+
different dependencies. A version rebuilt from its tag, when the index lost it, has its dependencies resolved
|
|
188
|
+
against the repository as it stood at that tag, then frozen the same way. An entry written before sous
|
|
189
|
+
recorded dependencies carries none, stays that way, and installs by resolving its manifest's ranges.
|
|
190
|
+
|
|
191
|
+
**Fields this sous does not know are kept, not refused.** A later sous may add fields to the index, at any
|
|
192
|
+
level. This one reads such an index, validates every field it defines as strictly as before, and ignores the
|
|
193
|
+
rest; a release it runs carries a published version's entry forward with those fields intact. A sous older
|
|
194
|
+
than the one that began tolerating unknown fields refuses the whole index once a repository publishes one.
|
|
195
|
+
|
|
180
196
|
**The `seeded` field.** Sous ships the `core` recipe in its own npm package, so a project can build before
|
|
181
197
|
reaching the network. That copy is folded into the official repository's index in memory and resolved like
|
|
182
198
|
any other, marked `seeded: true`; if the repository already publishes it, the published copy wins. Sous
|
|
@@ -331,9 +331,26 @@ published identity and then the path inside it, on a line of its own:
|
|
|
331
331
|
```
|
|
332
332
|
|
|
333
333
|
The `~` is required. A bare `@path` is always a relative path or a declared alias, so an include
|
|
334
|
-
line can never quietly stop meaning a file on disk.
|
|
335
|
-
against
|
|
336
|
-
|
|
334
|
+
line can never quietly stop meaning a file on disk. In your own templates, `~<namespace>`
|
|
335
|
+
resolves against every recipe the lockfile pins, whatever brought it in: a recipe you subscribe
|
|
336
|
+
to, a member of a set you subscribe to, or a library another recipe depends on. Inside a recipe,
|
|
337
|
+
it resolves only against that recipe's own `depends` and `subscribes` at the versions the
|
|
338
|
+
lockfile pins. The path after the recipe name may not be absolute or hold a `.` or `..` segment.
|
|
339
|
+
|
|
340
|
+
An include naming a recipe the lockfile does not pin fails the build. When sous can tell what
|
|
341
|
+
used to bring the recipe in (a switched-off subscription, or an earlier version of a set that
|
|
342
|
+
listed it), the error names it:
|
|
343
|
+
|
|
344
|
+
```text
|
|
345
|
+
Error: Include not found: @~workflow/member/memories/member.md
|
|
346
|
+
in file: .sous/prompts/AGENTS.md
|
|
347
|
+
namespace: workflow
|
|
348
|
+
recipe: workflow/member
|
|
349
|
+
The namespace "workflow" holds no recipe named "workflow/member".
|
|
350
|
+
Version 1.0.0 of "omakase/house" brought "workflow/member" in, but the version this project
|
|
351
|
+
pins, 1.1.0, does not, so the lockfile no longer pins it.
|
|
352
|
+
Subscribe to "workflow/member" directly to keep including it, or remove the include.
|
|
353
|
+
```
|
|
337
354
|
|
|
338
355
|
## Where to go next
|
|
339
356
|
|
package/package.json
CHANGED
|
@@ -148,8 +148,9 @@ written `@~<namespace>/<recipe>/<path inside that recipe>`:
|
|
|
148
148
|
|
|
149
149
|
Scoping is deliberate. A file inside a recipe may address that recipe itself plus the
|
|
150
150
|
recipes it declares under `depends` or `subscribes`; a file in the project's own templates
|
|
151
|
-
may address the project's
|
|
152
|
-
so a reference can never quietly pick up a recipe
|
|
151
|
+
may address every recipe the project's lockfile pins, whatever brought it in. Anything else
|
|
152
|
+
is an error naming what was missing, so a reference can never quietly pick up a recipe
|
|
153
|
+
nobody asked for.
|
|
153
154
|
|
|
154
155
|
Built-in **aliases** are reserved, always begin with `~`, and are consulted before recipe
|
|
155
156
|
namespaces. There is exactly one:
|
package/src/commands/build.ts
CHANGED
|
@@ -1,23 +1,14 @@
|
|
|
1
1
|
import { Flags } from "@oclif/core";
|
|
2
2
|
import { BaseCommand } from "../base-command.js";
|
|
3
|
-
import {
|
|
3
|
+
import { runProjectBuild } from "../lib/build-service.js";
|
|
4
4
|
import { PidService } from "../lib/pid-service.js";
|
|
5
5
|
import { resolveRootScope } from "../lib/settings.js";
|
|
6
|
-
import { describeLinkedRepos } from "../lib/repos/links.js";
|
|
7
6
|
import { resolveStoreSettings } from "../lib/repos/store/settings.js";
|
|
8
|
-
import { prepareRepositoriesForBuild } from "../lib/build-preparation.js";
|
|
9
7
|
import { subscriptionServiceFor } from "../lib/repos/subscription-service.js";
|
|
10
8
|
import { buildReloadWatchConfig, startConfigReloadWatch } from "../lib/watch-loop.js";
|
|
11
9
|
import type { WatchHandle } from "../lib/watch-service.js";
|
|
12
10
|
import { WatchService } from "../lib/watch-service.js";
|
|
13
|
-
import {
|
|
14
|
-
footer,
|
|
15
|
-
heading,
|
|
16
|
-
log,
|
|
17
|
-
paragraph,
|
|
18
|
-
showCommandVars,
|
|
19
|
-
warning,
|
|
20
|
-
} from "../utils/formatting.js";
|
|
11
|
+
import { log, paragraph, showCommandVars } from "../utils/formatting.js";
|
|
21
12
|
|
|
22
13
|
export default class Build extends BaseCommand {
|
|
23
14
|
static description =
|
|
@@ -49,7 +40,7 @@ export default class Build extends BaseCommand {
|
|
|
49
40
|
default: false,
|
|
50
41
|
}),
|
|
51
42
|
strict: Flags.boolean({
|
|
52
|
-
description: "
|
|
43
|
+
description: "Treat compile warnings as errors",
|
|
53
44
|
default: false,
|
|
54
45
|
}),
|
|
55
46
|
watch: Flags.boolean({
|
|
@@ -71,37 +62,20 @@ export default class Build extends BaseCommand {
|
|
|
71
62
|
"No Prune": flags["no-prune"],
|
|
72
63
|
});
|
|
73
64
|
|
|
74
|
-
// A linked repository is read from a working copy instead of a published
|
|
75
|
-
// version, so it is announced every single time; a build that silently
|
|
76
|
-
// produced something different would be far worse than a noisy one.
|
|
77
|
-
const linked = describeLinkedRepos(this.configContext.sousDir);
|
|
78
|
-
if (linked.length > 0) warning(linked.join("\n"));
|
|
79
|
-
|
|
80
|
-
const repositories = subscriptionServiceFor({
|
|
81
|
-
configContext: this.configContext,
|
|
82
|
-
settings: this.settings,
|
|
83
|
-
shellEnv: this.shellEnv,
|
|
84
|
-
});
|
|
85
|
-
|
|
86
|
-
if (!flags["dry-run"] && !flags["no-compile"]) {
|
|
87
|
-
await prepareRepositoriesForBuild(repositories);
|
|
88
|
-
}
|
|
89
|
-
|
|
90
|
-
heading("Building");
|
|
91
|
-
|
|
92
65
|
const buildOptions = {
|
|
93
66
|
strict: flags.strict,
|
|
94
67
|
rebuild: flags.rebuild,
|
|
95
68
|
dryRun: flags["dry-run"],
|
|
96
69
|
noCompile: flags["no-compile"],
|
|
97
70
|
noPrune: flags["no-prune"],
|
|
98
|
-
configContext: this.configContext,
|
|
99
71
|
};
|
|
100
72
|
|
|
101
|
-
const
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
73
|
+
const success = await runProjectBuild({
|
|
74
|
+
settings: this.settings,
|
|
75
|
+
configContext: this.configContext,
|
|
76
|
+
shellEnv: this.shellEnv,
|
|
77
|
+
options: buildOptions,
|
|
78
|
+
});
|
|
105
79
|
|
|
106
80
|
if (!success && !flags.watch) {
|
|
107
81
|
this.exit(1);
|
|
@@ -129,17 +103,23 @@ export default class Build extends BaseCommand {
|
|
|
129
103
|
|
|
130
104
|
const watchService = new WatchService();
|
|
131
105
|
|
|
132
|
-
// Reruns
|
|
106
|
+
// Reruns the build with the command's current settings. Called for
|
|
133
107
|
// partial rebuilds (with the changed file) and, after a clean reload, for
|
|
134
|
-
// full rebuilds
|
|
108
|
+
// full rebuilds, which prepare the recipes again the way the first build did.
|
|
135
109
|
const rebuild = async (changedFile?: string) => {
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
110
|
+
// A failed rebuild has already listed its errors, and the watch keeps
|
|
111
|
+
// running so the next save can fix them.
|
|
112
|
+
await runProjectBuild({
|
|
113
|
+
settings: this.settings,
|
|
114
|
+
configContext: this.configContext,
|
|
115
|
+
shellEnv: this.shellEnv,
|
|
116
|
+
heading: "Rebuilding",
|
|
117
|
+
options: {
|
|
118
|
+
...buildOptions,
|
|
119
|
+
// --rebuild means full clean build on every trigger; skip partial optimisation
|
|
120
|
+
changedFile: buildOptions.rebuild ? undefined : changedFile,
|
|
121
|
+
},
|
|
141
122
|
});
|
|
142
|
-
footer();
|
|
143
123
|
};
|
|
144
124
|
|
|
145
125
|
const { handle, triggerFullRebuild } = startConfigReloadWatch({
|
|
@@ -154,6 +134,11 @@ export default class Build extends BaseCommand {
|
|
|
154
134
|
// and a failure never breaks the watch; the last good answer stands.
|
|
155
135
|
const pollSeconds = resolveStoreSettings(this.settings).watchPollSeconds;
|
|
156
136
|
if (pollSeconds > 0) {
|
|
137
|
+
const repositories = subscriptionServiceFor({
|
|
138
|
+
configContext: this.configContext,
|
|
139
|
+
settings: this.settings,
|
|
140
|
+
shellEnv: this.shellEnv,
|
|
141
|
+
});
|
|
157
142
|
const poll = setInterval(() => {
|
|
158
143
|
void repositories
|
|
159
144
|
.checkUpstream()
|
package/src/commands/compile.ts
CHANGED
|
@@ -34,7 +34,7 @@ export default class Compile extends BaseCommand {
|
|
|
34
34
|
static flags = {
|
|
35
35
|
...BaseCommand.baseFlags,
|
|
36
36
|
strict: Flags.boolean({
|
|
37
|
-
description: "
|
|
37
|
+
description: "Treat compile warnings as errors",
|
|
38
38
|
default: false,
|
|
39
39
|
}),
|
|
40
40
|
rebuild: Flags.boolean({
|
|
@@ -113,7 +113,7 @@ export default class Compile extends BaseCommand {
|
|
|
113
113
|
|
|
114
114
|
footer();
|
|
115
115
|
|
|
116
|
-
if (!success &&
|
|
116
|
+
if (!success && !flags.watch) {
|
|
117
117
|
this.exit(1);
|
|
118
118
|
}
|
|
119
119
|
|
package/src/commands/init.ts
CHANGED
|
@@ -17,8 +17,7 @@ import path from "node:path";
|
|
|
17
17
|
import { Args, Flags } from "@oclif/core";
|
|
18
18
|
import { confirm } from "@inquirer/prompts";
|
|
19
19
|
import { BaseCommand } from "../base-command.js";
|
|
20
|
-
import {
|
|
21
|
-
import { buildProjectOutputs } from "../lib/build-service.js";
|
|
20
|
+
import { runProjectBuild } from "../lib/build-service.js";
|
|
22
21
|
import {
|
|
23
22
|
CONFIG_FILE_NAMES,
|
|
24
23
|
SOUS_DIR_NAME,
|
|
@@ -28,7 +27,6 @@ import {
|
|
|
28
27
|
} from "../lib/config-discovery.js";
|
|
29
28
|
import { ConfigError } from "../lib/errors.js";
|
|
30
29
|
import { nonInteractiveError } from "../lib/interactive.js";
|
|
31
|
-
import { subscriptionServiceFor } from "../lib/repos/subscription-service.js";
|
|
32
30
|
import {
|
|
33
31
|
PROJECT_CONFIG_FORMATS,
|
|
34
32
|
SOUS_PACKAGE_NAME,
|
|
@@ -45,7 +43,6 @@ import {
|
|
|
45
43
|
blankLine,
|
|
46
44
|
dryRunNotice,
|
|
47
45
|
footer,
|
|
48
|
-
heading,
|
|
49
46
|
log,
|
|
50
47
|
paragraph,
|
|
51
48
|
section,
|
|
@@ -262,8 +259,8 @@ export default class Init extends BaseCommand {
|
|
|
262
259
|
}
|
|
263
260
|
|
|
264
261
|
/**
|
|
265
|
-
* Adopts the config just written and builds the project with it,
|
|
266
|
-
* same
|
|
262
|
+
* Adopts the config just written and builds the project with it, through
|
|
263
|
+
* the same build `sous build` runs: the repositories are prepared (which
|
|
267
264
|
* seeds the core recipe into the store and pins it in the lockfile), then
|
|
268
265
|
* the outputs are compiled.
|
|
269
266
|
*
|
|
@@ -274,17 +271,12 @@ export default class Init extends BaseCommand {
|
|
|
274
271
|
resolveConfigFlag(configPath, this.configLocator.cwd, this.configLocator.confDirOverride)
|
|
275
272
|
);
|
|
276
273
|
|
|
277
|
-
await
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
);
|
|
284
|
-
|
|
285
|
-
heading("Building the project");
|
|
286
|
-
|
|
287
|
-
const succeeded = await buildProjectOutputs(this.settings, this.configContext);
|
|
274
|
+
const succeeded = await runProjectBuild({
|
|
275
|
+
settings: this.settings,
|
|
276
|
+
configContext: this.configContext,
|
|
277
|
+
shellEnv: this.shellEnv,
|
|
278
|
+
heading: "Building the project",
|
|
279
|
+
});
|
|
288
280
|
|
|
289
281
|
if (!succeeded) {
|
|
290
282
|
throw new ConfigError(
|
package/src/commands/launch.ts
CHANGED
|
@@ -2,7 +2,8 @@ import { Args, Flags } from "@oclif/core";
|
|
|
2
2
|
import { spawnSync } from "node:child_process";
|
|
3
3
|
import fs from "node:fs";
|
|
4
4
|
import { BaseCommand } from "../base-command.js";
|
|
5
|
-
import {
|
|
5
|
+
import { runProjectBuild } from "../lib/build-service.js";
|
|
6
|
+
import { ConfigError } from "../lib/errors.js";
|
|
6
7
|
import { resolveRootScope, resolveTools } from "../lib/settings.js";
|
|
7
8
|
import { displayError, footer, heading, showCommandVars } from "../utils/formatting.js";
|
|
8
9
|
|
|
@@ -85,16 +86,21 @@ export default class Launch extends BaseCommand {
|
|
|
85
86
|
...(passThroughArgs.length > 0 && { "Tool Args": passThroughArgs.join(" ") }),
|
|
86
87
|
});
|
|
87
88
|
|
|
88
|
-
const buildService = new BuildService();
|
|
89
|
-
|
|
90
89
|
do {
|
|
91
|
-
// Build step (unless --no-build)
|
|
90
|
+
// Build step (unless --no-build). A failed build stops here: the agent
|
|
91
|
+
// would otherwise start on outputs the build could not bring up to date.
|
|
92
92
|
if (!flags["no-build"]) {
|
|
93
|
-
|
|
94
|
-
|
|
93
|
+
const built = await runProjectBuild({
|
|
94
|
+
settings: this.settings,
|
|
95
95
|
configContext: this.configContext,
|
|
96
|
+
shellEnv: this.shellEnv,
|
|
96
97
|
});
|
|
97
|
-
|
|
98
|
+
if (!built) {
|
|
99
|
+
throw new ConfigError(
|
|
100
|
+
`The build failed, so ${args.tool} was not started. Fix the errors listed ` +
|
|
101
|
+
`above, or pass --no-build to start it with the outputs as they are.`
|
|
102
|
+
);
|
|
103
|
+
}
|
|
98
104
|
}
|
|
99
105
|
|
|
100
106
|
// Config-defined args first, then pass-through, then promptFile content
|
package/src/commands/prune.ts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { Flags } from "@oclif/core";
|
|
2
2
|
import { BaseCommand } from "../base-command.js";
|
|
3
|
-
import {
|
|
4
|
-
import {
|
|
3
|
+
import { runProjectBuild } from "../lib/build-service.js";
|
|
4
|
+
import { showCommandVars } from "../utils/formatting.js";
|
|
5
5
|
|
|
6
6
|
export default class Prune extends BaseCommand {
|
|
7
7
|
static description = "Remove output files that are no longer in the current config";
|
|
@@ -22,19 +22,22 @@ export default class Prune extends BaseCommand {
|
|
|
22
22
|
async run(): Promise<void> {
|
|
23
23
|
const { flags } = await this.parse(Prune);
|
|
24
24
|
|
|
25
|
-
const stateFilePath = resolveStateFilePath(this.settings, this.configContext);
|
|
26
|
-
|
|
27
25
|
showCommandVars({
|
|
28
26
|
Project: this.projectLabel,
|
|
29
27
|
Config: this.configContext.configPath,
|
|
30
28
|
"Dry Run": flags["dry-run"],
|
|
31
29
|
});
|
|
32
30
|
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
31
|
+
// Prune goes through the same build path as everything else, with the
|
|
32
|
+
// compile step switched off: what counts as current depends on the recipes
|
|
33
|
+
// the lockfile pins, so they are restored first, or a recipe missing from
|
|
34
|
+
// the store would have its outputs pruned as if it were gone.
|
|
35
|
+
await runProjectBuild({
|
|
36
|
+
settings: this.settings,
|
|
37
|
+
configContext: this.configContext,
|
|
38
|
+
shellEnv: this.shellEnv,
|
|
39
|
+
heading: "Pruning",
|
|
40
|
+
options: { noCompile: true, dryRun: flags["dry-run"] },
|
|
41
|
+
});
|
|
39
42
|
}
|
|
40
43
|
}
|
|
@@ -14,7 +14,7 @@
|
|
|
14
14
|
|
|
15
15
|
import { Args, Flags } from "@oclif/core";
|
|
16
16
|
import { BaseCommand } from "../../base-command.js";
|
|
17
|
-
import {
|
|
17
|
+
import { runProjectBuild } from "../../lib/build-service.js";
|
|
18
18
|
import { ConfigError } from "../../lib/errors.js";
|
|
19
19
|
import { resolveRootScope } from "../../lib/settings.js";
|
|
20
20
|
import { subscriptionServiceFor } from "../../lib/repos/subscription-service.js";
|
|
@@ -175,11 +175,12 @@ export default class RepoRemove extends BaseCommand {
|
|
|
175
175
|
private async rebuildProject(name: string): Promise<void> {
|
|
176
176
|
await this.reloadDiscoveredConfig();
|
|
177
177
|
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
178
|
+
const succeeded = await runProjectBuild({
|
|
179
|
+
settings: this.settings,
|
|
180
|
+
configContext: this.configContext,
|
|
181
|
+
shellEnv: this.shellEnv,
|
|
182
|
+
heading: "Building the project",
|
|
183
|
+
});
|
|
183
184
|
|
|
184
185
|
if (!succeeded) {
|
|
185
186
|
throw new ConfigError(
|
|
@@ -2,7 +2,7 @@ import fs from "node:fs";
|
|
|
2
2
|
import path from "node:path";
|
|
3
3
|
import { Args, Flags } from "@oclif/core";
|
|
4
4
|
import { BaseCommand } from "../../base-command.js";
|
|
5
|
-
import {
|
|
5
|
+
import { runProjectBuild } from "../../lib/build-service.js";
|
|
6
6
|
import { ConfigError, isConfigError } from "../../lib/errors.js";
|
|
7
7
|
import {
|
|
8
8
|
NonInteractiveError,
|
|
@@ -409,11 +409,12 @@ export default class RepoUnlink extends BaseCommand {
|
|
|
409
409
|
private async rebuildProject(name: string): Promise<void> {
|
|
410
410
|
await this.reloadDiscoveredConfig();
|
|
411
411
|
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
412
|
+
const succeeded = await runProjectBuild({
|
|
413
|
+
settings: this.settings,
|
|
414
|
+
configContext: this.configContext,
|
|
415
|
+
shellEnv: this.shellEnv,
|
|
416
|
+
heading: "Building the project",
|
|
417
|
+
});
|
|
417
418
|
|
|
418
419
|
if (!succeeded) {
|
|
419
420
|
throw new ConfigError(
|
|
@@ -24,7 +24,7 @@
|
|
|
24
24
|
|
|
25
25
|
import { Args, Flags } from "@oclif/core";
|
|
26
26
|
import { BaseCommand } from "../../base-command.js";
|
|
27
|
-
import {
|
|
27
|
+
import { runProjectBuild } from "../../lib/build-service.js";
|
|
28
28
|
import { ConfigError } from "../../lib/errors.js";
|
|
29
29
|
import { subscriptionServiceFor } from "../../lib/repos/subscription-service.js";
|
|
30
30
|
import { formatAskReport } from "../../lib/vars/ask.js";
|
|
@@ -267,11 +267,12 @@ export default class SubscriptionAdd extends BaseCommand {
|
|
|
267
267
|
private async rebuildProject(key: string): Promise<void> {
|
|
268
268
|
await this.reloadDiscoveredConfig();
|
|
269
269
|
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
270
|
+
const succeeded = await runProjectBuild({
|
|
271
|
+
settings: this.settings,
|
|
272
|
+
configContext: this.configContext,
|
|
273
|
+
shellEnv: this.shellEnv,
|
|
274
|
+
heading: "Building the project",
|
|
275
|
+
});
|
|
275
276
|
|
|
276
277
|
if (!succeeded) {
|
|
277
278
|
throw new ConfigError(
|
|
@@ -12,7 +12,7 @@
|
|
|
12
12
|
|
|
13
13
|
import { Args, Flags } from "@oclif/core";
|
|
14
14
|
import { BaseCommand } from "../../base-command.js";
|
|
15
|
-
import {
|
|
15
|
+
import { runProjectBuild } from "../../lib/build-service.js";
|
|
16
16
|
import { ConfigError } from "../../lib/errors.js";
|
|
17
17
|
import { subscriptionServiceFor } from "../../lib/repos/subscription-service.js";
|
|
18
18
|
import { renderTable, type TableColumn } from "../../utils/table.js";
|
|
@@ -163,11 +163,12 @@ export default class SubscriptionRemove extends BaseCommand {
|
|
|
163
163
|
private async rebuildProject(key: string): Promise<void> {
|
|
164
164
|
await this.reloadDiscoveredConfig();
|
|
165
165
|
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
166
|
+
const succeeded = await runProjectBuild({
|
|
167
|
+
settings: this.settings,
|
|
168
|
+
configContext: this.configContext,
|
|
169
|
+
shellEnv: this.shellEnv,
|
|
170
|
+
heading: "Building the project",
|
|
171
|
+
});
|
|
171
172
|
|
|
172
173
|
if (!succeeded) {
|
|
173
174
|
throw new ConfigError(
|
|
@@ -19,7 +19,7 @@
|
|
|
19
19
|
|
|
20
20
|
import { Args, Flags } from "@oclif/core";
|
|
21
21
|
import { BaseCommand } from "../../base-command.js";
|
|
22
|
-
import {
|
|
22
|
+
import { runProjectBuild } from "../../lib/build-service.js";
|
|
23
23
|
import { ConfigError } from "../../lib/errors.js";
|
|
24
24
|
import { subscriptionServiceFor } from "../../lib/repos/subscription-service.js";
|
|
25
25
|
import { describeUpdateScope } from "../../lib/repos/update-plan.js";
|
|
@@ -198,11 +198,12 @@ export default class SubscriptionUpdate extends BaseCommand {
|
|
|
198
198
|
private async rebuildProject(): Promise<void> {
|
|
199
199
|
await this.reloadDiscoveredConfig();
|
|
200
200
|
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
201
|
+
const succeeded = await runProjectBuild({
|
|
202
|
+
settings: this.settings,
|
|
203
|
+
configContext: this.configContext,
|
|
204
|
+
shellEnv: this.shellEnv,
|
|
205
|
+
heading: "Building the project",
|
|
206
|
+
});
|
|
206
207
|
|
|
207
208
|
if (!succeeded) {
|
|
208
209
|
throw new ConfigError(
|
|
@@ -1,9 +1,8 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* The step that runs before a project's outputs are compiled, and what it says
|
|
3
|
-
* while it runs. `
|
|
4
|
-
*
|
|
5
|
-
*
|
|
6
|
-
* either command.
|
|
3
|
+
* while it runs. `runProjectBuild` (`build-service.ts`) calls it, and every
|
|
4
|
+
* command that builds goes through that one function, so the step and its
|
|
5
|
+
* reporting are the same whichever command the user typed.
|
|
7
6
|
*/
|
|
8
7
|
|
|
9
8
|
import type { SubscriptionService } from "./repos/subscription-service.js";
|