@backstage/plugin-scaffolder-backend 1.20.0 → 1.21.0-next.1
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 +79 -0
- package/alpha/package.json +1 -1
- package/config.d.ts +16 -0
- package/dist/alpha.cjs.js +41 -21
- package/dist/alpha.cjs.js.map +1 -1
- package/dist/cjs/{router-e667d04e.cjs.js → router-43c4dd8a.cjs.js} +245 -77
- package/dist/cjs/router-43c4dd8a.cjs.js.map +1 -0
- package/dist/index.cjs.js +2 -2
- package/dist/index.d.ts +31 -2
- package/package.json +20 -20
- package/dist/cjs/router-e667d04e.cjs.js.map +0 -1
|
@@ -27,11 +27,11 @@ var gerrit = require('@backstage/plugin-scaffolder-backend-module-gerrit');
|
|
|
27
27
|
var gitlab = require('@backstage/plugin-scaffolder-backend-module-gitlab');
|
|
28
28
|
var uuid = require('uuid');
|
|
29
29
|
var ObservableImpl = require('zen-observable');
|
|
30
|
+
var lodash = require('lodash');
|
|
30
31
|
var PQueue = require('p-queue');
|
|
31
32
|
var winston = require('winston');
|
|
32
33
|
var nunjucks = require('nunjucks');
|
|
33
34
|
var stream = require('stream');
|
|
34
|
-
var lodash = require('lodash');
|
|
35
35
|
var pluginPermissionNode = require('@backstage/plugin-permission-node');
|
|
36
36
|
var promClient = require('prom-client');
|
|
37
37
|
var pluginPermissionCommon = require('@backstage/plugin-permission-common');
|
|
@@ -640,6 +640,11 @@ function createFetchPlainAction(options) {
|
|
|
640
640
|
title: "Target Path",
|
|
641
641
|
description: "Target path within the working directory to download the contents to.",
|
|
642
642
|
type: "string"
|
|
643
|
+
},
|
|
644
|
+
token: {
|
|
645
|
+
title: "Token",
|
|
646
|
+
description: "An optional token to use for authentication when reading the resources.",
|
|
647
|
+
type: "string"
|
|
643
648
|
}
|
|
644
649
|
}
|
|
645
650
|
}
|
|
@@ -655,7 +660,8 @@ function createFetchPlainAction(options) {
|
|
|
655
660
|
integrations,
|
|
656
661
|
baseUrl: (_b = ctx.templateInfo) == null ? void 0 : _b.baseUrl,
|
|
657
662
|
fetchUrl: ctx.input.url,
|
|
658
|
-
outputPath
|
|
663
|
+
outputPath,
|
|
664
|
+
token: ctx.input.token
|
|
659
665
|
});
|
|
660
666
|
}
|
|
661
667
|
});
|
|
@@ -700,6 +706,11 @@ function createFetchPlainFileAction(options) {
|
|
|
700
706
|
title: "Target Path",
|
|
701
707
|
description: "Target path within the working directory to download the file as.",
|
|
702
708
|
type: "string"
|
|
709
|
+
},
|
|
710
|
+
token: {
|
|
711
|
+
title: "Token",
|
|
712
|
+
description: "An optional token to use for authentication when reading the resources.",
|
|
713
|
+
type: "string"
|
|
703
714
|
}
|
|
704
715
|
}
|
|
705
716
|
}
|
|
@@ -717,12 +728,22 @@ function createFetchPlainFileAction(options) {
|
|
|
717
728
|
integrations,
|
|
718
729
|
baseUrl: (_a = ctx.templateInfo) == null ? void 0 : _a.baseUrl,
|
|
719
730
|
fetchUrl: ctx.input.url,
|
|
720
|
-
outputPath
|
|
731
|
+
outputPath,
|
|
732
|
+
token: ctx.input.token
|
|
721
733
|
});
|
|
722
734
|
}
|
|
723
735
|
});
|
|
724
736
|
}
|
|
725
737
|
|
|
738
|
+
function isNoNodeSnapshotOptionProvided() {
|
|
739
|
+
var _a;
|
|
740
|
+
return ((_a = process.env.NODE_OPTIONS) == null ? void 0 : _a.includes("--no-node-snapshot")) || process.argv.includes("--no-node-snapshot");
|
|
741
|
+
}
|
|
742
|
+
function getMajorNodeVersion() {
|
|
743
|
+
const version = process.versions.node;
|
|
744
|
+
return parseInt(version.split(".")[0], 10);
|
|
745
|
+
}
|
|
746
|
+
|
|
726
747
|
const mkScript = (nunjucksSource) => `
|
|
727
748
|
const { render, renderCompat } = (() => {
|
|
728
749
|
const module = {};
|
|
@@ -733,6 +754,7 @@ const { render, renderCompat } = (() => {
|
|
|
733
754
|
|
|
734
755
|
const env = module.exports.configure({
|
|
735
756
|
autoescape: false,
|
|
757
|
+
...JSON.parse(nunjucksConfigs),
|
|
736
758
|
tags: {
|
|
737
759
|
variableStart: '\${{',
|
|
738
760
|
variableEnd: '}}',
|
|
@@ -741,6 +763,7 @@ const { render, renderCompat } = (() => {
|
|
|
741
763
|
|
|
742
764
|
const compatEnv = module.exports.configure({
|
|
743
765
|
autoescape: false,
|
|
766
|
+
...JSON.parse(nunjucksConfigs),
|
|
744
767
|
tags: {
|
|
745
768
|
variableStart: '{{',
|
|
746
769
|
variableEnd: '}}',
|
|
@@ -793,8 +816,16 @@ class SecureTemplater {
|
|
|
793
816
|
const {
|
|
794
817
|
cookiecutterCompat,
|
|
795
818
|
templateFilters = {},
|
|
796
|
-
templateGlobals = {}
|
|
819
|
+
templateGlobals = {},
|
|
820
|
+
nunjucksConfigs = {}
|
|
797
821
|
} = options;
|
|
822
|
+
const nodeVersion = getMajorNodeVersion();
|
|
823
|
+
if (nodeVersion >= 20 && !isNoNodeSnapshotOptionProvided()) {
|
|
824
|
+
throw new Error(
|
|
825
|
+
`When using Node.js version 20 or newer, the scaffolder backend plugin requires that it be started with the --no-node-snapshot option.
|
|
826
|
+
Please make sure that you have NODE_OPTIONS=--no-node-snapshot in your environment.`
|
|
827
|
+
);
|
|
828
|
+
}
|
|
798
829
|
const isolate = new isolatedVm.Isolate({ memoryLimit: 128 });
|
|
799
830
|
const context = await isolate.createContext();
|
|
800
831
|
const contextGlobal = context.global;
|
|
@@ -808,6 +839,7 @@ class SecureTemplater {
|
|
|
808
839
|
const nunjucksScript = await isolate.compileScript(
|
|
809
840
|
mkScript(nunjucksSource)
|
|
810
841
|
);
|
|
842
|
+
await contextGlobal.set("nunjucksConfigs", JSON.stringify(nunjucksConfigs));
|
|
811
843
|
const availableFilters = Object.keys(templateFilters);
|
|
812
844
|
await contextGlobal.set(
|
|
813
845
|
"availableTemplateFilters",
|
|
@@ -884,7 +916,7 @@ const createDefaultFilters = ({
|
|
|
884
916
|
|
|
885
917
|
const examples$2 = [
|
|
886
918
|
{
|
|
887
|
-
description: "Downloads a
|
|
919
|
+
description: "Downloads a skeleton directory that lives alongside the template file and fill it out with values.",
|
|
888
920
|
example: yaml__default["default"].stringify({
|
|
889
921
|
steps: [
|
|
890
922
|
{
|
|
@@ -969,6 +1001,11 @@ function createFetchTemplateAction(options) {
|
|
|
969
1001
|
title: "Replace files",
|
|
970
1002
|
description: "If set, replace files in targetPath instead of skipping existing ones.",
|
|
971
1003
|
type: "boolean"
|
|
1004
|
+
},
|
|
1005
|
+
token: {
|
|
1006
|
+
title: "Token",
|
|
1007
|
+
description: "An optional token to use for authentication when reading the resources.",
|
|
1008
|
+
type: "string"
|
|
972
1009
|
}
|
|
973
1010
|
}
|
|
974
1011
|
}
|
|
@@ -1020,7 +1057,8 @@ function createFetchTemplateAction(options) {
|
|
|
1020
1057
|
integrations,
|
|
1021
1058
|
baseUrl: (_b = ctx.templateInfo) == null ? void 0 : _b.baseUrl,
|
|
1022
1059
|
fetchUrl: ctx.input.url,
|
|
1023
|
-
outputPath: templateDir
|
|
1060
|
+
outputPath: templateDir,
|
|
1061
|
+
token: ctx.input.token
|
|
1024
1062
|
});
|
|
1025
1063
|
ctx.logger.info("Listing files and directories in template");
|
|
1026
1064
|
const allEntriesInTemplate = await globby__default["default"](`**/*`, {
|
|
@@ -1053,7 +1091,11 @@ function createFetchTemplateAction(options) {
|
|
|
1053
1091
|
...defaultTemplateFilters,
|
|
1054
1092
|
...additionalTemplateFilters
|
|
1055
1093
|
},
|
|
1056
|
-
templateGlobals: additionalTemplateGlobals
|
|
1094
|
+
templateGlobals: additionalTemplateGlobals,
|
|
1095
|
+
nunjucksConfigs: {
|
|
1096
|
+
trimBlocks: ctx.input.trimBlocks,
|
|
1097
|
+
lstripBlocks: ctx.input.lstripBlocks
|
|
1098
|
+
}
|
|
1057
1099
|
});
|
|
1058
1100
|
for (const location of allEntriesInTemplate) {
|
|
1059
1101
|
let renderContents;
|
|
@@ -1419,6 +1461,36 @@ class TemplateActionRegistry {
|
|
|
1419
1461
|
}
|
|
1420
1462
|
}
|
|
1421
1463
|
|
|
1464
|
+
const trimEventsTillLastRecovery = (events) => {
|
|
1465
|
+
const recoveredEventInd = events.slice().reverse().findIndex((event) => event.type === "recovered");
|
|
1466
|
+
if (recoveredEventInd >= 0) {
|
|
1467
|
+
const ind = events.length - recoveredEventInd - 1;
|
|
1468
|
+
const { recoverStrategy } = events[ind].body;
|
|
1469
|
+
if (recoverStrategy === "startOver") {
|
|
1470
|
+
return {
|
|
1471
|
+
events: recoveredEventInd === 0 ? [] : events.slice(ind)
|
|
1472
|
+
};
|
|
1473
|
+
}
|
|
1474
|
+
}
|
|
1475
|
+
return { events };
|
|
1476
|
+
};
|
|
1477
|
+
|
|
1478
|
+
const intervalFromNowTill = (timeoutS, knex) => {
|
|
1479
|
+
let heartbeatInterval = knex.raw(`? - interval '${timeoutS} seconds'`, [
|
|
1480
|
+
knex.fn.now()
|
|
1481
|
+
]);
|
|
1482
|
+
if (knex.client.config.client.includes("mysql")) {
|
|
1483
|
+
heartbeatInterval = knex.raw(
|
|
1484
|
+
`date_sub(now(), interval ${timeoutS} second)`
|
|
1485
|
+
);
|
|
1486
|
+
} else if (knex.client.config.client.includes("sqlite3")) {
|
|
1487
|
+
heartbeatInterval = knex.raw(`datetime('now', ?)`, [
|
|
1488
|
+
`-${timeoutS} seconds`
|
|
1489
|
+
]);
|
|
1490
|
+
}
|
|
1491
|
+
return heartbeatInterval;
|
|
1492
|
+
};
|
|
1493
|
+
|
|
1422
1494
|
var __defProp$3 = Object.defineProperty;
|
|
1423
1495
|
var __defNormalProp$3 = (obj, key, value) => key in obj ? __defProp$3(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value;
|
|
1424
1496
|
var __publicField$3 = (obj, key, value) => {
|
|
@@ -1455,6 +1527,28 @@ class DatabaseTaskStore {
|
|
|
1455
1527
|
await this.runMigrations(database, client);
|
|
1456
1528
|
return new DatabaseTaskStore(client);
|
|
1457
1529
|
}
|
|
1530
|
+
isRecoverableTask(spec) {
|
|
1531
|
+
var _a, _b;
|
|
1532
|
+
return ["startOver"].includes(
|
|
1533
|
+
(_b = (_a = spec.EXPERIMENTAL_recovery) == null ? void 0 : _a.EXPERIMENTAL_strategy) != null ? _b : "none"
|
|
1534
|
+
);
|
|
1535
|
+
}
|
|
1536
|
+
parseSpec({ spec, id }) {
|
|
1537
|
+
try {
|
|
1538
|
+
return JSON.parse(spec);
|
|
1539
|
+
} catch (error) {
|
|
1540
|
+
throw new Error(`Failed to parse spec of task '${id}', ${error}`);
|
|
1541
|
+
}
|
|
1542
|
+
}
|
|
1543
|
+
parseTaskSecrets(taskRow) {
|
|
1544
|
+
try {
|
|
1545
|
+
return taskRow.secrets ? JSON.parse(taskRow.secrets) : void 0;
|
|
1546
|
+
} catch (error) {
|
|
1547
|
+
throw new Error(
|
|
1548
|
+
`Failed to parse secrets of task '${taskRow.id}', ${error}`
|
|
1549
|
+
);
|
|
1550
|
+
}
|
|
1551
|
+
}
|
|
1458
1552
|
static async getClient(database) {
|
|
1459
1553
|
if (isPluginDatabaseManager(database)) {
|
|
1460
1554
|
return database.getClient();
|
|
@@ -1539,30 +1633,26 @@ class DatabaseTaskStore {
|
|
|
1539
1633
|
if (!task) {
|
|
1540
1634
|
return void 0;
|
|
1541
1635
|
}
|
|
1636
|
+
const spec = this.parseSpec(task);
|
|
1542
1637
|
const updateCount = await tx("tasks").where({ id: task.id, status: "open" }).update({
|
|
1543
1638
|
status: "processing",
|
|
1544
1639
|
last_heartbeat_at: this.db.fn.now(),
|
|
1545
|
-
// remove the secrets when moving to processing state.
|
|
1546
|
-
secrets: null
|
|
1640
|
+
// remove the secrets for non-recoverable tasks when moving to processing state.
|
|
1641
|
+
secrets: this.isRecoverableTask(spec) ? task.secrets : null
|
|
1547
1642
|
});
|
|
1548
1643
|
if (updateCount < 1) {
|
|
1549
1644
|
return void 0;
|
|
1550
1645
|
}
|
|
1551
|
-
|
|
1552
|
-
|
|
1553
|
-
|
|
1554
|
-
|
|
1555
|
-
|
|
1556
|
-
|
|
1557
|
-
|
|
1558
|
-
|
|
1559
|
-
|
|
1560
|
-
|
|
1561
|
-
secrets
|
|
1562
|
-
};
|
|
1563
|
-
} catch (error) {
|
|
1564
|
-
throw new Error(`Failed to parse spec of task '${task.id}', ${error}`);
|
|
1565
|
-
}
|
|
1646
|
+
const secrets = this.parseTaskSecrets(task);
|
|
1647
|
+
return {
|
|
1648
|
+
id: task.id,
|
|
1649
|
+
spec,
|
|
1650
|
+
status: "processing",
|
|
1651
|
+
lastHeartbeatAt: task.last_heartbeat_at,
|
|
1652
|
+
createdAt: task.created_at,
|
|
1653
|
+
createdBy: (_a = task.created_by) != null ? _a : void 0,
|
|
1654
|
+
secrets
|
|
1655
|
+
};
|
|
1566
1656
|
});
|
|
1567
1657
|
}
|
|
1568
1658
|
async heartbeatTask(taskId) {
|
|
@@ -1575,20 +1665,10 @@ class DatabaseTaskStore {
|
|
|
1575
1665
|
}
|
|
1576
1666
|
async listStaleTasks(options) {
|
|
1577
1667
|
const { timeoutS } = options;
|
|
1578
|
-
|
|
1579
|
-
this.db.fn.now()
|
|
1580
|
-
]);
|
|
1581
|
-
if (this.db.client.config.client.includes("mysql")) {
|
|
1582
|
-
heartbeatInterval = this.db.raw(
|
|
1583
|
-
`date_sub(now(), interval ${timeoutS} second)`
|
|
1584
|
-
);
|
|
1585
|
-
} else if (this.db.client.config.client.includes("sqlite3")) {
|
|
1586
|
-
heartbeatInterval = this.db.raw(`datetime('now', ?)`, [
|
|
1587
|
-
`-${timeoutS} seconds`
|
|
1588
|
-
]);
|
|
1589
|
-
}
|
|
1668
|
+
const heartbeatInterval = intervalFromNowTill(timeoutS, this.db);
|
|
1590
1669
|
const rawRows = await this.db("tasks").where("status", "processing").andWhere("last_heartbeat_at", "<=", heartbeatInterval);
|
|
1591
1670
|
const tasks = rawRows.map((row) => ({
|
|
1671
|
+
recovery: JSON.parse(row.spec).EXPERIMENTAL_recovery,
|
|
1592
1672
|
taskId: row.id
|
|
1593
1673
|
}));
|
|
1594
1674
|
return { tasks };
|
|
@@ -1609,7 +1689,8 @@ class DatabaseTaskStore {
|
|
|
1609
1689
|
}).limit(1).select();
|
|
1610
1690
|
const updateTask = async (criteria) => {
|
|
1611
1691
|
const updateCount = await tx("tasks").where(criteria).update({
|
|
1612
|
-
status
|
|
1692
|
+
status,
|
|
1693
|
+
secrets: null
|
|
1613
1694
|
});
|
|
1614
1695
|
if (updateCount !== 1) {
|
|
1615
1696
|
throw new errors.ConflictError(
|
|
@@ -1679,7 +1760,7 @@ class DatabaseTaskStore {
|
|
|
1679
1760
|
);
|
|
1680
1761
|
}
|
|
1681
1762
|
});
|
|
1682
|
-
return
|
|
1763
|
+
return trimEventsTillLastRecovery(events);
|
|
1683
1764
|
}
|
|
1684
1765
|
async shutdownTask(options) {
|
|
1685
1766
|
const { taskId } = options;
|
|
@@ -1718,7 +1799,72 @@ class DatabaseTaskStore {
|
|
|
1718
1799
|
body: serializedBody
|
|
1719
1800
|
});
|
|
1720
1801
|
}
|
|
1802
|
+
async recoverTasks(options) {
|
|
1803
|
+
const taskIdsToRecover = [];
|
|
1804
|
+
const timeoutS = luxon.Duration.fromObject(options.timeout).as("seconds");
|
|
1805
|
+
await this.db.transaction(async (tx) => {
|
|
1806
|
+
var _a, _b;
|
|
1807
|
+
const heartbeatInterval = intervalFromNowTill(timeoutS, this.db);
|
|
1808
|
+
const result = await tx("tasks").where("status", "processing").andWhere("last_heartbeat_at", "<=", heartbeatInterval).update(
|
|
1809
|
+
{
|
|
1810
|
+
status: "open",
|
|
1811
|
+
last_heartbeat_at: this.db.fn.now()
|
|
1812
|
+
},
|
|
1813
|
+
["id", "spec"]
|
|
1814
|
+
);
|
|
1815
|
+
taskIdsToRecover.push(...result.map((i) => i.id));
|
|
1816
|
+
for (const { id, spec } of result) {
|
|
1817
|
+
const taskSpec = JSON.parse(spec);
|
|
1818
|
+
await this.db("task_events").insert({
|
|
1819
|
+
task_id: id,
|
|
1820
|
+
event_type: "recovered",
|
|
1821
|
+
body: JSON.stringify({
|
|
1822
|
+
recoverStrategy: (_b = (_a = taskSpec.EXPERIMENTAL_recovery) == null ? void 0 : _a.EXPERIMENTAL_strategy) != null ? _b : "none"
|
|
1823
|
+
})
|
|
1824
|
+
});
|
|
1825
|
+
}
|
|
1826
|
+
});
|
|
1827
|
+
return { ids: taskIdsToRecover };
|
|
1828
|
+
}
|
|
1829
|
+
}
|
|
1830
|
+
|
|
1831
|
+
function isTruthy(value) {
|
|
1832
|
+
return lodash.isArray(value) ? value.length > 0 : !!value;
|
|
1833
|
+
}
|
|
1834
|
+
function generateExampleOutput(schema) {
|
|
1835
|
+
var _a, _b;
|
|
1836
|
+
const { examples } = schema;
|
|
1837
|
+
if (examples && Array.isArray(examples)) {
|
|
1838
|
+
return examples[0];
|
|
1839
|
+
}
|
|
1840
|
+
if (schema.type === "object") {
|
|
1841
|
+
return Object.fromEntries(
|
|
1842
|
+
Object.entries((_a = schema.properties) != null ? _a : {}).map(([key, value]) => [
|
|
1843
|
+
key,
|
|
1844
|
+
generateExampleOutput(value)
|
|
1845
|
+
])
|
|
1846
|
+
);
|
|
1847
|
+
} else if (schema.type === "array") {
|
|
1848
|
+
const [firstSchema] = (_b = [schema.items]) == null ? void 0 : _b.flat();
|
|
1849
|
+
if (firstSchema) {
|
|
1850
|
+
return [generateExampleOutput(firstSchema)];
|
|
1851
|
+
}
|
|
1852
|
+
return [];
|
|
1853
|
+
} else if (schema.type === "string") {
|
|
1854
|
+
return "<example>";
|
|
1855
|
+
} else if (schema.type === "number") {
|
|
1856
|
+
return 0;
|
|
1857
|
+
} else if (schema.type === "boolean") {
|
|
1858
|
+
return false;
|
|
1859
|
+
}
|
|
1860
|
+
return "<unknown>";
|
|
1721
1861
|
}
|
|
1862
|
+
const readDuration$1 = (config$1, key, defaultValue) => {
|
|
1863
|
+
if (config$1 == null ? void 0 : config$1.has(key)) {
|
|
1864
|
+
return config.readDurationFromConfig(config$1, { key });
|
|
1865
|
+
}
|
|
1866
|
+
return defaultValue;
|
|
1867
|
+
};
|
|
1722
1868
|
|
|
1723
1869
|
var __defProp$2 = Object.defineProperty;
|
|
1724
1870
|
var __defNormalProp$2 = (obj, key, value) => key in obj ? __defProp$2(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value;
|
|
@@ -1803,9 +1949,10 @@ function defer() {
|
|
|
1803
1949
|
return { promise, resolve };
|
|
1804
1950
|
}
|
|
1805
1951
|
class StorageTaskBroker {
|
|
1806
|
-
constructor(storage, logger) {
|
|
1952
|
+
constructor(storage, logger, config) {
|
|
1807
1953
|
this.storage = storage;
|
|
1808
1954
|
this.logger = logger;
|
|
1955
|
+
this.config = config;
|
|
1809
1956
|
__publicField$2(this, "deferredDispatch", defer());
|
|
1810
1957
|
}
|
|
1811
1958
|
async list(options) {
|
|
@@ -1838,6 +1985,26 @@ class StorageTaskBroker {
|
|
|
1838
1985
|
}
|
|
1839
1986
|
});
|
|
1840
1987
|
}
|
|
1988
|
+
async recoverTasks() {
|
|
1989
|
+
var _a, _b, _c, _d;
|
|
1990
|
+
const enabled = (_a = this.config && this.config.getOptionalBoolean(
|
|
1991
|
+
"scaffolder.EXPERIMENTAL_recoverTasks"
|
|
1992
|
+
)) != null ? _a : false;
|
|
1993
|
+
if (enabled) {
|
|
1994
|
+
const defaultTimeout = { seconds: 30 };
|
|
1995
|
+
const timeout = readDuration$1(
|
|
1996
|
+
this.config,
|
|
1997
|
+
"scaffolder.EXPERIMENTAL_recoverTasksTimeout",
|
|
1998
|
+
defaultTimeout
|
|
1999
|
+
);
|
|
2000
|
+
const { ids: recoveredTaskIds } = (_d = await ((_c = (_b = this.storage).recoverTasks) == null ? void 0 : _c.call(_b, {
|
|
2001
|
+
timeout
|
|
2002
|
+
}))) != null ? _d : { ids: [] };
|
|
2003
|
+
if (recoveredTaskIds.length > 0) {
|
|
2004
|
+
this.signalDispatch();
|
|
2005
|
+
}
|
|
2006
|
+
}
|
|
2007
|
+
}
|
|
1841
2008
|
/**
|
|
1842
2009
|
* {@inheritdoc TaskBroker.claim}
|
|
1843
2010
|
*/
|
|
@@ -1945,38 +2112,6 @@ class StorageTaskBroker {
|
|
|
1945
2112
|
}
|
|
1946
2113
|
}
|
|
1947
2114
|
|
|
1948
|
-
function isTruthy(value) {
|
|
1949
|
-
return lodash.isArray(value) ? value.length > 0 : !!value;
|
|
1950
|
-
}
|
|
1951
|
-
function generateExampleOutput(schema) {
|
|
1952
|
-
var _a, _b;
|
|
1953
|
-
const { examples } = schema;
|
|
1954
|
-
if (examples && Array.isArray(examples)) {
|
|
1955
|
-
return examples[0];
|
|
1956
|
-
}
|
|
1957
|
-
if (schema.type === "object") {
|
|
1958
|
-
return Object.fromEntries(
|
|
1959
|
-
Object.entries((_a = schema.properties) != null ? _a : {}).map(([key, value]) => [
|
|
1960
|
-
key,
|
|
1961
|
-
generateExampleOutput(value)
|
|
1962
|
-
])
|
|
1963
|
-
);
|
|
1964
|
-
} else if (schema.type === "array") {
|
|
1965
|
-
const [firstSchema] = (_b = [schema.items]) == null ? void 0 : _b.flat();
|
|
1966
|
-
if (firstSchema) {
|
|
1967
|
-
return [generateExampleOutput(firstSchema)];
|
|
1968
|
-
}
|
|
1969
|
-
return [];
|
|
1970
|
-
} else if (schema.type === "string") {
|
|
1971
|
-
return "<example>";
|
|
1972
|
-
} else if (schema.type === "number") {
|
|
1973
|
-
return 0;
|
|
1974
|
-
} else if (schema.type === "boolean") {
|
|
1975
|
-
return false;
|
|
1976
|
-
}
|
|
1977
|
-
return "<unknown>";
|
|
1978
|
-
}
|
|
1979
|
-
|
|
1980
2115
|
function createCounterMetric(config) {
|
|
1981
2116
|
let metric = promClient.register.getSingleMetric(config.name);
|
|
1982
2117
|
if (!metric) {
|
|
@@ -2510,6 +2645,10 @@ class TaskWorker {
|
|
|
2510
2645
|
constructor(options) {
|
|
2511
2646
|
this.options = options;
|
|
2512
2647
|
__publicField(this, "taskQueue");
|
|
2648
|
+
__publicField(this, "logger");
|
|
2649
|
+
__publicField(this, "stopWorkers");
|
|
2650
|
+
this.stopWorkers = false;
|
|
2651
|
+
this.logger = options.logger;
|
|
2513
2652
|
this.taskQueue = new PQueue__default["default"]({
|
|
2514
2653
|
concurrency: options.concurrentTasksLimit
|
|
2515
2654
|
});
|
|
@@ -2543,15 +2682,34 @@ class TaskWorker {
|
|
|
2543
2682
|
permissions
|
|
2544
2683
|
});
|
|
2545
2684
|
}
|
|
2685
|
+
async recoverTasks() {
|
|
2686
|
+
var _a, _b, _c;
|
|
2687
|
+
try {
|
|
2688
|
+
await ((_b = (_a = this.options.taskBroker).recoverTasks) == null ? void 0 : _b.call(_a));
|
|
2689
|
+
} catch (err) {
|
|
2690
|
+
(_c = this.logger) == null ? void 0 : _c.error(errors.stringifyError(err));
|
|
2691
|
+
}
|
|
2692
|
+
}
|
|
2546
2693
|
start() {
|
|
2547
2694
|
(async () => {
|
|
2548
|
-
|
|
2695
|
+
while (!this.stopWorkers) {
|
|
2696
|
+
await new Promise((resolve) => setTimeout(resolve, 1e4));
|
|
2697
|
+
await this.recoverTasks();
|
|
2698
|
+
}
|
|
2699
|
+
})();
|
|
2700
|
+
(async () => {
|
|
2701
|
+
while (!this.stopWorkers) {
|
|
2549
2702
|
await this.onReadyToClaimTask();
|
|
2550
|
-
|
|
2551
|
-
|
|
2703
|
+
if (!this.stopWorkers) {
|
|
2704
|
+
const task = await this.options.taskBroker.claim();
|
|
2705
|
+
void this.taskQueue.add(() => this.runOneTask(task));
|
|
2706
|
+
}
|
|
2552
2707
|
}
|
|
2553
2708
|
})();
|
|
2554
2709
|
}
|
|
2710
|
+
stop() {
|
|
2711
|
+
this.stopWorkers = true;
|
|
2712
|
+
}
|
|
2555
2713
|
onReadyToClaimTask() {
|
|
2556
2714
|
if (this.taskQueue.pending < this.options.concurrentTasksLimit) {
|
|
2557
2715
|
return Promise.resolve();
|
|
@@ -2812,7 +2970,7 @@ async function createRouter(options) {
|
|
|
2812
2970
|
let taskBroker;
|
|
2813
2971
|
if (!options.taskBroker) {
|
|
2814
2972
|
const databaseTaskStore = await DatabaseTaskStore.create({ database });
|
|
2815
|
-
taskBroker = new StorageTaskBroker(databaseTaskStore, logger);
|
|
2973
|
+
taskBroker = new StorageTaskBroker(databaseTaskStore, logger, config);
|
|
2816
2974
|
if (scheduler && databaseTaskStore.listStaleTasks) {
|
|
2817
2975
|
await scheduler.scheduleTask({
|
|
2818
2976
|
id: "close_stale_tasks",
|
|
@@ -2869,7 +3027,16 @@ async function createRouter(options) {
|
|
|
2869
3027
|
additionalTemplateGlobals
|
|
2870
3028
|
});
|
|
2871
3029
|
actionsToRegister.forEach((action) => actionRegistry.register(action));
|
|
2872
|
-
workers.forEach((worker) => worker.start());
|
|
3030
|
+
const launchWorkers = () => workers.forEach((worker) => worker.start());
|
|
3031
|
+
const shutdownWorkers = () => {
|
|
3032
|
+
workers.forEach((worker) => worker.stop());
|
|
3033
|
+
};
|
|
3034
|
+
if (options.lifecycle) {
|
|
3035
|
+
options.lifecycle.addStartupHook(launchWorkers);
|
|
3036
|
+
options.lifecycle.addShutdownHook(shutdownWorkers);
|
|
3037
|
+
} else {
|
|
3038
|
+
launchWorkers();
|
|
3039
|
+
}
|
|
2873
3040
|
const dryRunner = createDryRunner({
|
|
2874
3041
|
actionRegistry,
|
|
2875
3042
|
integrations,
|
|
@@ -2983,6 +3150,7 @@ async function createRouter(options) {
|
|
|
2983
3150
|
name: (_b2 = step.name) != null ? _b2 : step.action
|
|
2984
3151
|
};
|
|
2985
3152
|
}),
|
|
3153
|
+
EXPERIMENTAL_recovery: template.spec.EXPERIMENTAL_recovery,
|
|
2986
3154
|
output: (_b = template.spec.output) != null ? _b : {},
|
|
2987
3155
|
parameters: values,
|
|
2988
3156
|
user: {
|
|
@@ -3214,4 +3382,4 @@ exports.createRouter = createRouter;
|
|
|
3214
3382
|
exports.createWaitAction = createWaitAction;
|
|
3215
3383
|
exports.scaffolderActionRules = scaffolderActionRules;
|
|
3216
3384
|
exports.scaffolderTemplateRules = scaffolderTemplateRules;
|
|
3217
|
-
//# sourceMappingURL=router-
|
|
3385
|
+
//# sourceMappingURL=router-43c4dd8a.cjs.js.map
|