depflow 3.0.0-dev.10 → 3.0.0-dev.12
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 +513 -117
- package/dist/builder/Builder.d.ts +279 -0
- package/dist/builder/Builder.js +391 -0
- package/dist/builder/Builder.js.map +1 -0
- package/dist/cli/Actions.d.ts +187 -0
- package/dist/cli/Actions.js +793 -0
- package/dist/cli/Actions.js.map +1 -0
- package/dist/cli/CLI.d.ts +170 -0
- package/dist/cli/CLI.js +231 -0
- package/dist/cli/CLI.js.map +1 -0
- package/dist/cli/DepflowCLI.d.ts +62 -0
- package/dist/cli/DepflowCLI.js +229 -0
- package/dist/cli/DepflowCLI.js.map +1 -0
- package/dist/cli/Flags.d.ts +106 -0
- package/dist/cli/Flags.js +125 -0
- package/dist/cli/Flags.js.map +1 -0
- package/dist/cli/bin.js +63 -0
- package/dist/cli/bin.js.map +1 -0
- package/dist/config/Config.d.ts +690 -0
- package/dist/config/Config.js +45 -0
- package/dist/config/Config.js.map +1 -0
- package/dist/config/ImportMap.d.ts +60 -0
- package/dist/config/ImportMap.js +66 -0
- package/dist/config/ImportMap.js.map +1 -0
- package/dist/config/JsonSchema.d.ts +12 -0
- package/dist/config/JsonSchema.js +26 -0
- package/dist/config/JsonSchema.js.map +1 -0
- package/dist/config/Settings.d.ts +201 -0
- package/dist/config/Settings.js +191 -0
- package/dist/config/Settings.js.map +1 -0
- package/dist/config/TSConfig.d.ts +73 -0
- package/dist/config/TSConfig.js +73 -0
- package/dist/config/TSConfig.js.map +1 -0
- package/dist/config/codecs/JsonCodec.d.ts +30 -0
- package/dist/config/codecs/JsonCodec.js +31 -0
- package/dist/config/codecs/JsonCodec.js.map +1 -0
- package/dist/config/codecs/YamlCodec.d.ts +62 -0
- package/dist/config/codecs/YamlCodec.js +89 -0
- package/dist/config/codecs/YamlCodec.js.map +1 -0
- package/dist/dependency/DepUtils.d.ts +78 -0
- package/dist/dependency/DepUtils.js +122 -0
- package/dist/dependency/DepUtils.js.map +1 -0
- package/dist/dependency/Dependency.d.ts +81 -0
- package/dist/dependency/Dependency.js +71 -0
- package/dist/dependency/Dependency.js.map +1 -0
- package/dist/dependency/Git.d.ts +75 -0
- package/dist/dependency/Git.js +97 -0
- package/dist/dependency/Git.js.map +1 -0
- package/dist/dependency/GitDependency.d.ts +30 -0
- package/dist/dependency/GitDependency.js +79 -0
- package/dist/dependency/GitDependency.js.map +1 -0
- package/dist/dependency/NpmDependency.d.ts +27 -0
- package/dist/dependency/NpmDependency.js +91 -0
- package/dist/dependency/NpmDependency.js.map +1 -0
- package/{build/support/PathResolver → dist/resolve}/AliasCompiler.d.ts +18 -5
- package/{build/support/PathResolver → dist/resolve}/AliasCompiler.js +31 -9
- package/dist/resolve/AliasCompiler.js.map +1 -0
- package/{build/support/PathResolver → dist/resolve}/PathResolver.d.ts +25 -8
- package/dist/resolve/PathResolver.js +133 -0
- package/dist/resolve/PathResolver.js.map +1 -0
- package/dist/resolve/PathRewriter.d.ts +24 -0
- package/{build/support/PathResolver → dist/resolve}/PathRewriter.js +21 -11
- package/dist/resolve/PathRewriter.js.map +1 -0
- package/{build → dist}/schema/Config/Builder.d.ts +114 -49
- package/dist/schema/Config/Builder.js +78 -0
- package/dist/schema/Config/Builder.js.map +1 -0
- package/{build → dist}/schema/Config/Config.d.ts +169 -78
- package/dist/schema/Config/Config.js +62 -0
- package/dist/schema/Config/Config.js.map +1 -0
- package/{build → dist}/schema/Config/Dependency.d.ts +219 -89
- package/dist/schema/Config/Dependency.js +80 -0
- package/dist/schema/Config/Dependency.js.map +1 -0
- package/{build → dist}/schema/Config/Extractor.d.ts +41 -17
- package/dist/schema/Config/Extractor.js +43 -0
- package/dist/schema/Config/Extractor.js.map +1 -0
- package/{build → dist}/schema/Config/Resolver.d.ts +25 -8
- package/dist/schema/Config/Resolver.js +43 -0
- package/dist/schema/Config/Resolver.js.map +1 -0
- package/{build → dist}/schema/Config/Transform.d.ts +22 -5
- package/dist/schema/Config/Transform.js +51 -0
- package/dist/schema/Config/Transform.js.map +1 -0
- package/dist/schema/Defaults.d.ts +20 -0
- package/dist/schema/Defaults.js +24 -0
- package/dist/schema/Defaults.js.map +1 -0
- package/dist/schema/ImportMap.d.ts +30 -0
- package/dist/schema/ImportMap.js +28 -0
- package/dist/schema/ImportMap.js.map +1 -0
- package/{build → dist}/schema/TSConfig.d.ts +23 -9
- package/dist/schema/TSConfig.js +39 -0
- package/dist/schema/TSConfig.js.map +1 -0
- package/{build → dist}/schema/schema.d.ts +11 -2
- package/{build → dist}/schema/schema.js +11 -2
- package/dist/schema/schema.js.map +1 -0
- package/dist/task/Command.d.ts +287 -0
- package/dist/task/Command.js +404 -0
- package/dist/task/Command.js.map +1 -0
- package/dist/task/Group.d.ts +15 -0
- package/dist/task/Group.js +29 -0
- package/dist/task/Group.js.map +1 -0
- package/dist/task/OutputPolicy.d.ts +262 -0
- package/dist/task/OutputPolicy.js +364 -0
- package/dist/task/OutputPolicy.js.map +1 -0
- package/dist/task/Task.d.ts +210 -0
- package/dist/task/Task.js +312 -0
- package/dist/task/Task.js.map +1 -0
- package/package.json +14 -10
- package/.gitignore +0 -7
- package/build/cli/DepFLowCLI.d.ts +0 -16
- package/build/cli/DepFLowCLI.js +0 -254
- package/build/cli/DepFLowCLI.js.map +0 -1
- package/build/cli/bin.js +0 -38
- package/build/cli/bin.js.map +0 -1
- package/build/config/Config.d.ts +0 -28
- package/build/config/Config.js +0 -46
- package/build/config/Config.js.map +0 -1
- package/build/config/ImportMap.d.ts +0 -51
- package/build/config/ImportMap.js +0 -97
- package/build/config/ImportMap.js.map +0 -1
- package/build/config/TSConfig.d.ts +0 -54
- package/build/config/TSConfig.js +0 -104
- package/build/config/TSConfig.js.map +0 -1
- package/build/schema/Config/Builder.js +0 -48
- package/build/schema/Config/Builder.js.map +0 -1
- package/build/schema/Config/Config.js +0 -20
- package/build/schema/Config/Config.js.map +0 -1
- package/build/schema/Config/Dependency.js +0 -25
- package/build/schema/Config/Dependency.js.map +0 -1
- package/build/schema/Config/Extractor.js +0 -25
- package/build/schema/Config/Extractor.js.map +0 -1
- package/build/schema/Config/Resolver.js +0 -25
- package/build/schema/Config/Resolver.js.map +0 -1
- package/build/schema/Config/Transform.js +0 -27
- package/build/schema/Config/Transform.js.map +0 -1
- package/build/schema/ImportMap.d.ts +0 -22
- package/build/schema/ImportMap.js +0 -12
- package/build/schema/ImportMap.js.map +0 -1
- package/build/schema/TSConfig.js +0 -19
- package/build/schema/TSConfig.js.map +0 -1
- package/build/schema/schema.js.map +0 -1
- package/build/support/Builder/Builder.d.ts +0 -46
- package/build/support/Builder/Builder.js +0 -116
- package/build/support/Builder/Builder.js.map +0 -1
- package/build/support/Dependency/Dependency.d.ts +0 -38
- package/build/support/Dependency/Dependency.js +0 -14
- package/build/support/Dependency/Dependency.js.map +0 -1
- package/build/support/Dependency/GitDependency.d.ts +0 -20
- package/build/support/Dependency/GitDependency.js +0 -67
- package/build/support/Dependency/GitDependency.js.map +0 -1
- package/build/support/Dependency/NpmDependency.d.ts +0 -18
- package/build/support/Dependency/NpmDependency.js +0 -65
- package/build/support/Dependency/NpmDependency.js.map +0 -1
- package/build/support/Git.d.ts +0 -53
- package/build/support/Git.js +0 -86
- package/build/support/Git.js.map +0 -1
- package/build/support/PathResolver/AliasCompiler.js.map +0 -1
- package/build/support/PathResolver/PathResolver.js +0 -105
- package/build/support/PathResolver/PathResolver.js.map +0 -1
- package/build/support/PathResolver/PathRewriter.d.ts +0 -18
- package/build/support/PathResolver/PathRewriter.js.map +0 -1
- package/build/support/PathResolver/Utils.d.ts +0 -36
- package/build/support/PathResolver/Utils.js +0 -45
- package/build/support/PathResolver/Utils.js.map +0 -1
- package/build/support/Task/Command.d.ts +0 -49
- package/build/support/Task/Command.js +0 -100
- package/build/support/Task/Command.js.map +0 -1
- package/build/support/Task/Task.d.ts +0 -84
- package/build/support/Task/Task.js +0 -145
- package/build/support/Task/Task.js.map +0 -1
- package/build/support/Utils.d.ts +0 -40
- package/build/support/Utils.js +0 -138
- package/build/support/Utils.js.map +0 -1
- package/build/support/Validator.d.ts +0 -22
- package/build/support/Validator.js +0 -28
- package/build/support/Validator.js.map +0 -1
- /package/{build → dist}/cli/bin.d.ts +0 -0
|
@@ -0,0 +1,279 @@
|
|
|
1
|
+
import type Logger from "@netfeez/vterm";
|
|
2
|
+
import Task from "../task/Task.js";
|
|
3
|
+
import OutputPolicy from "../task/OutputPolicy.js";
|
|
4
|
+
import schema from "../schema/schema.js";
|
|
5
|
+
export declare class Builder {
|
|
6
|
+
protected readonly pipeline: schema.Builder;
|
|
7
|
+
protected readonly cwd: string;
|
|
8
|
+
protected readonly projectRoot: string;
|
|
9
|
+
protected readonly logger: Logger | null;
|
|
10
|
+
protected readonly policy: OutputPolicy | null;
|
|
11
|
+
/** The step that stopped the pipeline, or `null` while it is still walking it. */
|
|
12
|
+
protected vStopped: Builder.Stopped | null;
|
|
13
|
+
constructor(info: Builder.Info);
|
|
14
|
+
/**
|
|
15
|
+
* Runs the pipeline and reports what every step of it did.
|
|
16
|
+
* @returns A promise resolving with the report of the run, or rejecting with a
|
|
17
|
+
* {@link Builder.Failure} carrying that same report, when a step stopped the pipeline.
|
|
18
|
+
*
|
|
19
|
+
* @remarks The report is the verdict of the run, and it is produced whether the run succeeded or
|
|
20
|
+
* failed: a caller that catches a {@link Builder.Failure} gets the steps that ran before the one
|
|
21
|
+
* that stopped it, and the steps that never ran at all, so nothing about the pipeline is missing
|
|
22
|
+
* from a failure.
|
|
23
|
+
*
|
|
24
|
+
* @remarks The group of the run is closed in a `finally`, so the steps of a pipeline that threw leave
|
|
25
|
+
* the output as closed as the ones of a pipeline that finished. It is opened only when the run
|
|
26
|
+
* decorates its output, because a group that draws nothing still leaves a box and an indentation
|
|
27
|
+
* around every line written inside it.
|
|
28
|
+
*/
|
|
29
|
+
run(): Promise<Builder.Report>;
|
|
30
|
+
/**
|
|
31
|
+
* Walks the pipeline in order, running each step until one of them stops it.
|
|
32
|
+
* @returns A promise resolving with one report per step of the pipeline, the ones that ran and the
|
|
33
|
+
* ones the stop left behind.
|
|
34
|
+
*/
|
|
35
|
+
protected walk(): Promise<Builder.StepReport[]>;
|
|
36
|
+
/**
|
|
37
|
+
* Runs one step of the pipeline and turns it into its report.
|
|
38
|
+
* @param index - The position of the step inside the pipeline, starting at 1.
|
|
39
|
+
* @param entry - The entry of the pipeline describing the step.
|
|
40
|
+
* @returns A promise resolving with the report of the step.
|
|
41
|
+
*
|
|
42
|
+
* @remarks An entry that carries no step at all is reported as one that did not run, the way the
|
|
43
|
+
* pipeline has always walked over it, and the report accounts for it all the same.
|
|
44
|
+
*/
|
|
45
|
+
protected runStep(index: number, entry: schema.BuilderEntry): Promise<Builder.StepReport>;
|
|
46
|
+
/**
|
|
47
|
+
* Runs the commands of a `run` step as a Task, settling the step with the verdict of the task.
|
|
48
|
+
* @param index - The position of the step inside the pipeline, starting at 1.
|
|
49
|
+
* @param entry - The `run` entry describing the step.
|
|
50
|
+
* @returns A promise resolving with the report of the step.
|
|
51
|
+
*
|
|
52
|
+
* @remarks A step that does not tolerate its failures stops the pipeline, so the steps after it are
|
|
53
|
+
* reported as steps that never ran. The step itself is not rethrown from here: the report of the run
|
|
54
|
+
* is the verdict of the pipeline, and rejecting twice would report the same failure twice.
|
|
55
|
+
*/
|
|
56
|
+
protected runRun(index: number, entry: schema.RunEntry): Promise<Builder.StepReport>;
|
|
57
|
+
/**
|
|
58
|
+
* How many steps of the pipeline run commands, the number a command identity is qualified by.
|
|
59
|
+
* @returns The amount of `run` entries in the pipeline.
|
|
60
|
+
*/
|
|
61
|
+
protected get steps(): number;
|
|
62
|
+
/**
|
|
63
|
+
* Runs the shell commands of a build step as a Task, reporting them through the policy of the run.
|
|
64
|
+
* The verdict of the task is the verdict of the step: it is awaited, never read from an event.
|
|
65
|
+
* @param commands - The shell commands to run, in order.
|
|
66
|
+
* @param cwd - The working directory the commands run in.
|
|
67
|
+
* @param options - The options of the step: its deadline and whether its failures are tolerated.
|
|
68
|
+
* @param identity - The identity the commands of this task are reported under.
|
|
69
|
+
* @returns A promise resolving with the summary of the run of the task.
|
|
70
|
+
* @throws { Task.Failure } When the step fails and does not tolerate failures.
|
|
71
|
+
*/
|
|
72
|
+
protected runTask(commands: string[], cwd: string, options: Task.Options, identity: OutputPolicy.Identity): Promise<Task.FinishData>;
|
|
73
|
+
/**
|
|
74
|
+
* Applies a global transformation to every file of the working directory, the one the configuration
|
|
75
|
+
* asks for through its glob.
|
|
76
|
+
* @param options - The transformation to apply, either as a search and replace pair or as a plain
|
|
77
|
+
* regular expression.
|
|
78
|
+
* @returns A promise resolving when every file of the glob has been transformed.
|
|
79
|
+
*/
|
|
80
|
+
protected runGlobalTransform(options: schema.GlobalTransform): Promise<void>;
|
|
81
|
+
/**
|
|
82
|
+
* Resolves the destination folder of an extraction entry, which is relative to the project root, and
|
|
83
|
+
* guarantees it stays inside it.
|
|
84
|
+
* @param to - The destination folder declared by the entry.
|
|
85
|
+
* @returns The normalized path of the destination folder.
|
|
86
|
+
* @throws { Error } When a relative destination escapes the project root.
|
|
87
|
+
*
|
|
88
|
+
* @remarks An absolute destination is taken as it stands: writing outside the project is a decision
|
|
89
|
+
* the configuration takes on purpose, and spelling it out as an absolute path is what makes it one.
|
|
90
|
+
* A relative destination cannot, because a `..` that walks out of the project is a mistake and not a
|
|
91
|
+
* decision. The project root is a destination like any other, so it is not treated as an escape.
|
|
92
|
+
*/
|
|
93
|
+
protected resolveDestination(to: string): string;
|
|
94
|
+
/**
|
|
95
|
+
* Runs the extraction process based on the provided entry configuration, which can be either a string or an array of extraction entries.
|
|
96
|
+
* It handles the copying of files from the specified source to the target location, applying any necessary path transformations based on the provided replacer configuration.
|
|
97
|
+
* This method is essential for managing the file extraction phase of the build process, allowing for flexible and customizable file handling as defined in the builder configuration.
|
|
98
|
+
* @param entry - A string or an array of extraction entries that define the source, target, and optional path transformation for the extraction process.
|
|
99
|
+
* @returns A promise that resolves when the extraction process is complete, or rejects with an error if any issues occur during the file copying or transformation.
|
|
100
|
+
* @throws Will throw an error if any issues occur during the file copying process, such as problems with reading or writing files, or if the provided entry configuration is invalid.
|
|
101
|
+
*/
|
|
102
|
+
protected runExtractor(entry: string | schema.Extractor): Promise<void>;
|
|
103
|
+
/**
|
|
104
|
+
* Creates a string replacement function based on the provided replacer configuration, which can be either a simple string or an object defining a search pattern and replacement string. This method is used to generate a function that can be applied to strings for dynamic transformations during the build process, allowing for flexible and customizable string manipulation as defined in the builder configuration.
|
|
105
|
+
* @param replacer - A configuration for string replacement, which can be either a string (used as a regex pattern) or an object containing a search pattern and replacement string.
|
|
106
|
+
* @returns A function that takes a string as input and returns a new string with the specified replacements applied, based on the provided replacer configuration.
|
|
107
|
+
* @throws Will throw an error if the provided replacer configuration is invalid, such as if it is neither a string nor an object with the required properties.
|
|
108
|
+
*/
|
|
109
|
+
protected static replacer(replacer?: schema.Transform): Builder.Transformer;
|
|
110
|
+
/**
|
|
111
|
+
* The kind of step an entry of the pipeline describes, decided by the key it carries.
|
|
112
|
+
* @param entry - The entry of the pipeline.
|
|
113
|
+
* @returns The kind of the step, or `null` when the entry describes no step at all.
|
|
114
|
+
*/
|
|
115
|
+
protected static kind(entry: schema.BuilderEntry): Builder.Kind | null;
|
|
116
|
+
/**
|
|
117
|
+
* The identity the commands of a `run` step are reported under.
|
|
118
|
+
*
|
|
119
|
+
* @remarks A command is identified as `C<M>` inside its own task, and as `S<N> · C<M>` when the
|
|
120
|
+
* pipeline runs more than one of them: with a single `run` step there is nothing to qualify it
|
|
121
|
+
* against, and with more than one there is, so `C1` alone would name two different commands.
|
|
122
|
+
*
|
|
123
|
+
* @param index - The position of the step inside the pipeline, starting at 1.
|
|
124
|
+
* @param steps - How many `run` steps the pipeline has.
|
|
125
|
+
* @returns The identity of the commands of the step.
|
|
126
|
+
*/
|
|
127
|
+
protected static identity(index: number, steps: number): OutputPolicy.Identity;
|
|
128
|
+
/**
|
|
129
|
+
* The report of a command inside a step: who it was and how it ended, without what it printed.
|
|
130
|
+
*
|
|
131
|
+
* @remarks The output of a command is not part of the report: a report says what the pipeline did,
|
|
132
|
+
* and a build that printed a log would carry that log in every summary it produced, in every layer
|
|
133
|
+
* that received it. The output stays where it was produced, in the results of the task, and it is
|
|
134
|
+
* read once, when the task settles.
|
|
135
|
+
*
|
|
136
|
+
* @param result - The result of the command.
|
|
137
|
+
* @returns The report of the command.
|
|
138
|
+
*/
|
|
139
|
+
protected static commandReport(result: Task.commandResult): Builder.CommandReport;
|
|
140
|
+
/**
|
|
141
|
+
* The report of a step that ran and had nothing to report beyond having run.
|
|
142
|
+
* @param index - The position of the step inside the pipeline, starting at 1.
|
|
143
|
+
* @param kind - The kind of the step, which a step that only works on files never has.
|
|
144
|
+
* @returns The report of the step.
|
|
145
|
+
*/
|
|
146
|
+
protected static settled(index: number, kind: Builder.Kind): Builder.StepReport;
|
|
147
|
+
/**
|
|
148
|
+
* The report of a step that never ran, either because an earlier one stopped the pipeline or because
|
|
149
|
+
* the entry describes no step at all.
|
|
150
|
+
* @param index - The position of the step inside the pipeline, starting at 1.
|
|
151
|
+
* @param kind - The kind of the step, `null` when the entry describes none.
|
|
152
|
+
* @returns The report of the step that did not run.
|
|
153
|
+
*/
|
|
154
|
+
protected static pending(index: number, kind: Builder.Kind | null): Builder.StepReport;
|
|
155
|
+
/**
|
|
156
|
+
* The report of a whole pipeline.
|
|
157
|
+
* @param steps - One report per step of the pipeline, in order.
|
|
158
|
+
* @returns The report of the run.
|
|
159
|
+
*/
|
|
160
|
+
protected static report(steps: Builder.StepReport[]): Builder.Report;
|
|
161
|
+
/**
|
|
162
|
+
* Why the pipeline stopped, read from the step that stopped it: the reason the step could not
|
|
163
|
+
* complete, or the commands that failed and it did not tolerate.
|
|
164
|
+
* @param report - The report of the run.
|
|
165
|
+
* @returns The reason the run did not finish successfully.
|
|
166
|
+
*/
|
|
167
|
+
protected static reason(report: Builder.Report): Builder.Reason;
|
|
168
|
+
}
|
|
169
|
+
export declare namespace Builder {
|
|
170
|
+
type Transformer = (str: string) => string;
|
|
171
|
+
type Kind = 'run' | 'extract' | 'transform';
|
|
172
|
+
type Entry = schema.RunEntry | schema.ExtractEntry | schema.TransformEntry;
|
|
173
|
+
type Reason = Task.Reason | 'command' | 'step';
|
|
174
|
+
interface Info {
|
|
175
|
+
pipeline: schema.Builder;
|
|
176
|
+
/**
|
|
177
|
+
* The folder the pipeline reads and works in: the checkout of a dependency, or the project itself
|
|
178
|
+
* for a `dep run` action. It is the base of every glob, of every `run` step and of every `map`.
|
|
179
|
+
*/
|
|
180
|
+
cwd: string;
|
|
181
|
+
/**
|
|
182
|
+
* The root of the project the pipeline deposits into. It is the base of every relative `to` of an
|
|
183
|
+
* extraction step, which is where the artifacts of a dependency are meant to land.
|
|
184
|
+
*/
|
|
185
|
+
projectRoot: string;
|
|
186
|
+
logger?: Logger | null;
|
|
187
|
+
/**
|
|
188
|
+
* The policy the output of the commands of the pipeline is reported through. Without it the
|
|
189
|
+
* builder falls back to the logger it was given, which streams the live output and says nothing
|
|
190
|
+
* else about it.
|
|
191
|
+
*/
|
|
192
|
+
policy?: OutputPolicy | null;
|
|
193
|
+
}
|
|
194
|
+
/** What one command of a `run` step did, without what it printed. */
|
|
195
|
+
interface CommandReport {
|
|
196
|
+
/** The position of the command inside its step, starting at 1. */
|
|
197
|
+
ordinal: number;
|
|
198
|
+
/** The human readable form of the command, whole, as it was given. */
|
|
199
|
+
label: string;
|
|
200
|
+
/** Whether the command ended as it was asked to. */
|
|
201
|
+
success: boolean;
|
|
202
|
+
/** The exit status, or `null` when there was none. */
|
|
203
|
+
code: number | null;
|
|
204
|
+
/** The signal that took the command down, or `null` when there was none. */
|
|
205
|
+
signal: NodeJS.Signals | null;
|
|
206
|
+
/** Why there is no status at all, or `null` when the command reported one. */
|
|
207
|
+
reason: Task.Reason | null;
|
|
208
|
+
/** The time the command took, in milliseconds. */
|
|
209
|
+
time: number;
|
|
210
|
+
}
|
|
211
|
+
/**
|
|
212
|
+
* What one step of the pipeline did.
|
|
213
|
+
*
|
|
214
|
+
* @remarks The kind of a step is `null` only for an entry that describes no step at all, which the
|
|
215
|
+
* pipeline walks over: the report accounts for every entry of the pipeline, and an entry that runs
|
|
216
|
+
* nothing has no kind to account for it with.
|
|
217
|
+
*/
|
|
218
|
+
interface StepReport {
|
|
219
|
+
/** The position of the step inside the pipeline, starting at 1. */
|
|
220
|
+
index: number;
|
|
221
|
+
/** The kind of the step, `null` when the entry describes none. */
|
|
222
|
+
kind: Kind | null;
|
|
223
|
+
/** Whether the step ran, `false` for the steps an earlier failure left behind. */
|
|
224
|
+
ran: boolean;
|
|
225
|
+
/** Whether the step ended as it was asked to. */
|
|
226
|
+
success: boolean;
|
|
227
|
+
/** How many commands of the step failed. */
|
|
228
|
+
failed: number;
|
|
229
|
+
/** Whether the step tolerated the failures it had, so the pipeline kept going. */
|
|
230
|
+
tolerated: boolean;
|
|
231
|
+
/** Why the step could not complete, or `null` when it completed. */
|
|
232
|
+
reason: Reason | null;
|
|
233
|
+
/** One report per command of a `run` step, empty for every other kind. */
|
|
234
|
+
commands: CommandReport[];
|
|
235
|
+
}
|
|
236
|
+
/** What a whole pipeline did. */
|
|
237
|
+
interface Report {
|
|
238
|
+
/** Whether every step of the pipeline ran and succeeded. */
|
|
239
|
+
ok: boolean;
|
|
240
|
+
/** How many commands ran, across every step. */
|
|
241
|
+
commands: number;
|
|
242
|
+
/** How many of them failed. */
|
|
243
|
+
failed: number;
|
|
244
|
+
/** How many of those failures a step tolerated, so the pipeline kept going. */
|
|
245
|
+
tolerated: number;
|
|
246
|
+
/** One report per step of the pipeline, in order, the ones that ran and the ones that did not. */
|
|
247
|
+
steps: StepReport[];
|
|
248
|
+
}
|
|
249
|
+
/** Why the pipeline stopped, and at which step, kept while the remaining steps are still being reported. */
|
|
250
|
+
interface Stopped {
|
|
251
|
+
index: number;
|
|
252
|
+
reason: Reason;
|
|
253
|
+
}
|
|
254
|
+
/**
|
|
255
|
+
* The failure of a pipeline, carrying the report of the run that produced it.
|
|
256
|
+
*
|
|
257
|
+
* @remarks The report is the same one the run would have resolved with, so a caller that only wants
|
|
258
|
+
* to know how the pipeline went catches this and reads it instead of rebuilding it from the steps
|
|
259
|
+
* that did run.
|
|
260
|
+
*/
|
|
261
|
+
class Failure extends Error {
|
|
262
|
+
readonly report: Report;
|
|
263
|
+
readonly reason: Reason;
|
|
264
|
+
/**
|
|
265
|
+
* Creates the failure of a pipeline.
|
|
266
|
+
* @param report - The report of the run.
|
|
267
|
+
* @param reason - Why the pipeline stopped.
|
|
268
|
+
*/
|
|
269
|
+
constructor(report: Report, reason?: Reason);
|
|
270
|
+
/**
|
|
271
|
+
* The message describing which step stopped the pipeline and why, in one line.
|
|
272
|
+
* @param report - The report of the run.
|
|
273
|
+
* @param reason - Why the pipeline stopped.
|
|
274
|
+
* @returns The message of the failure.
|
|
275
|
+
*/
|
|
276
|
+
private static message;
|
|
277
|
+
}
|
|
278
|
+
}
|
|
279
|
+
export default Builder;
|
|
@@ -0,0 +1,391 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @author NetFeez <netfeez.dev@gmail.com>.
|
|
3
|
+
* @description Runs dependency build pipelines: shell commands, file extraction and content transformations.
|
|
4
|
+
* @license Apache-2.0
|
|
5
|
+
*/
|
|
6
|
+
import { File, Path } from "@netfeez/common-node";
|
|
7
|
+
import Task from "../task/Task.js";
|
|
8
|
+
import Group from "../task/Group.js";
|
|
9
|
+
export class Builder {
|
|
10
|
+
pipeline;
|
|
11
|
+
cwd;
|
|
12
|
+
projectRoot;
|
|
13
|
+
logger;
|
|
14
|
+
policy;
|
|
15
|
+
/** The step that stopped the pipeline, or `null` while it is still walking it. */
|
|
16
|
+
vStopped = null;
|
|
17
|
+
constructor(info) {
|
|
18
|
+
this.pipeline = info.pipeline;
|
|
19
|
+
this.cwd = info.cwd;
|
|
20
|
+
this.projectRoot = info.projectRoot;
|
|
21
|
+
this.logger = info.logger || null;
|
|
22
|
+
this.policy = info.policy || null;
|
|
23
|
+
}
|
|
24
|
+
/**
|
|
25
|
+
* Runs the pipeline and reports what every step of it did.
|
|
26
|
+
* @returns A promise resolving with the report of the run, or rejecting with a
|
|
27
|
+
* {@link Builder.Failure} carrying that same report, when a step stopped the pipeline.
|
|
28
|
+
*
|
|
29
|
+
* @remarks The report is the verdict of the run, and it is produced whether the run succeeded or
|
|
30
|
+
* failed: a caller that catches a {@link Builder.Failure} gets the steps that ran before the one
|
|
31
|
+
* that stopped it, and the steps that never ran at all, so nothing about the pipeline is missing
|
|
32
|
+
* from a failure.
|
|
33
|
+
*
|
|
34
|
+
* @remarks The group of the run is closed in a `finally`, so the steps of a pipeline that threw leave
|
|
35
|
+
* the output as closed as the ones of a pipeline that finished. It is opened only when the run
|
|
36
|
+
* decorates its output, because a group that draws nothing still leaves a box and an indentation
|
|
37
|
+
* around every line written inside it.
|
|
38
|
+
*/
|
|
39
|
+
async run() {
|
|
40
|
+
const boxed = this.policy === null || this.policy.decoration === 'on';
|
|
41
|
+
if (boxed)
|
|
42
|
+
this.logger?.group(Group.create('#00FFB4'));
|
|
43
|
+
try {
|
|
44
|
+
const steps = await this.walk();
|
|
45
|
+
const report = Builder.report(steps);
|
|
46
|
+
this.policy?.settleBuild(report);
|
|
47
|
+
if (!report.ok)
|
|
48
|
+
throw new Builder.Failure(report, Builder.reason(report));
|
|
49
|
+
return report;
|
|
50
|
+
}
|
|
51
|
+
finally {
|
|
52
|
+
if (boxed)
|
|
53
|
+
this.logger?.end();
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
/**
|
|
57
|
+
* Walks the pipeline in order, running each step until one of them stops it.
|
|
58
|
+
* @returns A promise resolving with one report per step of the pipeline, the ones that ran and the
|
|
59
|
+
* ones the stop left behind.
|
|
60
|
+
*/
|
|
61
|
+
async walk() {
|
|
62
|
+
const steps = [];
|
|
63
|
+
for (const [position, entry] of this.pipeline.entries()) {
|
|
64
|
+
const index = position + 1;
|
|
65
|
+
if (this.vStopped)
|
|
66
|
+
steps.push(Builder.pending(index, Builder.kind(entry)));
|
|
67
|
+
else
|
|
68
|
+
steps.push(await this.runStep(index, entry));
|
|
69
|
+
}
|
|
70
|
+
return steps;
|
|
71
|
+
}
|
|
72
|
+
/**
|
|
73
|
+
* Runs one step of the pipeline and turns it into its report.
|
|
74
|
+
* @param index - The position of the step inside the pipeline, starting at 1.
|
|
75
|
+
* @param entry - The entry of the pipeline describing the step.
|
|
76
|
+
* @returns A promise resolving with the report of the step.
|
|
77
|
+
*
|
|
78
|
+
* @remarks An entry that carries no step at all is reported as one that did not run, the way the
|
|
79
|
+
* pipeline has always walked over it, and the report accounts for it all the same.
|
|
80
|
+
*/
|
|
81
|
+
async runStep(index, entry) {
|
|
82
|
+
if ('run' in entry && entry.run)
|
|
83
|
+
return this.runRun(index, entry);
|
|
84
|
+
if ('extract' in entry && entry.extract)
|
|
85
|
+
await this.runExtractor(entry.extract);
|
|
86
|
+
else if ('transform' in entry && entry.transform)
|
|
87
|
+
await this.runGlobalTransform(entry.transform);
|
|
88
|
+
else
|
|
89
|
+
return Builder.pending(index, null);
|
|
90
|
+
return Builder.settled(index, 'extract' in entry ? 'extract' : 'transform');
|
|
91
|
+
}
|
|
92
|
+
/**
|
|
93
|
+
* Runs the commands of a `run` step as a Task, settling the step with the verdict of the task.
|
|
94
|
+
* @param index - The position of the step inside the pipeline, starting at 1.
|
|
95
|
+
* @param entry - The `run` entry describing the step.
|
|
96
|
+
* @returns A promise resolving with the report of the step.
|
|
97
|
+
*
|
|
98
|
+
* @remarks A step that does not tolerate its failures stops the pipeline, so the steps after it are
|
|
99
|
+
* reported as steps that never ran. The step itself is not rethrown from here: the report of the run
|
|
100
|
+
* is the verdict of the pipeline, and rejecting twice would report the same failure twice.
|
|
101
|
+
*/
|
|
102
|
+
async runRun(index, entry) {
|
|
103
|
+
const commands = typeof entry.run === 'string' ? [entry.run] : entry.run;
|
|
104
|
+
const identity = Builder.identity(index, this.steps);
|
|
105
|
+
try {
|
|
106
|
+
const outcome = await this.runTask(commands, this.cwd, { maxTimeMs: entry.maxTimeMs, allowFails: entry.allowFails }, identity);
|
|
107
|
+
return {
|
|
108
|
+
index,
|
|
109
|
+
kind: 'run',
|
|
110
|
+
ran: true,
|
|
111
|
+
success: outcome.success,
|
|
112
|
+
failed: outcome.fails,
|
|
113
|
+
tolerated: entry.allowFails === true && outcome.fails > 0,
|
|
114
|
+
reason: null,
|
|
115
|
+
commands: outcome.results.map(Builder.commandReport)
|
|
116
|
+
};
|
|
117
|
+
}
|
|
118
|
+
catch (failure) {
|
|
119
|
+
const outcome = failure instanceof Task.Failure ? failure.outcome : null;
|
|
120
|
+
this.vStopped = { index, reason: failure instanceof Task.Failure ? failure.reason : 'step' };
|
|
121
|
+
if (failure instanceof Task.Failure)
|
|
122
|
+
this.policy?.stepFailed(identity, failure);
|
|
123
|
+
return {
|
|
124
|
+
index,
|
|
125
|
+
kind: 'run',
|
|
126
|
+
ran: true,
|
|
127
|
+
success: false,
|
|
128
|
+
failed: outcome?.fails ?? 0,
|
|
129
|
+
tolerated: false,
|
|
130
|
+
reason: this.vStopped.reason,
|
|
131
|
+
commands: (outcome?.results ?? []).map(Builder.commandReport)
|
|
132
|
+
};
|
|
133
|
+
}
|
|
134
|
+
}
|
|
135
|
+
/**
|
|
136
|
+
* How many steps of the pipeline run commands, the number a command identity is qualified by.
|
|
137
|
+
* @returns The amount of `run` entries in the pipeline.
|
|
138
|
+
*/
|
|
139
|
+
get steps() {
|
|
140
|
+
return this.pipeline.filter(entry => Builder.kind(entry) === 'run').length;
|
|
141
|
+
}
|
|
142
|
+
/**
|
|
143
|
+
* Runs the shell commands of a build step as a Task, reporting them through the policy of the run.
|
|
144
|
+
* The verdict of the task is the verdict of the step: it is awaited, never read from an event.
|
|
145
|
+
* @param commands - The shell commands to run, in order.
|
|
146
|
+
* @param cwd - The working directory the commands run in.
|
|
147
|
+
* @param options - The options of the step: its deadline and whether its failures are tolerated.
|
|
148
|
+
* @param identity - The identity the commands of this task are reported under.
|
|
149
|
+
* @returns A promise resolving with the summary of the run of the task.
|
|
150
|
+
* @throws { Task.Failure } When the step fails and does not tolerate failures.
|
|
151
|
+
*/
|
|
152
|
+
async runTask(commands, cwd, options, identity) {
|
|
153
|
+
const task = new Task(cwd, commands, options);
|
|
154
|
+
if (this.policy) {
|
|
155
|
+
task.on('line', (line, ordinal) => this.policy?.live(identity, line, ordinal, 'stdout'));
|
|
156
|
+
task.on('err', (line, ordinal) => this.policy?.live(identity, line, ordinal, 'stderr'));
|
|
157
|
+
task.once('finish', (data) => this.policy?.settleTask(identity, data));
|
|
158
|
+
}
|
|
159
|
+
else if (this.logger) {
|
|
160
|
+
task.on('line', (line) => this.logger?.info(`${line}`));
|
|
161
|
+
task.on('err', (line) => this.logger?.error(`${line}`));
|
|
162
|
+
}
|
|
163
|
+
return task.run();
|
|
164
|
+
}
|
|
165
|
+
/**
|
|
166
|
+
* Applies a global transformation to every file of the working directory, the one the configuration
|
|
167
|
+
* asks for through its glob.
|
|
168
|
+
* @param options - The transformation to apply, either as a search and replace pair or as a plain
|
|
169
|
+
* regular expression.
|
|
170
|
+
* @returns A promise resolving when every file of the glob has been transformed.
|
|
171
|
+
*/
|
|
172
|
+
async runGlobalTransform(options) {
|
|
173
|
+
this.logger?.log(`&C7Applying global transformation in &C3${this.cwd}&C7...`);
|
|
174
|
+
const transformAction = Builder.replacer(options);
|
|
175
|
+
const glob = options.glob;
|
|
176
|
+
await File.smartProcess(glob, this.cwd, {
|
|
177
|
+
cwd: this.cwd,
|
|
178
|
+
}, async ({ src, dest }) => {
|
|
179
|
+
const content = await File.read(src);
|
|
180
|
+
const transformedContent = transformAction(content);
|
|
181
|
+
await File.write(dest, transformedContent);
|
|
182
|
+
});
|
|
183
|
+
}
|
|
184
|
+
/**
|
|
185
|
+
* Resolves the destination folder of an extraction entry, which is relative to the project root, and
|
|
186
|
+
* guarantees it stays inside it.
|
|
187
|
+
* @param to - The destination folder declared by the entry.
|
|
188
|
+
* @returns The normalized path of the destination folder.
|
|
189
|
+
* @throws { Error } When a relative destination escapes the project root.
|
|
190
|
+
*
|
|
191
|
+
* @remarks An absolute destination is taken as it stands: writing outside the project is a decision
|
|
192
|
+
* the configuration takes on purpose, and spelling it out as an absolute path is what makes it one.
|
|
193
|
+
* A relative destination cannot, because a `..` that walks out of the project is a mistake and not a
|
|
194
|
+
* decision. The project root is a destination like any other, so it is not treated as an escape.
|
|
195
|
+
*/
|
|
196
|
+
resolveDestination(to) {
|
|
197
|
+
if (Path.isAbsolute(to))
|
|
198
|
+
return to;
|
|
199
|
+
const root = Path.normalize(this.projectRoot);
|
|
200
|
+
const destination = Path.normalize(Path.join(root, to));
|
|
201
|
+
if (destination !== root && !Path.isInside(root, destination))
|
|
202
|
+
throw new Error(`Extraction destination resolves outside of the project root: ${destination}`);
|
|
203
|
+
return destination;
|
|
204
|
+
}
|
|
205
|
+
/**
|
|
206
|
+
* Runs the extraction process based on the provided entry configuration, which can be either a string or an array of extraction entries.
|
|
207
|
+
* It handles the copying of files from the specified source to the target location, applying any necessary path transformations based on the provided replacer configuration.
|
|
208
|
+
* This method is essential for managing the file extraction phase of the build process, allowing for flexible and customizable file handling as defined in the builder configuration.
|
|
209
|
+
* @param entry - A string or an array of extraction entries that define the source, target, and optional path transformation for the extraction process.
|
|
210
|
+
* @returns A promise that resolves when the extraction process is complete, or rejects with an error if any issues occur during the file copying or transformation.
|
|
211
|
+
* @throws Will throw an error if any issues occur during the file copying process, such as problems with reading or writing files, or if the provided entry configuration is invalid.
|
|
212
|
+
*/
|
|
213
|
+
async runExtractor(entry) {
|
|
214
|
+
const extractor = typeof entry === 'string'
|
|
215
|
+
? { '**/*': { to: entry } }
|
|
216
|
+
: entry;
|
|
217
|
+
for (const [from, entry] of Object.entries(extractor)) {
|
|
218
|
+
const { to, map, transform } = typeof entry === 'string' ? { to: entry } : entry;
|
|
219
|
+
const mapper = Builder.replacer(map);
|
|
220
|
+
const replacer = Builder.replacer(transform);
|
|
221
|
+
const destination = this.resolveDestination(to);
|
|
222
|
+
this.logger?.log(`&C7Extracting &C3${from}&C7 to &C3${to}&C7...`);
|
|
223
|
+
await File.smartProcess(from, destination, {
|
|
224
|
+
cwd: this.cwd,
|
|
225
|
+
map: mapper,
|
|
226
|
+
}, async ({ src, dest }) => {
|
|
227
|
+
const content = await File.read(src);
|
|
228
|
+
const transformedContent = replacer(content);
|
|
229
|
+
await File.write(dest, transformedContent);
|
|
230
|
+
});
|
|
231
|
+
}
|
|
232
|
+
}
|
|
233
|
+
/**
|
|
234
|
+
* Creates a string replacement function based on the provided replacer configuration, which can be either a simple string or an object defining a search pattern and replacement string. This method is used to generate a function that can be applied to strings for dynamic transformations during the build process, allowing for flexible and customizable string manipulation as defined in the builder configuration.
|
|
235
|
+
* @param replacer - A configuration for string replacement, which can be either a string (used as a regex pattern) or an object containing a search pattern and replacement string.
|
|
236
|
+
* @returns A function that takes a string as input and returns a new string with the specified replacements applied, based on the provided replacer configuration.
|
|
237
|
+
* @throws Will throw an error if the provided replacer configuration is invalid, such as if it is neither a string nor an object with the required properties.
|
|
238
|
+
*/
|
|
239
|
+
static replacer(replacer) {
|
|
240
|
+
if (typeof replacer === 'string') {
|
|
241
|
+
const search = new RegExp(replacer, 'g');
|
|
242
|
+
return (str) => str.replace(search, '');
|
|
243
|
+
}
|
|
244
|
+
else if (replacer !== null && typeof replacer === 'object') {
|
|
245
|
+
const flags = replacer.flags || 'g';
|
|
246
|
+
const search = new RegExp(replacer.search, flags);
|
|
247
|
+
const replace = replacer.replace || '';
|
|
248
|
+
return (str) => str.replace(search, replace);
|
|
249
|
+
}
|
|
250
|
+
else
|
|
251
|
+
return (str) => str;
|
|
252
|
+
}
|
|
253
|
+
/**
|
|
254
|
+
* The kind of step an entry of the pipeline describes, decided by the key it carries.
|
|
255
|
+
* @param entry - The entry of the pipeline.
|
|
256
|
+
* @returns The kind of the step, or `null` when the entry describes no step at all.
|
|
257
|
+
*/
|
|
258
|
+
static kind(entry) {
|
|
259
|
+
if ('run' in entry && entry.run)
|
|
260
|
+
return 'run';
|
|
261
|
+
if ('extract' in entry && entry.extract)
|
|
262
|
+
return 'extract';
|
|
263
|
+
if ('transform' in entry && entry.transform)
|
|
264
|
+
return 'transform';
|
|
265
|
+
return null;
|
|
266
|
+
}
|
|
267
|
+
/**
|
|
268
|
+
* The identity the commands of a `run` step are reported under.
|
|
269
|
+
*
|
|
270
|
+
* @remarks A command is identified as `C<M>` inside its own task, and as `S<N> · C<M>` when the
|
|
271
|
+
* pipeline runs more than one of them: with a single `run` step there is nothing to qualify it
|
|
272
|
+
* against, and with more than one there is, so `C1` alone would name two different commands.
|
|
273
|
+
*
|
|
274
|
+
* @param index - The position of the step inside the pipeline, starting at 1.
|
|
275
|
+
* @param steps - How many `run` steps the pipeline has.
|
|
276
|
+
* @returns The identity of the commands of the step.
|
|
277
|
+
*/
|
|
278
|
+
static identity(index, steps) {
|
|
279
|
+
return { step: steps > 1 ? index : null };
|
|
280
|
+
}
|
|
281
|
+
/**
|
|
282
|
+
* The report of a command inside a step: who it was and how it ended, without what it printed.
|
|
283
|
+
*
|
|
284
|
+
* @remarks The output of a command is not part of the report: a report says what the pipeline did,
|
|
285
|
+
* and a build that printed a log would carry that log in every summary it produced, in every layer
|
|
286
|
+
* that received it. The output stays where it was produced, in the results of the task, and it is
|
|
287
|
+
* read once, when the task settles.
|
|
288
|
+
*
|
|
289
|
+
* @param result - The result of the command.
|
|
290
|
+
* @returns The report of the command.
|
|
291
|
+
*/
|
|
292
|
+
static commandReport(result) {
|
|
293
|
+
return {
|
|
294
|
+
ordinal: result.ordinal,
|
|
295
|
+
label: result.label,
|
|
296
|
+
success: result.success,
|
|
297
|
+
code: result.code,
|
|
298
|
+
signal: result.signal,
|
|
299
|
+
reason: result.reason,
|
|
300
|
+
time: result.time
|
|
301
|
+
};
|
|
302
|
+
}
|
|
303
|
+
/**
|
|
304
|
+
* The report of a step that ran and had nothing to report beyond having run.
|
|
305
|
+
* @param index - The position of the step inside the pipeline, starting at 1.
|
|
306
|
+
* @param kind - The kind of the step, which a step that only works on files never has.
|
|
307
|
+
* @returns The report of the step.
|
|
308
|
+
*/
|
|
309
|
+
static settled(index, kind) {
|
|
310
|
+
return { index, kind, ran: true, success: true, failed: 0, tolerated: false, reason: null, commands: [] };
|
|
311
|
+
}
|
|
312
|
+
/**
|
|
313
|
+
* The report of a step that never ran, either because an earlier one stopped the pipeline or because
|
|
314
|
+
* the entry describes no step at all.
|
|
315
|
+
* @param index - The position of the step inside the pipeline, starting at 1.
|
|
316
|
+
* @param kind - The kind of the step, `null` when the entry describes none.
|
|
317
|
+
* @returns The report of the step that did not run.
|
|
318
|
+
*/
|
|
319
|
+
static pending(index, kind) {
|
|
320
|
+
return { index, kind, ran: false, success: false, failed: 0, tolerated: false, reason: null, commands: [] };
|
|
321
|
+
}
|
|
322
|
+
/**
|
|
323
|
+
* The report of a whole pipeline.
|
|
324
|
+
* @param steps - One report per step of the pipeline, in order.
|
|
325
|
+
* @returns The report of the run.
|
|
326
|
+
*/
|
|
327
|
+
static report(steps) {
|
|
328
|
+
const ran = steps.filter(step => step.ran);
|
|
329
|
+
return {
|
|
330
|
+
ok: ran.length === steps.length && ran.every(step => step.success),
|
|
331
|
+
commands: ran.reduce((total, step) => total + step.commands.length, 0),
|
|
332
|
+
failed: ran.reduce((total, step) => total + step.failed, 0),
|
|
333
|
+
tolerated: ran.filter(step => step.tolerated).reduce((total, step) => total + step.failed, 0),
|
|
334
|
+
steps
|
|
335
|
+
};
|
|
336
|
+
}
|
|
337
|
+
/**
|
|
338
|
+
* Why the pipeline stopped, read from the step that stopped it: the reason the step could not
|
|
339
|
+
* complete, or the commands that failed and it did not tolerate.
|
|
340
|
+
* @param report - The report of the run.
|
|
341
|
+
* @returns The reason the run did not finish successfully.
|
|
342
|
+
*/
|
|
343
|
+
static reason(report) {
|
|
344
|
+
const step = report.steps.find(entry => entry.ran && !entry.success);
|
|
345
|
+
if (!step)
|
|
346
|
+
return 'step';
|
|
347
|
+
if (step.reason !== null)
|
|
348
|
+
return step.reason;
|
|
349
|
+
return step.failed > 0 ? 'command' : 'step';
|
|
350
|
+
}
|
|
351
|
+
}
|
|
352
|
+
(function (Builder) {
|
|
353
|
+
/**
|
|
354
|
+
* The failure of a pipeline, carrying the report of the run that produced it.
|
|
355
|
+
*
|
|
356
|
+
* @remarks The report is the same one the run would have resolved with, so a caller that only wants
|
|
357
|
+
* to know how the pipeline went catches this and reads it instead of rebuilding it from the steps
|
|
358
|
+
* that did run.
|
|
359
|
+
*/
|
|
360
|
+
class Failure extends Error {
|
|
361
|
+
report;
|
|
362
|
+
reason;
|
|
363
|
+
/**
|
|
364
|
+
* Creates the failure of a pipeline.
|
|
365
|
+
* @param report - The report of the run.
|
|
366
|
+
* @param reason - Why the pipeline stopped.
|
|
367
|
+
*/
|
|
368
|
+
constructor(report, reason = 'step') {
|
|
369
|
+
super(Failure.message(report, reason));
|
|
370
|
+
this.report = report;
|
|
371
|
+
this.reason = reason;
|
|
372
|
+
this.name = 'Builder.Failure';
|
|
373
|
+
}
|
|
374
|
+
/**
|
|
375
|
+
* The message describing which step stopped the pipeline and why, in one line.
|
|
376
|
+
* @param report - The report of the run.
|
|
377
|
+
* @param reason - Why the pipeline stopped.
|
|
378
|
+
* @returns The message of the failure.
|
|
379
|
+
*/
|
|
380
|
+
static message(report, reason) {
|
|
381
|
+
const step = report.steps.find(entry => entry.ran && !entry.success);
|
|
382
|
+
const where = step ? `Step ${step.index}` : 'The pipeline';
|
|
383
|
+
const because = reason === 'command' ? `${report.failed} failed command(s)`
|
|
384
|
+
: reason === 'step' ? 'a step of it failed' : reason;
|
|
385
|
+
return `${where} stopped the pipeline: ${because}.`;
|
|
386
|
+
}
|
|
387
|
+
}
|
|
388
|
+
Builder.Failure = Failure;
|
|
389
|
+
})(Builder || (Builder = {}));
|
|
390
|
+
export default Builder;
|
|
391
|
+
//# sourceMappingURL=Builder.js.map
|