@backstage/plugin-scaffolder-backend 1.12.0-next.2 → 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 +55 -0
- package/alpha/package.json +1 -1
- package/dist/alpha.cjs.js +402 -164
- package/dist/alpha.cjs.js.map +1 -1
- package/dist/index.cjs.js +403 -164
- package/dist/index.cjs.js.map +1 -1
- package/dist/index.d.ts +35 -9
- package/package.json +19 -16
package/dist/index.cjs.js
CHANGED
|
@@ -11,6 +11,7 @@ var backendCommon = require('@backstage/backend-common');
|
|
|
11
11
|
var zod = require('zod');
|
|
12
12
|
var integration = require('@backstage/integration');
|
|
13
13
|
var path = require('path');
|
|
14
|
+
var luxon = require('luxon');
|
|
14
15
|
var globby = require('globby');
|
|
15
16
|
var isbinaryfile = require('isbinaryfile');
|
|
16
17
|
var vm2 = require('vm2');
|
|
@@ -26,7 +27,6 @@ var fs$1 = require('fs');
|
|
|
26
27
|
var limiterFactory = require('p-limit');
|
|
27
28
|
var node = require('@gitbeaker/node');
|
|
28
29
|
var uuid = require('uuid');
|
|
29
|
-
var luxon = require('luxon');
|
|
30
30
|
var ObservableImpl = require('zen-observable');
|
|
31
31
|
var PQueue = require('p-queue');
|
|
32
32
|
var winston = require('winston');
|
|
@@ -39,7 +39,7 @@ var express = require('express');
|
|
|
39
39
|
var Router = require('express-promise-router');
|
|
40
40
|
var url = require('url');
|
|
41
41
|
var os = require('os');
|
|
42
|
-
var
|
|
42
|
+
var pluginCatalogNode = require('@backstage/plugin-catalog-node');
|
|
43
43
|
|
|
44
44
|
function _interopDefaultLegacy (e) { return e && typeof e === 'object' && 'default' in e ? e : { 'default': e }; }
|
|
45
45
|
|
|
@@ -77,14 +77,14 @@ var express__default = /*#__PURE__*/_interopDefaultLegacy(express);
|
|
|
77
77
|
var Router__default = /*#__PURE__*/_interopDefaultLegacy(Router);
|
|
78
78
|
var os__default = /*#__PURE__*/_interopDefaultLegacy(os);
|
|
79
79
|
|
|
80
|
-
const id$
|
|
81
|
-
const examples$
|
|
80
|
+
const id$4 = "catalog:register";
|
|
81
|
+
const examples$4 = [
|
|
82
82
|
{
|
|
83
83
|
description: "Register with the catalog",
|
|
84
84
|
example: yaml__default["default"].stringify({
|
|
85
85
|
steps: [
|
|
86
86
|
{
|
|
87
|
-
action: id$
|
|
87
|
+
action: id$4,
|
|
88
88
|
id: "register-with-catalog",
|
|
89
89
|
name: "Register with the catalog",
|
|
90
90
|
input: {
|
|
@@ -98,9 +98,9 @@ const examples$3 = [
|
|
|
98
98
|
function createCatalogRegisterAction(options) {
|
|
99
99
|
const { catalogClient, integrations } = options;
|
|
100
100
|
return pluginScaffolderNode.createTemplateAction({
|
|
101
|
-
id: id$
|
|
101
|
+
id: id$4,
|
|
102
102
|
description: "Registers entities from a catalog descriptor file in the workspace into the software catalog.",
|
|
103
|
-
examples: examples$
|
|
103
|
+
examples: examples$4,
|
|
104
104
|
schema: {
|
|
105
105
|
input: {
|
|
106
106
|
oneOf: [
|
|
@@ -218,14 +218,14 @@ function createCatalogRegisterAction(options) {
|
|
|
218
218
|
});
|
|
219
219
|
}
|
|
220
220
|
|
|
221
|
-
const id$
|
|
222
|
-
const examples$
|
|
221
|
+
const id$3 = "catalog:write";
|
|
222
|
+
const examples$3 = [
|
|
223
223
|
{
|
|
224
224
|
description: "Write a catalog yaml file",
|
|
225
225
|
example: yaml__namespace.stringify({
|
|
226
226
|
steps: [
|
|
227
227
|
{
|
|
228
|
-
action: id$
|
|
228
|
+
action: id$3,
|
|
229
229
|
id: "create-catalog-info-file",
|
|
230
230
|
name: "Create catalog file",
|
|
231
231
|
input: {
|
|
@@ -250,7 +250,7 @@ const examples$2 = [
|
|
|
250
250
|
];
|
|
251
251
|
function createCatalogWriteAction() {
|
|
252
252
|
return pluginScaffolderNode.createTemplateAction({
|
|
253
|
-
id: id$
|
|
253
|
+
id: id$3,
|
|
254
254
|
description: "Writes the catalog-info.yaml for your template",
|
|
255
255
|
schema: {
|
|
256
256
|
input: zod.z.object({
|
|
@@ -261,7 +261,7 @@ function createCatalogWriteAction() {
|
|
|
261
261
|
)
|
|
262
262
|
})
|
|
263
263
|
},
|
|
264
|
-
examples: examples$
|
|
264
|
+
examples: examples$3,
|
|
265
265
|
supportsDryRun: true,
|
|
266
266
|
async handler(ctx) {
|
|
267
267
|
ctx.logStream.write(`Writing catalog-info.yaml`);
|
|
@@ -275,14 +275,14 @@ function createCatalogWriteAction() {
|
|
|
275
275
|
});
|
|
276
276
|
}
|
|
277
277
|
|
|
278
|
-
const id$
|
|
279
|
-
const examples$
|
|
278
|
+
const id$2 = "catalog:fetch";
|
|
279
|
+
const examples$2 = [
|
|
280
280
|
{
|
|
281
281
|
description: "Fetch entity by reference",
|
|
282
282
|
example: yaml__default["default"].stringify({
|
|
283
283
|
steps: [
|
|
284
284
|
{
|
|
285
|
-
action: id$
|
|
285
|
+
action: id$2,
|
|
286
286
|
id: "fetch",
|
|
287
287
|
name: "Fetch catalog entity",
|
|
288
288
|
input: {
|
|
@@ -291,17 +291,31 @@ const examples$1 = [
|
|
|
291
291
|
}
|
|
292
292
|
]
|
|
293
293
|
})
|
|
294
|
+
},
|
|
295
|
+
{
|
|
296
|
+
description: "Fetch multiple entities by referencse",
|
|
297
|
+
example: yaml__default["default"].stringify({
|
|
298
|
+
steps: [
|
|
299
|
+
{
|
|
300
|
+
action: id$2,
|
|
301
|
+
id: "fetchMultiple",
|
|
302
|
+
name: "Fetch catalog entities",
|
|
303
|
+
input: {
|
|
304
|
+
entityRefs: ["component:default/name"]
|
|
305
|
+
}
|
|
306
|
+
}
|
|
307
|
+
]
|
|
308
|
+
})
|
|
294
309
|
}
|
|
295
310
|
];
|
|
296
311
|
function createFetchCatalogEntityAction(options) {
|
|
297
312
|
const { catalogClient } = options;
|
|
298
313
|
return pluginScaffolderNode.createTemplateAction({
|
|
299
|
-
id: id$
|
|
300
|
-
description: "Returns entity from the catalog by entity reference",
|
|
301
|
-
examples: examples$
|
|
314
|
+
id: id$2,
|
|
315
|
+
description: "Returns entity or entities from the catalog by entity reference(s)",
|
|
316
|
+
examples: examples$2,
|
|
302
317
|
schema: {
|
|
303
318
|
input: {
|
|
304
|
-
required: ["entityRef"],
|
|
305
319
|
type: "object",
|
|
306
320
|
properties: {
|
|
307
321
|
entityRef: {
|
|
@@ -309,9 +323,14 @@ function createFetchCatalogEntityAction(options) {
|
|
|
309
323
|
title: "Entity reference",
|
|
310
324
|
description: "Entity reference of the entity to get"
|
|
311
325
|
},
|
|
326
|
+
entityRefs: {
|
|
327
|
+
type: "array",
|
|
328
|
+
title: "Entity references",
|
|
329
|
+
description: "Entity references of the entities to get"
|
|
330
|
+
},
|
|
312
331
|
optional: {
|
|
313
332
|
title: "Optional",
|
|
314
|
-
description: "
|
|
333
|
+
description: "Allow the entity or entities to optionally exist. Default: false",
|
|
315
334
|
type: "boolean"
|
|
316
335
|
}
|
|
317
336
|
}
|
|
@@ -322,40 +341,62 @@ function createFetchCatalogEntityAction(options) {
|
|
|
322
341
|
entity: {
|
|
323
342
|
title: "Entity found by the entity reference",
|
|
324
343
|
type: "object",
|
|
325
|
-
description: "Object containing same values used in the Entity schema."
|
|
344
|
+
description: "Object containing same values used in the Entity schema. Only when used with `entityRef` parameter."
|
|
345
|
+
},
|
|
346
|
+
entities: {
|
|
347
|
+
title: "Entities found by the entity references",
|
|
348
|
+
type: "array",
|
|
349
|
+
items: { type: "object" },
|
|
350
|
+
description: "Array containing objects with same values used in the Entity schema. Only when used with `entityRefs` parameter."
|
|
326
351
|
}
|
|
327
352
|
}
|
|
328
353
|
}
|
|
329
354
|
},
|
|
330
355
|
async handler(ctx) {
|
|
331
|
-
var _a;
|
|
332
|
-
const { entityRef, optional } = ctx.input;
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
356
|
+
var _a, _b;
|
|
357
|
+
const { entityRef, entityRefs, optional } = ctx.input;
|
|
358
|
+
if (!entityRef && !entityRefs) {
|
|
359
|
+
if (optional) {
|
|
360
|
+
return;
|
|
361
|
+
}
|
|
362
|
+
throw new Error("Missing entity reference or references");
|
|
363
|
+
}
|
|
364
|
+
if (entityRef) {
|
|
365
|
+
const entity = await catalogClient.getEntityByRef(entityRef, {
|
|
336
366
|
token: (_a = ctx.secrets) == null ? void 0 : _a.backstageToken
|
|
337
367
|
});
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
throw e;
|
|
368
|
+
if (!entity && !optional) {
|
|
369
|
+
throw new Error(`Entity ${entityRef} not found`);
|
|
341
370
|
}
|
|
371
|
+
ctx.output("entity", entity != null ? entity : null);
|
|
342
372
|
}
|
|
343
|
-
if (
|
|
344
|
-
|
|
373
|
+
if (entityRefs) {
|
|
374
|
+
const entities = await catalogClient.getEntitiesByRefs(
|
|
375
|
+
{ entityRefs },
|
|
376
|
+
{
|
|
377
|
+
token: (_b = ctx.secrets) == null ? void 0 : _b.backstageToken
|
|
378
|
+
}
|
|
379
|
+
);
|
|
380
|
+
const finalEntities = entities.items.map((e, i) => {
|
|
381
|
+
if (!e && !optional) {
|
|
382
|
+
throw new Error(`Entity ${entityRefs[i]} not found`);
|
|
383
|
+
}
|
|
384
|
+
return e != null ? e : null;
|
|
385
|
+
});
|
|
386
|
+
ctx.output("entities", finalEntities);
|
|
345
387
|
}
|
|
346
|
-
ctx.output("entity", entity != null ? entity : null);
|
|
347
388
|
}
|
|
348
389
|
});
|
|
349
390
|
}
|
|
350
391
|
|
|
351
|
-
const id = "debug:log";
|
|
352
|
-
const examples = [
|
|
392
|
+
const id$1 = "debug:log";
|
|
393
|
+
const examples$1 = [
|
|
353
394
|
{
|
|
354
395
|
description: "Write a debug message",
|
|
355
396
|
example: yaml__default["default"].stringify({
|
|
356
397
|
steps: [
|
|
357
398
|
{
|
|
358
|
-
action: id,
|
|
399
|
+
action: id$1,
|
|
359
400
|
id: "write-debug-line",
|
|
360
401
|
name: 'Write "Hello Backstage!" log line',
|
|
361
402
|
input: {
|
|
@@ -370,7 +411,7 @@ const examples = [
|
|
|
370
411
|
example: yaml__default["default"].stringify({
|
|
371
412
|
steps: [
|
|
372
413
|
{
|
|
373
|
-
action: id,
|
|
414
|
+
action: id$1,
|
|
374
415
|
id: "write-workspace-directory",
|
|
375
416
|
name: "List the workspace directory",
|
|
376
417
|
input: {
|
|
@@ -383,9 +424,9 @@ const examples = [
|
|
|
383
424
|
];
|
|
384
425
|
function createDebugLogAction() {
|
|
385
426
|
return pluginScaffolderNode.createTemplateAction({
|
|
386
|
-
id,
|
|
427
|
+
id: id$1,
|
|
387
428
|
description: "Writes a message into the log or lists all files in the workspace.",
|
|
388
|
-
examples,
|
|
429
|
+
examples: examples$1,
|
|
389
430
|
schema: {
|
|
390
431
|
input: {
|
|
391
432
|
type: "object",
|
|
@@ -432,6 +473,98 @@ async function recursiveReadDir(dir) {
|
|
|
432
473
|
return files.reduce((a, f) => a.concat(f), []);
|
|
433
474
|
}
|
|
434
475
|
|
|
476
|
+
const id = "debug:wait";
|
|
477
|
+
const MAX_WAIT_TIME_IN_ISO = "T00:00:30";
|
|
478
|
+
const examples = [
|
|
479
|
+
{
|
|
480
|
+
description: "Waiting for 5 seconds",
|
|
481
|
+
example: yaml__default["default"].stringify({
|
|
482
|
+
steps: [
|
|
483
|
+
{
|
|
484
|
+
action: id,
|
|
485
|
+
id: "wait-5sec",
|
|
486
|
+
name: "Waiting for 5 seconds",
|
|
487
|
+
input: {
|
|
488
|
+
seconds: 5
|
|
489
|
+
}
|
|
490
|
+
}
|
|
491
|
+
]
|
|
492
|
+
})
|
|
493
|
+
},
|
|
494
|
+
{
|
|
495
|
+
description: "Waiting for 5 minutes",
|
|
496
|
+
example: yaml__default["default"].stringify({
|
|
497
|
+
steps: [
|
|
498
|
+
{
|
|
499
|
+
action: id,
|
|
500
|
+
id: "wait-5min",
|
|
501
|
+
name: "Waiting for 5 minutes",
|
|
502
|
+
input: {
|
|
503
|
+
minutes: 5
|
|
504
|
+
}
|
|
505
|
+
}
|
|
506
|
+
]
|
|
507
|
+
})
|
|
508
|
+
}
|
|
509
|
+
];
|
|
510
|
+
function createWaitAction(options) {
|
|
511
|
+
const toDuration = (maxWaitTime) => {
|
|
512
|
+
if (maxWaitTime) {
|
|
513
|
+
if (maxWaitTime instanceof luxon.Duration) {
|
|
514
|
+
return maxWaitTime;
|
|
515
|
+
}
|
|
516
|
+
return luxon.Duration.fromObject(maxWaitTime);
|
|
517
|
+
}
|
|
518
|
+
return luxon.Duration.fromISOTime(MAX_WAIT_TIME_IN_ISO);
|
|
519
|
+
};
|
|
520
|
+
return pluginScaffolderNode.createTemplateAction({
|
|
521
|
+
id,
|
|
522
|
+
description: "Waits for a certain period of time.",
|
|
523
|
+
examples,
|
|
524
|
+
schema: {
|
|
525
|
+
input: {
|
|
526
|
+
type: "object",
|
|
527
|
+
properties: {
|
|
528
|
+
minutes: {
|
|
529
|
+
title: "Waiting period in minutes.",
|
|
530
|
+
type: "number"
|
|
531
|
+
},
|
|
532
|
+
seconds: {
|
|
533
|
+
title: "Waiting period in seconds.",
|
|
534
|
+
type: "number"
|
|
535
|
+
},
|
|
536
|
+
milliseconds: {
|
|
537
|
+
title: "Waiting period in milliseconds.",
|
|
538
|
+
type: "number"
|
|
539
|
+
}
|
|
540
|
+
}
|
|
541
|
+
}
|
|
542
|
+
},
|
|
543
|
+
async handler(ctx) {
|
|
544
|
+
const delayTime = luxon.Duration.fromObject(ctx.input);
|
|
545
|
+
const maxWait = toDuration(options == null ? void 0 : options.maxWaitTime);
|
|
546
|
+
if (delayTime.minus(maxWait).toMillis() > 0) {
|
|
547
|
+
throw new Error(
|
|
548
|
+
`Waiting duration is longer than the maximum threshold of ${maxWait.toHuman()}`
|
|
549
|
+
);
|
|
550
|
+
}
|
|
551
|
+
await new Promise((resolve) => {
|
|
552
|
+
var _a;
|
|
553
|
+
const controller = new AbortController();
|
|
554
|
+
const timeoutHandle = setTimeout(abort, delayTime.toMillis());
|
|
555
|
+
(_a = ctx.signal) == null ? void 0 : _a.addEventListener("abort", abort);
|
|
556
|
+
function abort() {
|
|
557
|
+
var _a2;
|
|
558
|
+
(_a2 = ctx.signal) == null ? void 0 : _a2.removeEventListener("abort", abort);
|
|
559
|
+
clearTimeout(timeoutHandle);
|
|
560
|
+
controller.abort();
|
|
561
|
+
resolve("finished");
|
|
562
|
+
}
|
|
563
|
+
});
|
|
564
|
+
}
|
|
565
|
+
});
|
|
566
|
+
}
|
|
567
|
+
|
|
435
568
|
async function fetchContents(options) {
|
|
436
569
|
const { reader, integrations, baseUrl, fetchUrl = ".", outputPath } = options;
|
|
437
570
|
let fetchUrlIsAbsolute = false;
|
|
@@ -3928,7 +4061,7 @@ const createPublishGitlabMergeRequestAction = (options) => {
|
|
|
3928
4061
|
title: "Gitlab Project path",
|
|
3929
4062
|
type: "string"
|
|
3930
4063
|
},
|
|
3931
|
-
|
|
4064
|
+
mergeRequestUrl: {
|
|
3932
4065
|
title: "MergeRequest(MR) URL",
|
|
3933
4066
|
type: "string",
|
|
3934
4067
|
description: "Link to the merge request in GitLab"
|
|
@@ -4100,6 +4233,7 @@ const createBuiltinActions = (options) => {
|
|
|
4100
4233
|
config
|
|
4101
4234
|
}),
|
|
4102
4235
|
createDebugLogAction(),
|
|
4236
|
+
createWaitAction(),
|
|
4103
4237
|
createCatalogRegisterAction({ catalogClient, integrations }),
|
|
4104
4238
|
createFetchCatalogEntityAction({ catalogClient }),
|
|
4105
4239
|
createCatalogWriteAction(),
|
|
@@ -4266,7 +4400,7 @@ class DatabaseTaskStore {
|
|
|
4266
4400
|
const updateCount = await tx("tasks").where({ id: task.id, status: "open" }).update({
|
|
4267
4401
|
status: "processing",
|
|
4268
4402
|
last_heartbeat_at: this.db.fn.now(),
|
|
4269
|
-
// remove the secrets when moving
|
|
4403
|
+
// remove the secrets when moving to processing state.
|
|
4270
4404
|
secrets: null
|
|
4271
4405
|
});
|
|
4272
4406
|
if (updateCount < 1) {
|
|
@@ -4314,7 +4448,7 @@ class DatabaseTaskStore {
|
|
|
4314
4448
|
async completeTask(options) {
|
|
4315
4449
|
const { taskId, status, eventBody } = options;
|
|
4316
4450
|
let oldStatus;
|
|
4317
|
-
if (
|
|
4451
|
+
if (["failed", "completed", "cancelled"].includes(status)) {
|
|
4318
4452
|
oldStatus = "processing";
|
|
4319
4453
|
} else {
|
|
4320
4454
|
throw new Error(
|
|
@@ -4325,6 +4459,30 @@ class DatabaseTaskStore {
|
|
|
4325
4459
|
const [task] = await tx("tasks").where({
|
|
4326
4460
|
id: taskId
|
|
4327
4461
|
}).limit(1).select();
|
|
4462
|
+
const updateTask = async (criteria) => {
|
|
4463
|
+
const updateCount = await tx("tasks").where(criteria).update({
|
|
4464
|
+
status
|
|
4465
|
+
});
|
|
4466
|
+
if (updateCount !== 1) {
|
|
4467
|
+
throw new errors.ConflictError(
|
|
4468
|
+
`Failed to update status to '${status}' for taskId ${taskId}`
|
|
4469
|
+
);
|
|
4470
|
+
}
|
|
4471
|
+
await tx("task_events").insert({
|
|
4472
|
+
task_id: taskId,
|
|
4473
|
+
event_type: "completion",
|
|
4474
|
+
body: JSON.stringify(eventBody)
|
|
4475
|
+
});
|
|
4476
|
+
};
|
|
4477
|
+
if (status === "cancelled") {
|
|
4478
|
+
await updateTask({
|
|
4479
|
+
id: taskId
|
|
4480
|
+
});
|
|
4481
|
+
return;
|
|
4482
|
+
}
|
|
4483
|
+
if (task.status === "cancelled") {
|
|
4484
|
+
return;
|
|
4485
|
+
}
|
|
4328
4486
|
if (!task) {
|
|
4329
4487
|
throw new Error(`No task with taskId ${taskId} found`);
|
|
4330
4488
|
}
|
|
@@ -4333,21 +4491,9 @@ class DatabaseTaskStore {
|
|
|
4333
4491
|
`Refusing to update status of run '${taskId}' to status '${status}' as it is currently '${task.status}', expected '${oldStatus}'`
|
|
4334
4492
|
);
|
|
4335
4493
|
}
|
|
4336
|
-
|
|
4494
|
+
await updateTask({
|
|
4337
4495
|
id: taskId,
|
|
4338
4496
|
status: oldStatus
|
|
4339
|
-
}).update({
|
|
4340
|
-
status
|
|
4341
|
-
});
|
|
4342
|
-
if (updateCount !== 1) {
|
|
4343
|
-
throw new errors.ConflictError(
|
|
4344
|
-
`Failed to update status to '${status}' for taskId ${taskId}`
|
|
4345
|
-
);
|
|
4346
|
-
}
|
|
4347
|
-
await tx("task_events").insert({
|
|
4348
|
-
task_id: taskId,
|
|
4349
|
-
event_type: "completion",
|
|
4350
|
-
body: JSON.stringify(eventBody)
|
|
4351
4497
|
});
|
|
4352
4498
|
});
|
|
4353
4499
|
}
|
|
@@ -4415,24 +4561,37 @@ class DatabaseTaskStore {
|
|
|
4415
4561
|
}
|
|
4416
4562
|
});
|
|
4417
4563
|
}
|
|
4564
|
+
async cancelTask(options) {
|
|
4565
|
+
const { taskId, body } = options;
|
|
4566
|
+
const serializedBody = JSON.stringify(body);
|
|
4567
|
+
await this.db("task_events").insert({
|
|
4568
|
+
task_id: taskId,
|
|
4569
|
+
event_type: "cancelled",
|
|
4570
|
+
body: serializedBody
|
|
4571
|
+
});
|
|
4572
|
+
}
|
|
4418
4573
|
}
|
|
4419
4574
|
|
|
4420
4575
|
class TaskManager {
|
|
4421
4576
|
// Runs heartbeat internally
|
|
4422
|
-
constructor(task, storage, logger) {
|
|
4577
|
+
constructor(task, storage, signal, logger) {
|
|
4423
4578
|
this.task = task;
|
|
4424
4579
|
this.storage = storage;
|
|
4580
|
+
this.signal = signal;
|
|
4425
4581
|
this.logger = logger;
|
|
4426
4582
|
this.isDone = false;
|
|
4427
4583
|
}
|
|
4428
|
-
static create(task, storage, logger) {
|
|
4429
|
-
const agent = new TaskManager(task, storage, logger);
|
|
4584
|
+
static create(task, storage, abortSignal, logger) {
|
|
4585
|
+
const agent = new TaskManager(task, storage, abortSignal, logger);
|
|
4430
4586
|
agent.startTimeout();
|
|
4431
4587
|
return agent;
|
|
4432
4588
|
}
|
|
4433
4589
|
get spec() {
|
|
4434
4590
|
return this.task.spec;
|
|
4435
4591
|
}
|
|
4592
|
+
get cancelSignal() {
|
|
4593
|
+
return this.signal;
|
|
4594
|
+
}
|
|
4436
4595
|
get secrets() {
|
|
4437
4596
|
return this.task.secrets;
|
|
4438
4597
|
}
|
|
@@ -4502,6 +4661,28 @@ class StorageTaskBroker {
|
|
|
4502
4661
|
}
|
|
4503
4662
|
return await this.storage.list({ createdBy: options == null ? void 0 : options.createdBy });
|
|
4504
4663
|
}
|
|
4664
|
+
async registerCancellable(taskId, abortController) {
|
|
4665
|
+
let shouldUnsubscribe = false;
|
|
4666
|
+
const subscription = this.event$({ taskId, after: void 0 }).subscribe({
|
|
4667
|
+
error: (_) => {
|
|
4668
|
+
subscription.unsubscribe();
|
|
4669
|
+
},
|
|
4670
|
+
next: ({ events }) => {
|
|
4671
|
+
for (const event of events) {
|
|
4672
|
+
if (event.type === "cancelled") {
|
|
4673
|
+
abortController.abort();
|
|
4674
|
+
shouldUnsubscribe = true;
|
|
4675
|
+
}
|
|
4676
|
+
if (event.type === "completion") {
|
|
4677
|
+
shouldUnsubscribe = true;
|
|
4678
|
+
}
|
|
4679
|
+
}
|
|
4680
|
+
if (shouldUnsubscribe) {
|
|
4681
|
+
subscription.unsubscribe();
|
|
4682
|
+
}
|
|
4683
|
+
}
|
|
4684
|
+
});
|
|
4685
|
+
}
|
|
4505
4686
|
/**
|
|
4506
4687
|
* {@inheritdoc TaskBroker.claim}
|
|
4507
4688
|
*/
|
|
@@ -4509,6 +4690,8 @@ class StorageTaskBroker {
|
|
|
4509
4690
|
for (; ; ) {
|
|
4510
4691
|
const pendingTask = await this.storage.claimTask();
|
|
4511
4692
|
if (pendingTask) {
|
|
4693
|
+
const abortController = new AbortController();
|
|
4694
|
+
await this.registerCancellable(pendingTask.id, abortController);
|
|
4512
4695
|
return TaskManager.create(
|
|
4513
4696
|
{
|
|
4514
4697
|
taskId: pendingTask.id,
|
|
@@ -4517,6 +4700,7 @@ class StorageTaskBroker {
|
|
|
4517
4700
|
createdBy: pendingTask.createdBy
|
|
4518
4701
|
},
|
|
4519
4702
|
this.storage,
|
|
4703
|
+
abortController.signal,
|
|
4520
4704
|
this.logger
|
|
4521
4705
|
);
|
|
4522
4706
|
}
|
|
@@ -4591,6 +4775,19 @@ class StorageTaskBroker {
|
|
|
4591
4775
|
this.deferredDispatch.resolve();
|
|
4592
4776
|
this.deferredDispatch = defer();
|
|
4593
4777
|
}
|
|
4778
|
+
async cancel(taskId) {
|
|
4779
|
+
var _a, _b;
|
|
4780
|
+
const { events } = await this.storage.listEvents({ taskId });
|
|
4781
|
+
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;
|
|
4782
|
+
await ((_b = (_a = this.storage).cancelTask) == null ? void 0 : _b.call(_a, {
|
|
4783
|
+
taskId,
|
|
4784
|
+
body: {
|
|
4785
|
+
message: `Step ${currentStepId} has been cancelled.`,
|
|
4786
|
+
stepId: currentStepId,
|
|
4787
|
+
status: "cancelled"
|
|
4788
|
+
}
|
|
4789
|
+
}));
|
|
4790
|
+
}
|
|
4594
4791
|
}
|
|
4595
4792
|
|
|
4596
4793
|
function isTruthy(value) {
|
|
@@ -4722,8 +4919,107 @@ class NunjucksWorkflowRunner {
|
|
|
4722
4919
|
return value;
|
|
4723
4920
|
});
|
|
4724
4921
|
}
|
|
4725
|
-
async
|
|
4922
|
+
async executeStep(task, step, context, renderTemplate, taskTrack, workspacePath) {
|
|
4726
4923
|
var _a, _b, _c, _d, _e, _f, _g;
|
|
4924
|
+
const stepTrack = await this.tracker.stepStart(task, step);
|
|
4925
|
+
if (task.cancelSignal.aborted) {
|
|
4926
|
+
throw new Error(`Step ${step.name} has been cancelled.`);
|
|
4927
|
+
}
|
|
4928
|
+
try {
|
|
4929
|
+
if (step.if) {
|
|
4930
|
+
const ifResult = await this.render(step.if, context, renderTemplate);
|
|
4931
|
+
if (!isTruthy(ifResult)) {
|
|
4932
|
+
await stepTrack.skipFalsy();
|
|
4933
|
+
return;
|
|
4934
|
+
}
|
|
4935
|
+
}
|
|
4936
|
+
const action = this.options.actionRegistry.get(step.action);
|
|
4937
|
+
const { taskLogger, streamLogger } = createStepLogger({ task, step });
|
|
4938
|
+
if (task.isDryRun) {
|
|
4939
|
+
const redactedSecrets = Object.fromEntries(
|
|
4940
|
+
Object.entries((_a = task.secrets) != null ? _a : {}).map((secret) => [
|
|
4941
|
+
secret[0],
|
|
4942
|
+
"[REDACTED]"
|
|
4943
|
+
])
|
|
4944
|
+
);
|
|
4945
|
+
const debugInput = (_b = step.input && this.render(
|
|
4946
|
+
step.input,
|
|
4947
|
+
{
|
|
4948
|
+
...context,
|
|
4949
|
+
secrets: redactedSecrets
|
|
4950
|
+
},
|
|
4951
|
+
renderTemplate
|
|
4952
|
+
)) != null ? _b : {};
|
|
4953
|
+
taskLogger.info(
|
|
4954
|
+
`Running ${action.id} in dry-run mode with inputs (secrets redacted): ${JSON.stringify(
|
|
4955
|
+
debugInput,
|
|
4956
|
+
void 0,
|
|
4957
|
+
2
|
|
4958
|
+
)}`
|
|
4959
|
+
);
|
|
4960
|
+
if (!action.supportsDryRun) {
|
|
4961
|
+
await taskTrack.skipDryRun(step, action);
|
|
4962
|
+
const outputSchema = (_c = action.schema) == null ? void 0 : _c.output;
|
|
4963
|
+
if (outputSchema) {
|
|
4964
|
+
context.steps[step.id] = {
|
|
4965
|
+
output: generateExampleOutput(outputSchema)
|
|
4966
|
+
};
|
|
4967
|
+
} else {
|
|
4968
|
+
context.steps[step.id] = { output: {} };
|
|
4969
|
+
}
|
|
4970
|
+
return;
|
|
4971
|
+
}
|
|
4972
|
+
}
|
|
4973
|
+
const input = (_e = step.input && this.render(
|
|
4974
|
+
step.input,
|
|
4975
|
+
{ ...context, secrets: (_d = task.secrets) != null ? _d : {} },
|
|
4976
|
+
renderTemplate
|
|
4977
|
+
)) != null ? _e : {};
|
|
4978
|
+
if ((_f = action.schema) == null ? void 0 : _f.input) {
|
|
4979
|
+
const validateResult = jsonschema.validate(input, action.schema.input);
|
|
4980
|
+
if (!validateResult.valid) {
|
|
4981
|
+
const errors$1 = validateResult.errors.join(", ");
|
|
4982
|
+
throw new errors.InputError(
|
|
4983
|
+
`Invalid input passed to action ${action.id}, ${errors$1}`
|
|
4984
|
+
);
|
|
4985
|
+
}
|
|
4986
|
+
}
|
|
4987
|
+
const tmpDirs = new Array();
|
|
4988
|
+
const stepOutput = {};
|
|
4989
|
+
await action.handler({
|
|
4990
|
+
input,
|
|
4991
|
+
secrets: (_g = task.secrets) != null ? _g : {},
|
|
4992
|
+
logger: taskLogger,
|
|
4993
|
+
logStream: streamLogger,
|
|
4994
|
+
workspacePath,
|
|
4995
|
+
createTemporaryDirectory: async () => {
|
|
4996
|
+
const tmpDir = await fs__default["default"].mkdtemp(`${workspacePath}_step-${step.id}-`);
|
|
4997
|
+
tmpDirs.push(tmpDir);
|
|
4998
|
+
return tmpDir;
|
|
4999
|
+
},
|
|
5000
|
+
output(name, value) {
|
|
5001
|
+
stepOutput[name] = value;
|
|
5002
|
+
},
|
|
5003
|
+
templateInfo: task.spec.templateInfo,
|
|
5004
|
+
user: task.spec.user,
|
|
5005
|
+
isDryRun: task.isDryRun,
|
|
5006
|
+
signal: task.cancelSignal
|
|
5007
|
+
});
|
|
5008
|
+
for (const tmpDir of tmpDirs) {
|
|
5009
|
+
await fs__default["default"].remove(tmpDir);
|
|
5010
|
+
}
|
|
5011
|
+
context.steps[step.id] = { output: stepOutput };
|
|
5012
|
+
if (task.cancelSignal.aborted) {
|
|
5013
|
+
throw new Error(`Step ${step.name} has been cancelled.`);
|
|
5014
|
+
}
|
|
5015
|
+
await stepTrack.markSuccessful();
|
|
5016
|
+
} catch (err) {
|
|
5017
|
+
await taskTrack.markFailed(step, err);
|
|
5018
|
+
await stepTrack.markFailed();
|
|
5019
|
+
throw err;
|
|
5020
|
+
}
|
|
5021
|
+
}
|
|
5022
|
+
async execute(task) {
|
|
4727
5023
|
if (!isValidTaskSpec(task.spec)) {
|
|
4728
5024
|
throw new errors.InputError(
|
|
4729
5025
|
"Wrong template version executed with the workflow engine"
|
|
@@ -4733,7 +5029,11 @@ class NunjucksWorkflowRunner {
|
|
|
4733
5029
|
this.options.workingDirectory,
|
|
4734
5030
|
await task.getWorkspaceName()
|
|
4735
5031
|
);
|
|
4736
|
-
const {
|
|
5032
|
+
const {
|
|
5033
|
+
additionalTemplateFilters,
|
|
5034
|
+
additionalTemplateGlobals,
|
|
5035
|
+
integrations
|
|
5036
|
+
} = this.options;
|
|
4737
5037
|
const renderTemplate = await SecureTemplater.loadRenderer({
|
|
4738
5038
|
// TODO(blam): let's work out how we can deprecate this.
|
|
4739
5039
|
// We shouldn't really need to be exposing these now we can deal with
|
|
@@ -4742,8 +5042,8 @@ class NunjucksWorkflowRunner {
|
|
|
4742
5042
|
parseRepoUrl(url) {
|
|
4743
5043
|
return parseRepoUrl(url, integrations);
|
|
4744
5044
|
},
|
|
4745
|
-
additionalTemplateFilters
|
|
4746
|
-
additionalTemplateGlobals
|
|
5045
|
+
additionalTemplateFilters,
|
|
5046
|
+
additionalTemplateGlobals
|
|
4747
5047
|
});
|
|
4748
5048
|
try {
|
|
4749
5049
|
const taskTrack = await this.tracker.taskStart(task);
|
|
@@ -4754,105 +5054,14 @@ class NunjucksWorkflowRunner {
|
|
|
4754
5054
|
user: task.spec.user
|
|
4755
5055
|
};
|
|
4756
5056
|
for (const step of task.spec.steps) {
|
|
4757
|
-
|
|
4758
|
-
|
|
4759
|
-
|
|
4760
|
-
|
|
4761
|
-
|
|
4762
|
-
|
|
4763
|
-
|
|
4764
|
-
|
|
4765
|
-
if (!isTruthy(ifResult)) {
|
|
4766
|
-
await stepTrack.skipFalsy();
|
|
4767
|
-
continue;
|
|
4768
|
-
}
|
|
4769
|
-
}
|
|
4770
|
-
const action = this.options.actionRegistry.get(step.action);
|
|
4771
|
-
const { taskLogger, streamLogger } = createStepLogger({ task, step });
|
|
4772
|
-
if (task.isDryRun) {
|
|
4773
|
-
const redactedSecrets = Object.fromEntries(
|
|
4774
|
-
Object.entries((_a = task.secrets) != null ? _a : {}).map((secret) => [
|
|
4775
|
-
secret[0],
|
|
4776
|
-
"[REDACTED]"
|
|
4777
|
-
])
|
|
4778
|
-
);
|
|
4779
|
-
const debugInput = (_b = step.input && this.render(
|
|
4780
|
-
step.input,
|
|
4781
|
-
{
|
|
4782
|
-
...context,
|
|
4783
|
-
secrets: redactedSecrets
|
|
4784
|
-
},
|
|
4785
|
-
renderTemplate
|
|
4786
|
-
)) != null ? _b : {};
|
|
4787
|
-
taskLogger.info(
|
|
4788
|
-
`Running ${action.id} in dry-run mode with inputs (secrets redacted): ${JSON.stringify(
|
|
4789
|
-
debugInput,
|
|
4790
|
-
void 0,
|
|
4791
|
-
2
|
|
4792
|
-
)}`
|
|
4793
|
-
);
|
|
4794
|
-
if (!action.supportsDryRun) {
|
|
4795
|
-
await taskTrack.skipDryRun(step, action);
|
|
4796
|
-
const outputSchema = (_c = action.schema) == null ? void 0 : _c.output;
|
|
4797
|
-
if (outputSchema) {
|
|
4798
|
-
context.steps[step.id] = {
|
|
4799
|
-
output: generateExampleOutput(outputSchema)
|
|
4800
|
-
};
|
|
4801
|
-
} else {
|
|
4802
|
-
context.steps[step.id] = { output: {} };
|
|
4803
|
-
}
|
|
4804
|
-
continue;
|
|
4805
|
-
}
|
|
4806
|
-
}
|
|
4807
|
-
const input = (_e = step.input && this.render(
|
|
4808
|
-
step.input,
|
|
4809
|
-
{ ...context, secrets: (_d = task.secrets) != null ? _d : {} },
|
|
4810
|
-
renderTemplate
|
|
4811
|
-
)) != null ? _e : {};
|
|
4812
|
-
if ((_f = action.schema) == null ? void 0 : _f.input) {
|
|
4813
|
-
const validateResult = jsonschema.validate(
|
|
4814
|
-
input,
|
|
4815
|
-
action.schema.input
|
|
4816
|
-
);
|
|
4817
|
-
if (!validateResult.valid) {
|
|
4818
|
-
const errors$1 = validateResult.errors.join(", ");
|
|
4819
|
-
throw new errors.InputError(
|
|
4820
|
-
`Invalid input passed to action ${action.id}, ${errors$1}`
|
|
4821
|
-
);
|
|
4822
|
-
}
|
|
4823
|
-
}
|
|
4824
|
-
const tmpDirs = new Array();
|
|
4825
|
-
const stepOutput = {};
|
|
4826
|
-
await action.handler({
|
|
4827
|
-
input,
|
|
4828
|
-
secrets: (_g = task.secrets) != null ? _g : {},
|
|
4829
|
-
logger: taskLogger,
|
|
4830
|
-
logStream: streamLogger,
|
|
4831
|
-
workspacePath,
|
|
4832
|
-
createTemporaryDirectory: async () => {
|
|
4833
|
-
const tmpDir = await fs__default["default"].mkdtemp(
|
|
4834
|
-
`${workspacePath}_step-${step.id}-`
|
|
4835
|
-
);
|
|
4836
|
-
tmpDirs.push(tmpDir);
|
|
4837
|
-
return tmpDir;
|
|
4838
|
-
},
|
|
4839
|
-
output(name, value) {
|
|
4840
|
-
stepOutput[name] = value;
|
|
4841
|
-
},
|
|
4842
|
-
templateInfo: task.spec.templateInfo,
|
|
4843
|
-
user: task.spec.user,
|
|
4844
|
-
isDryRun: task.isDryRun
|
|
4845
|
-
});
|
|
4846
|
-
for (const tmpDir of tmpDirs) {
|
|
4847
|
-
await fs__default["default"].remove(tmpDir);
|
|
4848
|
-
}
|
|
4849
|
-
context.steps[step.id] = { output: stepOutput };
|
|
4850
|
-
await stepTrack.markSuccessful();
|
|
4851
|
-
} catch (err) {
|
|
4852
|
-
await taskTrack.markFailed(step, err);
|
|
4853
|
-
await stepTrack.markFailed();
|
|
4854
|
-
throw err;
|
|
4855
|
-
}
|
|
5057
|
+
await this.executeStep(
|
|
5058
|
+
task,
|
|
5059
|
+
step,
|
|
5060
|
+
context,
|
|
5061
|
+
renderTemplate,
|
|
5062
|
+
taskTrack,
|
|
5063
|
+
workspacePath
|
|
5064
|
+
);
|
|
4856
5065
|
}
|
|
4857
5066
|
const output = this.render(task.spec.output, context, renderTemplate);
|
|
4858
5067
|
await taskTrack.markSuccessful();
|
|
@@ -4919,8 +5128,21 @@ function scaffoldingTracker() {
|
|
|
4919
5128
|
});
|
|
4920
5129
|
taskTimer({ result: "failed" });
|
|
4921
5130
|
}
|
|
5131
|
+
async function markCancelled(step) {
|
|
5132
|
+
await task.emitLog(`Step ${step.id} has been cancelled.`, {
|
|
5133
|
+
stepId: step.id,
|
|
5134
|
+
status: "cancelled"
|
|
5135
|
+
});
|
|
5136
|
+
taskCount.inc({
|
|
5137
|
+
template,
|
|
5138
|
+
user,
|
|
5139
|
+
result: "cancelled"
|
|
5140
|
+
});
|
|
5141
|
+
taskTimer({ result: "cancelled" });
|
|
5142
|
+
}
|
|
4922
5143
|
return {
|
|
4923
5144
|
skipDryRun,
|
|
5145
|
+
markCancelled,
|
|
4924
5146
|
markSuccessful,
|
|
4925
5147
|
markFailed
|
|
4926
5148
|
};
|
|
@@ -4948,6 +5170,14 @@ function scaffoldingTracker() {
|
|
|
4948
5170
|
});
|
|
4949
5171
|
stepTimer({ result: "ok" });
|
|
4950
5172
|
}
|
|
5173
|
+
async function markCancelled() {
|
|
5174
|
+
stepCount.inc({
|
|
5175
|
+
template,
|
|
5176
|
+
step: step.name,
|
|
5177
|
+
result: "cancelled"
|
|
5178
|
+
});
|
|
5179
|
+
stepTimer({ result: "cancelled" });
|
|
5180
|
+
}
|
|
4951
5181
|
async function markFailed() {
|
|
4952
5182
|
stepCount.inc({
|
|
4953
5183
|
template,
|
|
@@ -4964,8 +5194,9 @@ function scaffoldingTracker() {
|
|
|
4964
5194
|
stepTimer({ result: "skipped" });
|
|
4965
5195
|
}
|
|
4966
5196
|
return {
|
|
4967
|
-
|
|
5197
|
+
markCancelled,
|
|
4968
5198
|
markFailed,
|
|
5199
|
+
markSuccessful,
|
|
4969
5200
|
skipFalsy
|
|
4970
5201
|
};
|
|
4971
5202
|
}
|
|
@@ -5089,6 +5320,7 @@ function createDryRunner(options) {
|
|
|
5089
5320
|
);
|
|
5090
5321
|
try {
|
|
5091
5322
|
await deserializeDirectoryContents(contentsPath, input.directoryContents);
|
|
5323
|
+
const abortSignal = new AbortController().signal;
|
|
5092
5324
|
const result = await workflowRunner.execute({
|
|
5093
5325
|
spec: {
|
|
5094
5326
|
...input.spec,
|
|
@@ -5112,6 +5344,7 @@ function createDryRunner(options) {
|
|
|
5112
5344
|
done: false,
|
|
5113
5345
|
isDryRun: true,
|
|
5114
5346
|
getWorkspaceName: async () => `dry-run-${dryRunId}`,
|
|
5347
|
+
cancelSignal: abortSignal,
|
|
5115
5348
|
async emitLog(message, logMetadata) {
|
|
5116
5349
|
if ((logMetadata == null ? void 0 : logMetadata.stepId) === dryRunId) {
|
|
5117
5350
|
return;
|
|
@@ -5123,7 +5356,7 @@ function createDryRunner(options) {
|
|
|
5123
5356
|
}
|
|
5124
5357
|
});
|
|
5125
5358
|
},
|
|
5126
|
-
async
|
|
5359
|
+
complete: async () => {
|
|
5127
5360
|
throw new Error("Not implemented");
|
|
5128
5361
|
}
|
|
5129
5362
|
});
|
|
@@ -5454,6 +5687,11 @@ async function createRouter(options) {
|
|
|
5454
5687
|
}
|
|
5455
5688
|
delete task.secrets;
|
|
5456
5689
|
res.status(200).json(task);
|
|
5690
|
+
}).post("/v2/tasks/:taskId/cancel", async (req, res) => {
|
|
5691
|
+
var _a;
|
|
5692
|
+
const { taskId } = req.params;
|
|
5693
|
+
await ((_a = taskBroker.cancel) == null ? void 0 : _a.call(taskBroker, taskId));
|
|
5694
|
+
res.status(200).json({ status: "cancelled" });
|
|
5457
5695
|
}).get("/v2/tasks/:taskId/eventstream", async (req, res) => {
|
|
5458
5696
|
const { taskId } = req.params;
|
|
5459
5697
|
const after = req.query.after !== void 0 ? Number(req.query.after) : void 0;
|
|
@@ -5610,7 +5848,7 @@ class ScaffolderEntitiesProcessor {
|
|
|
5610
5848
|
defaultNamespace: selfRef.namespace
|
|
5611
5849
|
});
|
|
5612
5850
|
emit(
|
|
5613
|
-
|
|
5851
|
+
pluginCatalogNode.processingResult.relation({
|
|
5614
5852
|
source: selfRef,
|
|
5615
5853
|
type: catalogModel.RELATION_OWNED_BY,
|
|
5616
5854
|
target: {
|
|
@@ -5621,7 +5859,7 @@ class ScaffolderEntitiesProcessor {
|
|
|
5621
5859
|
})
|
|
5622
5860
|
);
|
|
5623
5861
|
emit(
|
|
5624
|
-
|
|
5862
|
+
pluginCatalogNode.processingResult.relation({
|
|
5625
5863
|
source: {
|
|
5626
5864
|
kind: targetRef.kind,
|
|
5627
5865
|
namespace: targetRef.namespace,
|
|
@@ -5670,6 +5908,7 @@ exports.createPublishGitlabAction = createPublishGitlabAction;
|
|
|
5670
5908
|
exports.createPublishGitlabMergeRequestAction = createPublishGitlabMergeRequestAction;
|
|
5671
5909
|
exports.createRouter = createRouter;
|
|
5672
5910
|
exports.createTemplateAction = createTemplateAction;
|
|
5911
|
+
exports.createWaitAction = createWaitAction;
|
|
5673
5912
|
exports.executeShellCommand = executeShellCommand;
|
|
5674
5913
|
exports.fetchContents = fetchContents;
|
|
5675
5914
|
//# sourceMappingURL=index.cjs.js.map
|