@backstage/plugin-scaffolder-backend 0.15.11 → 0.15.15
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 +88 -0
- package/assets/nunjucks.js.txt +10385 -0
- package/config.d.ts +12 -12
- package/dist/index.cjs.js +326 -178
- package/dist/index.cjs.js.map +1 -1
- package/dist/index.d.ts +315 -2
- package/package.json +20 -16
package/dist/index.cjs.js
CHANGED
|
@@ -5,31 +5,32 @@ Object.defineProperty(exports, '__esModule', { value: true });
|
|
|
5
5
|
var errors = require('@backstage/errors');
|
|
6
6
|
var catalogModel = require('@backstage/catalog-model');
|
|
7
7
|
var fs = require('fs-extra');
|
|
8
|
-
var path = require('path');
|
|
9
8
|
var yaml = require('yaml');
|
|
10
9
|
var backendCommon = require('@backstage/backend-common');
|
|
10
|
+
var path = require('path');
|
|
11
11
|
var globby = require('globby');
|
|
12
|
-
var nunjucks = require('nunjucks');
|
|
13
12
|
var isbinaryfile = require('isbinaryfile');
|
|
13
|
+
var vm2 = require('vm2');
|
|
14
14
|
var pluginScaffolderBackendModuleCookiecutter = require('@backstage/plugin-scaffolder-backend-module-cookiecutter');
|
|
15
15
|
var child_process = require('child_process');
|
|
16
16
|
var stream = require('stream');
|
|
17
17
|
var azureDevopsNodeApi = require('azure-devops-node-api');
|
|
18
|
-
var fetch = require('
|
|
18
|
+
var fetch = require('node-fetch');
|
|
19
19
|
var integration = require('@backstage/integration');
|
|
20
20
|
var rest = require('@octokit/rest');
|
|
21
21
|
var lodash = require('lodash');
|
|
22
22
|
var octokitPluginCreatePullRequest = require('octokit-plugin-create-pull-request');
|
|
23
23
|
var node = require('@gitbeaker/node');
|
|
24
24
|
var webhooks = require('@octokit/webhooks');
|
|
25
|
-
var express = require('express');
|
|
26
|
-
var Router = require('express-promise-router');
|
|
27
|
-
var jsonschema = require('jsonschema');
|
|
28
25
|
var uuid = require('uuid');
|
|
29
26
|
var luxon = require('luxon');
|
|
30
|
-
var os = require('os');
|
|
31
27
|
var Handlebars = require('handlebars');
|
|
32
28
|
var winston = require('winston');
|
|
29
|
+
var jsonschema = require('jsonschema');
|
|
30
|
+
var nunjucks = require('nunjucks');
|
|
31
|
+
var express = require('express');
|
|
32
|
+
var Router = require('express-promise-router');
|
|
33
|
+
var os = require('os');
|
|
33
34
|
var pluginCatalogBackend = require('@backstage/plugin-catalog-backend');
|
|
34
35
|
var pluginScaffolderCommon = require('@backstage/plugin-scaffolder-common');
|
|
35
36
|
|
|
@@ -56,17 +57,16 @@ function _interopNamespace(e) {
|
|
|
56
57
|
}
|
|
57
58
|
|
|
58
59
|
var fs__default = /*#__PURE__*/_interopDefaultLegacy(fs);
|
|
59
|
-
var path__namespace = /*#__PURE__*/_interopNamespace(path);
|
|
60
|
-
var path__default = /*#__PURE__*/_interopDefaultLegacy(path);
|
|
61
60
|
var yaml__namespace = /*#__PURE__*/_interopNamespace(yaml);
|
|
61
|
+
var path__default = /*#__PURE__*/_interopDefaultLegacy(path);
|
|
62
62
|
var globby__default = /*#__PURE__*/_interopDefaultLegacy(globby);
|
|
63
|
-
var nunjucks__default = /*#__PURE__*/_interopDefaultLegacy(nunjucks);
|
|
64
63
|
var fetch__default = /*#__PURE__*/_interopDefaultLegacy(fetch);
|
|
64
|
+
var Handlebars__namespace = /*#__PURE__*/_interopNamespace(Handlebars);
|
|
65
|
+
var winston__namespace = /*#__PURE__*/_interopNamespace(winston);
|
|
66
|
+
var nunjucks__default = /*#__PURE__*/_interopDefaultLegacy(nunjucks);
|
|
65
67
|
var express__default = /*#__PURE__*/_interopDefaultLegacy(express);
|
|
66
68
|
var Router__default = /*#__PURE__*/_interopDefaultLegacy(Router);
|
|
67
69
|
var os__default = /*#__PURE__*/_interopDefaultLegacy(os);
|
|
68
|
-
var Handlebars__namespace = /*#__PURE__*/_interopNamespace(Handlebars);
|
|
69
|
-
var winston__namespace = /*#__PURE__*/_interopNamespace(winston);
|
|
70
70
|
|
|
71
71
|
const createTemplateAction = (templateAction) => {
|
|
72
72
|
return templateAction;
|
|
@@ -182,7 +182,7 @@ function createCatalogWriteAction() {
|
|
|
182
182
|
async handler(ctx) {
|
|
183
183
|
ctx.logStream.write(`Writing catalog-info.yaml`);
|
|
184
184
|
const {entity} = ctx.input;
|
|
185
|
-
await fs__default['default'].writeFile(
|
|
185
|
+
await fs__default['default'].writeFile(backendCommon.resolveSafeChildPath(ctx.workspacePath, "catalog-info.yaml"), yaml__namespace.stringify(entity));
|
|
186
186
|
}
|
|
187
187
|
});
|
|
188
188
|
}
|
|
@@ -226,7 +226,7 @@ ${files.map((f) => ` - ${path.relative(ctx.workspacePath, f)}`).join("\n")}`);
|
|
|
226
226
|
async function recursiveReadDir(dir) {
|
|
227
227
|
const subdirs = await fs.readdir(dir);
|
|
228
228
|
const files = await Promise.all(subdirs.map(async (subdir) => {
|
|
229
|
-
const res = path.
|
|
229
|
+
const res = path.join(dir, subdir);
|
|
230
230
|
return (await fs.stat(res)).isDirectory() ? recursiveReadDir(res) : [res];
|
|
231
231
|
}));
|
|
232
232
|
return files.reduce((a, f) => a.concat(f), []);
|
|
@@ -250,7 +250,7 @@ async function fetchContents({
|
|
|
250
250
|
}
|
|
251
251
|
if (!fetchUrlIsAbsolute && (baseUrl == null ? void 0 : baseUrl.startsWith("file://"))) {
|
|
252
252
|
const basePath = baseUrl.slice("file://".length);
|
|
253
|
-
const srcDir = backendCommon.resolveSafeChildPath(
|
|
253
|
+
const srcDir = backendCommon.resolveSafeChildPath(path__default['default'].dirname(basePath), fetchUrl);
|
|
254
254
|
await fs__default['default'].copy(srcDir, outputPath);
|
|
255
255
|
} else {
|
|
256
256
|
let readUrl;
|
|
@@ -313,7 +313,100 @@ function createFetchPlainAction(options) {
|
|
|
313
313
|
});
|
|
314
314
|
}
|
|
315
315
|
|
|
316
|
-
|
|
316
|
+
const mkScript = (nunjucksSource) => `
|
|
317
|
+
const { render, renderCompat } = (() => {
|
|
318
|
+
const module = {};
|
|
319
|
+
const process = { env: {} };
|
|
320
|
+
const require = (pkg) => { if (pkg === 'events') { return function (){}; }};
|
|
321
|
+
|
|
322
|
+
${nunjucksSource}
|
|
323
|
+
|
|
324
|
+
const env = module.exports.configure({
|
|
325
|
+
autoescape: false,
|
|
326
|
+
tags: {
|
|
327
|
+
variableStart: '\${{',
|
|
328
|
+
variableEnd: '}}',
|
|
329
|
+
},
|
|
330
|
+
});
|
|
331
|
+
|
|
332
|
+
const compatEnv = module.exports.configure({
|
|
333
|
+
autoescape: false,
|
|
334
|
+
tags: {
|
|
335
|
+
variableStart: '{{',
|
|
336
|
+
variableEnd: '}}',
|
|
337
|
+
},
|
|
338
|
+
});
|
|
339
|
+
compatEnv.addFilter('jsonify', compatEnv.getFilter('dump'));
|
|
340
|
+
|
|
341
|
+
if (typeof parseRepoUrl !== 'undefined') {
|
|
342
|
+
const safeHelperRef = parseRepoUrl;
|
|
343
|
+
|
|
344
|
+
env.addFilter('parseRepoUrl', repoUrl => {
|
|
345
|
+
return JSON.parse(safeHelperRef(repoUrl))
|
|
346
|
+
});
|
|
347
|
+
env.addFilter('projectSlug', repoUrl => {
|
|
348
|
+
const { owner, repo } = JSON.parse(safeHelperRef(repoUrl));
|
|
349
|
+
return owner + '/' + repo;
|
|
350
|
+
});
|
|
351
|
+
}
|
|
352
|
+
|
|
353
|
+
let uninstallCompat = undefined;
|
|
354
|
+
|
|
355
|
+
function render(str, values) {
|
|
356
|
+
try {
|
|
357
|
+
if (uninstallCompat) {
|
|
358
|
+
uninstallCompat();
|
|
359
|
+
uninstallCompat = undefined;
|
|
360
|
+
}
|
|
361
|
+
return env.renderString(str, JSON.parse(values));
|
|
362
|
+
} catch (error) {
|
|
363
|
+
// Make sure errors don't leak anything
|
|
364
|
+
throw new Error(String(error.message));
|
|
365
|
+
}
|
|
366
|
+
}
|
|
367
|
+
|
|
368
|
+
function renderCompat(str, values) {
|
|
369
|
+
try {
|
|
370
|
+
if (!uninstallCompat) {
|
|
371
|
+
uninstallCompat = module.exports.installJinjaCompat();
|
|
372
|
+
}
|
|
373
|
+
return compatEnv.renderString(str, JSON.parse(values));
|
|
374
|
+
} catch (error) {
|
|
375
|
+
// Make sure errors don't leak anything
|
|
376
|
+
throw new Error(String(error.message));
|
|
377
|
+
}
|
|
378
|
+
}
|
|
379
|
+
|
|
380
|
+
return { render, renderCompat };
|
|
381
|
+
})();
|
|
382
|
+
`;
|
|
383
|
+
class SecureTemplater {
|
|
384
|
+
static async loadRenderer(options = {}) {
|
|
385
|
+
const {parseRepoUrl, cookiecutterCompat} = options;
|
|
386
|
+
let sandbox = void 0;
|
|
387
|
+
if (parseRepoUrl) {
|
|
388
|
+
sandbox = {
|
|
389
|
+
parseRepoUrl: (url) => JSON.stringify(parseRepoUrl(url))
|
|
390
|
+
};
|
|
391
|
+
}
|
|
392
|
+
const vm = new vm2.VM({sandbox});
|
|
393
|
+
const nunjucksSource = await fs__default['default'].readFile(backendCommon.resolvePackagePath("@backstage/plugin-scaffolder-backend", "assets/nunjucks.js.txt"), "utf-8");
|
|
394
|
+
vm.run(mkScript(nunjucksSource));
|
|
395
|
+
const render = (template, values) => {
|
|
396
|
+
if (!vm) {
|
|
397
|
+
throw new Error("SecureTemplater has not been initialized");
|
|
398
|
+
}
|
|
399
|
+
vm.setGlobal("templateStr", template);
|
|
400
|
+
vm.setGlobal("templateValues", JSON.stringify(values));
|
|
401
|
+
if (cookiecutterCompat) {
|
|
402
|
+
return vm.run(`renderCompat(templateStr, templateValues)`);
|
|
403
|
+
}
|
|
404
|
+
return vm.run(`render(templateStr, templateValues)`);
|
|
405
|
+
};
|
|
406
|
+
return render;
|
|
407
|
+
}
|
|
408
|
+
}
|
|
409
|
+
|
|
317
410
|
function createFetchTemplateAction(options) {
|
|
318
411
|
const {reader, integrations} = options;
|
|
319
412
|
return createTemplateAction({
|
|
@@ -364,7 +457,7 @@ function createFetchTemplateAction(options) {
|
|
|
364
457
|
var _a;
|
|
365
458
|
ctx.logger.info("Fetching template content from remote URL");
|
|
366
459
|
const workDir = await ctx.createTemporaryDirectory();
|
|
367
|
-
const templateDir =
|
|
460
|
+
const templateDir = backendCommon.resolveSafeChildPath(workDir, "template");
|
|
368
461
|
const targetPath = (_a = ctx.input.targetPath) != null ? _a : "./";
|
|
369
462
|
const outputDir = backendCommon.resolveSafeChildPath(ctx.workspacePath, targetPath);
|
|
370
463
|
if (ctx.input.copyWithoutRender && !Array.isArray(ctx.input.copyWithoutRender)) {
|
|
@@ -400,23 +493,14 @@ function createFetchTemplateAction(options) {
|
|
|
400
493
|
onlyFiles: false,
|
|
401
494
|
markDirectories: true
|
|
402
495
|
})))).flat());
|
|
403
|
-
const templater = nunjucks__default['default'].configure({
|
|
404
|
-
...ctx.input.cookiecutterCompat ? {} : {
|
|
405
|
-
tags: {
|
|
406
|
-
variableStart: "${{",
|
|
407
|
-
variableEnd: "}}"
|
|
408
|
-
}
|
|
409
|
-
},
|
|
410
|
-
autoescape: false
|
|
411
|
-
});
|
|
412
|
-
if (ctx.input.cookiecutterCompat) {
|
|
413
|
-
templater.addFilter("jsonify", templater.getFilter("dump"));
|
|
414
|
-
}
|
|
415
496
|
const {cookiecutterCompat, values} = ctx.input;
|
|
416
497
|
const context = {
|
|
417
498
|
[cookiecutterCompat ? "cookiecutter" : "values"]: values
|
|
418
499
|
};
|
|
419
500
|
ctx.logger.info(`Processing ${allEntriesInTemplate.length} template files/directories with input values`, ctx.input.values);
|
|
501
|
+
const renderTemplate = await SecureTemplater.loadRenderer({
|
|
502
|
+
cookiecutterCompat: ctx.input.cookiecutterCompat
|
|
503
|
+
});
|
|
420
504
|
for (const location of allEntriesInTemplate) {
|
|
421
505
|
let renderFilename;
|
|
422
506
|
let renderContents;
|
|
@@ -431,9 +515,12 @@ function createFetchTemplateAction(options) {
|
|
|
431
515
|
renderFilename = renderContents = !nonTemplatedEntries.has(location);
|
|
432
516
|
}
|
|
433
517
|
if (renderFilename) {
|
|
434
|
-
localOutputPath =
|
|
518
|
+
localOutputPath = renderTemplate(localOutputPath, context);
|
|
519
|
+
}
|
|
520
|
+
const outputPath = backendCommon.resolveSafeChildPath(outputDir, localOutputPath);
|
|
521
|
+
if (outputDir === outputPath) {
|
|
522
|
+
continue;
|
|
435
523
|
}
|
|
436
|
-
const outputPath = path.resolve(outputDir, localOutputPath);
|
|
437
524
|
if (!renderContents && !extension) {
|
|
438
525
|
ctx.logger.info(`Copying file/directory ${location} without processing.`);
|
|
439
526
|
}
|
|
@@ -441,7 +528,7 @@ function createFetchTemplateAction(options) {
|
|
|
441
528
|
ctx.logger.info(`Writing directory ${location} to template output path.`);
|
|
442
529
|
await fs__default['default'].ensureDir(outputPath);
|
|
443
530
|
} else {
|
|
444
|
-
const inputFilePath =
|
|
531
|
+
const inputFilePath = backendCommon.resolveSafeChildPath(templateDir, location);
|
|
445
532
|
if (await isbinaryfile.isBinaryFile(inputFilePath)) {
|
|
446
533
|
ctx.logger.info(`Copying binary file ${location} to template output path.`);
|
|
447
534
|
await fs__default['default'].copy(inputFilePath, outputPath);
|
|
@@ -449,7 +536,7 @@ function createFetchTemplateAction(options) {
|
|
|
449
536
|
const statsObj = await fs__default['default'].stat(inputFilePath);
|
|
450
537
|
ctx.logger.info(`Writing file ${location} to template output path with mode ${statsObj.mode}.`);
|
|
451
538
|
const inputFileContents = await fs__default['default'].readFile(inputFilePath, "utf-8");
|
|
452
|
-
await fs__default['default'].outputFile(outputPath, renderContents ?
|
|
539
|
+
await fs__default['default'].outputFile(outputPath, renderContents ? renderTemplate(inputFileContents, context) : inputFileContents, {mode: statsObj.mode});
|
|
453
540
|
}
|
|
454
541
|
}
|
|
455
542
|
}
|
|
@@ -560,10 +647,11 @@ const createFilesystemRenameAction = () => {
|
|
|
560
647
|
const runCommand = async ({
|
|
561
648
|
command,
|
|
562
649
|
args,
|
|
563
|
-
logStream = new stream.PassThrough()
|
|
650
|
+
logStream = new stream.PassThrough(),
|
|
651
|
+
options
|
|
564
652
|
}) => {
|
|
565
653
|
await new Promise((resolve, reject) => {
|
|
566
|
-
const process = child_process.spawn(command, args);
|
|
654
|
+
const process = child_process.spawn(command, args, options);
|
|
567
655
|
process.stdout.on("data", (stream) => {
|
|
568
656
|
logStream.write(stream);
|
|
569
657
|
});
|
|
@@ -714,6 +802,11 @@ const parseRepoUrl = (repoUrl, integrations) => {
|
|
|
714
802
|
}
|
|
715
803
|
return {host, owner, repo, organization, workspace, project};
|
|
716
804
|
};
|
|
805
|
+
const isExecutable = (fileMode) => {
|
|
806
|
+
const executeBitMask = 73;
|
|
807
|
+
const res = fileMode & executeBitMask;
|
|
808
|
+
return res > 0;
|
|
809
|
+
};
|
|
717
810
|
|
|
718
811
|
function createPublishAzureAction(options) {
|
|
719
812
|
const {integrations, config} = options;
|
|
@@ -1392,11 +1485,10 @@ const createPublishGithubPullRequestAction = ({
|
|
|
1392
1485
|
dot: true
|
|
1393
1486
|
});
|
|
1394
1487
|
const fileContents = await Promise.all(localFilePaths.map((filePath) => {
|
|
1395
|
-
const absPath =
|
|
1488
|
+
const absPath = backendCommon.resolveSafeChildPath(fileRoot, filePath);
|
|
1396
1489
|
const base64EncodedContent = fs__default['default'].readFileSync(absPath).toString("base64");
|
|
1397
1490
|
const fileStat = fs__default['default'].statSync(absPath);
|
|
1398
|
-
const
|
|
1399
|
-
const githubTreeItemMode = isExecutable ? "100755" : "100644";
|
|
1491
|
+
const githubTreeItemMode = isExecutable(fileStat.mode) ? "100755" : "100644";
|
|
1400
1492
|
const encoding = "base64";
|
|
1401
1493
|
return {
|
|
1402
1494
|
encoding,
|
|
@@ -1747,38 +1839,16 @@ class TemplateActionRegistry {
|
|
|
1747
1839
|
}
|
|
1748
1840
|
}
|
|
1749
1841
|
|
|
1750
|
-
class CatalogEntityClient {
|
|
1751
|
-
constructor(catalogClient) {
|
|
1752
|
-
this.catalogClient = catalogClient;
|
|
1753
|
-
}
|
|
1754
|
-
async findTemplate(templateName, options) {
|
|
1755
|
-
const {items: templates} = await this.catalogClient.getEntities({
|
|
1756
|
-
filter: {
|
|
1757
|
-
kind: "template",
|
|
1758
|
-
"metadata.name": templateName
|
|
1759
|
-
}
|
|
1760
|
-
}, options);
|
|
1761
|
-
if (templates.length !== 1) {
|
|
1762
|
-
if (templates.length > 1) {
|
|
1763
|
-
throw new errors.ConflictError("Templates lookup resulted in multiple matches");
|
|
1764
|
-
} else {
|
|
1765
|
-
throw new errors.NotFoundError("Template not found");
|
|
1766
|
-
}
|
|
1767
|
-
}
|
|
1768
|
-
return templates[0];
|
|
1769
|
-
}
|
|
1770
|
-
}
|
|
1771
|
-
|
|
1772
1842
|
const migrationsDir = backendCommon.resolvePackagePath("@backstage/plugin-scaffolder-backend", "migrations");
|
|
1773
1843
|
class DatabaseTaskStore {
|
|
1774
|
-
|
|
1775
|
-
|
|
1776
|
-
}
|
|
1777
|
-
static async create(knex) {
|
|
1778
|
-
await knex.migrate.latest({
|
|
1844
|
+
static async create(options) {
|
|
1845
|
+
await options.database.migrate.latest({
|
|
1779
1846
|
directory: migrationsDir
|
|
1780
1847
|
});
|
|
1781
|
-
return new DatabaseTaskStore(
|
|
1848
|
+
return new DatabaseTaskStore(options);
|
|
1849
|
+
}
|
|
1850
|
+
constructor(options) {
|
|
1851
|
+
this.db = options.database;
|
|
1782
1852
|
}
|
|
1783
1853
|
async getTask(taskId) {
|
|
1784
1854
|
const [result] = await this.db("tasks").where({id: taskId}).select();
|
|
@@ -1934,7 +2004,7 @@ class DatabaseTaskStore {
|
|
|
1934
2004
|
}
|
|
1935
2005
|
}
|
|
1936
2006
|
|
|
1937
|
-
class
|
|
2007
|
+
class TaskManager {
|
|
1938
2008
|
constructor(state, storage, logger) {
|
|
1939
2009
|
this.state = state;
|
|
1940
2010
|
this.storage = storage;
|
|
@@ -1942,7 +2012,7 @@ class TaskAgent {
|
|
|
1942
2012
|
this.isDone = false;
|
|
1943
2013
|
}
|
|
1944
2014
|
static create(state, storage, logger) {
|
|
1945
|
-
const agent = new
|
|
2015
|
+
const agent = new TaskManager(state, storage, logger);
|
|
1946
2016
|
agent.startTimeout();
|
|
1947
2017
|
return agent;
|
|
1948
2018
|
}
|
|
@@ -2008,7 +2078,7 @@ class StorageTaskBroker {
|
|
|
2008
2078
|
for (; ; ) {
|
|
2009
2079
|
const pendingTask = await this.storage.claimTask();
|
|
2010
2080
|
if (pendingTask) {
|
|
2011
|
-
return
|
|
2081
|
+
return TaskManager.create({
|
|
2012
2082
|
taskId: pendingTask.id,
|
|
2013
2083
|
spec: pendingTask.spec,
|
|
2014
2084
|
secrets: pendingTask.secrets
|
|
@@ -2050,7 +2120,7 @@ class StorageTaskBroker {
|
|
|
2050
2120
|
await new Promise((resolve) => setTimeout(resolve, 1e3));
|
|
2051
2121
|
}
|
|
2052
2122
|
})();
|
|
2053
|
-
return unsubscribe;
|
|
2123
|
+
return {unsubscribe};
|
|
2054
2124
|
}
|
|
2055
2125
|
async vacuumTasks(timeoutS) {
|
|
2056
2126
|
const {tasks} = await this.storage.listStaleTasks(timeoutS);
|
|
@@ -2077,70 +2147,12 @@ class StorageTaskBroker {
|
|
|
2077
2147
|
}
|
|
2078
2148
|
}
|
|
2079
2149
|
|
|
2080
|
-
class TaskWorker {
|
|
2081
|
-
constructor(options) {
|
|
2082
|
-
this.options = options;
|
|
2083
|
-
}
|
|
2084
|
-
start() {
|
|
2085
|
-
(async () => {
|
|
2086
|
-
for (; ; ) {
|
|
2087
|
-
const task = await this.options.taskBroker.claim();
|
|
2088
|
-
await this.runOneTask(task);
|
|
2089
|
-
}
|
|
2090
|
-
})();
|
|
2091
|
-
}
|
|
2092
|
-
async runOneTask(task) {
|
|
2093
|
-
try {
|
|
2094
|
-
const {output} = task.spec.apiVersion === "scaffolder.backstage.io/v1beta3" ? await this.options.runners.workflowRunner.execute(task) : await this.options.runners.legacyWorkflowRunner.execute(task);
|
|
2095
|
-
await task.complete("completed", {output});
|
|
2096
|
-
} catch (error) {
|
|
2097
|
-
errors.assertError(error);
|
|
2098
|
-
await task.complete("failed", {
|
|
2099
|
-
error: {name: error.name, message: error.message}
|
|
2100
|
-
});
|
|
2101
|
-
}
|
|
2102
|
-
}
|
|
2103
|
-
}
|
|
2104
|
-
|
|
2105
|
-
async function getWorkingDirectory(config, logger) {
|
|
2106
|
-
if (!config.has("backend.workingDirectory")) {
|
|
2107
|
-
return os__default['default'].tmpdir();
|
|
2108
|
-
}
|
|
2109
|
-
const workingDirectory = config.getString("backend.workingDirectory");
|
|
2110
|
-
try {
|
|
2111
|
-
await fs__default['default'].access(workingDirectory, fs__default['default'].constants.F_OK | fs__default['default'].constants.W_OK);
|
|
2112
|
-
logger.info(`using working directory: ${workingDirectory}`);
|
|
2113
|
-
} catch (err) {
|
|
2114
|
-
errors.assertError(err);
|
|
2115
|
-
logger.error(`working directory ${workingDirectory} ${err.code === "ENOENT" ? "does not exist" : "is not writable"}`);
|
|
2116
|
-
throw err;
|
|
2117
|
-
}
|
|
2118
|
-
return workingDirectory;
|
|
2119
|
-
}
|
|
2120
|
-
function getEntityBaseUrl(entity) {
|
|
2121
|
-
var _a, _b;
|
|
2122
|
-
let location = (_a = entity.metadata.annotations) == null ? void 0 : _a[catalogModel.SOURCE_LOCATION_ANNOTATION];
|
|
2123
|
-
if (!location) {
|
|
2124
|
-
location = (_b = entity.metadata.annotations) == null ? void 0 : _b[catalogModel.LOCATION_ANNOTATION];
|
|
2125
|
-
}
|
|
2126
|
-
if (!location) {
|
|
2127
|
-
return void 0;
|
|
2128
|
-
}
|
|
2129
|
-
const {type, target} = catalogModel.parseLocationReference(location);
|
|
2130
|
-
if (type === "url") {
|
|
2131
|
-
return target;
|
|
2132
|
-
} else if (type === "file") {
|
|
2133
|
-
return `file://${target}`;
|
|
2134
|
-
}
|
|
2135
|
-
return void 0;
|
|
2136
|
-
}
|
|
2137
|
-
|
|
2138
2150
|
function isTruthy(value) {
|
|
2139
2151
|
return lodash.isArray(value) ? value.length > 0 : !!value;
|
|
2140
2152
|
}
|
|
2141
2153
|
|
|
2142
2154
|
const isValidTaskSpec$1 = (taskSpec) => taskSpec.apiVersion === "backstage.io/v1beta2";
|
|
2143
|
-
class
|
|
2155
|
+
class HandlebarsWorkflowRunner {
|
|
2144
2156
|
constructor(options) {
|
|
2145
2157
|
this.options = options;
|
|
2146
2158
|
this.handlebars = Handlebars__namespace.create();
|
|
@@ -2251,6 +2263,9 @@ class LegacyWorkflowRunner {
|
|
|
2251
2263
|
this.options.logger.debug(`Running ${action.id} with input`, {
|
|
2252
2264
|
input: JSON.stringify(input, null, 2)
|
|
2253
2265
|
});
|
|
2266
|
+
if (!task.spec.metadata) {
|
|
2267
|
+
console.warn("DEPRECATION NOTICE: metadata is undefined. metadata will be required in the future.");
|
|
2268
|
+
}
|
|
2254
2269
|
await action.handler({
|
|
2255
2270
|
baseUrl: task.spec.baseUrl,
|
|
2256
2271
|
logger: taskLogger,
|
|
@@ -2265,7 +2280,8 @@ class LegacyWorkflowRunner {
|
|
|
2265
2280
|
},
|
|
2266
2281
|
output(name, value) {
|
|
2267
2282
|
stepOutputs[name] = value;
|
|
2268
|
-
}
|
|
2283
|
+
},
|
|
2284
|
+
metadata: task.spec.metadata
|
|
2269
2285
|
});
|
|
2270
2286
|
for (const tmpDir of tmpDirs) {
|
|
2271
2287
|
await fs__default['default'].remove(tmpDir);
|
|
@@ -2316,7 +2332,10 @@ class LegacyWorkflowRunner {
|
|
|
2316
2332
|
const isValidTaskSpec = (taskSpec) => {
|
|
2317
2333
|
return taskSpec.apiVersion === "scaffolder.backstage.io/v1beta3";
|
|
2318
2334
|
};
|
|
2319
|
-
const createStepLogger = ({
|
|
2335
|
+
const createStepLogger = ({
|
|
2336
|
+
task,
|
|
2337
|
+
step
|
|
2338
|
+
}) => {
|
|
2320
2339
|
const metadata = {stepId: step.id};
|
|
2321
2340
|
const taskLogger = winston__namespace.createLogger({
|
|
2322
2341
|
level: process.env.LOG_LEVEL || "info",
|
|
@@ -2333,38 +2352,30 @@ const createStepLogger = ({task, step}) => {
|
|
|
2333
2352
|
taskLogger.add(new winston__namespace.transports.Stream({stream: streamLogger}));
|
|
2334
2353
|
return {taskLogger, streamLogger};
|
|
2335
2354
|
};
|
|
2336
|
-
class
|
|
2355
|
+
class NunjucksWorkflowRunner {
|
|
2337
2356
|
constructor(options) {
|
|
2338
2357
|
this.options = options;
|
|
2339
|
-
|
|
2358
|
+
}
|
|
2359
|
+
isSingleTemplateString(input) {
|
|
2360
|
+
var _a, _b;
|
|
2361
|
+
const {parser, nodes} = nunjucks__default['default'];
|
|
2362
|
+
const parsed = parser.parse(input, {}, {
|
|
2340
2363
|
autoescape: false,
|
|
2341
2364
|
tags: {
|
|
2342
2365
|
variableStart: "${{",
|
|
2343
2366
|
variableEnd: "}}"
|
|
2344
2367
|
}
|
|
2345
|
-
};
|
|
2346
|
-
this.nunjucks = nunjucks__default['default'].configure(this.nunjucksOptions);
|
|
2347
|
-
this.nunjucks.addFilter("parseRepoUrl", (repoUrl) => {
|
|
2348
|
-
return parseRepoUrl(repoUrl, this.options.integrations);
|
|
2349
|
-
});
|
|
2350
|
-
this.nunjucks.addFilter("projectSlug", (repoUrl) => {
|
|
2351
|
-
const {owner, repo} = parseRepoUrl(repoUrl, this.options.integrations);
|
|
2352
|
-
return `${owner}/${repo}`;
|
|
2353
2368
|
});
|
|
2369
|
+
return parsed.children.length === 1 && !(((_b = (_a = parsed.children[0]) == null ? void 0 : _a.children) == null ? void 0 : _b[0]) instanceof nodes.TemplateData);
|
|
2354
2370
|
}
|
|
2355
|
-
|
|
2356
|
-
const {parser, nodes} = require("nunjucks");
|
|
2357
|
-
const parsed = parser.parse(input, {}, this.nunjucksOptions);
|
|
2358
|
-
return parsed.children.length === 1 && !(parsed.children[0] instanceof nodes.TemplateData);
|
|
2359
|
-
}
|
|
2360
|
-
render(input, context) {
|
|
2371
|
+
render(input, context, renderTemplate) {
|
|
2361
2372
|
return JSON.parse(JSON.stringify(input), (_key, value) => {
|
|
2362
2373
|
try {
|
|
2363
2374
|
if (typeof value === "string") {
|
|
2364
2375
|
try {
|
|
2365
2376
|
if (this.isSingleTemplateString(value)) {
|
|
2366
2377
|
const wrappedDumped = value.replace(/\${{(.+)}}/g, "${{ ( $1 ) | dump }}");
|
|
2367
|
-
const templated2 =
|
|
2378
|
+
const templated2 = renderTemplate(wrappedDumped, context);
|
|
2368
2379
|
if (templated2 === "") {
|
|
2369
2380
|
return void 0;
|
|
2370
2381
|
}
|
|
@@ -2373,7 +2384,7 @@ class DefaultWorkflowRunner {
|
|
|
2373
2384
|
} catch (ex) {
|
|
2374
2385
|
this.options.logger.error(`Failed to parse template string: ${value} with error ${ex.message}`);
|
|
2375
2386
|
}
|
|
2376
|
-
const templated =
|
|
2387
|
+
const templated = renderTemplate(value, context);
|
|
2377
2388
|
if (templated === "") {
|
|
2378
2389
|
return void 0;
|
|
2379
2390
|
}
|
|
@@ -2391,6 +2402,12 @@ class DefaultWorkflowRunner {
|
|
|
2391
2402
|
throw new errors.InputError("Wrong template version executed with the workflow engine");
|
|
2392
2403
|
}
|
|
2393
2404
|
const workspacePath = path__default['default'].join(this.options.workingDirectory, await task.getWorkspaceName());
|
|
2405
|
+
const {integrations} = this.options;
|
|
2406
|
+
const renderTemplate = await SecureTemplater.loadRenderer({
|
|
2407
|
+
parseRepoUrl(url) {
|
|
2408
|
+
return parseRepoUrl(url, integrations);
|
|
2409
|
+
}
|
|
2410
|
+
});
|
|
2394
2411
|
try {
|
|
2395
2412
|
await fs__default['default'].ensureDir(workspacePath);
|
|
2396
2413
|
await task.emitLog(`Starting up task with ${task.spec.steps.length} steps`);
|
|
@@ -2401,7 +2418,7 @@ class DefaultWorkflowRunner {
|
|
|
2401
2418
|
for (const step of task.spec.steps) {
|
|
2402
2419
|
try {
|
|
2403
2420
|
if (step.if) {
|
|
2404
|
-
const ifResult = await this.render(step.if, context);
|
|
2421
|
+
const ifResult = await this.render(step.if, context, renderTemplate);
|
|
2405
2422
|
if (!isTruthy(ifResult)) {
|
|
2406
2423
|
await task.emitLog(`Skipping step ${step.id} because it's if condition was false`, {stepId: step.id, status: "skipped"});
|
|
2407
2424
|
continue;
|
|
@@ -2413,7 +2430,7 @@ class DefaultWorkflowRunner {
|
|
|
2413
2430
|
});
|
|
2414
2431
|
const action = this.options.actionRegistry.get(step.action);
|
|
2415
2432
|
const {taskLogger, streamLogger} = createStepLogger({task, step});
|
|
2416
|
-
const input = (_a = step.input && this.render(step.input, context)) != null ? _a : {};
|
|
2433
|
+
const input = (_a = step.input && this.render(step.input, context, renderTemplate)) != null ? _a : {};
|
|
2417
2434
|
if ((_b = action.schema) == null ? void 0 : _b.input) {
|
|
2418
2435
|
const validateResult = jsonschema.validate(input, action.schema.input);
|
|
2419
2436
|
if (!validateResult.valid) {
|
|
@@ -2423,6 +2440,9 @@ class DefaultWorkflowRunner {
|
|
|
2423
2440
|
}
|
|
2424
2441
|
const tmpDirs = new Array();
|
|
2425
2442
|
const stepOutput = {};
|
|
2443
|
+
if (!task.spec.metadata) {
|
|
2444
|
+
console.warn("DEPRECATION NOTICE: metadata is undefined. metadata will be required in the future.");
|
|
2445
|
+
}
|
|
2426
2446
|
await action.handler({
|
|
2427
2447
|
baseUrl: task.spec.baseUrl,
|
|
2428
2448
|
input,
|
|
@@ -2436,7 +2456,8 @@ class DefaultWorkflowRunner {
|
|
|
2436
2456
|
},
|
|
2437
2457
|
output(name, value) {
|
|
2438
2458
|
stepOutput[name] = value;
|
|
2439
|
-
}
|
|
2459
|
+
},
|
|
2460
|
+
metadata: task.spec.metadata
|
|
2440
2461
|
});
|
|
2441
2462
|
for (const tmpDir of tmpDirs) {
|
|
2442
2463
|
await fs__default['default'].remove(tmpDir);
|
|
@@ -2454,7 +2475,7 @@ class DefaultWorkflowRunner {
|
|
|
2454
2475
|
throw err;
|
|
2455
2476
|
}
|
|
2456
2477
|
}
|
|
2457
|
-
const output = this.render(task.spec.output, context);
|
|
2478
|
+
const output = this.render(task.spec.output, context, renderTemplate);
|
|
2458
2479
|
return {output};
|
|
2459
2480
|
} finally {
|
|
2460
2481
|
if (workspacePath) {
|
|
@@ -2464,6 +2485,111 @@ class DefaultWorkflowRunner {
|
|
|
2464
2485
|
}
|
|
2465
2486
|
}
|
|
2466
2487
|
|
|
2488
|
+
class TaskWorker {
|
|
2489
|
+
constructor(options) {
|
|
2490
|
+
this.options = options;
|
|
2491
|
+
}
|
|
2492
|
+
static async create(options) {
|
|
2493
|
+
const {
|
|
2494
|
+
taskBroker,
|
|
2495
|
+
logger,
|
|
2496
|
+
actionRegistry,
|
|
2497
|
+
integrations,
|
|
2498
|
+
workingDirectory
|
|
2499
|
+
} = options;
|
|
2500
|
+
const legacyWorkflowRunner = new HandlebarsWorkflowRunner({
|
|
2501
|
+
logger,
|
|
2502
|
+
actionRegistry,
|
|
2503
|
+
integrations,
|
|
2504
|
+
workingDirectory
|
|
2505
|
+
});
|
|
2506
|
+
const workflowRunner = new NunjucksWorkflowRunner({
|
|
2507
|
+
actionRegistry,
|
|
2508
|
+
integrations,
|
|
2509
|
+
logger,
|
|
2510
|
+
workingDirectory
|
|
2511
|
+
});
|
|
2512
|
+
return new TaskWorker({
|
|
2513
|
+
taskBroker,
|
|
2514
|
+
runners: {legacyWorkflowRunner, workflowRunner}
|
|
2515
|
+
});
|
|
2516
|
+
}
|
|
2517
|
+
start() {
|
|
2518
|
+
(async () => {
|
|
2519
|
+
for (; ; ) {
|
|
2520
|
+
const task = await this.options.taskBroker.claim();
|
|
2521
|
+
await this.runOneTask(task);
|
|
2522
|
+
}
|
|
2523
|
+
})();
|
|
2524
|
+
}
|
|
2525
|
+
async runOneTask(task) {
|
|
2526
|
+
try {
|
|
2527
|
+
const {output} = task.spec.apiVersion === "scaffolder.backstage.io/v1beta3" ? await this.options.runners.workflowRunner.execute(task) : await this.options.runners.legacyWorkflowRunner.execute(task);
|
|
2528
|
+
await task.complete("completed", {output});
|
|
2529
|
+
} catch (error) {
|
|
2530
|
+
errors.assertError(error);
|
|
2531
|
+
await task.complete("failed", {
|
|
2532
|
+
error: {name: error.name, message: error.message}
|
|
2533
|
+
});
|
|
2534
|
+
}
|
|
2535
|
+
}
|
|
2536
|
+
}
|
|
2537
|
+
|
|
2538
|
+
class CatalogEntityClient {
|
|
2539
|
+
constructor(catalogClient) {
|
|
2540
|
+
this.catalogClient = catalogClient;
|
|
2541
|
+
}
|
|
2542
|
+
async findTemplate(templateName, options) {
|
|
2543
|
+
const {items: templates} = await this.catalogClient.getEntities({
|
|
2544
|
+
filter: {
|
|
2545
|
+
kind: "template",
|
|
2546
|
+
"metadata.name": templateName
|
|
2547
|
+
}
|
|
2548
|
+
}, options);
|
|
2549
|
+
if (templates.length !== 1) {
|
|
2550
|
+
if (templates.length > 1) {
|
|
2551
|
+
throw new errors.ConflictError("Templates lookup resulted in multiple matches");
|
|
2552
|
+
} else {
|
|
2553
|
+
throw new errors.NotFoundError("Template not found");
|
|
2554
|
+
}
|
|
2555
|
+
}
|
|
2556
|
+
return templates[0];
|
|
2557
|
+
}
|
|
2558
|
+
}
|
|
2559
|
+
|
|
2560
|
+
async function getWorkingDirectory(config, logger) {
|
|
2561
|
+
if (!config.has("backend.workingDirectory")) {
|
|
2562
|
+
return os__default['default'].tmpdir();
|
|
2563
|
+
}
|
|
2564
|
+
const workingDirectory = config.getString("backend.workingDirectory");
|
|
2565
|
+
try {
|
|
2566
|
+
await fs__default['default'].access(workingDirectory, fs__default['default'].constants.F_OK | fs__default['default'].constants.W_OK);
|
|
2567
|
+
logger.info(`using working directory: ${workingDirectory}`);
|
|
2568
|
+
} catch (err) {
|
|
2569
|
+
errors.assertError(err);
|
|
2570
|
+
logger.error(`working directory ${workingDirectory} ${err.code === "ENOENT" ? "does not exist" : "is not writable"}`);
|
|
2571
|
+
throw err;
|
|
2572
|
+
}
|
|
2573
|
+
return workingDirectory;
|
|
2574
|
+
}
|
|
2575
|
+
function getEntityBaseUrl(entity) {
|
|
2576
|
+
var _a, _b;
|
|
2577
|
+
let location = (_a = entity.metadata.annotations) == null ? void 0 : _a[catalogModel.SOURCE_LOCATION_ANNOTATION];
|
|
2578
|
+
if (!location) {
|
|
2579
|
+
location = (_b = entity.metadata.annotations) == null ? void 0 : _b[catalogModel.LOCATION_ANNOTATION];
|
|
2580
|
+
}
|
|
2581
|
+
if (!location) {
|
|
2582
|
+
return void 0;
|
|
2583
|
+
}
|
|
2584
|
+
const {type, target} = catalogModel.parseLocationReference(location);
|
|
2585
|
+
if (type === "url") {
|
|
2586
|
+
return target;
|
|
2587
|
+
} else if (type === "file") {
|
|
2588
|
+
return `file://${target}`;
|
|
2589
|
+
}
|
|
2590
|
+
return void 0;
|
|
2591
|
+
}
|
|
2592
|
+
|
|
2467
2593
|
function isSupportedTemplate(entity) {
|
|
2468
2594
|
return entity.apiVersion === "backstage.io/v1beta2" || entity.apiVersion === "scaffolder.backstage.io/v1beta3";
|
|
2469
2595
|
}
|
|
@@ -2484,29 +2610,24 @@ async function createRouter(options) {
|
|
|
2484
2610
|
const workingDirectory = await getWorkingDirectory(config, logger);
|
|
2485
2611
|
const entityClient = new CatalogEntityClient(catalogClient);
|
|
2486
2612
|
const integrations = integration.ScmIntegrations.fromConfig(config);
|
|
2487
|
-
|
|
2488
|
-
|
|
2613
|
+
let taskBroker;
|
|
2614
|
+
if (!options.taskBroker) {
|
|
2615
|
+
const databaseTaskStore = await DatabaseTaskStore.create({
|
|
2616
|
+
database: await database.getClient()
|
|
2617
|
+
});
|
|
2618
|
+
taskBroker = new StorageTaskBroker(databaseTaskStore, logger);
|
|
2619
|
+
} else {
|
|
2620
|
+
taskBroker = options.taskBroker;
|
|
2621
|
+
}
|
|
2489
2622
|
const actionRegistry = new TemplateActionRegistry();
|
|
2490
|
-
const legacyWorkflowRunner = new LegacyWorkflowRunner({
|
|
2491
|
-
logger,
|
|
2492
|
-
actionRegistry,
|
|
2493
|
-
integrations,
|
|
2494
|
-
workingDirectory
|
|
2495
|
-
});
|
|
2496
|
-
const workflowRunner = new DefaultWorkflowRunner({
|
|
2497
|
-
actionRegistry,
|
|
2498
|
-
integrations,
|
|
2499
|
-
logger,
|
|
2500
|
-
workingDirectory
|
|
2501
|
-
});
|
|
2502
2623
|
const workers = [];
|
|
2503
2624
|
for (let i = 0; i < (taskWorkers || 1); i++) {
|
|
2504
|
-
const worker =
|
|
2625
|
+
const worker = await TaskWorker.create({
|
|
2505
2626
|
taskBroker,
|
|
2506
|
-
|
|
2507
|
-
|
|
2508
|
-
|
|
2509
|
-
|
|
2627
|
+
actionRegistry,
|
|
2628
|
+
integrations,
|
|
2629
|
+
logger,
|
|
2630
|
+
workingDirectory
|
|
2510
2631
|
});
|
|
2511
2632
|
workers.push(worker);
|
|
2512
2633
|
}
|
|
@@ -2556,7 +2677,7 @@ async function createRouter(options) {
|
|
|
2556
2677
|
});
|
|
2557
2678
|
res.json(actionsList);
|
|
2558
2679
|
}).post("/v2/tasks", async (req, res) => {
|
|
2559
|
-
var _a, _b, _c;
|
|
2680
|
+
var _a, _b, _c, _d, _e;
|
|
2560
2681
|
const templateName = req.body.templateName;
|
|
2561
2682
|
const values = req.body.values;
|
|
2562
2683
|
const token = getBearerToken(req.headers.authorization);
|
|
@@ -2585,7 +2706,8 @@ async function createRouter(options) {
|
|
|
2585
2706
|
name: (_b2 = step.name) != null ? _b2 : step.action
|
|
2586
2707
|
};
|
|
2587
2708
|
}),
|
|
2588
|
-
output: (_b = template.spec.output) != null ? _b : {}
|
|
2709
|
+
output: (_b = template.spec.output) != null ? _b : {},
|
|
2710
|
+
metadata: {name: (_c = template.metadata) == null ? void 0 : _c.name}
|
|
2589
2711
|
} : {
|
|
2590
2712
|
apiVersion: template.apiVersion,
|
|
2591
2713
|
baseUrl,
|
|
@@ -2598,7 +2720,8 @@ async function createRouter(options) {
|
|
|
2598
2720
|
name: (_b2 = step.name) != null ? _b2 : step.action
|
|
2599
2721
|
};
|
|
2600
2722
|
}),
|
|
2601
|
-
output: (
|
|
2723
|
+
output: (_d = template.spec.output) != null ? _d : {},
|
|
2724
|
+
metadata: {name: (_e = template.metadata) == null ? void 0 : _e.name}
|
|
2602
2725
|
};
|
|
2603
2726
|
} else {
|
|
2604
2727
|
throw new errors.InputError(`Unsupported apiVersion field in schema entity, ${template.apiVersion}`);
|
|
@@ -2617,14 +2740,15 @@ async function createRouter(options) {
|
|
|
2617
2740
|
res.status(200).json(task);
|
|
2618
2741
|
}).get("/v2/tasks/:taskId/eventstream", async (req, res) => {
|
|
2619
2742
|
const {taskId} = req.params;
|
|
2620
|
-
const after = Number(req.query.after)
|
|
2743
|
+
const after = req.query.after !== void 0 ? Number(req.query.after) : void 0;
|
|
2621
2744
|
logger.debug(`Event stream observing taskId '${taskId}' opened`);
|
|
2622
2745
|
res.writeHead(200, {
|
|
2623
2746
|
Connection: "keep-alive",
|
|
2624
2747
|
"Cache-Control": "no-cache",
|
|
2625
2748
|
"Content-Type": "text/event-stream"
|
|
2626
2749
|
});
|
|
2627
|
-
const unsubscribe = taskBroker.observe({taskId, after}, (error, {events}) => {
|
|
2750
|
+
const {unsubscribe} = taskBroker.observe({taskId, after}, (error, {events}) => {
|
|
2751
|
+
var _a;
|
|
2628
2752
|
if (error) {
|
|
2629
2753
|
logger.error(`Received error from event stream when observing taskId '${taskId}', ${error}`);
|
|
2630
2754
|
}
|
|
@@ -2638,7 +2762,7 @@ data: ${JSON.stringify(event)}
|
|
|
2638
2762
|
shouldUnsubscribe = true;
|
|
2639
2763
|
}
|
|
2640
2764
|
}
|
|
2641
|
-
res.flush();
|
|
2765
|
+
(_a = res.flush) == null ? void 0 : _a.call(res);
|
|
2642
2766
|
if (shouldUnsubscribe)
|
|
2643
2767
|
unsubscribe();
|
|
2644
2768
|
});
|
|
@@ -2646,6 +2770,27 @@ data: ${JSON.stringify(event)}
|
|
|
2646
2770
|
unsubscribe();
|
|
2647
2771
|
logger.debug(`Event stream observing taskId '${taskId}' closed`);
|
|
2648
2772
|
});
|
|
2773
|
+
}).get("/v2/tasks/:taskId/events", async (req, res) => {
|
|
2774
|
+
const {taskId} = req.params;
|
|
2775
|
+
const after = Number(req.query.after) || void 0;
|
|
2776
|
+
let unsubscribe = () => {
|
|
2777
|
+
};
|
|
2778
|
+
const timeout = setTimeout(() => {
|
|
2779
|
+
unsubscribe();
|
|
2780
|
+
res.json([]);
|
|
2781
|
+
}, 3e4);
|
|
2782
|
+
({unsubscribe} = taskBroker.observe({taskId, after}, (error, {events}) => {
|
|
2783
|
+
clearTimeout(timeout);
|
|
2784
|
+
unsubscribe();
|
|
2785
|
+
if (error) {
|
|
2786
|
+
logger.error(`Received error from log when observing taskId '${taskId}', ${error}`);
|
|
2787
|
+
}
|
|
2788
|
+
res.json(events);
|
|
2789
|
+
}));
|
|
2790
|
+
req.on("close", () => {
|
|
2791
|
+
unsubscribe();
|
|
2792
|
+
clearTimeout(timeout);
|
|
2793
|
+
});
|
|
2649
2794
|
});
|
|
2650
2795
|
const app = express__default['default']();
|
|
2651
2796
|
app.set("logger", logger);
|
|
@@ -2712,8 +2857,11 @@ Object.defineProperty(exports, 'createFetchCookiecutterAction', {
|
|
|
2712
2857
|
}
|
|
2713
2858
|
});
|
|
2714
2859
|
exports.CatalogEntityClient = CatalogEntityClient;
|
|
2860
|
+
exports.DatabaseTaskStore = DatabaseTaskStore;
|
|
2715
2861
|
exports.OctokitProvider = OctokitProvider;
|
|
2716
2862
|
exports.ScaffolderEntitiesProcessor = ScaffolderEntitiesProcessor;
|
|
2863
|
+
exports.TaskManager = TaskManager;
|
|
2864
|
+
exports.TaskWorker = TaskWorker;
|
|
2717
2865
|
exports.TemplateActionRegistry = TemplateActionRegistry;
|
|
2718
2866
|
exports.createBuiltinActions = createBuiltinActions;
|
|
2719
2867
|
exports.createCatalogRegisterAction = createCatalogRegisterAction;
|