@backstage/plugin-scaffolder-backend 1.12.0 → 1.12.1-next.0
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/CHANGELOG.md +24 -0
- package/alpha/package.json +1 -1
- package/dist/alpha.cjs.js +347 -150
- package/dist/alpha.cjs.js.map +1 -1
- package/dist/index.cjs.js +348 -150
- package/dist/index.cjs.js.map +1 -1
- package/dist/index.d.ts +31 -7
- package/package.json +14 -11
package/dist/alpha.cjs.js
CHANGED
|
@@ -5,7 +5,7 @@ Object.defineProperty(exports, '__esModule', { value: true });
|
|
|
5
5
|
var backendPluginApi = require('@backstage/backend-plugin-api');
|
|
6
6
|
var alpha = require('@backstage/plugin-catalog-node/alpha');
|
|
7
7
|
var catalogModel = require('@backstage/catalog-model');
|
|
8
|
-
var
|
|
8
|
+
var pluginCatalogNode = require('@backstage/plugin-catalog-node');
|
|
9
9
|
var pluginScaffolderCommon = require('@backstage/plugin-scaffolder-common');
|
|
10
10
|
var backendCommon = require('@backstage/backend-common');
|
|
11
11
|
var integration = require('@backstage/integration');
|
|
@@ -15,6 +15,7 @@ var yaml = require('yaml');
|
|
|
15
15
|
var fs = require('fs-extra');
|
|
16
16
|
var zod = require('zod');
|
|
17
17
|
var path = require('path');
|
|
18
|
+
var luxon = require('luxon');
|
|
18
19
|
var globby = require('globby');
|
|
19
20
|
var isbinaryfile = require('isbinaryfile');
|
|
20
21
|
var vm2 = require('vm2');
|
|
@@ -30,7 +31,6 @@ var fs$1 = require('fs');
|
|
|
30
31
|
var limiterFactory = require('p-limit');
|
|
31
32
|
var node = require('@gitbeaker/node');
|
|
32
33
|
var uuid = require('uuid');
|
|
33
|
-
var luxon = require('luxon');
|
|
34
34
|
var ObservableImpl = require('zen-observable');
|
|
35
35
|
var PQueue = require('p-queue');
|
|
36
36
|
var winston = require('winston');
|
|
@@ -105,7 +105,7 @@ class ScaffolderEntitiesProcessor {
|
|
|
105
105
|
defaultNamespace: selfRef.namespace
|
|
106
106
|
});
|
|
107
107
|
emit(
|
|
108
|
-
|
|
108
|
+
pluginCatalogNode.processingResult.relation({
|
|
109
109
|
source: selfRef,
|
|
110
110
|
type: catalogModel.RELATION_OWNED_BY,
|
|
111
111
|
target: {
|
|
@@ -116,7 +116,7 @@ class ScaffolderEntitiesProcessor {
|
|
|
116
116
|
})
|
|
117
117
|
);
|
|
118
118
|
emit(
|
|
119
|
-
|
|
119
|
+
pluginCatalogNode.processingResult.relation({
|
|
120
120
|
source: {
|
|
121
121
|
kind: targetRef.kind,
|
|
122
122
|
namespace: targetRef.namespace,
|
|
@@ -147,14 +147,14 @@ const catalogModuleTemplateKind = backendPluginApi.createBackendModule({
|
|
|
147
147
|
}
|
|
148
148
|
});
|
|
149
149
|
|
|
150
|
-
const id$
|
|
151
|
-
const examples$
|
|
150
|
+
const id$4 = "catalog:register";
|
|
151
|
+
const examples$4 = [
|
|
152
152
|
{
|
|
153
153
|
description: "Register with the catalog",
|
|
154
154
|
example: yaml__default["default"].stringify({
|
|
155
155
|
steps: [
|
|
156
156
|
{
|
|
157
|
-
action: id$
|
|
157
|
+
action: id$4,
|
|
158
158
|
id: "register-with-catalog",
|
|
159
159
|
name: "Register with the catalog",
|
|
160
160
|
input: {
|
|
@@ -168,9 +168,9 @@ const examples$3 = [
|
|
|
168
168
|
function createCatalogRegisterAction(options) {
|
|
169
169
|
const { catalogClient, integrations } = options;
|
|
170
170
|
return pluginScaffolderNode.createTemplateAction({
|
|
171
|
-
id: id$
|
|
171
|
+
id: id$4,
|
|
172
172
|
description: "Registers entities from a catalog descriptor file in the workspace into the software catalog.",
|
|
173
|
-
examples: examples$
|
|
173
|
+
examples: examples$4,
|
|
174
174
|
schema: {
|
|
175
175
|
input: {
|
|
176
176
|
oneOf: [
|
|
@@ -288,14 +288,14 @@ function createCatalogRegisterAction(options) {
|
|
|
288
288
|
});
|
|
289
289
|
}
|
|
290
290
|
|
|
291
|
-
const id$
|
|
292
|
-
const examples$
|
|
291
|
+
const id$3 = "catalog:write";
|
|
292
|
+
const examples$3 = [
|
|
293
293
|
{
|
|
294
294
|
description: "Write a catalog yaml file",
|
|
295
295
|
example: yaml__namespace.stringify({
|
|
296
296
|
steps: [
|
|
297
297
|
{
|
|
298
|
-
action: id$
|
|
298
|
+
action: id$3,
|
|
299
299
|
id: "create-catalog-info-file",
|
|
300
300
|
name: "Create catalog file",
|
|
301
301
|
input: {
|
|
@@ -320,7 +320,7 @@ const examples$2 = [
|
|
|
320
320
|
];
|
|
321
321
|
function createCatalogWriteAction() {
|
|
322
322
|
return pluginScaffolderNode.createTemplateAction({
|
|
323
|
-
id: id$
|
|
323
|
+
id: id$3,
|
|
324
324
|
description: "Writes the catalog-info.yaml for your template",
|
|
325
325
|
schema: {
|
|
326
326
|
input: zod.z.object({
|
|
@@ -331,7 +331,7 @@ function createCatalogWriteAction() {
|
|
|
331
331
|
)
|
|
332
332
|
})
|
|
333
333
|
},
|
|
334
|
-
examples: examples$
|
|
334
|
+
examples: examples$3,
|
|
335
335
|
supportsDryRun: true,
|
|
336
336
|
async handler(ctx) {
|
|
337
337
|
ctx.logStream.write(`Writing catalog-info.yaml`);
|
|
@@ -345,14 +345,14 @@ function createCatalogWriteAction() {
|
|
|
345
345
|
});
|
|
346
346
|
}
|
|
347
347
|
|
|
348
|
-
const id$
|
|
349
|
-
const examples$
|
|
348
|
+
const id$2 = "catalog:fetch";
|
|
349
|
+
const examples$2 = [
|
|
350
350
|
{
|
|
351
351
|
description: "Fetch entity by reference",
|
|
352
352
|
example: yaml__default["default"].stringify({
|
|
353
353
|
steps: [
|
|
354
354
|
{
|
|
355
|
-
action: id$
|
|
355
|
+
action: id$2,
|
|
356
356
|
id: "fetch",
|
|
357
357
|
name: "Fetch catalog entity",
|
|
358
358
|
input: {
|
|
@@ -367,7 +367,7 @@ const examples$1 = [
|
|
|
367
367
|
example: yaml__default["default"].stringify({
|
|
368
368
|
steps: [
|
|
369
369
|
{
|
|
370
|
-
action: id$
|
|
370
|
+
action: id$2,
|
|
371
371
|
id: "fetchMultiple",
|
|
372
372
|
name: "Fetch catalog entities",
|
|
373
373
|
input: {
|
|
@@ -381,9 +381,9 @@ const examples$1 = [
|
|
|
381
381
|
function createFetchCatalogEntityAction(options) {
|
|
382
382
|
const { catalogClient } = options;
|
|
383
383
|
return pluginScaffolderNode.createTemplateAction({
|
|
384
|
-
id: id$
|
|
384
|
+
id: id$2,
|
|
385
385
|
description: "Returns entity or entities from the catalog by entity reference(s)",
|
|
386
|
-
examples: examples$
|
|
386
|
+
examples: examples$2,
|
|
387
387
|
schema: {
|
|
388
388
|
input: {
|
|
389
389
|
type: "object",
|
|
@@ -459,14 +459,14 @@ function createFetchCatalogEntityAction(options) {
|
|
|
459
459
|
});
|
|
460
460
|
}
|
|
461
461
|
|
|
462
|
-
const id = "debug:log";
|
|
463
|
-
const examples = [
|
|
462
|
+
const id$1 = "debug:log";
|
|
463
|
+
const examples$1 = [
|
|
464
464
|
{
|
|
465
465
|
description: "Write a debug message",
|
|
466
466
|
example: yaml__default["default"].stringify({
|
|
467
467
|
steps: [
|
|
468
468
|
{
|
|
469
|
-
action: id,
|
|
469
|
+
action: id$1,
|
|
470
470
|
id: "write-debug-line",
|
|
471
471
|
name: 'Write "Hello Backstage!" log line',
|
|
472
472
|
input: {
|
|
@@ -481,7 +481,7 @@ const examples = [
|
|
|
481
481
|
example: yaml__default["default"].stringify({
|
|
482
482
|
steps: [
|
|
483
483
|
{
|
|
484
|
-
action: id,
|
|
484
|
+
action: id$1,
|
|
485
485
|
id: "write-workspace-directory",
|
|
486
486
|
name: "List the workspace directory",
|
|
487
487
|
input: {
|
|
@@ -494,9 +494,9 @@ const examples = [
|
|
|
494
494
|
];
|
|
495
495
|
function createDebugLogAction() {
|
|
496
496
|
return pluginScaffolderNode.createTemplateAction({
|
|
497
|
-
id,
|
|
497
|
+
id: id$1,
|
|
498
498
|
description: "Writes a message into the log or lists all files in the workspace.",
|
|
499
|
-
examples,
|
|
499
|
+
examples: examples$1,
|
|
500
500
|
schema: {
|
|
501
501
|
input: {
|
|
502
502
|
type: "object",
|
|
@@ -543,6 +543,98 @@ async function recursiveReadDir(dir) {
|
|
|
543
543
|
return files.reduce((a, f) => a.concat(f), []);
|
|
544
544
|
}
|
|
545
545
|
|
|
546
|
+
const id = "debug:wait";
|
|
547
|
+
const MAX_WAIT_TIME_IN_ISO = "T00:00:30";
|
|
548
|
+
const examples = [
|
|
549
|
+
{
|
|
550
|
+
description: "Waiting for 5 seconds",
|
|
551
|
+
example: yaml__default["default"].stringify({
|
|
552
|
+
steps: [
|
|
553
|
+
{
|
|
554
|
+
action: id,
|
|
555
|
+
id: "wait-5sec",
|
|
556
|
+
name: "Waiting for 5 seconds",
|
|
557
|
+
input: {
|
|
558
|
+
seconds: 5
|
|
559
|
+
}
|
|
560
|
+
}
|
|
561
|
+
]
|
|
562
|
+
})
|
|
563
|
+
},
|
|
564
|
+
{
|
|
565
|
+
description: "Waiting for 5 minutes",
|
|
566
|
+
example: yaml__default["default"].stringify({
|
|
567
|
+
steps: [
|
|
568
|
+
{
|
|
569
|
+
action: id,
|
|
570
|
+
id: "wait-5min",
|
|
571
|
+
name: "Waiting for 5 minutes",
|
|
572
|
+
input: {
|
|
573
|
+
minutes: 5
|
|
574
|
+
}
|
|
575
|
+
}
|
|
576
|
+
]
|
|
577
|
+
})
|
|
578
|
+
}
|
|
579
|
+
];
|
|
580
|
+
function createWaitAction(options) {
|
|
581
|
+
const toDuration = (maxWaitTime) => {
|
|
582
|
+
if (maxWaitTime) {
|
|
583
|
+
if (maxWaitTime instanceof luxon.Duration) {
|
|
584
|
+
return maxWaitTime;
|
|
585
|
+
}
|
|
586
|
+
return luxon.Duration.fromObject(maxWaitTime);
|
|
587
|
+
}
|
|
588
|
+
return luxon.Duration.fromISOTime(MAX_WAIT_TIME_IN_ISO);
|
|
589
|
+
};
|
|
590
|
+
return pluginScaffolderNode.createTemplateAction({
|
|
591
|
+
id,
|
|
592
|
+
description: "Waits for a certain period of time.",
|
|
593
|
+
examples,
|
|
594
|
+
schema: {
|
|
595
|
+
input: {
|
|
596
|
+
type: "object",
|
|
597
|
+
properties: {
|
|
598
|
+
minutes: {
|
|
599
|
+
title: "Waiting period in minutes.",
|
|
600
|
+
type: "number"
|
|
601
|
+
},
|
|
602
|
+
seconds: {
|
|
603
|
+
title: "Waiting period in seconds.",
|
|
604
|
+
type: "number"
|
|
605
|
+
},
|
|
606
|
+
milliseconds: {
|
|
607
|
+
title: "Waiting period in milliseconds.",
|
|
608
|
+
type: "number"
|
|
609
|
+
}
|
|
610
|
+
}
|
|
611
|
+
}
|
|
612
|
+
},
|
|
613
|
+
async handler(ctx) {
|
|
614
|
+
const delayTime = luxon.Duration.fromObject(ctx.input);
|
|
615
|
+
const maxWait = toDuration(options == null ? void 0 : options.maxWaitTime);
|
|
616
|
+
if (delayTime.minus(maxWait).toMillis() > 0) {
|
|
617
|
+
throw new Error(
|
|
618
|
+
`Waiting duration is longer than the maximum threshold of ${maxWait.toHuman()}`
|
|
619
|
+
);
|
|
620
|
+
}
|
|
621
|
+
await new Promise((resolve) => {
|
|
622
|
+
var _a;
|
|
623
|
+
const controller = new AbortController();
|
|
624
|
+
const timeoutHandle = setTimeout(abort, delayTime.toMillis());
|
|
625
|
+
(_a = ctx.signal) == null ? void 0 : _a.addEventListener("abort", abort);
|
|
626
|
+
function abort() {
|
|
627
|
+
var _a2;
|
|
628
|
+
(_a2 = ctx.signal) == null ? void 0 : _a2.removeEventListener("abort", abort);
|
|
629
|
+
clearTimeout(timeoutHandle);
|
|
630
|
+
controller.abort();
|
|
631
|
+
resolve("finished");
|
|
632
|
+
}
|
|
633
|
+
});
|
|
634
|
+
}
|
|
635
|
+
});
|
|
636
|
+
}
|
|
637
|
+
|
|
546
638
|
async function fetchContents(options) {
|
|
547
639
|
const { reader, integrations, baseUrl, fetchUrl = ".", outputPath } = options;
|
|
548
640
|
let fetchUrlIsAbsolute = false;
|
|
@@ -4011,7 +4103,7 @@ const createPublishGitlabMergeRequestAction = (options) => {
|
|
|
4011
4103
|
title: "Gitlab Project path",
|
|
4012
4104
|
type: "string"
|
|
4013
4105
|
},
|
|
4014
|
-
|
|
4106
|
+
mergeRequestUrl: {
|
|
4015
4107
|
title: "MergeRequest(MR) URL",
|
|
4016
4108
|
type: "string",
|
|
4017
4109
|
description: "Link to the merge request in GitLab"
|
|
@@ -4183,6 +4275,7 @@ const createBuiltinActions = (options) => {
|
|
|
4183
4275
|
config
|
|
4184
4276
|
}),
|
|
4185
4277
|
createDebugLogAction(),
|
|
4278
|
+
createWaitAction(),
|
|
4186
4279
|
createCatalogRegisterAction({ catalogClient, integrations }),
|
|
4187
4280
|
createFetchCatalogEntityAction({ catalogClient }),
|
|
4188
4281
|
createCatalogWriteAction(),
|
|
@@ -4349,7 +4442,7 @@ class DatabaseTaskStore {
|
|
|
4349
4442
|
const updateCount = await tx("tasks").where({ id: task.id, status: "open" }).update({
|
|
4350
4443
|
status: "processing",
|
|
4351
4444
|
last_heartbeat_at: this.db.fn.now(),
|
|
4352
|
-
// remove the secrets when moving
|
|
4445
|
+
// remove the secrets when moving to processing state.
|
|
4353
4446
|
secrets: null
|
|
4354
4447
|
});
|
|
4355
4448
|
if (updateCount < 1) {
|
|
@@ -4397,7 +4490,7 @@ class DatabaseTaskStore {
|
|
|
4397
4490
|
async completeTask(options) {
|
|
4398
4491
|
const { taskId, status, eventBody } = options;
|
|
4399
4492
|
let oldStatus;
|
|
4400
|
-
if (
|
|
4493
|
+
if (["failed", "completed", "cancelled"].includes(status)) {
|
|
4401
4494
|
oldStatus = "processing";
|
|
4402
4495
|
} else {
|
|
4403
4496
|
throw new Error(
|
|
@@ -4408,6 +4501,30 @@ class DatabaseTaskStore {
|
|
|
4408
4501
|
const [task] = await tx("tasks").where({
|
|
4409
4502
|
id: taskId
|
|
4410
4503
|
}).limit(1).select();
|
|
4504
|
+
const updateTask = async (criteria) => {
|
|
4505
|
+
const updateCount = await tx("tasks").where(criteria).update({
|
|
4506
|
+
status
|
|
4507
|
+
});
|
|
4508
|
+
if (updateCount !== 1) {
|
|
4509
|
+
throw new errors.ConflictError(
|
|
4510
|
+
`Failed to update status to '${status}' for taskId ${taskId}`
|
|
4511
|
+
);
|
|
4512
|
+
}
|
|
4513
|
+
await tx("task_events").insert({
|
|
4514
|
+
task_id: taskId,
|
|
4515
|
+
event_type: "completion",
|
|
4516
|
+
body: JSON.stringify(eventBody)
|
|
4517
|
+
});
|
|
4518
|
+
};
|
|
4519
|
+
if (status === "cancelled") {
|
|
4520
|
+
await updateTask({
|
|
4521
|
+
id: taskId
|
|
4522
|
+
});
|
|
4523
|
+
return;
|
|
4524
|
+
}
|
|
4525
|
+
if (task.status === "cancelled") {
|
|
4526
|
+
return;
|
|
4527
|
+
}
|
|
4411
4528
|
if (!task) {
|
|
4412
4529
|
throw new Error(`No task with taskId ${taskId} found`);
|
|
4413
4530
|
}
|
|
@@ -4416,21 +4533,9 @@ class DatabaseTaskStore {
|
|
|
4416
4533
|
`Refusing to update status of run '${taskId}' to status '${status}' as it is currently '${task.status}', expected '${oldStatus}'`
|
|
4417
4534
|
);
|
|
4418
4535
|
}
|
|
4419
|
-
|
|
4536
|
+
await updateTask({
|
|
4420
4537
|
id: taskId,
|
|
4421
4538
|
status: oldStatus
|
|
4422
|
-
}).update({
|
|
4423
|
-
status
|
|
4424
|
-
});
|
|
4425
|
-
if (updateCount !== 1) {
|
|
4426
|
-
throw new errors.ConflictError(
|
|
4427
|
-
`Failed to update status to '${status}' for taskId ${taskId}`
|
|
4428
|
-
);
|
|
4429
|
-
}
|
|
4430
|
-
await tx("task_events").insert({
|
|
4431
|
-
task_id: taskId,
|
|
4432
|
-
event_type: "completion",
|
|
4433
|
-
body: JSON.stringify(eventBody)
|
|
4434
4539
|
});
|
|
4435
4540
|
});
|
|
4436
4541
|
}
|
|
@@ -4498,24 +4603,37 @@ class DatabaseTaskStore {
|
|
|
4498
4603
|
}
|
|
4499
4604
|
});
|
|
4500
4605
|
}
|
|
4606
|
+
async cancelTask(options) {
|
|
4607
|
+
const { taskId, body } = options;
|
|
4608
|
+
const serializedBody = JSON.stringify(body);
|
|
4609
|
+
await this.db("task_events").insert({
|
|
4610
|
+
task_id: taskId,
|
|
4611
|
+
event_type: "cancelled",
|
|
4612
|
+
body: serializedBody
|
|
4613
|
+
});
|
|
4614
|
+
}
|
|
4501
4615
|
}
|
|
4502
4616
|
|
|
4503
4617
|
class TaskManager {
|
|
4504
4618
|
// Runs heartbeat internally
|
|
4505
|
-
constructor(task, storage, logger) {
|
|
4619
|
+
constructor(task, storage, signal, logger) {
|
|
4506
4620
|
this.task = task;
|
|
4507
4621
|
this.storage = storage;
|
|
4622
|
+
this.signal = signal;
|
|
4508
4623
|
this.logger = logger;
|
|
4509
4624
|
this.isDone = false;
|
|
4510
4625
|
}
|
|
4511
|
-
static create(task, storage, logger) {
|
|
4512
|
-
const agent = new TaskManager(task, storage, logger);
|
|
4626
|
+
static create(task, storage, abortSignal, logger) {
|
|
4627
|
+
const agent = new TaskManager(task, storage, abortSignal, logger);
|
|
4513
4628
|
agent.startTimeout();
|
|
4514
4629
|
return agent;
|
|
4515
4630
|
}
|
|
4516
4631
|
get spec() {
|
|
4517
4632
|
return this.task.spec;
|
|
4518
4633
|
}
|
|
4634
|
+
get cancelSignal() {
|
|
4635
|
+
return this.signal;
|
|
4636
|
+
}
|
|
4519
4637
|
get secrets() {
|
|
4520
4638
|
return this.task.secrets;
|
|
4521
4639
|
}
|
|
@@ -4585,6 +4703,28 @@ class StorageTaskBroker {
|
|
|
4585
4703
|
}
|
|
4586
4704
|
return await this.storage.list({ createdBy: options == null ? void 0 : options.createdBy });
|
|
4587
4705
|
}
|
|
4706
|
+
async registerCancellable(taskId, abortController) {
|
|
4707
|
+
let shouldUnsubscribe = false;
|
|
4708
|
+
const subscription = this.event$({ taskId, after: void 0 }).subscribe({
|
|
4709
|
+
error: (_) => {
|
|
4710
|
+
subscription.unsubscribe();
|
|
4711
|
+
},
|
|
4712
|
+
next: ({ events }) => {
|
|
4713
|
+
for (const event of events) {
|
|
4714
|
+
if (event.type === "cancelled") {
|
|
4715
|
+
abortController.abort();
|
|
4716
|
+
shouldUnsubscribe = true;
|
|
4717
|
+
}
|
|
4718
|
+
if (event.type === "completion") {
|
|
4719
|
+
shouldUnsubscribe = true;
|
|
4720
|
+
}
|
|
4721
|
+
}
|
|
4722
|
+
if (shouldUnsubscribe) {
|
|
4723
|
+
subscription.unsubscribe();
|
|
4724
|
+
}
|
|
4725
|
+
}
|
|
4726
|
+
});
|
|
4727
|
+
}
|
|
4588
4728
|
/**
|
|
4589
4729
|
* {@inheritdoc TaskBroker.claim}
|
|
4590
4730
|
*/
|
|
@@ -4592,6 +4732,8 @@ class StorageTaskBroker {
|
|
|
4592
4732
|
for (; ; ) {
|
|
4593
4733
|
const pendingTask = await this.storage.claimTask();
|
|
4594
4734
|
if (pendingTask) {
|
|
4735
|
+
const abortController = new AbortController();
|
|
4736
|
+
await this.registerCancellable(pendingTask.id, abortController);
|
|
4595
4737
|
return TaskManager.create(
|
|
4596
4738
|
{
|
|
4597
4739
|
taskId: pendingTask.id,
|
|
@@ -4600,6 +4742,7 @@ class StorageTaskBroker {
|
|
|
4600
4742
|
createdBy: pendingTask.createdBy
|
|
4601
4743
|
},
|
|
4602
4744
|
this.storage,
|
|
4745
|
+
abortController.signal,
|
|
4603
4746
|
this.logger
|
|
4604
4747
|
);
|
|
4605
4748
|
}
|
|
@@ -4674,6 +4817,19 @@ class StorageTaskBroker {
|
|
|
4674
4817
|
this.deferredDispatch.resolve();
|
|
4675
4818
|
this.deferredDispatch = defer();
|
|
4676
4819
|
}
|
|
4820
|
+
async cancel(taskId) {
|
|
4821
|
+
var _a, _b;
|
|
4822
|
+
const { events } = await this.storage.listEvents({ taskId });
|
|
4823
|
+
const currentStepId = events.length > 0 ? events.filter(({ body }) => body == null ? void 0 : body.stepId).reduce((prev, curr) => prev.id > curr.id ? prev : curr).body.stepId : 0;
|
|
4824
|
+
await ((_b = (_a = this.storage).cancelTask) == null ? void 0 : _b.call(_a, {
|
|
4825
|
+
taskId,
|
|
4826
|
+
body: {
|
|
4827
|
+
message: `Step ${currentStepId} has been cancelled.`,
|
|
4828
|
+
stepId: currentStepId,
|
|
4829
|
+
status: "cancelled"
|
|
4830
|
+
}
|
|
4831
|
+
}));
|
|
4832
|
+
}
|
|
4677
4833
|
}
|
|
4678
4834
|
|
|
4679
4835
|
function isTruthy(value) {
|
|
@@ -4805,8 +4961,107 @@ class NunjucksWorkflowRunner {
|
|
|
4805
4961
|
return value;
|
|
4806
4962
|
});
|
|
4807
4963
|
}
|
|
4808
|
-
async
|
|
4964
|
+
async executeStep(task, step, context, renderTemplate, taskTrack, workspacePath) {
|
|
4809
4965
|
var _a, _b, _c, _d, _e, _f, _g;
|
|
4966
|
+
const stepTrack = await this.tracker.stepStart(task, step);
|
|
4967
|
+
if (task.cancelSignal.aborted) {
|
|
4968
|
+
throw new Error(`Step ${step.name} has been cancelled.`);
|
|
4969
|
+
}
|
|
4970
|
+
try {
|
|
4971
|
+
if (step.if) {
|
|
4972
|
+
const ifResult = await this.render(step.if, context, renderTemplate);
|
|
4973
|
+
if (!isTruthy(ifResult)) {
|
|
4974
|
+
await stepTrack.skipFalsy();
|
|
4975
|
+
return;
|
|
4976
|
+
}
|
|
4977
|
+
}
|
|
4978
|
+
const action = this.options.actionRegistry.get(step.action);
|
|
4979
|
+
const { taskLogger, streamLogger } = createStepLogger({ task, step });
|
|
4980
|
+
if (task.isDryRun) {
|
|
4981
|
+
const redactedSecrets = Object.fromEntries(
|
|
4982
|
+
Object.entries((_a = task.secrets) != null ? _a : {}).map((secret) => [
|
|
4983
|
+
secret[0],
|
|
4984
|
+
"[REDACTED]"
|
|
4985
|
+
])
|
|
4986
|
+
);
|
|
4987
|
+
const debugInput = (_b = step.input && this.render(
|
|
4988
|
+
step.input,
|
|
4989
|
+
{
|
|
4990
|
+
...context,
|
|
4991
|
+
secrets: redactedSecrets
|
|
4992
|
+
},
|
|
4993
|
+
renderTemplate
|
|
4994
|
+
)) != null ? _b : {};
|
|
4995
|
+
taskLogger.info(
|
|
4996
|
+
`Running ${action.id} in dry-run mode with inputs (secrets redacted): ${JSON.stringify(
|
|
4997
|
+
debugInput,
|
|
4998
|
+
void 0,
|
|
4999
|
+
2
|
|
5000
|
+
)}`
|
|
5001
|
+
);
|
|
5002
|
+
if (!action.supportsDryRun) {
|
|
5003
|
+
await taskTrack.skipDryRun(step, action);
|
|
5004
|
+
const outputSchema = (_c = action.schema) == null ? void 0 : _c.output;
|
|
5005
|
+
if (outputSchema) {
|
|
5006
|
+
context.steps[step.id] = {
|
|
5007
|
+
output: generateExampleOutput(outputSchema)
|
|
5008
|
+
};
|
|
5009
|
+
} else {
|
|
5010
|
+
context.steps[step.id] = { output: {} };
|
|
5011
|
+
}
|
|
5012
|
+
return;
|
|
5013
|
+
}
|
|
5014
|
+
}
|
|
5015
|
+
const input = (_e = step.input && this.render(
|
|
5016
|
+
step.input,
|
|
5017
|
+
{ ...context, secrets: (_d = task.secrets) != null ? _d : {} },
|
|
5018
|
+
renderTemplate
|
|
5019
|
+
)) != null ? _e : {};
|
|
5020
|
+
if ((_f = action.schema) == null ? void 0 : _f.input) {
|
|
5021
|
+
const validateResult = jsonschema.validate(input, action.schema.input);
|
|
5022
|
+
if (!validateResult.valid) {
|
|
5023
|
+
const errors$1 = validateResult.errors.join(", ");
|
|
5024
|
+
throw new errors.InputError(
|
|
5025
|
+
`Invalid input passed to action ${action.id}, ${errors$1}`
|
|
5026
|
+
);
|
|
5027
|
+
}
|
|
5028
|
+
}
|
|
5029
|
+
const tmpDirs = new Array();
|
|
5030
|
+
const stepOutput = {};
|
|
5031
|
+
await action.handler({
|
|
5032
|
+
input,
|
|
5033
|
+
secrets: (_g = task.secrets) != null ? _g : {},
|
|
5034
|
+
logger: taskLogger,
|
|
5035
|
+
logStream: streamLogger,
|
|
5036
|
+
workspacePath,
|
|
5037
|
+
createTemporaryDirectory: async () => {
|
|
5038
|
+
const tmpDir = await fs__default["default"].mkdtemp(`${workspacePath}_step-${step.id}-`);
|
|
5039
|
+
tmpDirs.push(tmpDir);
|
|
5040
|
+
return tmpDir;
|
|
5041
|
+
},
|
|
5042
|
+
output(name, value) {
|
|
5043
|
+
stepOutput[name] = value;
|
|
5044
|
+
},
|
|
5045
|
+
templateInfo: task.spec.templateInfo,
|
|
5046
|
+
user: task.spec.user,
|
|
5047
|
+
isDryRun: task.isDryRun,
|
|
5048
|
+
signal: task.cancelSignal
|
|
5049
|
+
});
|
|
5050
|
+
for (const tmpDir of tmpDirs) {
|
|
5051
|
+
await fs__default["default"].remove(tmpDir);
|
|
5052
|
+
}
|
|
5053
|
+
context.steps[step.id] = { output: stepOutput };
|
|
5054
|
+
if (task.cancelSignal.aborted) {
|
|
5055
|
+
throw new Error(`Step ${step.name} has been cancelled.`);
|
|
5056
|
+
}
|
|
5057
|
+
await stepTrack.markSuccessful();
|
|
5058
|
+
} catch (err) {
|
|
5059
|
+
await taskTrack.markFailed(step, err);
|
|
5060
|
+
await stepTrack.markFailed();
|
|
5061
|
+
throw err;
|
|
5062
|
+
}
|
|
5063
|
+
}
|
|
5064
|
+
async execute(task) {
|
|
4810
5065
|
if (!isValidTaskSpec(task.spec)) {
|
|
4811
5066
|
throw new errors.InputError(
|
|
4812
5067
|
"Wrong template version executed with the workflow engine"
|
|
@@ -4816,7 +5071,11 @@ class NunjucksWorkflowRunner {
|
|
|
4816
5071
|
this.options.workingDirectory,
|
|
4817
5072
|
await task.getWorkspaceName()
|
|
4818
5073
|
);
|
|
4819
|
-
const {
|
|
5074
|
+
const {
|
|
5075
|
+
additionalTemplateFilters,
|
|
5076
|
+
additionalTemplateGlobals,
|
|
5077
|
+
integrations
|
|
5078
|
+
} = this.options;
|
|
4820
5079
|
const renderTemplate = await SecureTemplater.loadRenderer({
|
|
4821
5080
|
// TODO(blam): let's work out how we can deprecate this.
|
|
4822
5081
|
// We shouldn't really need to be exposing these now we can deal with
|
|
@@ -4825,8 +5084,8 @@ class NunjucksWorkflowRunner {
|
|
|
4825
5084
|
parseRepoUrl(url) {
|
|
4826
5085
|
return parseRepoUrl(url, integrations);
|
|
4827
5086
|
},
|
|
4828
|
-
additionalTemplateFilters
|
|
4829
|
-
additionalTemplateGlobals
|
|
5087
|
+
additionalTemplateFilters,
|
|
5088
|
+
additionalTemplateGlobals
|
|
4830
5089
|
});
|
|
4831
5090
|
try {
|
|
4832
5091
|
const taskTrack = await this.tracker.taskStart(task);
|
|
@@ -4837,105 +5096,14 @@ class NunjucksWorkflowRunner {
|
|
|
4837
5096
|
user: task.spec.user
|
|
4838
5097
|
};
|
|
4839
5098
|
for (const step of task.spec.steps) {
|
|
4840
|
-
|
|
4841
|
-
|
|
4842
|
-
|
|
4843
|
-
|
|
4844
|
-
|
|
4845
|
-
|
|
4846
|
-
|
|
4847
|
-
|
|
4848
|
-
if (!isTruthy(ifResult)) {
|
|
4849
|
-
await stepTrack.skipFalsy();
|
|
4850
|
-
continue;
|
|
4851
|
-
}
|
|
4852
|
-
}
|
|
4853
|
-
const action = this.options.actionRegistry.get(step.action);
|
|
4854
|
-
const { taskLogger, streamLogger } = createStepLogger({ task, step });
|
|
4855
|
-
if (task.isDryRun) {
|
|
4856
|
-
const redactedSecrets = Object.fromEntries(
|
|
4857
|
-
Object.entries((_a = task.secrets) != null ? _a : {}).map((secret) => [
|
|
4858
|
-
secret[0],
|
|
4859
|
-
"[REDACTED]"
|
|
4860
|
-
])
|
|
4861
|
-
);
|
|
4862
|
-
const debugInput = (_b = step.input && this.render(
|
|
4863
|
-
step.input,
|
|
4864
|
-
{
|
|
4865
|
-
...context,
|
|
4866
|
-
secrets: redactedSecrets
|
|
4867
|
-
},
|
|
4868
|
-
renderTemplate
|
|
4869
|
-
)) != null ? _b : {};
|
|
4870
|
-
taskLogger.info(
|
|
4871
|
-
`Running ${action.id} in dry-run mode with inputs (secrets redacted): ${JSON.stringify(
|
|
4872
|
-
debugInput,
|
|
4873
|
-
void 0,
|
|
4874
|
-
2
|
|
4875
|
-
)}`
|
|
4876
|
-
);
|
|
4877
|
-
if (!action.supportsDryRun) {
|
|
4878
|
-
await taskTrack.skipDryRun(step, action);
|
|
4879
|
-
const outputSchema = (_c = action.schema) == null ? void 0 : _c.output;
|
|
4880
|
-
if (outputSchema) {
|
|
4881
|
-
context.steps[step.id] = {
|
|
4882
|
-
output: generateExampleOutput(outputSchema)
|
|
4883
|
-
};
|
|
4884
|
-
} else {
|
|
4885
|
-
context.steps[step.id] = { output: {} };
|
|
4886
|
-
}
|
|
4887
|
-
continue;
|
|
4888
|
-
}
|
|
4889
|
-
}
|
|
4890
|
-
const input = (_e = step.input && this.render(
|
|
4891
|
-
step.input,
|
|
4892
|
-
{ ...context, secrets: (_d = task.secrets) != null ? _d : {} },
|
|
4893
|
-
renderTemplate
|
|
4894
|
-
)) != null ? _e : {};
|
|
4895
|
-
if ((_f = action.schema) == null ? void 0 : _f.input) {
|
|
4896
|
-
const validateResult = jsonschema.validate(
|
|
4897
|
-
input,
|
|
4898
|
-
action.schema.input
|
|
4899
|
-
);
|
|
4900
|
-
if (!validateResult.valid) {
|
|
4901
|
-
const errors$1 = validateResult.errors.join(", ");
|
|
4902
|
-
throw new errors.InputError(
|
|
4903
|
-
`Invalid input passed to action ${action.id}, ${errors$1}`
|
|
4904
|
-
);
|
|
4905
|
-
}
|
|
4906
|
-
}
|
|
4907
|
-
const tmpDirs = new Array();
|
|
4908
|
-
const stepOutput = {};
|
|
4909
|
-
await action.handler({
|
|
4910
|
-
input,
|
|
4911
|
-
secrets: (_g = task.secrets) != null ? _g : {},
|
|
4912
|
-
logger: taskLogger,
|
|
4913
|
-
logStream: streamLogger,
|
|
4914
|
-
workspacePath,
|
|
4915
|
-
createTemporaryDirectory: async () => {
|
|
4916
|
-
const tmpDir = await fs__default["default"].mkdtemp(
|
|
4917
|
-
`${workspacePath}_step-${step.id}-`
|
|
4918
|
-
);
|
|
4919
|
-
tmpDirs.push(tmpDir);
|
|
4920
|
-
return tmpDir;
|
|
4921
|
-
},
|
|
4922
|
-
output(name, value) {
|
|
4923
|
-
stepOutput[name] = value;
|
|
4924
|
-
},
|
|
4925
|
-
templateInfo: task.spec.templateInfo,
|
|
4926
|
-
user: task.spec.user,
|
|
4927
|
-
isDryRun: task.isDryRun
|
|
4928
|
-
});
|
|
4929
|
-
for (const tmpDir of tmpDirs) {
|
|
4930
|
-
await fs__default["default"].remove(tmpDir);
|
|
4931
|
-
}
|
|
4932
|
-
context.steps[step.id] = { output: stepOutput };
|
|
4933
|
-
await stepTrack.markSuccessful();
|
|
4934
|
-
} catch (err) {
|
|
4935
|
-
await taskTrack.markFailed(step, err);
|
|
4936
|
-
await stepTrack.markFailed();
|
|
4937
|
-
throw err;
|
|
4938
|
-
}
|
|
5099
|
+
await this.executeStep(
|
|
5100
|
+
task,
|
|
5101
|
+
step,
|
|
5102
|
+
context,
|
|
5103
|
+
renderTemplate,
|
|
5104
|
+
taskTrack,
|
|
5105
|
+
workspacePath
|
|
5106
|
+
);
|
|
4939
5107
|
}
|
|
4940
5108
|
const output = this.render(task.spec.output, context, renderTemplate);
|
|
4941
5109
|
await taskTrack.markSuccessful();
|
|
@@ -5002,8 +5170,21 @@ function scaffoldingTracker() {
|
|
|
5002
5170
|
});
|
|
5003
5171
|
taskTimer({ result: "failed" });
|
|
5004
5172
|
}
|
|
5173
|
+
async function markCancelled(step) {
|
|
5174
|
+
await task.emitLog(`Step ${step.id} has been cancelled.`, {
|
|
5175
|
+
stepId: step.id,
|
|
5176
|
+
status: "cancelled"
|
|
5177
|
+
});
|
|
5178
|
+
taskCount.inc({
|
|
5179
|
+
template,
|
|
5180
|
+
user,
|
|
5181
|
+
result: "cancelled"
|
|
5182
|
+
});
|
|
5183
|
+
taskTimer({ result: "cancelled" });
|
|
5184
|
+
}
|
|
5005
5185
|
return {
|
|
5006
5186
|
skipDryRun,
|
|
5187
|
+
markCancelled,
|
|
5007
5188
|
markSuccessful,
|
|
5008
5189
|
markFailed
|
|
5009
5190
|
};
|
|
@@ -5031,6 +5212,14 @@ function scaffoldingTracker() {
|
|
|
5031
5212
|
});
|
|
5032
5213
|
stepTimer({ result: "ok" });
|
|
5033
5214
|
}
|
|
5215
|
+
async function markCancelled() {
|
|
5216
|
+
stepCount.inc({
|
|
5217
|
+
template,
|
|
5218
|
+
step: step.name,
|
|
5219
|
+
result: "cancelled"
|
|
5220
|
+
});
|
|
5221
|
+
stepTimer({ result: "cancelled" });
|
|
5222
|
+
}
|
|
5034
5223
|
async function markFailed() {
|
|
5035
5224
|
stepCount.inc({
|
|
5036
5225
|
template,
|
|
@@ -5047,8 +5236,9 @@ function scaffoldingTracker() {
|
|
|
5047
5236
|
stepTimer({ result: "skipped" });
|
|
5048
5237
|
}
|
|
5049
5238
|
return {
|
|
5050
|
-
|
|
5239
|
+
markCancelled,
|
|
5051
5240
|
markFailed,
|
|
5241
|
+
markSuccessful,
|
|
5052
5242
|
skipFalsy
|
|
5053
5243
|
};
|
|
5054
5244
|
}
|
|
@@ -5172,6 +5362,7 @@ function createDryRunner(options) {
|
|
|
5172
5362
|
);
|
|
5173
5363
|
try {
|
|
5174
5364
|
await deserializeDirectoryContents(contentsPath, input.directoryContents);
|
|
5365
|
+
const abortSignal = new AbortController().signal;
|
|
5175
5366
|
const result = await workflowRunner.execute({
|
|
5176
5367
|
spec: {
|
|
5177
5368
|
...input.spec,
|
|
@@ -5195,6 +5386,7 @@ function createDryRunner(options) {
|
|
|
5195
5386
|
done: false,
|
|
5196
5387
|
isDryRun: true,
|
|
5197
5388
|
getWorkspaceName: async () => `dry-run-${dryRunId}`,
|
|
5389
|
+
cancelSignal: abortSignal,
|
|
5198
5390
|
async emitLog(message, logMetadata) {
|
|
5199
5391
|
if ((logMetadata == null ? void 0 : logMetadata.stepId) === dryRunId) {
|
|
5200
5392
|
return;
|
|
@@ -5206,7 +5398,7 @@ function createDryRunner(options) {
|
|
|
5206
5398
|
}
|
|
5207
5399
|
});
|
|
5208
5400
|
},
|
|
5209
|
-
async
|
|
5401
|
+
complete: async () => {
|
|
5210
5402
|
throw new Error("Not implemented");
|
|
5211
5403
|
}
|
|
5212
5404
|
});
|
|
@@ -5537,6 +5729,11 @@ async function createRouter(options) {
|
|
|
5537
5729
|
}
|
|
5538
5730
|
delete task.secrets;
|
|
5539
5731
|
res.status(200).json(task);
|
|
5732
|
+
}).post("/v2/tasks/:taskId/cancel", async (req, res) => {
|
|
5733
|
+
var _a;
|
|
5734
|
+
const { taskId } = req.params;
|
|
5735
|
+
await ((_a = taskBroker.cancel) == null ? void 0 : _a.call(taskBroker, taskId));
|
|
5736
|
+
res.status(200).json({ status: "cancelled" });
|
|
5540
5737
|
}).get("/v2/tasks/:taskId/eventstream", async (req, res) => {
|
|
5541
5738
|
const { taskId } = req.params;
|
|
5542
5739
|
const after = req.query.after !== void 0 ? Number(req.query.after) : void 0;
|