@dbx-tools/projen 0.6.153 → 0.6.160
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 +53 -29
- package/index.ts +2 -0
- package/package.json +4 -4
- package/src/pnpm-workspace.ts +8 -1
- package/src/project-js.ts +64 -5
- package/src/project-py.ts +221 -42
- package/src/project-rs.ts +382 -92
- package/src/release-dispatch.ts +36 -0
- package/src/release.ts +187 -17
- package/tasks/bump.ts +21 -3
- package/tasks/uniffi-release.mjs +14 -2
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
/** Shared release events and immutable source verification. */
|
|
2
|
+
export const DOWNSTREAM_RELEASE_EVENT = "release";
|
|
3
|
+
export const RUST_RELEASE_EVENT = "rust-release";
|
|
4
|
+
export const RELEASE_TAG =
|
|
5
|
+
"${{ github.event_name == 'repository_dispatch' && github.event.client_payload.release_tag || inputs.release_tag }}";
|
|
6
|
+
export const RELEASE_SHA =
|
|
7
|
+
"${{ github.event_name == 'repository_dispatch' && github.event.client_payload.expected_sha || inputs.expected_sha }}";
|
|
8
|
+
|
|
9
|
+
/** Check out and verify the exact commit carried by a release event. */
|
|
10
|
+
export function releaseSourceSteps(): readonly Record<string, unknown>[] {
|
|
11
|
+
return [
|
|
12
|
+
{
|
|
13
|
+
name: "Checkout release commit",
|
|
14
|
+
uses: "actions/checkout@v6",
|
|
15
|
+
with: {
|
|
16
|
+
ref: RELEASE_SHA,
|
|
17
|
+
"fetch-depth": 1,
|
|
18
|
+
},
|
|
19
|
+
},
|
|
20
|
+
{
|
|
21
|
+
name: "Verify release source",
|
|
22
|
+
shell: "bash",
|
|
23
|
+
env: {
|
|
24
|
+
RELEASE_TAG,
|
|
25
|
+
EXPECTED_SHA: RELEASE_SHA,
|
|
26
|
+
},
|
|
27
|
+
run: [
|
|
28
|
+
'test -n "$RELEASE_TAG"',
|
|
29
|
+
'test -n "$EXPECTED_SHA"',
|
|
30
|
+
'git fetch --force origin "+refs/tags/$RELEASE_TAG:refs/tags/$RELEASE_TAG"',
|
|
31
|
+
'test "$(git rev-parse "$RELEASE_TAG^{commit}")" = "$EXPECTED_SHA"',
|
|
32
|
+
'test "$(git rev-parse HEAD)" = "$EXPECTED_SHA"',
|
|
33
|
+
].join("\n"),
|
|
34
|
+
},
|
|
35
|
+
];
|
|
36
|
+
}
|
package/src/release.ts
CHANGED
|
@@ -4,10 +4,20 @@
|
|
|
4
4
|
* the project has a GitHub component - authors the tag-driven npm publish
|
|
5
5
|
* workflow that the pushed tag triggers.
|
|
6
6
|
*/
|
|
7
|
-
import {
|
|
7
|
+
import { rmSync } from "node:fs";
|
|
8
|
+
import { resolve } from "node:path";
|
|
9
|
+
import { Component, YamlFile } from "projen";
|
|
8
10
|
import { GithubWorkflow } from "projen/lib/github";
|
|
9
11
|
import { JobPermission, type JobStep } from "projen/lib/github/workflows-model";
|
|
12
|
+
import { object } from "@dbx-tools/shared-core";
|
|
10
13
|
import { applyTasks, taskScript, type DBXToolsNodeProject } from "./project.ts";
|
|
14
|
+
import {
|
|
15
|
+
DOWNSTREAM_RELEASE_EVENT,
|
|
16
|
+
RELEASE_SHA,
|
|
17
|
+
RELEASE_TAG,
|
|
18
|
+
RUST_RELEASE_EVENT,
|
|
19
|
+
releaseSourceSteps,
|
|
20
|
+
} from "./release-dispatch.ts";
|
|
11
21
|
|
|
12
22
|
const NODE_VERSION = "lts/*";
|
|
13
23
|
const NPM_REGISTRY_URL = "https://registry.npmjs.org";
|
|
@@ -168,10 +178,27 @@ export class DBXToolsRelease extends Component {
|
|
|
168
178
|
this.standaloneReleases = options.standaloneReleases ?? [];
|
|
169
179
|
this.upstreamWorkflow = options.upstreamWorkflow;
|
|
170
180
|
this.workflowName = options.workflowName ?? "node-release";
|
|
181
|
+
if (project.github) this.authorReleaseDispatcher(project);
|
|
171
182
|
}
|
|
172
183
|
|
|
173
184
|
public override preSynthesize(): void {
|
|
174
185
|
const project = this.project as DBXToolsNodeProject;
|
|
186
|
+
const rust = project.dbxToolsConfig.rust;
|
|
187
|
+
if (
|
|
188
|
+
!this.workflowName &&
|
|
189
|
+
this.standaloneReleases.length === 0 &&
|
|
190
|
+
typeof project.dbxToolsConfig.pythonReleaseWorkflow !== "string" &&
|
|
191
|
+
!(object.isRecord(rust) && typeof rust.releaseWorkflow === "string")
|
|
192
|
+
) {
|
|
193
|
+
project.tryRemoveFile(".github/workflows/release-dispatch.yml");
|
|
194
|
+
}
|
|
195
|
+
const releaseEvent =
|
|
196
|
+
object.isRecord(rust) && typeof rust.releaseWorkflow === "string"
|
|
197
|
+
? RUST_RELEASE_EVENT
|
|
198
|
+
: DOWNSTREAM_RELEASE_EVENT;
|
|
199
|
+
project
|
|
200
|
+
.tryFindObjectFile(".github/workflows/release-dispatch.yml")
|
|
201
|
+
?.addOverride("jobs.dispatch.steps.1.env.RELEASE_EVENT", releaseEvent);
|
|
175
202
|
// Release the standalone projects in the SAME run, at the same version. They
|
|
176
203
|
// are not workspace members, so nothing else would ever bring them along.
|
|
177
204
|
const siblingArgs = this.standaloneReleases
|
|
@@ -195,11 +222,60 @@ export class DBXToolsRelease extends Component {
|
|
|
195
222
|
}
|
|
196
223
|
}
|
|
197
224
|
|
|
225
|
+
public override postSynthesize(): void {
|
|
226
|
+
rmSync(resolve(this.project.outdir, ".github/workflows/rust-release-dispatch.yml"), {
|
|
227
|
+
force: true,
|
|
228
|
+
});
|
|
229
|
+
}
|
|
230
|
+
|
|
231
|
+
private authorReleaseDispatcher(project: DBXToolsNodeProject): void {
|
|
232
|
+
new YamlFile(project, ".github/workflows/release-dispatch.yml", {
|
|
233
|
+
obj: {
|
|
234
|
+
name: "release-dispatch",
|
|
235
|
+
on: { push: { tags: [`${this.tagPrefix}*`] } },
|
|
236
|
+
permissions: { contents: "write" },
|
|
237
|
+
jobs: {
|
|
238
|
+
dispatch: {
|
|
239
|
+
"runs-on": "ubuntu-latest",
|
|
240
|
+
steps: [
|
|
241
|
+
{
|
|
242
|
+
name: "Checkout release tag",
|
|
243
|
+
uses: "actions/checkout@v6",
|
|
244
|
+
with: { "fetch-depth": 1 },
|
|
245
|
+
},
|
|
246
|
+
{
|
|
247
|
+
name: "Dispatch release",
|
|
248
|
+
shell: "bash",
|
|
249
|
+
env: {
|
|
250
|
+
GH_TOKEN: "${{ github.token }}",
|
|
251
|
+
RELEASE_TAG: "${{ github.ref_name }}",
|
|
252
|
+
RELEASE_EVENT: DOWNSTREAM_RELEASE_EVENT,
|
|
253
|
+
},
|
|
254
|
+
run: [
|
|
255
|
+
`case "$RELEASE_TAG" in ${this.tagPrefix}*) ;; *) exit 1 ;; esac`,
|
|
256
|
+
'EXPECTED_SHA="$(git rev-parse "$RELEASE_TAG^{commit}")"',
|
|
257
|
+
[
|
|
258
|
+
'gh api --method POST "repos/$GITHUB_REPOSITORY/dispatches"',
|
|
259
|
+
'--raw-field event_type="$RELEASE_EVENT"',
|
|
260
|
+
'--raw-field "client_payload[release_tag]=$RELEASE_TAG"',
|
|
261
|
+
'--raw-field "client_payload[expected_sha]=$EXPECTED_SHA"',
|
|
262
|
+
].join(" \\\n "),
|
|
263
|
+
].join("\n"),
|
|
264
|
+
},
|
|
265
|
+
],
|
|
266
|
+
},
|
|
267
|
+
},
|
|
268
|
+
},
|
|
269
|
+
});
|
|
270
|
+
}
|
|
271
|
+
|
|
198
272
|
/** Author the common tag trigger, concurrency policy, permissions, and publish job. */
|
|
199
273
|
private authorPublishWorkflow(
|
|
200
274
|
project: DBXToolsNodeProject,
|
|
201
275
|
{ name, tagPrefix, steps, workingDirectory, upstreamWorkflow }: PublishWorkflow,
|
|
202
276
|
): void {
|
|
277
|
+
const setupSteps = publishSetupSteps();
|
|
278
|
+
const branchDispatch = upstreamWorkflow === undefined;
|
|
203
279
|
const workflow = new GithubWorkflow(project.github!, name, {
|
|
204
280
|
// Serialize publishes so two tags landing together cannot race to the
|
|
205
281
|
// registry, but never cancel a run already in flight: a half-published
|
|
@@ -209,14 +285,19 @@ export class DBXToolsRelease extends Component {
|
|
|
209
285
|
});
|
|
210
286
|
// Read-only floor for any job that does not declare its own permissions;
|
|
211
287
|
// the publish job below overrides it with the `id-token` it needs.
|
|
212
|
-
workflow.file?.addOverride("permissions", {
|
|
288
|
+
workflow.file?.addOverride("permissions", {
|
|
289
|
+
contents: "read",
|
|
290
|
+
...(upstreamWorkflow ? { actions: "read" } : {}),
|
|
291
|
+
});
|
|
213
292
|
if (upstreamWorkflow) {
|
|
214
293
|
workflow.file?.addOverride("on.workflow_run", {
|
|
215
294
|
workflows: [upstreamWorkflow],
|
|
216
295
|
types: ["completed"],
|
|
217
296
|
});
|
|
218
297
|
} else {
|
|
219
|
-
workflow.on
|
|
298
|
+
workflow.file?.addOverride("on.repository_dispatch", {
|
|
299
|
+
types: [DOWNSTREAM_RELEASE_EVENT],
|
|
300
|
+
});
|
|
220
301
|
}
|
|
221
302
|
// Manual trigger for testing the workflow WITHOUT reaching npm: a
|
|
222
303
|
// `workflow_dispatch` run has no tag, so the publish script forces
|
|
@@ -224,6 +305,20 @@ export class DBXToolsRelease extends Component {
|
|
|
224
305
|
// additionally lets a tag push be dry-run on demand.
|
|
225
306
|
workflow.file?.addOverride("on.workflow_dispatch", {
|
|
226
307
|
inputs: {
|
|
308
|
+
...(branchDispatch
|
|
309
|
+
? {
|
|
310
|
+
release_tag: {
|
|
311
|
+
description: "Release tag to package during a dry run",
|
|
312
|
+
type: "string",
|
|
313
|
+
required: true,
|
|
314
|
+
},
|
|
315
|
+
expected_sha: {
|
|
316
|
+
description: "Commit the release tag must reference",
|
|
317
|
+
type: "string",
|
|
318
|
+
required: true,
|
|
319
|
+
},
|
|
320
|
+
}
|
|
321
|
+
: {}),
|
|
227
322
|
dry_run: {
|
|
228
323
|
description: "Pack and validate but do not upload to npm",
|
|
229
324
|
type: "boolean",
|
|
@@ -234,12 +329,16 @@ export class DBXToolsRelease extends Component {
|
|
|
234
329
|
workflow.addJob("publish", {
|
|
235
330
|
...(upstreamWorkflow
|
|
236
331
|
? {
|
|
237
|
-
if: "${{ github.event_name != 'workflow_run' || (github.event.workflow_run.conclusion == 'success' && github.event.workflow_run.event == 'workflow_run') }}",
|
|
332
|
+
if: "${{ github.event_name != 'workflow_run' || (github.event.workflow_run.conclusion == 'success' && (github.event.workflow_run.event == 'workflow_run' || github.event.workflow_run.event == 'push' || github.event.workflow_run.event == 'repository_dispatch')) }}",
|
|
238
333
|
}
|
|
239
334
|
: {}),
|
|
240
335
|
runsOn: ["ubuntu-latest"],
|
|
241
336
|
// `id-token: write` lets npm mint the OIDC token for provenance attestation.
|
|
242
|
-
permissions: {
|
|
337
|
+
permissions: {
|
|
338
|
+
...(upstreamWorkflow ? { actions: JobPermission.READ } : {}),
|
|
339
|
+
contents: JobPermission.READ,
|
|
340
|
+
idToken: JobPermission.WRITE,
|
|
341
|
+
},
|
|
243
342
|
timeoutMinutes: 30,
|
|
244
343
|
// `DRY_RUN_INPUT` is `--dry-run` when the dispatch input is true, else empty;
|
|
245
344
|
// the publish script also FORCES it on any `workflow_dispatch` run.
|
|
@@ -248,25 +347,65 @@ export class DBXToolsRelease extends Component {
|
|
|
248
347
|
DRY_RUN_INPUT: "${{ github.event.inputs.dry_run == 'true' && '--dry-run' || '' }}",
|
|
249
348
|
},
|
|
250
349
|
steps: [
|
|
251
|
-
...
|
|
252
|
-
|
|
350
|
+
...(upstreamWorkflow
|
|
351
|
+
? [
|
|
352
|
+
{
|
|
353
|
+
name: "Download release metadata",
|
|
354
|
+
uses: "actions/download-artifact@v8",
|
|
355
|
+
with: {
|
|
356
|
+
name: "release-metadata",
|
|
357
|
+
path: ".release",
|
|
358
|
+
"run-id": "${{ github.event.workflow_run.id }}",
|
|
359
|
+
"github-token": "${{ github.token }}",
|
|
360
|
+
},
|
|
361
|
+
},
|
|
362
|
+
{
|
|
363
|
+
name: "Read release metadata",
|
|
364
|
+
id: "release_metadata",
|
|
365
|
+
shell: "bash",
|
|
366
|
+
run: [
|
|
367
|
+
'RELEASE_TAG="$(cat .release/tag)"',
|
|
368
|
+
'EXPECTED_SHA="$(cat .release/sha)"',
|
|
369
|
+
'test -n "$RELEASE_TAG"',
|
|
370
|
+
'test -n "$EXPECTED_SHA"',
|
|
371
|
+
'echo "release_tag=$RELEASE_TAG" >> "$GITHUB_OUTPUT"',
|
|
372
|
+
'echo "expected_sha=$EXPECTED_SHA" >> "$GITHUB_OUTPUT"',
|
|
373
|
+
].join("\n"),
|
|
374
|
+
},
|
|
375
|
+
]
|
|
376
|
+
: []),
|
|
377
|
+
{
|
|
378
|
+
...setupSteps[0]!,
|
|
379
|
+
...(upstreamWorkflow
|
|
253
380
|
? {
|
|
254
|
-
...step,
|
|
255
381
|
with: {
|
|
256
|
-
...
|
|
257
|
-
ref: "${{
|
|
382
|
+
...setupSteps[0]!.with,
|
|
383
|
+
ref: "${{ steps.release_metadata.outputs.expected_sha }}",
|
|
384
|
+
"fetch-depth": 0,
|
|
258
385
|
},
|
|
259
386
|
}
|
|
260
|
-
:
|
|
261
|
-
|
|
387
|
+
: {
|
|
388
|
+
with: {
|
|
389
|
+
...setupSteps[0]!.with,
|
|
390
|
+
ref: RELEASE_SHA,
|
|
391
|
+
"fetch-depth": 1,
|
|
392
|
+
},
|
|
393
|
+
}),
|
|
394
|
+
},
|
|
262
395
|
...(upstreamWorkflow
|
|
263
396
|
? [
|
|
264
397
|
{
|
|
265
|
-
name: "
|
|
398
|
+
name: "Verify release source",
|
|
399
|
+
shell: "bash",
|
|
400
|
+
env: {
|
|
401
|
+
SOURCE_RELEASE_TAG: "${{ steps.release_metadata.outputs.release_tag }}",
|
|
402
|
+
EXPECTED_SHA: "${{ steps.release_metadata.outputs.expected_sha }}",
|
|
403
|
+
},
|
|
266
404
|
run: [
|
|
267
|
-
'
|
|
268
|
-
|
|
269
|
-
|
|
405
|
+
'git fetch --force origin "+refs/tags/$SOURCE_RELEASE_TAG:refs/tags/$SOURCE_RELEASE_TAG"',
|
|
406
|
+
'test "$(git rev-parse "$SOURCE_RELEASE_TAG^{commit}")" = "$EXPECTED_SHA"',
|
|
407
|
+
'test "$(git rev-parse HEAD)" = "$EXPECTED_SHA"',
|
|
408
|
+
`RELEASE_TAG="$(git tag --points-at HEAD --list "${tagPrefix}*" | sort -V | tail -1)"`,
|
|
270
409
|
'test -n "$RELEASE_TAG"',
|
|
271
410
|
'echo "RELEASE_TAG=$RELEASE_TAG" >> "$GITHUB_ENV"',
|
|
272
411
|
`echo "RELEASE_VERSION=\${RELEASE_TAG#${tagPrefix}}" >> "$GITHUB_ENV"`,
|
|
@@ -274,6 +413,36 @@ export class DBXToolsRelease extends Component {
|
|
|
274
413
|
} satisfies JobStep,
|
|
275
414
|
]
|
|
276
415
|
: []),
|
|
416
|
+
...(branchDispatch ? releaseSourceSteps().slice(1) : []),
|
|
417
|
+
...(branchDispatch
|
|
418
|
+
? [
|
|
419
|
+
{
|
|
420
|
+
name: "Resolve package release tag",
|
|
421
|
+
shell: "bash",
|
|
422
|
+
env: {
|
|
423
|
+
SOURCE_RELEASE_TAG: RELEASE_TAG,
|
|
424
|
+
EXPECTED_SHA: RELEASE_SHA,
|
|
425
|
+
},
|
|
426
|
+
run: [
|
|
427
|
+
`SOURCE_VERSION="\${SOURCE_RELEASE_TAG#${this.tagPrefix}}"`,
|
|
428
|
+
`RELEASE_TAG="${tagPrefix}\${SOURCE_VERSION}"`,
|
|
429
|
+
'git fetch --force origin "+refs/tags/$RELEASE_TAG:refs/tags/$RELEASE_TAG"',
|
|
430
|
+
'test "$(git rev-parse "$RELEASE_TAG^{commit}")" = "$EXPECTED_SHA"',
|
|
431
|
+
'echo "RELEASE_TAG=$RELEASE_TAG" >> "$GITHUB_ENV"',
|
|
432
|
+
`echo "RELEASE_VERSION=\${RELEASE_TAG#${tagPrefix}}" >> "$GITHUB_ENV"`,
|
|
433
|
+
"mkdir -p .release",
|
|
434
|
+
'printf "%s\\n" "$SOURCE_RELEASE_TAG" > .release/tag',
|
|
435
|
+
'printf "%s\\n" "$EXPECTED_SHA" > .release/sha',
|
|
436
|
+
].join("\n"),
|
|
437
|
+
} satisfies JobStep,
|
|
438
|
+
]
|
|
439
|
+
: []),
|
|
440
|
+
...setupSteps.slice(1),
|
|
441
|
+
{
|
|
442
|
+
name: "Upload release metadata",
|
|
443
|
+
uses: "actions/upload-artifact@v7",
|
|
444
|
+
with: { name: "release-metadata", path: ".release" },
|
|
445
|
+
},
|
|
277
446
|
...steps,
|
|
278
447
|
],
|
|
279
448
|
...(workingDirectory ? { defaults: { run: { workingDirectory } } } : {}),
|
|
@@ -344,6 +513,7 @@ export class DBXToolsRelease extends Component {
|
|
|
344
513
|
this.authorPublishWorkflow(project, {
|
|
345
514
|
name,
|
|
346
515
|
tagPrefix,
|
|
516
|
+
upstreamWorkflow: this.upstreamWorkflow,
|
|
347
517
|
steps: [
|
|
348
518
|
{
|
|
349
519
|
name: "Set version from tag and publish",
|
|
@@ -352,7 +522,7 @@ export class DBXToolsRelease extends Component {
|
|
|
352
522
|
' VERSION="0.0.0-dry.${GITHUB_RUN_NUMBER}"',
|
|
353
523
|
" DRY_RUN=--dry-run",
|
|
354
524
|
"else",
|
|
355
|
-
` VERSION="\${GITHUB_REF_NAME#${tagPrefix}}"`,
|
|
525
|
+
` VERSION="\${RELEASE_VERSION:-\${GITHUB_REF_NAME#${tagPrefix}}}"`,
|
|
356
526
|
' DRY_RUN="${DRY_RUN_INPUT}"',
|
|
357
527
|
"fi",
|
|
358
528
|
"chmod -R u+w . || true",
|
package/tasks/bump.ts
CHANGED
|
@@ -115,6 +115,16 @@ function git(args: string[], capture = false): string {
|
|
|
115
115
|
return res.stdout?.trim() ?? "";
|
|
116
116
|
}
|
|
117
117
|
|
|
118
|
+
function assertReleaseTagsPointToHead(tags: readonly string[]): void {
|
|
119
|
+
const head = git(["rev-parse", "HEAD"], true);
|
|
120
|
+
for (const tag of tags) {
|
|
121
|
+
const target = git(["rev-parse", `${tag}^{commit}`], true);
|
|
122
|
+
if (!head || target !== head) {
|
|
123
|
+
throw new Error(`Release tag ${tag} does not point to the pushed HEAD commit`);
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
|
|
118
128
|
/**
|
|
119
129
|
* Write ONLY the `version` field into a manifest projen owns (read-only, so
|
|
120
130
|
* bracketed by a chmod that restores the mode). Used for the ROOT and the
|
|
@@ -324,9 +334,17 @@ program
|
|
|
324
334
|
|
|
325
335
|
if (push) {
|
|
326
336
|
git(["push", "origin", "HEAD"]);
|
|
327
|
-
//
|
|
328
|
-
//
|
|
329
|
-
|
|
337
|
+
// The managed Databricks pre-push hook treats a newly created tag's
|
|
338
|
+
// all-zero remote SHA as a branch with no upstream and scans hundreds of
|
|
339
|
+
// historical commits. The release commit was already scanned by the
|
|
340
|
+
// branch push immediately above, and each annotated tag points to that
|
|
341
|
+
// exact commit, so bypass the hook only for the tag-ref transport.
|
|
342
|
+
// Push every tag in one invocation so a partial push cannot release
|
|
343
|
+
// only some namespaces at this version.
|
|
344
|
+
if (opts.tag) {
|
|
345
|
+
assertReleaseTagsPointToHead(tags);
|
|
346
|
+
git(["push", "--no-verify", "origin", ...tags]);
|
|
347
|
+
}
|
|
330
348
|
logger.success(`pushed ${opts.tag ? tags.join(", ") : "HEAD"} to origin`);
|
|
331
349
|
} else {
|
|
332
350
|
logger.info("skipped push (--no-push / --no-publish)");
|
package/tasks/uniffi-release.mjs
CHANGED
|
@@ -47,9 +47,21 @@ const required = (name) => {
|
|
|
47
47
|
};
|
|
48
48
|
|
|
49
49
|
const root = resolve(parsed.values.root ?? process.cwd());
|
|
50
|
+
const commandInvocation = (command, args) => {
|
|
51
|
+
if (process.platform !== "win32" || command !== "npm") return { command, args };
|
|
52
|
+
const npmCli = resolve(dirname(process.execPath), "node_modules", "npm", "bin", "npm-cli.js");
|
|
53
|
+
if (!existsSync(npmCli)) throw new Error(`Missing npm CLI ${npmCli}`);
|
|
54
|
+
return { command: process.execPath, args: [npmCli, ...args] };
|
|
55
|
+
};
|
|
50
56
|
const run = (command, args, cwd = root) => {
|
|
51
|
-
const
|
|
52
|
-
|
|
57
|
+
const invocation = commandInvocation(command, args);
|
|
58
|
+
const result = spawnSync(invocation.command, invocation.args, { cwd, stdio: "inherit" });
|
|
59
|
+
if (result.error) {
|
|
60
|
+
throw new Error(`${invocation.command} failed: ${result.error.message}`, {
|
|
61
|
+
cause: result.error,
|
|
62
|
+
});
|
|
63
|
+
}
|
|
64
|
+
if (result.status !== 0) throw new Error(`${invocation.command} exited with ${result.status}`);
|
|
53
65
|
};
|
|
54
66
|
|
|
55
67
|
const replaceVersion = (source, version) =>
|