@backstage/plugin-scaffolder-backend 0.15.4 → 0.15.8

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 CHANGED
@@ -1,5 +1,48 @@
1
1
  # @backstage/plugin-scaffolder-backend
2
2
 
3
+ ## 0.15.8
4
+
5
+ ### Patch Changes
6
+
7
+ - 42c618abf6: Use `resolveSafeChildPath` in the `fetchContents` function to forbid reading files outside the base directory when a template is registered from a `file:` location.
8
+ - 18083d1821: Introduce the new `scaffolder.backstage.io/v1beta3` template kind with nunjucks support 🥋
9
+ - Updated dependencies
10
+ - @backstage/integration@0.6.8
11
+ - @backstage/plugin-catalog-backend@0.17.0
12
+
13
+ ## 0.15.7
14
+
15
+ ### Patch Changes
16
+
17
+ - ca3086a7ad: Fixed a bug where the `catalog:register` action would not return any entity when running towards recent versions of the catalog.
18
+ - Updated dependencies
19
+ - @backstage/catalog-model@0.9.4
20
+ - @backstage/backend-common@0.9.6
21
+ - @backstage/catalog-client@0.5.0
22
+ - @backstage/integration@0.6.7
23
+
24
+ ## 0.15.6
25
+
26
+ ### Patch Changes
27
+
28
+ - Updated dependencies
29
+ - @backstage/integration@0.6.5
30
+ - @backstage/catalog-client@0.4.0
31
+ - @backstage/catalog-model@0.9.3
32
+ - @backstage/backend-common@0.9.4
33
+ - @backstage/config@0.1.10
34
+
35
+ ## 0.15.5
36
+
37
+ ### Patch Changes
38
+
39
+ - 618143c3c7: Action needed: If you are using the templates located at https://github.com/backstage/backstage/tree/master/ in your Backstage app directly using the URL via the `app-config.yaml`, you should copy over the templates inside your org and import from there. The templates have now been moved to https://github.com/backstage/software-templates. See https://github.com/backstage/backstage/issues/6415 for explanation.
40
+ - cfade02127: Change hardcoded branch `master` to \$defaultBranch in GitLab provider
41
+ - 96fef17a18: Upgrade git-parse-url to v11.6.0
42
+ - Updated dependencies
43
+ - @backstage/backend-common@0.9.3
44
+ - @backstage/integration@0.6.4
45
+
3
46
  ## 0.15.4
4
47
 
5
48
  ### Patch Changes
package/dist/index.cjs.js CHANGED
@@ -27,9 +27,11 @@ var Router = require('express-promise-router');
27
27
  var jsonschema = require('jsonschema');
28
28
  var uuid = require('uuid');
29
29
  var luxon = require('luxon');
30
+ var os = require('os');
30
31
  var Handlebars = require('handlebars');
31
32
  var winston = require('winston');
32
- var os = require('os');
33
+ var pluginCatalogBackend = require('@backstage/plugin-catalog-backend');
34
+ var pluginScaffolderCommon = require('@backstage/plugin-scaffolder-common');
33
35
 
34
36
  function _interopDefaultLegacy (e) { return e && typeof e === 'object' && 'default' in e ? e : { 'default': e }; }
35
37
 
@@ -54,6 +56,7 @@ function _interopNamespace(e) {
54
56
  }
55
57
 
56
58
  var fs__default = /*#__PURE__*/_interopDefaultLegacy(fs);
59
+ var path__namespace = /*#__PURE__*/_interopNamespace(path);
57
60
  var path__default = /*#__PURE__*/_interopDefaultLegacy(path);
58
61
  var yaml__namespace = /*#__PURE__*/_interopNamespace(yaml);
59
62
  var globby__default = /*#__PURE__*/_interopDefaultLegacy(globby);
@@ -61,9 +64,9 @@ var nunjucks__default = /*#__PURE__*/_interopDefaultLegacy(nunjucks);
61
64
  var fetch__default = /*#__PURE__*/_interopDefaultLegacy(fetch);
62
65
  var express__default = /*#__PURE__*/_interopDefaultLegacy(express);
63
66
  var Router__default = /*#__PURE__*/_interopDefaultLegacy(Router);
67
+ var os__default = /*#__PURE__*/_interopDefaultLegacy(os);
64
68
  var Handlebars__namespace = /*#__PURE__*/_interopNamespace(Handlebars);
65
69
  var winston__namespace = /*#__PURE__*/_interopNamespace(winston);
66
- var os__default = /*#__PURE__*/_interopDefaultLegacy(os);
67
70
 
68
71
  const createTemplateAction = (templateAction) => {
69
72
  return templateAction;
@@ -108,6 +111,7 @@ function createCatalogRegisterAction(options) {
108
111
  }
109
112
  },
110
113
  async handler(ctx) {
114
+ var _a;
111
115
  const {input} = ctx;
112
116
  let catalogInfoUrl;
113
117
  if ("catalogInfoUrl" in input) {
@@ -124,15 +128,21 @@ function createCatalogRegisterAction(options) {
124
128
  });
125
129
  }
126
130
  ctx.logger.info(`Registering ${catalogInfoUrl} in the catalog`);
131
+ await catalogClient.addLocation({
132
+ type: "url",
133
+ target: catalogInfoUrl
134
+ }, ctx.token ? {token: ctx.token} : {});
127
135
  const result = await catalogClient.addLocation({
136
+ dryRun: true,
128
137
  type: "url",
129
138
  target: catalogInfoUrl
130
139
  }, ctx.token ? {token: ctx.token} : {});
131
- if (result.entities.length >= 1) {
132
- const {kind, name, namespace} = catalogModel.getEntityName(result.entities[0]);
133
- ctx.output("entityRef", `${kind}:${namespace}/${name}`);
134
- ctx.output("catalogInfoUrl", catalogInfoUrl);
140
+ if (result.entities.length > 0) {
141
+ const {entities} = result;
142
+ const entity = (_a = entities.find((e) => !e.metadata.name.startsWith("generated-"))) != null ? _a : entities[0];
143
+ ctx.output("entityRef", catalogModel.stringifyEntityRef(entity));
135
144
  }
145
+ ctx.output("catalogInfoUrl", catalogInfoUrl);
136
146
  }
137
147
  });
138
148
  }
@@ -176,12 +186,16 @@ function createDebugLogAction() {
176
186
  listWorkspace: {
177
187
  title: "List all files in the workspace, if true.",
178
188
  type: "boolean"
189
+ },
190
+ extra: {
191
+ title: "Extra info"
179
192
  }
180
193
  }
181
194
  }
182
195
  },
183
196
  async handler(ctx) {
184
197
  var _a, _b;
198
+ ctx.logger.info(JSON.stringify(ctx.input, null, 2));
185
199
  if ((_a = ctx.input) == null ? void 0 : _a.message) {
186
200
  ctx.logStream.write(ctx.input.message);
187
201
  }
@@ -220,10 +234,7 @@ async function fetchContents({
220
234
  }
221
235
  if (!fetchUrlIsAbsolute && (baseUrl == null ? void 0 : baseUrl.startsWith("file://"))) {
222
236
  const basePath = baseUrl.slice("file://".length);
223
- if (path.isAbsolute(fetchUrl)) {
224
- throw new errors.InputError(`Fetch URL may not be absolute for file locations, ${fetchUrl}`);
225
- }
226
- const srcDir = path.resolve(basePath, "..", fetchUrl);
237
+ const srcDir = backendCommon.resolveSafeChildPath(path__namespace.dirname(basePath), fetchUrl);
227
238
  await fs__default['default'].copy(srcDir, outputPath);
228
239
  } else {
229
240
  let readUrl;
@@ -1462,7 +1473,7 @@ function createPublishGitlabAction(options) {
1462
1473
  visibility: repoVisibility
1463
1474
  });
1464
1475
  const remoteUrl = http_url_to_repo.replace(/\.git$/, "");
1465
- const repoContentsUrl = `${remoteUrl}/-/blob/master`;
1476
+ const repoContentsUrl = `${remoteUrl}/-/blob/${defaultBranch}`;
1466
1477
  const gitAuthorInfo = {
1467
1478
  name: config.getOptionalString("scaffolder.defaultAuthor.name"),
1468
1479
  email: config.getOptionalString("scaffolder.defaultAuthor.email")
@@ -2028,39 +2039,90 @@ class StorageTaskBroker {
2028
2039
  }
2029
2040
  }
2030
2041
 
2042
+ class TaskWorker {
2043
+ constructor(options) {
2044
+ this.options = options;
2045
+ }
2046
+ start() {
2047
+ (async () => {
2048
+ for (; ; ) {
2049
+ const task = await this.options.taskBroker.claim();
2050
+ await this.runOneTask(task);
2051
+ }
2052
+ })();
2053
+ }
2054
+ async runOneTask(task) {
2055
+ try {
2056
+ const {output} = task.spec.apiVersion === "scaffolder.backstage.io/v1beta3" ? await this.options.runners.workflowRunner.execute(task) : await this.options.runners.legacyWorkflowRunner.execute(task);
2057
+ await task.complete("completed", {output});
2058
+ } catch (error) {
2059
+ await task.complete("failed", {
2060
+ error: {name: error.name, message: error.message}
2061
+ });
2062
+ }
2063
+ }
2064
+ }
2065
+
2066
+ async function getWorkingDirectory(config, logger) {
2067
+ if (!config.has("backend.workingDirectory")) {
2068
+ return os__default['default'].tmpdir();
2069
+ }
2070
+ const workingDirectory = config.getString("backend.workingDirectory");
2071
+ try {
2072
+ await fs__default['default'].access(workingDirectory, fs__default['default'].constants.F_OK | fs__default['default'].constants.W_OK);
2073
+ logger.info(`using working directory: ${workingDirectory}`);
2074
+ } catch (err) {
2075
+ logger.error(`working directory ${workingDirectory} ${err.code === "ENOENT" ? "does not exist" : "is not writable"}`);
2076
+ throw err;
2077
+ }
2078
+ return workingDirectory;
2079
+ }
2080
+ function getEntityBaseUrl(entity) {
2081
+ var _a, _b;
2082
+ let location = (_a = entity.metadata.annotations) == null ? void 0 : _a[catalogModel.SOURCE_LOCATION_ANNOTATION];
2083
+ if (!location) {
2084
+ location = (_b = entity.metadata.annotations) == null ? void 0 : _b[catalogModel.LOCATION_ANNOTATION];
2085
+ }
2086
+ if (!location) {
2087
+ return void 0;
2088
+ }
2089
+ const {type, target} = catalogModel.parseLocationReference(location);
2090
+ if (type === "url") {
2091
+ return target;
2092
+ } else if (type === "file") {
2093
+ return `file://${target}`;
2094
+ }
2095
+ return void 0;
2096
+ }
2097
+
2031
2098
  function isTruthy(value) {
2032
2099
  return lodash.isArray(value) ? value.length > 0 : !!value;
2033
2100
  }
2034
2101
 
2035
- class TaskWorker {
2102
+ const isValidTaskSpec$1 = (taskSpec) => taskSpec.apiVersion === "backstage.io/v1beta2";
2103
+ class LegacyWorkflowRunner {
2036
2104
  constructor(options) {
2037
2105
  this.options = options;
2038
2106
  this.handlebars = Handlebars__namespace.create();
2039
2107
  this.handlebars.registerHelper("parseRepoUrl", (repoUrl) => {
2040
- return JSON.stringify(parseRepoUrl(repoUrl, options.integrations));
2108
+ return JSON.stringify(parseRepoUrl(repoUrl, this.options.integrations));
2041
2109
  });
2042
2110
  this.handlebars.registerHelper("projectSlug", (repoUrl) => {
2043
- const {owner, repo} = parseRepoUrl(repoUrl, options.integrations);
2111
+ const {owner, repo} = parseRepoUrl(repoUrl, this.options.integrations);
2044
2112
  return `${owner}/${repo}`;
2045
2113
  });
2046
2114
  this.handlebars.registerHelper("json", (obj) => JSON.stringify(obj));
2047
2115
  this.handlebars.registerHelper("not", (value) => !isTruthy(value));
2048
2116
  this.handlebars.registerHelper("eq", (a, b) => a === b);
2049
2117
  }
2050
- start() {
2051
- (async () => {
2052
- for (; ; ) {
2053
- const task = await this.options.taskBroker.claim();
2054
- await this.runOneTask(task);
2055
- }
2056
- })();
2057
- }
2058
- async runOneTask(task) {
2118
+ async execute(task) {
2059
2119
  var _a, _b;
2060
- let workspacePath = void 0;
2120
+ if (!isValidTaskSpec$1(task.spec)) {
2121
+ throw new errors.InputError(`Task spec is not a valid v1beta2 task spec`);
2122
+ }
2123
+ const {actionRegistry} = this.options;
2124
+ const workspacePath = path__default['default'].join(this.options.workingDirectory, await task.getWorkspaceName());
2061
2125
  try {
2062
- const {actionRegistry} = this.options;
2063
- workspacePath = path__default['default'].join(this.options.workingDirectory, await task.getWorkspaceName());
2064
2126
  await fs__default['default'].ensureDir(workspacePath);
2065
2127
  await task.emitLog(`Starting up task with ${task.spec.steps.length} steps`);
2066
2128
  const templateCtx = {parameters: task.spec.values, steps: {}};
@@ -2202,11 +2264,7 @@ class TaskWorker {
2202
2264
  }
2203
2265
  return value;
2204
2266
  });
2205
- await task.complete("completed", {output});
2206
- } catch (error) {
2207
- await task.complete("failed", {
2208
- error: {name: error.name, message: error.message}
2209
- });
2267
+ return {output};
2210
2268
  } finally {
2211
2269
  if (workspacePath) {
2212
2270
  await fs__default['default'].remove(workspacePath);
@@ -2215,40 +2273,159 @@ class TaskWorker {
2215
2273
  }
2216
2274
  }
2217
2275
 
2218
- async function getWorkingDirectory(config, logger) {
2219
- if (!config.has("backend.workingDirectory")) {
2220
- return os__default['default'].tmpdir();
2221
- }
2222
- const workingDirectory = config.getString("backend.workingDirectory");
2223
- try {
2224
- await fs__default['default'].access(workingDirectory, fs__default['default'].constants.F_OK | fs__default['default'].constants.W_OK);
2225
- logger.info(`using working directory: ${workingDirectory}`);
2226
- } catch (err) {
2227
- logger.error(`working directory ${workingDirectory} ${err.code === "ENOENT" ? "does not exist" : "is not writable"}`);
2228
- throw err;
2276
+ const isValidTaskSpec = (taskSpec) => {
2277
+ return taskSpec.apiVersion === "scaffolder.backstage.io/v1beta3";
2278
+ };
2279
+ const createStepLogger = ({task, step}) => {
2280
+ const metadata = {stepId: step.id};
2281
+ const taskLogger = winston__namespace.createLogger({
2282
+ level: process.env.LOG_LEVEL || "info",
2283
+ format: winston__namespace.format.combine(winston__namespace.format.colorize(), winston__namespace.format.timestamp(), winston__namespace.format.simple()),
2284
+ defaultMeta: {}
2285
+ });
2286
+ const streamLogger = new stream.PassThrough();
2287
+ streamLogger.on("data", async (data) => {
2288
+ const message = data.toString().trim();
2289
+ if ((message == null ? void 0 : message.length) > 1) {
2290
+ await task.emitLog(message, metadata);
2291
+ }
2292
+ });
2293
+ taskLogger.add(new winston__namespace.transports.Stream({stream: streamLogger}));
2294
+ return {taskLogger, streamLogger};
2295
+ };
2296
+ class DefaultWorkflowRunner {
2297
+ constructor(options) {
2298
+ this.options = options;
2299
+ this.nunjucksOptions = {
2300
+ autoescape: false,
2301
+ tags: {
2302
+ variableStart: "${{",
2303
+ variableEnd: "}}"
2304
+ }
2305
+ };
2306
+ this.nunjucks = nunjucks__default['default'].configure(this.nunjucksOptions);
2307
+ this.nunjucks.addFilter("parseRepoUrl", (repoUrl) => {
2308
+ return parseRepoUrl(repoUrl, this.options.integrations);
2309
+ });
2310
+ this.nunjucks.addFilter("projectSlug", (repoUrl) => {
2311
+ const {owner, repo} = parseRepoUrl(repoUrl, this.options.integrations);
2312
+ return `${owner}/${repo}`;
2313
+ });
2229
2314
  }
2230
- return workingDirectory;
2231
- }
2232
- function getEntityBaseUrl(entity) {
2233
- var _a, _b;
2234
- let location = (_a = entity.metadata.annotations) == null ? void 0 : _a[catalogModel.SOURCE_LOCATION_ANNOTATION];
2235
- if (!location) {
2236
- location = (_b = entity.metadata.annotations) == null ? void 0 : _b[catalogModel.LOCATION_ANNOTATION];
2315
+ isSingleTemplateString(input) {
2316
+ const {parser, nodes} = require("nunjucks");
2317
+ const parsed = parser.parse(input, {}, this.nunjucksOptions);
2318
+ return parsed.children.length === 1 && !(parsed.children[0] instanceof nodes.TemplateData);
2237
2319
  }
2238
- if (!location) {
2239
- return void 0;
2320
+ render(input, context) {
2321
+ return JSON.parse(JSON.stringify(input), (_key, value) => {
2322
+ try {
2323
+ if (typeof value === "string") {
2324
+ try {
2325
+ if (this.isSingleTemplateString(value)) {
2326
+ const wrappedDumped = value.replace(/\${{(.+)}}/g, "${{ ( $1 ) | dump }}");
2327
+ const templated2 = this.nunjucks.renderString(wrappedDumped, context);
2328
+ if (templated2 === "") {
2329
+ return void 0;
2330
+ }
2331
+ return JSON.parse(templated2);
2332
+ }
2333
+ } catch (ex) {
2334
+ this.options.logger.error(`Failed to parse template string: ${value} with error ${ex.message}`);
2335
+ }
2336
+ const templated = this.nunjucks.renderString(value, context);
2337
+ if (templated === "") {
2338
+ return void 0;
2339
+ }
2340
+ return templated;
2341
+ }
2342
+ } catch {
2343
+ return value;
2344
+ }
2345
+ return value;
2346
+ });
2240
2347
  }
2241
- const {type, target} = catalogModel.parseLocationReference(location);
2242
- if (type === "url") {
2243
- return target;
2244
- } else if (type === "file") {
2245
- return `file://${target}`;
2348
+ async execute(task) {
2349
+ var _a, _b;
2350
+ if (!isValidTaskSpec(task.spec)) {
2351
+ throw new errors.InputError("Wrong template version executed with the workflow engine");
2352
+ }
2353
+ const workspacePath = path__default['default'].join(this.options.workingDirectory, await task.getWorkspaceName());
2354
+ try {
2355
+ await fs__default['default'].ensureDir(workspacePath);
2356
+ await task.emitLog(`Starting up task with ${task.spec.steps.length} steps`);
2357
+ const context = {
2358
+ parameters: task.spec.parameters,
2359
+ steps: {}
2360
+ };
2361
+ for (const step of task.spec.steps) {
2362
+ try {
2363
+ if (step.if) {
2364
+ const ifResult = await this.render(step.if, context);
2365
+ if (!isTruthy(ifResult)) {
2366
+ await task.emitLog(`Skipping step ${step.id} because it's if condition was false`, {stepId: step.id, status: "skipped"});
2367
+ continue;
2368
+ }
2369
+ }
2370
+ await task.emitLog(`Beginning step ${step.name}`, {
2371
+ stepId: step.id,
2372
+ status: "processing"
2373
+ });
2374
+ const action = this.options.actionRegistry.get(step.action);
2375
+ const {taskLogger, streamLogger} = createStepLogger({task, step});
2376
+ const input = (_a = step.input && this.render(step.input, context)) != null ? _a : {};
2377
+ if ((_b = action.schema) == null ? void 0 : _b.input) {
2378
+ const validateResult = jsonschema.validate(input, action.schema.input);
2379
+ if (!validateResult.valid) {
2380
+ const errors$1 = validateResult.errors.join(", ");
2381
+ throw new errors.InputError(`Invalid input passed to action ${action.id}, ${errors$1}`);
2382
+ }
2383
+ }
2384
+ const tmpDirs = new Array();
2385
+ const stepOutput = {};
2386
+ await action.handler({
2387
+ baseUrl: task.spec.baseUrl,
2388
+ input,
2389
+ logger: taskLogger,
2390
+ logStream: streamLogger,
2391
+ workspacePath,
2392
+ createTemporaryDirectory: async () => {
2393
+ const tmpDir = await fs__default['default'].mkdtemp(`${workspacePath}_step-${step.id}-`);
2394
+ tmpDirs.push(tmpDir);
2395
+ return tmpDir;
2396
+ },
2397
+ output(name, value) {
2398
+ stepOutput[name] = value;
2399
+ }
2400
+ });
2401
+ for (const tmpDir of tmpDirs) {
2402
+ await fs__default['default'].remove(tmpDir);
2403
+ }
2404
+ context.steps[step.id] = {output: stepOutput};
2405
+ await task.emitLog(`Finished step ${step.name}`, {
2406
+ stepId: step.id,
2407
+ status: "completed"
2408
+ });
2409
+ } catch (err) {
2410
+ await task.emitLog(String(err.stack), {
2411
+ stepId: step.id,
2412
+ status: "failed"
2413
+ });
2414
+ throw err;
2415
+ }
2416
+ }
2417
+ const output = this.render(task.spec.output, context);
2418
+ return {output};
2419
+ } finally {
2420
+ if (workspacePath) {
2421
+ await fs__default['default'].remove(workspacePath);
2422
+ }
2423
+ }
2246
2424
  }
2247
- return void 0;
2248
2425
  }
2249
2426
 
2250
- function isBeta2Template(entity) {
2251
- return entity.apiVersion === "backstage.io/v1beta2";
2427
+ function isSupportedTemplate(entity) {
2428
+ return entity.apiVersion === "backstage.io/v1beta2" || entity.apiVersion === "scaffolder.backstage.io/v1beta3";
2252
2429
  }
2253
2430
  async function createRouter(options) {
2254
2431
  const router = Router__default['default']();
@@ -2270,14 +2447,26 @@ async function createRouter(options) {
2270
2447
  const databaseTaskStore = await DatabaseTaskStore.create(await database.getClient());
2271
2448
  const taskBroker = new StorageTaskBroker(databaseTaskStore, logger);
2272
2449
  const actionRegistry = new TemplateActionRegistry();
2450
+ const legacyWorkflowRunner = new LegacyWorkflowRunner({
2451
+ logger,
2452
+ actionRegistry,
2453
+ integrations,
2454
+ workingDirectory
2455
+ });
2456
+ const workflowRunner = new DefaultWorkflowRunner({
2457
+ actionRegistry,
2458
+ integrations,
2459
+ logger,
2460
+ workingDirectory
2461
+ });
2273
2462
  const workers = [];
2274
2463
  for (let i = 0; i < (taskWorkers || 1); i++) {
2275
2464
  const worker = new TaskWorker({
2276
- logger,
2277
2465
  taskBroker,
2278
- actionRegistry,
2279
- workingDirectory,
2280
- integrations
2466
+ runners: {
2467
+ legacyWorkflowRunner,
2468
+ workflowRunner
2469
+ }
2281
2470
  });
2282
2471
  workers.push(worker);
2283
2472
  }
@@ -2302,7 +2491,7 @@ async function createRouter(options) {
2302
2491
  const template = await entityClient.findTemplate(name, {
2303
2492
  token: getBearerToken(req.headers.authorization)
2304
2493
  });
2305
- if (isBeta2Template(template)) {
2494
+ if (isSupportedTemplate(template)) {
2306
2495
  const parameters = [(_a = template.spec.parameters) != null ? _a : []].flat();
2307
2496
  res.json({
2308
2497
  title: (_b = template.metadata.title) != null ? _b : template.metadata.name,
@@ -2327,7 +2516,7 @@ async function createRouter(options) {
2327
2516
  });
2328
2517
  res.json(actionsList);
2329
2518
  }).post("/v2/tasks", async (req, res) => {
2330
- var _a, _b;
2519
+ var _a, _b, _c;
2331
2520
  const templateName = req.body.templateName;
2332
2521
  const values = req.body.values;
2333
2522
  const token = getBearerToken(req.headers.authorization);
@@ -2335,7 +2524,7 @@ async function createRouter(options) {
2335
2524
  token
2336
2525
  });
2337
2526
  let taskSpec;
2338
- if (isBeta2Template(template)) {
2527
+ if (isSupportedTemplate(template)) {
2339
2528
  for (const parameters of [(_a = template.spec.parameters) != null ? _a : []].flat()) {
2340
2529
  const result2 = jsonschema.validate(values, parameters);
2341
2530
  if (!result2.valid) {
@@ -2344,7 +2533,8 @@ async function createRouter(options) {
2344
2533
  }
2345
2534
  }
2346
2535
  const baseUrl = getEntityBaseUrl(template);
2347
- taskSpec = {
2536
+ taskSpec = template.apiVersion === "backstage.io/v1beta2" ? {
2537
+ apiVersion: template.apiVersion,
2348
2538
  baseUrl,
2349
2539
  values,
2350
2540
  steps: template.spec.steps.map((step, index) => {
@@ -2356,6 +2546,19 @@ async function createRouter(options) {
2356
2546
  };
2357
2547
  }),
2358
2548
  output: (_b = template.spec.output) != null ? _b : {}
2549
+ } : {
2550
+ apiVersion: template.apiVersion,
2551
+ baseUrl,
2552
+ parameters: values,
2553
+ steps: template.spec.steps.map((step, index) => {
2554
+ var _a2, _b2;
2555
+ return {
2556
+ ...step,
2557
+ id: (_a2 = step.id) != null ? _a2 : `step-${index + 1}`,
2558
+ name: (_b2 = step.name) != null ? _b2 : step.action
2559
+ };
2560
+ }),
2561
+ output: (_c = template.spec.output) != null ? _c : {}
2359
2562
  };
2360
2563
  } else {
2361
2564
  throw new errors.InputError(`Unsupported apiVersion field in schema entity, ${template.apiVersion}`);
@@ -2414,6 +2617,54 @@ function getBearerToken(header) {
2414
2617
  return (_a = header == null ? void 0 : header.match(/Bearer\s+(\S+)/i)) == null ? void 0 : _a[1];
2415
2618
  }
2416
2619
 
2620
+ class ScaffolderEntitiesProcessor {
2621
+ constructor() {
2622
+ this.validators = [
2623
+ catalogModel.entityKindSchemaValidator(pluginScaffolderCommon.templateEntityV1beta3Schema)
2624
+ ];
2625
+ }
2626
+ async validateEntityKind(entity) {
2627
+ for (const validator of this.validators) {
2628
+ if (validator(entity)) {
2629
+ return true;
2630
+ }
2631
+ }
2632
+ return false;
2633
+ }
2634
+ async postProcessEntity(entity, _location, emit) {
2635
+ const selfRef = catalogModel.getEntityName(entity);
2636
+ if (entity.apiVersion === "scaffolder.backstage.io/v1beta3" && entity.kind === "Template") {
2637
+ const template = entity;
2638
+ const target = template.spec.owner;
2639
+ if (target) {
2640
+ const targetRef = catalogModel.parseEntityRef(target, {
2641
+ defaultKind: "Group",
2642
+ defaultNamespace: selfRef.namespace
2643
+ });
2644
+ emit(pluginCatalogBackend.results.relation({
2645
+ source: selfRef,
2646
+ type: catalogModel.RELATION_OWNED_BY,
2647
+ target: {
2648
+ kind: targetRef.kind,
2649
+ namespace: targetRef.namespace,
2650
+ name: targetRef.name
2651
+ }
2652
+ }));
2653
+ emit(pluginCatalogBackend.results.relation({
2654
+ source: {
2655
+ kind: targetRef.kind,
2656
+ namespace: targetRef.namespace,
2657
+ name: targetRef.name
2658
+ },
2659
+ type: catalogModel.RELATION_OWNER_OF,
2660
+ target: selfRef
2661
+ }));
2662
+ }
2663
+ }
2664
+ return entity;
2665
+ }
2666
+ }
2667
+
2417
2668
  Object.defineProperty(exports, 'createFetchCookiecutterAction', {
2418
2669
  enumerable: true,
2419
2670
  get: function () {
@@ -2422,6 +2673,7 @@ Object.defineProperty(exports, 'createFetchCookiecutterAction', {
2422
2673
  });
2423
2674
  exports.CatalogEntityClient = CatalogEntityClient;
2424
2675
  exports.OctokitProvider = OctokitProvider;
2676
+ exports.ScaffolderEntitiesProcessor = ScaffolderEntitiesProcessor;
2425
2677
  exports.TemplateActionRegistry = TemplateActionRegistry;
2426
2678
  exports.createBuiltinActions = createBuiltinActions;
2427
2679
  exports.createCatalogRegisterAction = createCatalogRegisterAction;