@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/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 pluginCatalogBackend = require('@backstage/plugin-catalog-backend');
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$3 = "catalog:register";
81
- const examples$3 = [
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$3,
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$3,
101
+ id: id$4,
102
102
  description: "Registers entities from a catalog descriptor file in the workspace into the software catalog.",
103
- examples: examples$3,
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$2 = "catalog:write";
222
- const examples$2 = [
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$2,
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$2,
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$2,
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$1 = "catalog:fetch";
279
- const examples$1 = [
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$1,
285
+ action: id$2,
286
286
  id: "fetch",
287
287
  name: "Fetch catalog entity",
288
288
  input: {
@@ -297,7 +297,7 @@ const examples$1 = [
297
297
  example: yaml__default["default"].stringify({
298
298
  steps: [
299
299
  {
300
- action: id$1,
300
+ action: id$2,
301
301
  id: "fetchMultiple",
302
302
  name: "Fetch catalog entities",
303
303
  input: {
@@ -311,9 +311,9 @@ const examples$1 = [
311
311
  function createFetchCatalogEntityAction(options) {
312
312
  const { catalogClient } = options;
313
313
  return pluginScaffolderNode.createTemplateAction({
314
- id: id$1,
314
+ id: id$2,
315
315
  description: "Returns entity or entities from the catalog by entity reference(s)",
316
- examples: examples$1,
316
+ examples: examples$2,
317
317
  schema: {
318
318
  input: {
319
319
  type: "object",
@@ -389,14 +389,14 @@ function createFetchCatalogEntityAction(options) {
389
389
  });
390
390
  }
391
391
 
392
- const id = "debug:log";
393
- const examples = [
392
+ const id$1 = "debug:log";
393
+ const examples$1 = [
394
394
  {
395
395
  description: "Write a debug message",
396
396
  example: yaml__default["default"].stringify({
397
397
  steps: [
398
398
  {
399
- action: id,
399
+ action: id$1,
400
400
  id: "write-debug-line",
401
401
  name: 'Write "Hello Backstage!" log line',
402
402
  input: {
@@ -411,7 +411,7 @@ const examples = [
411
411
  example: yaml__default["default"].stringify({
412
412
  steps: [
413
413
  {
414
- action: id,
414
+ action: id$1,
415
415
  id: "write-workspace-directory",
416
416
  name: "List the workspace directory",
417
417
  input: {
@@ -424,9 +424,9 @@ const examples = [
424
424
  ];
425
425
  function createDebugLogAction() {
426
426
  return pluginScaffolderNode.createTemplateAction({
427
- id,
427
+ id: id$1,
428
428
  description: "Writes a message into the log or lists all files in the workspace.",
429
- examples,
429
+ examples: examples$1,
430
430
  schema: {
431
431
  input: {
432
432
  type: "object",
@@ -473,6 +473,98 @@ async function recursiveReadDir(dir) {
473
473
  return files.reduce((a, f) => a.concat(f), []);
474
474
  }
475
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
+
476
568
  async function fetchContents(options) {
477
569
  const { reader, integrations, baseUrl, fetchUrl = ".", outputPath } = options;
478
570
  let fetchUrlIsAbsolute = false;
@@ -3969,7 +4061,7 @@ const createPublishGitlabMergeRequestAction = (options) => {
3969
4061
  title: "Gitlab Project path",
3970
4062
  type: "string"
3971
4063
  },
3972
- mergeRequestURL: {
4064
+ mergeRequestUrl: {
3973
4065
  title: "MergeRequest(MR) URL",
3974
4066
  type: "string",
3975
4067
  description: "Link to the merge request in GitLab"
@@ -4141,6 +4233,7 @@ const createBuiltinActions = (options) => {
4141
4233
  config
4142
4234
  }),
4143
4235
  createDebugLogAction(),
4236
+ createWaitAction(),
4144
4237
  createCatalogRegisterAction({ catalogClient, integrations }),
4145
4238
  createFetchCatalogEntityAction({ catalogClient }),
4146
4239
  createCatalogWriteAction(),
@@ -4307,7 +4400,7 @@ class DatabaseTaskStore {
4307
4400
  const updateCount = await tx("tasks").where({ id: task.id, status: "open" }).update({
4308
4401
  status: "processing",
4309
4402
  last_heartbeat_at: this.db.fn.now(),
4310
- // remove the secrets when moving moving to processing state.
4403
+ // remove the secrets when moving to processing state.
4311
4404
  secrets: null
4312
4405
  });
4313
4406
  if (updateCount < 1) {
@@ -4355,7 +4448,7 @@ class DatabaseTaskStore {
4355
4448
  async completeTask(options) {
4356
4449
  const { taskId, status, eventBody } = options;
4357
4450
  let oldStatus;
4358
- if (status === "failed" || status === "completed") {
4451
+ if (["failed", "completed", "cancelled"].includes(status)) {
4359
4452
  oldStatus = "processing";
4360
4453
  } else {
4361
4454
  throw new Error(
@@ -4366,6 +4459,30 @@ class DatabaseTaskStore {
4366
4459
  const [task] = await tx("tasks").where({
4367
4460
  id: taskId
4368
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
+ }
4369
4486
  if (!task) {
4370
4487
  throw new Error(`No task with taskId ${taskId} found`);
4371
4488
  }
@@ -4374,21 +4491,9 @@ class DatabaseTaskStore {
4374
4491
  `Refusing to update status of run '${taskId}' to status '${status}' as it is currently '${task.status}', expected '${oldStatus}'`
4375
4492
  );
4376
4493
  }
4377
- const updateCount = await tx("tasks").where({
4494
+ await updateTask({
4378
4495
  id: taskId,
4379
4496
  status: oldStatus
4380
- }).update({
4381
- status
4382
- });
4383
- if (updateCount !== 1) {
4384
- throw new errors.ConflictError(
4385
- `Failed to update status to '${status}' for taskId ${taskId}`
4386
- );
4387
- }
4388
- await tx("task_events").insert({
4389
- task_id: taskId,
4390
- event_type: "completion",
4391
- body: JSON.stringify(eventBody)
4392
4497
  });
4393
4498
  });
4394
4499
  }
@@ -4456,24 +4561,37 @@ class DatabaseTaskStore {
4456
4561
  }
4457
4562
  });
4458
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
+ }
4459
4573
  }
4460
4574
 
4461
4575
  class TaskManager {
4462
4576
  // Runs heartbeat internally
4463
- constructor(task, storage, logger) {
4577
+ constructor(task, storage, signal, logger) {
4464
4578
  this.task = task;
4465
4579
  this.storage = storage;
4580
+ this.signal = signal;
4466
4581
  this.logger = logger;
4467
4582
  this.isDone = false;
4468
4583
  }
4469
- static create(task, storage, logger) {
4470
- const agent = new TaskManager(task, storage, logger);
4584
+ static create(task, storage, abortSignal, logger) {
4585
+ const agent = new TaskManager(task, storage, abortSignal, logger);
4471
4586
  agent.startTimeout();
4472
4587
  return agent;
4473
4588
  }
4474
4589
  get spec() {
4475
4590
  return this.task.spec;
4476
4591
  }
4592
+ get cancelSignal() {
4593
+ return this.signal;
4594
+ }
4477
4595
  get secrets() {
4478
4596
  return this.task.secrets;
4479
4597
  }
@@ -4543,6 +4661,28 @@ class StorageTaskBroker {
4543
4661
  }
4544
4662
  return await this.storage.list({ createdBy: options == null ? void 0 : options.createdBy });
4545
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
+ }
4546
4686
  /**
4547
4687
  * {@inheritdoc TaskBroker.claim}
4548
4688
  */
@@ -4550,6 +4690,8 @@ class StorageTaskBroker {
4550
4690
  for (; ; ) {
4551
4691
  const pendingTask = await this.storage.claimTask();
4552
4692
  if (pendingTask) {
4693
+ const abortController = new AbortController();
4694
+ await this.registerCancellable(pendingTask.id, abortController);
4553
4695
  return TaskManager.create(
4554
4696
  {
4555
4697
  taskId: pendingTask.id,
@@ -4558,6 +4700,7 @@ class StorageTaskBroker {
4558
4700
  createdBy: pendingTask.createdBy
4559
4701
  },
4560
4702
  this.storage,
4703
+ abortController.signal,
4561
4704
  this.logger
4562
4705
  );
4563
4706
  }
@@ -4632,6 +4775,19 @@ class StorageTaskBroker {
4632
4775
  this.deferredDispatch.resolve();
4633
4776
  this.deferredDispatch = defer();
4634
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
+ }
4635
4791
  }
4636
4792
 
4637
4793
  function isTruthy(value) {
@@ -4763,8 +4919,107 @@ class NunjucksWorkflowRunner {
4763
4919
  return value;
4764
4920
  });
4765
4921
  }
4766
- async execute(task) {
4922
+ async executeStep(task, step, context, renderTemplate, taskTrack, workspacePath) {
4767
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) {
4768
5023
  if (!isValidTaskSpec(task.spec)) {
4769
5024
  throw new errors.InputError(
4770
5025
  "Wrong template version executed with the workflow engine"
@@ -4774,7 +5029,11 @@ class NunjucksWorkflowRunner {
4774
5029
  this.options.workingDirectory,
4775
5030
  await task.getWorkspaceName()
4776
5031
  );
4777
- const { integrations } = this.options;
5032
+ const {
5033
+ additionalTemplateFilters,
5034
+ additionalTemplateGlobals,
5035
+ integrations
5036
+ } = this.options;
4778
5037
  const renderTemplate = await SecureTemplater.loadRenderer({
4779
5038
  // TODO(blam): let's work out how we can deprecate this.
4780
5039
  // We shouldn't really need to be exposing these now we can deal with
@@ -4783,8 +5042,8 @@ class NunjucksWorkflowRunner {
4783
5042
  parseRepoUrl(url) {
4784
5043
  return parseRepoUrl(url, integrations);
4785
5044
  },
4786
- additionalTemplateFilters: this.options.additionalTemplateFilters,
4787
- additionalTemplateGlobals: this.options.additionalTemplateGlobals
5045
+ additionalTemplateFilters,
5046
+ additionalTemplateGlobals
4788
5047
  });
4789
5048
  try {
4790
5049
  const taskTrack = await this.tracker.taskStart(task);
@@ -4795,105 +5054,14 @@ class NunjucksWorkflowRunner {
4795
5054
  user: task.spec.user
4796
5055
  };
4797
5056
  for (const step of task.spec.steps) {
4798
- const stepTrack = await this.tracker.stepStart(task, step);
4799
- try {
4800
- if (step.if) {
4801
- const ifResult = await this.render(
4802
- step.if,
4803
- context,
4804
- renderTemplate
4805
- );
4806
- if (!isTruthy(ifResult)) {
4807
- await stepTrack.skipFalsy();
4808
- continue;
4809
- }
4810
- }
4811
- const action = this.options.actionRegistry.get(step.action);
4812
- const { taskLogger, streamLogger } = createStepLogger({ task, step });
4813
- if (task.isDryRun) {
4814
- const redactedSecrets = Object.fromEntries(
4815
- Object.entries((_a = task.secrets) != null ? _a : {}).map((secret) => [
4816
- secret[0],
4817
- "[REDACTED]"
4818
- ])
4819
- );
4820
- const debugInput = (_b = step.input && this.render(
4821
- step.input,
4822
- {
4823
- ...context,
4824
- secrets: redactedSecrets
4825
- },
4826
- renderTemplate
4827
- )) != null ? _b : {};
4828
- taskLogger.info(
4829
- `Running ${action.id} in dry-run mode with inputs (secrets redacted): ${JSON.stringify(
4830
- debugInput,
4831
- void 0,
4832
- 2
4833
- )}`
4834
- );
4835
- if (!action.supportsDryRun) {
4836
- await taskTrack.skipDryRun(step, action);
4837
- const outputSchema = (_c = action.schema) == null ? void 0 : _c.output;
4838
- if (outputSchema) {
4839
- context.steps[step.id] = {
4840
- output: generateExampleOutput(outputSchema)
4841
- };
4842
- } else {
4843
- context.steps[step.id] = { output: {} };
4844
- }
4845
- continue;
4846
- }
4847
- }
4848
- const input = (_e = step.input && this.render(
4849
- step.input,
4850
- { ...context, secrets: (_d = task.secrets) != null ? _d : {} },
4851
- renderTemplate
4852
- )) != null ? _e : {};
4853
- if ((_f = action.schema) == null ? void 0 : _f.input) {
4854
- const validateResult = jsonschema.validate(
4855
- input,
4856
- action.schema.input
4857
- );
4858
- if (!validateResult.valid) {
4859
- const errors$1 = validateResult.errors.join(", ");
4860
- throw new errors.InputError(
4861
- `Invalid input passed to action ${action.id}, ${errors$1}`
4862
- );
4863
- }
4864
- }
4865
- const tmpDirs = new Array();
4866
- const stepOutput = {};
4867
- await action.handler({
4868
- input,
4869
- secrets: (_g = task.secrets) != null ? _g : {},
4870
- logger: taskLogger,
4871
- logStream: streamLogger,
4872
- workspacePath,
4873
- createTemporaryDirectory: async () => {
4874
- const tmpDir = await fs__default["default"].mkdtemp(
4875
- `${workspacePath}_step-${step.id}-`
4876
- );
4877
- tmpDirs.push(tmpDir);
4878
- return tmpDir;
4879
- },
4880
- output(name, value) {
4881
- stepOutput[name] = value;
4882
- },
4883
- templateInfo: task.spec.templateInfo,
4884
- user: task.spec.user,
4885
- isDryRun: task.isDryRun
4886
- });
4887
- for (const tmpDir of tmpDirs) {
4888
- await fs__default["default"].remove(tmpDir);
4889
- }
4890
- context.steps[step.id] = { output: stepOutput };
4891
- await stepTrack.markSuccessful();
4892
- } catch (err) {
4893
- await taskTrack.markFailed(step, err);
4894
- await stepTrack.markFailed();
4895
- throw err;
4896
- }
5057
+ await this.executeStep(
5058
+ task,
5059
+ step,
5060
+ context,
5061
+ renderTemplate,
5062
+ taskTrack,
5063
+ workspacePath
5064
+ );
4897
5065
  }
4898
5066
  const output = this.render(task.spec.output, context, renderTemplate);
4899
5067
  await taskTrack.markSuccessful();
@@ -4960,8 +5128,21 @@ function scaffoldingTracker() {
4960
5128
  });
4961
5129
  taskTimer({ result: "failed" });
4962
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
+ }
4963
5143
  return {
4964
5144
  skipDryRun,
5145
+ markCancelled,
4965
5146
  markSuccessful,
4966
5147
  markFailed
4967
5148
  };
@@ -4989,6 +5170,14 @@ function scaffoldingTracker() {
4989
5170
  });
4990
5171
  stepTimer({ result: "ok" });
4991
5172
  }
5173
+ async function markCancelled() {
5174
+ stepCount.inc({
5175
+ template,
5176
+ step: step.name,
5177
+ result: "cancelled"
5178
+ });
5179
+ stepTimer({ result: "cancelled" });
5180
+ }
4992
5181
  async function markFailed() {
4993
5182
  stepCount.inc({
4994
5183
  template,
@@ -5005,8 +5194,9 @@ function scaffoldingTracker() {
5005
5194
  stepTimer({ result: "skipped" });
5006
5195
  }
5007
5196
  return {
5008
- markSuccessful,
5197
+ markCancelled,
5009
5198
  markFailed,
5199
+ markSuccessful,
5010
5200
  skipFalsy
5011
5201
  };
5012
5202
  }
@@ -5130,6 +5320,7 @@ function createDryRunner(options) {
5130
5320
  );
5131
5321
  try {
5132
5322
  await deserializeDirectoryContents(contentsPath, input.directoryContents);
5323
+ const abortSignal = new AbortController().signal;
5133
5324
  const result = await workflowRunner.execute({
5134
5325
  spec: {
5135
5326
  ...input.spec,
@@ -5153,6 +5344,7 @@ function createDryRunner(options) {
5153
5344
  done: false,
5154
5345
  isDryRun: true,
5155
5346
  getWorkspaceName: async () => `dry-run-${dryRunId}`,
5347
+ cancelSignal: abortSignal,
5156
5348
  async emitLog(message, logMetadata) {
5157
5349
  if ((logMetadata == null ? void 0 : logMetadata.stepId) === dryRunId) {
5158
5350
  return;
@@ -5164,7 +5356,7 @@ function createDryRunner(options) {
5164
5356
  }
5165
5357
  });
5166
5358
  },
5167
- async complete() {
5359
+ complete: async () => {
5168
5360
  throw new Error("Not implemented");
5169
5361
  }
5170
5362
  });
@@ -5495,6 +5687,11 @@ async function createRouter(options) {
5495
5687
  }
5496
5688
  delete task.secrets;
5497
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" });
5498
5695
  }).get("/v2/tasks/:taskId/eventstream", async (req, res) => {
5499
5696
  const { taskId } = req.params;
5500
5697
  const after = req.query.after !== void 0 ? Number(req.query.after) : void 0;
@@ -5651,7 +5848,7 @@ class ScaffolderEntitiesProcessor {
5651
5848
  defaultNamespace: selfRef.namespace
5652
5849
  });
5653
5850
  emit(
5654
- pluginCatalogBackend.processingResult.relation({
5851
+ pluginCatalogNode.processingResult.relation({
5655
5852
  source: selfRef,
5656
5853
  type: catalogModel.RELATION_OWNED_BY,
5657
5854
  target: {
@@ -5662,7 +5859,7 @@ class ScaffolderEntitiesProcessor {
5662
5859
  })
5663
5860
  );
5664
5861
  emit(
5665
- pluginCatalogBackend.processingResult.relation({
5862
+ pluginCatalogNode.processingResult.relation({
5666
5863
  source: {
5667
5864
  kind: targetRef.kind,
5668
5865
  namespace: targetRef.namespace,
@@ -5711,6 +5908,7 @@ exports.createPublishGitlabAction = createPublishGitlabAction;
5711
5908
  exports.createPublishGitlabMergeRequestAction = createPublishGitlabMergeRequestAction;
5712
5909
  exports.createRouter = createRouter;
5713
5910
  exports.createTemplateAction = createTemplateAction;
5911
+ exports.createWaitAction = createWaitAction;
5714
5912
  exports.executeShellCommand = executeShellCommand;
5715
5913
  exports.fetchContents = fetchContents;
5716
5914
  //# sourceMappingURL=index.cjs.js.map