@backstage/plugin-scaffolder-backend 1.25.0-next.2 → 1.26.0-next.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +95 -0
- package/alpha/package.json +1 -1
- package/dist/alpha.cjs.js +9 -3
- package/dist/alpha.cjs.js.map +1 -1
- package/dist/cjs/{router-B1lzg7oz.cjs.js → router-CC-UhVkG.cjs.js} +333 -92
- package/dist/cjs/router-CC-UhVkG.cjs.js.map +1 -0
- package/dist/index.cjs.js +3 -2
- package/dist/index.cjs.js.map +1 -1
- package/dist/index.d.ts +79 -1
- package/package.json +28 -27
- package/dist/cjs/router-B1lzg7oz.cjs.js.map +0 -1
|
@@ -32,6 +32,7 @@ var gitlab = require('@backstage/plugin-scaffolder-backend-module-gitlab');
|
|
|
32
32
|
var pluginScaffolderBackendModuleGitea = require('@backstage/plugin-scaffolder-backend-module-gitea');
|
|
33
33
|
var uuid = require('uuid');
|
|
34
34
|
var alpha = require('@backstage/plugin-scaffolder-node/alpha');
|
|
35
|
+
var os = require('os');
|
|
35
36
|
var ObservableImpl = require('zen-observable');
|
|
36
37
|
var lodash = require('lodash');
|
|
37
38
|
var PQueue = require('p-queue');
|
|
@@ -45,7 +46,6 @@ var pluginPermissionCommon = require('@backstage/plugin-permission-common');
|
|
|
45
46
|
var Transport = require('winston-transport');
|
|
46
47
|
var tripleBeam = require('triple-beam');
|
|
47
48
|
var url = require('url');
|
|
48
|
-
var os = require('os');
|
|
49
49
|
|
|
50
50
|
function _interopDefaultCompat (e) { return e && typeof e === 'object' && 'default' in e ? e : { default: e }; }
|
|
51
51
|
|
|
@@ -75,14 +75,14 @@ var path__default = /*#__PURE__*/_interopDefaultCompat(path);
|
|
|
75
75
|
var fs__default$1 = /*#__PURE__*/_interopDefaultCompat(fs$1);
|
|
76
76
|
var globby__default = /*#__PURE__*/_interopDefaultCompat(globby);
|
|
77
77
|
var get__default = /*#__PURE__*/_interopDefaultCompat(get);
|
|
78
|
+
var os__default = /*#__PURE__*/_interopDefaultCompat(os);
|
|
78
79
|
var ObservableImpl__default = /*#__PURE__*/_interopDefaultCompat(ObservableImpl);
|
|
79
80
|
var PQueue__default = /*#__PURE__*/_interopDefaultCompat(PQueue);
|
|
80
81
|
var winston__namespace = /*#__PURE__*/_interopNamespaceCompat(winston);
|
|
81
82
|
var nunjucks__default = /*#__PURE__*/_interopDefaultCompat(nunjucks);
|
|
82
83
|
var Transport__default = /*#__PURE__*/_interopDefaultCompat(Transport);
|
|
83
|
-
var os__default = /*#__PURE__*/_interopDefaultCompat(os);
|
|
84
84
|
|
|
85
|
-
const examples$
|
|
85
|
+
const examples$a = [
|
|
86
86
|
{
|
|
87
87
|
description: "Register with the catalog",
|
|
88
88
|
example: yaml__namespace.default.stringify({
|
|
@@ -106,7 +106,7 @@ function createCatalogRegisterAction(options) {
|
|
|
106
106
|
return pluginScaffolderNode.createTemplateAction({
|
|
107
107
|
id: id$4,
|
|
108
108
|
description: "Registers entities from a catalog descriptor file in the workspace into the software catalog.",
|
|
109
|
-
examples: examples$
|
|
109
|
+
examples: examples$a,
|
|
110
110
|
schema: {
|
|
111
111
|
input: {
|
|
112
112
|
oneOf: [
|
|
@@ -233,7 +233,7 @@ function createCatalogRegisterAction(options) {
|
|
|
233
233
|
});
|
|
234
234
|
}
|
|
235
235
|
|
|
236
|
-
const examples$
|
|
236
|
+
const examples$9 = [
|
|
237
237
|
{
|
|
238
238
|
description: "Write a catalog yaml file",
|
|
239
239
|
example: yaml__namespace.stringify({
|
|
@@ -277,7 +277,7 @@ function createCatalogWriteAction() {
|
|
|
277
277
|
)
|
|
278
278
|
})
|
|
279
279
|
},
|
|
280
|
-
examples: examples$
|
|
280
|
+
examples: examples$9,
|
|
281
281
|
supportsDryRun: true,
|
|
282
282
|
async handler(ctx) {
|
|
283
283
|
const { filePath, entity } = ctx.input;
|
|
@@ -303,7 +303,7 @@ function createCatalogWriteAction() {
|
|
|
303
303
|
});
|
|
304
304
|
}
|
|
305
305
|
|
|
306
|
-
const examples$
|
|
306
|
+
const examples$8 = [
|
|
307
307
|
{
|
|
308
308
|
description: "Fetch entity by reference",
|
|
309
309
|
example: yaml__namespace.default.stringify({
|
|
@@ -342,7 +342,7 @@ function createFetchCatalogEntityAction(options) {
|
|
|
342
342
|
return pluginScaffolderNode.createTemplateAction({
|
|
343
343
|
id: id$2,
|
|
344
344
|
description: "Returns entity or entities from the catalog by entity reference(s)",
|
|
345
|
-
examples: examples$
|
|
345
|
+
examples: examples$8,
|
|
346
346
|
supportsDryRun: true,
|
|
347
347
|
schema: {
|
|
348
348
|
input: zod.z.object({
|
|
@@ -420,7 +420,7 @@ function createFetchCatalogEntityAction(options) {
|
|
|
420
420
|
});
|
|
421
421
|
}
|
|
422
422
|
|
|
423
|
-
const examples$
|
|
423
|
+
const examples$7 = [
|
|
424
424
|
{
|
|
425
425
|
description: "Write a debug message",
|
|
426
426
|
example: yaml__namespace.default.stringify({
|
|
@@ -473,7 +473,7 @@ function createDebugLogAction() {
|
|
|
473
473
|
return pluginScaffolderNode.createTemplateAction({
|
|
474
474
|
id: id$1,
|
|
475
475
|
description: "Writes a message into the log and/or lists all files in the workspace.",
|
|
476
|
-
examples: examples$
|
|
476
|
+
examples: examples$7,
|
|
477
477
|
schema: {
|
|
478
478
|
input: zod.z.object({
|
|
479
479
|
message: zod.z.string({ description: "Message to output." }).optional(),
|
|
@@ -518,7 +518,7 @@ async function recursiveReadDir(dir) {
|
|
|
518
518
|
return files.reduce((a, f) => a.concat(f), []);
|
|
519
519
|
}
|
|
520
520
|
|
|
521
|
-
const examples$
|
|
521
|
+
const examples$6 = [
|
|
522
522
|
{
|
|
523
523
|
description: "Waiting for 50 milliseconds",
|
|
524
524
|
example: yaml__namespace.default.stringify({
|
|
@@ -581,7 +581,7 @@ function createWaitAction(options) {
|
|
|
581
581
|
return pluginScaffolderNode.createTemplateAction({
|
|
582
582
|
id,
|
|
583
583
|
description: "Waits for a certain period of time.",
|
|
584
|
-
examples: examples$
|
|
584
|
+
examples: examples$6,
|
|
585
585
|
schema: {
|
|
586
586
|
input: {
|
|
587
587
|
type: "object",
|
|
@@ -624,7 +624,7 @@ function createWaitAction(options) {
|
|
|
624
624
|
});
|
|
625
625
|
}
|
|
626
626
|
|
|
627
|
-
const examples$
|
|
627
|
+
const examples$5 = [
|
|
628
628
|
{
|
|
629
629
|
description: "Downloads content and places it in the workspace.",
|
|
630
630
|
example: yaml__namespace.default.stringify({
|
|
@@ -663,7 +663,7 @@ function createFetchPlainAction(options) {
|
|
|
663
663
|
const { reader, integrations } = options;
|
|
664
664
|
return pluginScaffolderNode.createTemplateAction({
|
|
665
665
|
id: ACTION_ID,
|
|
666
|
-
examples: examples$
|
|
666
|
+
examples: examples$5,
|
|
667
667
|
description: "Downloads content and places it in the workspace, or optionally in a subdirectory specified by the `targetPath` input option.",
|
|
668
668
|
schema: {
|
|
669
669
|
input: {
|
|
@@ -705,7 +705,7 @@ function createFetchPlainAction(options) {
|
|
|
705
705
|
});
|
|
706
706
|
}
|
|
707
707
|
|
|
708
|
-
const examples$
|
|
708
|
+
const examples$4 = [
|
|
709
709
|
{
|
|
710
710
|
description: "Downloads a file and places it in the workspace.",
|
|
711
711
|
example: yaml__namespace.default.stringify({
|
|
@@ -729,7 +729,7 @@ function createFetchPlainFileAction(options) {
|
|
|
729
729
|
return pluginScaffolderNode.createTemplateAction({
|
|
730
730
|
id: "fetch:plain:file",
|
|
731
731
|
description: "Downloads single file and places it in the workspace.",
|
|
732
|
-
examples: examples$
|
|
732
|
+
examples: examples$4,
|
|
733
733
|
schema: {
|
|
734
734
|
input: {
|
|
735
735
|
type: "object",
|
|
@@ -950,7 +950,7 @@ const createDefaultFilters = ({
|
|
|
950
950
|
};
|
|
951
951
|
};
|
|
952
952
|
|
|
953
|
-
const examples$
|
|
953
|
+
const examples$3 = [
|
|
954
954
|
{
|
|
955
955
|
description: "Downloads a skeleton directory that lives alongside the template file and fill it out with values.",
|
|
956
956
|
example: yaml__namespace.default.stringify({
|
|
@@ -986,7 +986,7 @@ function createFetchTemplateAction(options) {
|
|
|
986
986
|
return pluginScaffolderNode.createTemplateAction({
|
|
987
987
|
id: "fetch:template",
|
|
988
988
|
description: "Downloads a skeleton, templates variables into file and directory names and content, and places the result in the workspace, or optionally in a subdirectory specified by the `targetPath` input option.",
|
|
989
|
-
examples: examples$
|
|
989
|
+
examples: examples$3,
|
|
990
990
|
schema: {
|
|
991
991
|
input: {
|
|
992
992
|
type: "object",
|
|
@@ -1196,6 +1196,132 @@ function containsSkippedContent(localOutputPath) {
|
|
|
1196
1196
|
return localOutputPath === "" || localOutputPath.startsWith("/") || localOutputPath.includes("//");
|
|
1197
1197
|
}
|
|
1198
1198
|
|
|
1199
|
+
const examples$2 = [
|
|
1200
|
+
{
|
|
1201
|
+
description: "Downloads a template file and fill it out with values.",
|
|
1202
|
+
example: yaml__namespace.default.stringify({
|
|
1203
|
+
steps: [
|
|
1204
|
+
{
|
|
1205
|
+
action: "fetch:template:file",
|
|
1206
|
+
id: "fetch-template-file",
|
|
1207
|
+
name: "Fetch template file",
|
|
1208
|
+
input: {
|
|
1209
|
+
url: "./skeleton.txt",
|
|
1210
|
+
targetPath: "./target/skeleton.txt",
|
|
1211
|
+
values: {
|
|
1212
|
+
name: "test-project",
|
|
1213
|
+
count: 1234,
|
|
1214
|
+
itemList: ["first", "second", "third"]
|
|
1215
|
+
}
|
|
1216
|
+
}
|
|
1217
|
+
}
|
|
1218
|
+
]
|
|
1219
|
+
})
|
|
1220
|
+
}
|
|
1221
|
+
];
|
|
1222
|
+
|
|
1223
|
+
function createFetchTemplateFileAction(options) {
|
|
1224
|
+
const {
|
|
1225
|
+
reader,
|
|
1226
|
+
integrations,
|
|
1227
|
+
additionalTemplateFilters,
|
|
1228
|
+
additionalTemplateGlobals
|
|
1229
|
+
} = options;
|
|
1230
|
+
const defaultTemplateFilters = createDefaultFilters({ integrations });
|
|
1231
|
+
return pluginScaffolderNode.createTemplateAction({
|
|
1232
|
+
id: "fetch:template:file",
|
|
1233
|
+
description: "Downloads single file and places it in the workspace.",
|
|
1234
|
+
examples: examples$2,
|
|
1235
|
+
schema: {
|
|
1236
|
+
input: {
|
|
1237
|
+
type: "object",
|
|
1238
|
+
required: ["url", "targetPath"],
|
|
1239
|
+
properties: {
|
|
1240
|
+
url: {
|
|
1241
|
+
title: "Fetch URL",
|
|
1242
|
+
description: "Relative path or absolute URL pointing to the single file to fetch.",
|
|
1243
|
+
type: "string"
|
|
1244
|
+
},
|
|
1245
|
+
targetPath: {
|
|
1246
|
+
title: "Target Path",
|
|
1247
|
+
description: "Target path within the working directory to download the file as.",
|
|
1248
|
+
type: "string"
|
|
1249
|
+
},
|
|
1250
|
+
values: {
|
|
1251
|
+
title: "Template Values",
|
|
1252
|
+
description: "Values to pass on to the templating engine",
|
|
1253
|
+
type: "object"
|
|
1254
|
+
},
|
|
1255
|
+
cookiecutterCompat: {
|
|
1256
|
+
title: "Cookiecutter compatibility mode",
|
|
1257
|
+
description: "Enable features to maximise compatibility with templates built for fetch:cookiecutter",
|
|
1258
|
+
type: "boolean"
|
|
1259
|
+
},
|
|
1260
|
+
replace: {
|
|
1261
|
+
title: "Replace file",
|
|
1262
|
+
description: "If set, replace file in targetPath instead of overwriting existing one.",
|
|
1263
|
+
type: "boolean"
|
|
1264
|
+
},
|
|
1265
|
+
token: {
|
|
1266
|
+
title: "Token",
|
|
1267
|
+
description: "An optional token to use for authentication when reading the resources.",
|
|
1268
|
+
type: "string"
|
|
1269
|
+
}
|
|
1270
|
+
}
|
|
1271
|
+
}
|
|
1272
|
+
},
|
|
1273
|
+
supportsDryRun: true,
|
|
1274
|
+
async handler(ctx) {
|
|
1275
|
+
ctx.logger.info("Fetching template file content from remote URL");
|
|
1276
|
+
const workDir = await ctx.createTemporaryDirectory();
|
|
1277
|
+
const tmpFilePath = path__default.default.join(workDir, "tmp");
|
|
1278
|
+
const outputPath = backendPluginApi.resolveSafeChildPath(
|
|
1279
|
+
ctx.workspacePath,
|
|
1280
|
+
ctx.input.targetPath
|
|
1281
|
+
);
|
|
1282
|
+
if (fs__default.default.existsSync(outputPath) && !ctx.input.replace) {
|
|
1283
|
+
ctx.logger.info(
|
|
1284
|
+
`File ${ctx.input.targetPath} already exists in workspace, not replacing.`
|
|
1285
|
+
);
|
|
1286
|
+
return;
|
|
1287
|
+
}
|
|
1288
|
+
await pluginScaffolderNode.fetchFile({
|
|
1289
|
+
reader,
|
|
1290
|
+
integrations,
|
|
1291
|
+
baseUrl: ctx.templateInfo?.baseUrl,
|
|
1292
|
+
fetchUrl: ctx.input.url,
|
|
1293
|
+
outputPath: tmpFilePath,
|
|
1294
|
+
token: ctx.input.token
|
|
1295
|
+
});
|
|
1296
|
+
const { cookiecutterCompat, values } = ctx.input;
|
|
1297
|
+
const context = {
|
|
1298
|
+
[cookiecutterCompat ? "cookiecutter" : "values"]: values
|
|
1299
|
+
};
|
|
1300
|
+
ctx.logger.info(
|
|
1301
|
+
`Processing template file with input values`,
|
|
1302
|
+
ctx.input.values
|
|
1303
|
+
);
|
|
1304
|
+
const renderTemplate = await SecureTemplater.loadRenderer({
|
|
1305
|
+
cookiecutterCompat,
|
|
1306
|
+
templateFilters: {
|
|
1307
|
+
...defaultTemplateFilters,
|
|
1308
|
+
...additionalTemplateFilters
|
|
1309
|
+
},
|
|
1310
|
+
templateGlobals: additionalTemplateGlobals,
|
|
1311
|
+
nunjucksConfigs: {
|
|
1312
|
+
trimBlocks: ctx.input.trimBlocks,
|
|
1313
|
+
lstripBlocks: ctx.input.lstripBlocks
|
|
1314
|
+
}
|
|
1315
|
+
});
|
|
1316
|
+
const contents = await fs__default.default.readFile(tmpFilePath, "utf-8");
|
|
1317
|
+
const result = renderTemplate(contents, context);
|
|
1318
|
+
await fs__default.default.ensureDir(path__default.default.dirname(outputPath));
|
|
1319
|
+
await fs__default.default.outputFile(outputPath, result);
|
|
1320
|
+
ctx.logger.info(`Template file has been written to ${outputPath}`);
|
|
1321
|
+
}
|
|
1322
|
+
});
|
|
1323
|
+
}
|
|
1324
|
+
|
|
1199
1325
|
const examples$1 = [
|
|
1200
1326
|
{
|
|
1201
1327
|
description: "Delete specified files",
|
|
@@ -1371,6 +1497,12 @@ const createBuiltinActions = (options) => {
|
|
|
1371
1497
|
additionalTemplateFilters,
|
|
1372
1498
|
additionalTemplateGlobals
|
|
1373
1499
|
}),
|
|
1500
|
+
createFetchTemplateFileAction({
|
|
1501
|
+
integrations,
|
|
1502
|
+
reader,
|
|
1503
|
+
additionalTemplateFilters,
|
|
1504
|
+
additionalTemplateGlobals
|
|
1505
|
+
}),
|
|
1374
1506
|
gerrit.createPublishGerritAction({
|
|
1375
1507
|
integrations,
|
|
1376
1508
|
config
|
|
@@ -1453,7 +1585,8 @@ const createBuiltinActions = (options) => {
|
|
|
1453
1585
|
githubCredentialsProvider
|
|
1454
1586
|
}),
|
|
1455
1587
|
github.createGithubEnvironmentAction({
|
|
1456
|
-
integrations
|
|
1588
|
+
integrations,
|
|
1589
|
+
catalogClient
|
|
1457
1590
|
}),
|
|
1458
1591
|
github.createGithubDeployKeyAction({
|
|
1459
1592
|
integrations
|
|
@@ -1523,6 +1656,79 @@ const intervalFromNowTill = (timeoutS, knex) => {
|
|
|
1523
1656
|
return heartbeatInterval;
|
|
1524
1657
|
};
|
|
1525
1658
|
|
|
1659
|
+
async function getWorkingDirectory(config, logger) {
|
|
1660
|
+
if (!config.has("backend.workingDirectory")) {
|
|
1661
|
+
return os__default.default.tmpdir();
|
|
1662
|
+
}
|
|
1663
|
+
const workingDirectory = config.getString("backend.workingDirectory");
|
|
1664
|
+
try {
|
|
1665
|
+
await fs__default.default.access(workingDirectory, fs__default.default.constants.F_OK | fs__default.default.constants.W_OK);
|
|
1666
|
+
logger.info(`using working directory: ${workingDirectory}`);
|
|
1667
|
+
} catch (err) {
|
|
1668
|
+
errors.assertError(err);
|
|
1669
|
+
logger.error(
|
|
1670
|
+
`working directory ${workingDirectory} ${err.code === "ENOENT" ? "does not exist" : "is not writable"}`
|
|
1671
|
+
);
|
|
1672
|
+
throw err;
|
|
1673
|
+
}
|
|
1674
|
+
return workingDirectory;
|
|
1675
|
+
}
|
|
1676
|
+
function getEntityBaseUrl(entity) {
|
|
1677
|
+
let location = entity.metadata.annotations?.[catalogModel.ANNOTATION_SOURCE_LOCATION];
|
|
1678
|
+
if (!location) {
|
|
1679
|
+
location = entity.metadata.annotations?.[catalogModel.ANNOTATION_LOCATION];
|
|
1680
|
+
}
|
|
1681
|
+
if (!location) {
|
|
1682
|
+
return void 0;
|
|
1683
|
+
}
|
|
1684
|
+
const { type, target } = catalogModel.parseLocationRef(location);
|
|
1685
|
+
if (type === "url") {
|
|
1686
|
+
return target;
|
|
1687
|
+
} else if (type === "file") {
|
|
1688
|
+
return `file://${target}`;
|
|
1689
|
+
}
|
|
1690
|
+
return void 0;
|
|
1691
|
+
}
|
|
1692
|
+
async function findTemplate(options) {
|
|
1693
|
+
const { entityRef, token, catalogApi } = options;
|
|
1694
|
+
if (entityRef.kind.toLocaleLowerCase("en-US") !== "template") {
|
|
1695
|
+
throw new errors.InputError(`Invalid kind, only 'Template' kind is supported`);
|
|
1696
|
+
}
|
|
1697
|
+
const template = await catalogApi.getEntityByRef(entityRef, { token });
|
|
1698
|
+
if (!template) {
|
|
1699
|
+
throw new errors.NotFoundError(
|
|
1700
|
+
`Template ${catalogModel.stringifyEntityRef(entityRef)} not found`
|
|
1701
|
+
);
|
|
1702
|
+
}
|
|
1703
|
+
return template;
|
|
1704
|
+
}
|
|
1705
|
+
function parseStringsParam(param, paramName) {
|
|
1706
|
+
if (param === void 0) {
|
|
1707
|
+
return void 0;
|
|
1708
|
+
}
|
|
1709
|
+
const array = [param].flat();
|
|
1710
|
+
if (array.some((p) => typeof p !== "string")) {
|
|
1711
|
+
throw new errors.InputError(
|
|
1712
|
+
`Invalid ${paramName}, not a string or array of strings`
|
|
1713
|
+
);
|
|
1714
|
+
}
|
|
1715
|
+
return array;
|
|
1716
|
+
}
|
|
1717
|
+
function parseNumberParam(param, paramName) {
|
|
1718
|
+
return parseStringsParam(param, paramName)?.map((val) => {
|
|
1719
|
+
const ret = Number.parseInt(val, 10);
|
|
1720
|
+
if (isNaN(ret)) {
|
|
1721
|
+
throw new errors.InputError(
|
|
1722
|
+
`Invalid ${paramName} parameter "${val}", expected a number or array of numbers`
|
|
1723
|
+
);
|
|
1724
|
+
}
|
|
1725
|
+
return ret;
|
|
1726
|
+
});
|
|
1727
|
+
}
|
|
1728
|
+
function flattenParams(...params) {
|
|
1729
|
+
return [...params].flat().filter(Boolean);
|
|
1730
|
+
}
|
|
1731
|
+
|
|
1526
1732
|
const migrationsDir = backendPluginApi.resolvePackagePath(
|
|
1527
1733
|
"@backstage/plugin-scaffolder-backend",
|
|
1528
1734
|
"migrations"
|
|
@@ -1594,16 +1800,41 @@ class DatabaseTaskStore {
|
|
|
1594
1800
|
this.db = client;
|
|
1595
1801
|
}
|
|
1596
1802
|
async list(options) {
|
|
1803
|
+
const { createdBy, status, pagination, order, filters } = options ?? {};
|
|
1597
1804
|
const queryBuilder = this.db("tasks");
|
|
1598
|
-
if (
|
|
1599
|
-
|
|
1600
|
-
|
|
1805
|
+
if (createdBy || filters?.createdBy) {
|
|
1806
|
+
const arr = flattenParams(
|
|
1807
|
+
createdBy,
|
|
1808
|
+
filters?.createdBy
|
|
1809
|
+
);
|
|
1810
|
+
queryBuilder.whereIn("created_by", [...new Set(arr)]);
|
|
1811
|
+
}
|
|
1812
|
+
if (status || filters?.status) {
|
|
1813
|
+
const arr = flattenParams(
|
|
1814
|
+
status,
|
|
1815
|
+
filters?.status
|
|
1816
|
+
);
|
|
1817
|
+
queryBuilder.whereIn("status", [...new Set(arr)]);
|
|
1818
|
+
}
|
|
1819
|
+
if (order) {
|
|
1820
|
+
order.forEach((f) => {
|
|
1821
|
+
queryBuilder.orderBy(f.field, f.order);
|
|
1601
1822
|
});
|
|
1823
|
+
} else {
|
|
1824
|
+
queryBuilder.orderBy("created_at", "desc");
|
|
1825
|
+
}
|
|
1826
|
+
const countQuery = queryBuilder.clone();
|
|
1827
|
+
countQuery.count("tasks.id", { as: "count" });
|
|
1828
|
+
if (pagination?.limit !== void 0) {
|
|
1829
|
+
queryBuilder.limit(pagination.limit);
|
|
1602
1830
|
}
|
|
1603
|
-
if (
|
|
1604
|
-
queryBuilder.
|
|
1831
|
+
if (pagination?.offset !== void 0) {
|
|
1832
|
+
queryBuilder.offset(pagination.offset);
|
|
1605
1833
|
}
|
|
1606
|
-
const results = await
|
|
1834
|
+
const [results, [{ count }]] = await Promise.all([
|
|
1835
|
+
queryBuilder.select(),
|
|
1836
|
+
countQuery
|
|
1837
|
+
]);
|
|
1607
1838
|
const tasks = results.map((result) => ({
|
|
1608
1839
|
id: result.id,
|
|
1609
1840
|
spec: JSON.parse(result.spec),
|
|
@@ -1612,7 +1843,7 @@ class DatabaseTaskStore {
|
|
|
1612
1843
|
lastHeartbeatAt: parseSqlDateToIsoString(result.last_heartbeat_at),
|
|
1613
1844
|
createdAt: parseSqlDateToIsoString(result.created_at)
|
|
1614
1845
|
}));
|
|
1615
|
-
return { tasks };
|
|
1846
|
+
return { tasks, totalTasks: count };
|
|
1616
1847
|
}
|
|
1617
1848
|
async getTask(taskId) {
|
|
1618
1849
|
const [result] = await this.db("tasks").where({ id: taskId }).select();
|
|
@@ -1781,7 +2012,7 @@ class DatabaseTaskStore {
|
|
|
1781
2012
|
}
|
|
1782
2013
|
}
|
|
1783
2014
|
async listEvents(options) {
|
|
1784
|
-
const { taskId, after } = options;
|
|
2015
|
+
const { isTaskRecoverable, taskId, after } = options;
|
|
1785
2016
|
const rawEvents = await this.db("task_events").where({
|
|
1786
2017
|
task_id: taskId
|
|
1787
2018
|
}).andWhere((builder) => {
|
|
@@ -1794,6 +2025,7 @@ class DatabaseTaskStore {
|
|
|
1794
2025
|
const body = JSON.parse(event.body);
|
|
1795
2026
|
return {
|
|
1796
2027
|
id: Number(event.id),
|
|
2028
|
+
isTaskRecoverable,
|
|
1797
2029
|
taskId,
|
|
1798
2030
|
body,
|
|
1799
2031
|
type: event.event_type,
|
|
@@ -1864,6 +2096,28 @@ class DatabaseTaskStore {
|
|
|
1864
2096
|
body: serializedBody
|
|
1865
2097
|
});
|
|
1866
2098
|
}
|
|
2099
|
+
async retryTask(options) {
|
|
2100
|
+
await this.db.transaction(async (tx) => {
|
|
2101
|
+
const result = await tx("tasks").where("id", options.taskId).update(
|
|
2102
|
+
{
|
|
2103
|
+
status: "open",
|
|
2104
|
+
last_heartbeat_at: this.db.fn.now()
|
|
2105
|
+
},
|
|
2106
|
+
["id", "spec"]
|
|
2107
|
+
);
|
|
2108
|
+
for (const { id, spec } of result) {
|
|
2109
|
+
const taskSpec = JSON.parse(spec);
|
|
2110
|
+
await tx("task_events").where("task_id", id).andWhere((q) => q.whereIn("event_type", ["cancelled", "completion"])).del();
|
|
2111
|
+
await tx("task_events").insert({
|
|
2112
|
+
task_id: id,
|
|
2113
|
+
event_type: "recovered",
|
|
2114
|
+
body: JSON.stringify({
|
|
2115
|
+
recoverStrategy: taskSpec.EXPERIMENTAL_recovery?.EXPERIMENTAL_strategy ?? "none"
|
|
2116
|
+
})
|
|
2117
|
+
});
|
|
2118
|
+
}
|
|
2119
|
+
});
|
|
2120
|
+
}
|
|
1867
2121
|
async recoverTasks(options) {
|
|
1868
2122
|
const taskIdsToRecover = [];
|
|
1869
2123
|
const timeoutS = luxon.Duration.fromObject(options.timeout).as("seconds");
|
|
@@ -1975,6 +2229,7 @@ class DefaultWorkspaceService {
|
|
|
1975
2229
|
}
|
|
1976
2230
|
async rehydrateWorkspace(options) {
|
|
1977
2231
|
if (this.isWorkspaceSerializationEnabled()) {
|
|
2232
|
+
await fs__default.default.mkdirp(options.targetPath);
|
|
1978
2233
|
await this.workspaceProvider.rehydrateWorkspace(options);
|
|
1979
2234
|
}
|
|
1980
2235
|
}
|
|
@@ -2126,10 +2381,7 @@ class StorageTaskBroker {
|
|
|
2126
2381
|
"TaskStore does not implement the list method. Please implement the list method to be able to list tasks"
|
|
2127
2382
|
);
|
|
2128
2383
|
}
|
|
2129
|
-
return await this.storage.list({
|
|
2130
|
-
createdBy: options?.createdBy,
|
|
2131
|
-
status: options?.status
|
|
2132
|
-
});
|
|
2384
|
+
return await this.storage.list(options ?? {});
|
|
2133
2385
|
}
|
|
2134
2386
|
deferredDispatch = defer();
|
|
2135
2387
|
async registerCancellable(taskId, abortController) {
|
|
@@ -2144,7 +2396,7 @@ class StorageTaskBroker {
|
|
|
2144
2396
|
abortController.abort();
|
|
2145
2397
|
shouldUnsubscribe = true;
|
|
2146
2398
|
}
|
|
2147
|
-
if (event.type === "completion") {
|
|
2399
|
+
if (event.type === "completion" && !event.isTaskRecoverable) {
|
|
2148
2400
|
shouldUnsubscribe = true;
|
|
2149
2401
|
}
|
|
2150
2402
|
}
|
|
@@ -2226,8 +2478,14 @@ class StorageTaskBroker {
|
|
|
2226
2478
|
let after = options.after;
|
|
2227
2479
|
let cancelled = false;
|
|
2228
2480
|
(async () => {
|
|
2481
|
+
const task = await this.storage.getTask(taskId);
|
|
2482
|
+
const isTaskRecoverable = task.spec.EXPERIMENTAL_recovery?.EXPERIMENTAL_strategy === "startOver";
|
|
2229
2483
|
while (!cancelled) {
|
|
2230
|
-
const result = await this.storage.listEvents({
|
|
2484
|
+
const result = await this.storage.listEvents({
|
|
2485
|
+
isTaskRecoverable,
|
|
2486
|
+
taskId,
|
|
2487
|
+
after
|
|
2488
|
+
});
|
|
2231
2489
|
const { events } = result;
|
|
2232
2490
|
if (events.length) {
|
|
2233
2491
|
after = events[events.length - 1].id;
|
|
@@ -2281,6 +2539,10 @@ class StorageTaskBroker {
|
|
|
2281
2539
|
}
|
|
2282
2540
|
});
|
|
2283
2541
|
}
|
|
2542
|
+
async retry(taskId) {
|
|
2543
|
+
await this.storage.retryTask?.({ taskId });
|
|
2544
|
+
this.signalDispatch();
|
|
2545
|
+
}
|
|
2284
2546
|
}
|
|
2285
2547
|
|
|
2286
2548
|
function createCounterMetric(config) {
|
|
@@ -3219,53 +3481,6 @@ function createDryRunner(options) {
|
|
|
3219
3481
|
};
|
|
3220
3482
|
}
|
|
3221
3483
|
|
|
3222
|
-
async function getWorkingDirectory(config, logger) {
|
|
3223
|
-
if (!config.has("backend.workingDirectory")) {
|
|
3224
|
-
return os__default.default.tmpdir();
|
|
3225
|
-
}
|
|
3226
|
-
const workingDirectory = config.getString("backend.workingDirectory");
|
|
3227
|
-
try {
|
|
3228
|
-
await fs__default.default.access(workingDirectory, fs__default.default.constants.F_OK | fs__default.default.constants.W_OK);
|
|
3229
|
-
logger.info(`using working directory: ${workingDirectory}`);
|
|
3230
|
-
} catch (err) {
|
|
3231
|
-
errors.assertError(err);
|
|
3232
|
-
logger.error(
|
|
3233
|
-
`working directory ${workingDirectory} ${err.code === "ENOENT" ? "does not exist" : "is not writable"}`
|
|
3234
|
-
);
|
|
3235
|
-
throw err;
|
|
3236
|
-
}
|
|
3237
|
-
return workingDirectory;
|
|
3238
|
-
}
|
|
3239
|
-
function getEntityBaseUrl(entity) {
|
|
3240
|
-
let location = entity.metadata.annotations?.[catalogModel.ANNOTATION_SOURCE_LOCATION];
|
|
3241
|
-
if (!location) {
|
|
3242
|
-
location = entity.metadata.annotations?.[catalogModel.ANNOTATION_LOCATION];
|
|
3243
|
-
}
|
|
3244
|
-
if (!location) {
|
|
3245
|
-
return void 0;
|
|
3246
|
-
}
|
|
3247
|
-
const { type, target } = catalogModel.parseLocationRef(location);
|
|
3248
|
-
if (type === "url") {
|
|
3249
|
-
return target;
|
|
3250
|
-
} else if (type === "file") {
|
|
3251
|
-
return `file://${target}`;
|
|
3252
|
-
}
|
|
3253
|
-
return void 0;
|
|
3254
|
-
}
|
|
3255
|
-
async function findTemplate(options) {
|
|
3256
|
-
const { entityRef, token, catalogApi } = options;
|
|
3257
|
-
if (entityRef.kind.toLocaleLowerCase("en-US") !== "template") {
|
|
3258
|
-
throw new errors.InputError(`Invalid kind, only 'Template' kind is supported`);
|
|
3259
|
-
}
|
|
3260
|
-
const template = await catalogApi.getEntityByRef(entityRef, { token });
|
|
3261
|
-
if (!template) {
|
|
3262
|
-
throw new errors.NotFoundError(
|
|
3263
|
-
`Template ${catalogModel.stringifyEntityRef(entityRef)} not found`
|
|
3264
|
-
);
|
|
3265
|
-
}
|
|
3266
|
-
return template;
|
|
3267
|
-
}
|
|
3268
|
-
|
|
3269
3484
|
async function checkPermission(options) {
|
|
3270
3485
|
const { permissions, permissionService, credentials } = options;
|
|
3271
3486
|
if (permissionService) {
|
|
@@ -3600,22 +3815,37 @@ async function createRouter(options) {
|
|
|
3600
3815
|
permissions: [alpha$1.taskReadPermission],
|
|
3601
3816
|
permissionService: permissions
|
|
3602
3817
|
});
|
|
3603
|
-
const [userEntityRef] = [req.query.createdBy].flat();
|
|
3604
|
-
if (typeof userEntityRef !== "string" && typeof userEntityRef !== "undefined") {
|
|
3605
|
-
throw new errors.InputError("createdBy query parameter must be a string");
|
|
3606
|
-
}
|
|
3607
3818
|
if (!taskBroker.list) {
|
|
3608
3819
|
throw new Error(
|
|
3609
3820
|
"TaskBroker does not support listing tasks, please implement the list method on the TaskBroker."
|
|
3610
3821
|
);
|
|
3611
3822
|
}
|
|
3612
|
-
const
|
|
3613
|
-
|
|
3614
|
-
|
|
3615
|
-
|
|
3823
|
+
const createdBy = parseStringsParam(req.query.createdBy, "createdBy");
|
|
3824
|
+
const status = parseStringsParam(req.query.status, "status");
|
|
3825
|
+
const order = parseStringsParam(req.query.order, "order")?.map((item) => {
|
|
3826
|
+
const match = item.match(/^(asc|desc):(.+)$/);
|
|
3827
|
+
if (!match) {
|
|
3828
|
+
throw new errors.InputError(
|
|
3829
|
+
`Invalid order parameter "${item}", expected "<asc or desc>:<field name>"`
|
|
3830
|
+
);
|
|
3831
|
+
}
|
|
3832
|
+
return {
|
|
3833
|
+
order: match[1],
|
|
3834
|
+
field: match[2]
|
|
3835
|
+
};
|
|
3836
|
+
});
|
|
3837
|
+
const limit = parseNumberParam(req.query.limit, "limit");
|
|
3838
|
+
const offset = parseNumberParam(req.query.offset, "offset");
|
|
3616
3839
|
const tasks = await taskBroker.list({
|
|
3617
|
-
|
|
3618
|
-
|
|
3840
|
+
filters: {
|
|
3841
|
+
createdBy,
|
|
3842
|
+
status: status ? status : void 0
|
|
3843
|
+
},
|
|
3844
|
+
order,
|
|
3845
|
+
pagination: {
|
|
3846
|
+
limit: limit ? limit[0] : void 0,
|
|
3847
|
+
offset: offset ? offset[0] : void 0
|
|
3848
|
+
}
|
|
3619
3849
|
});
|
|
3620
3850
|
res.status(200).json(tasks);
|
|
3621
3851
|
}).get("/v2/tasks/:taskId", async (req, res) => {
|
|
@@ -3642,6 +3872,16 @@ async function createRouter(options) {
|
|
|
3642
3872
|
const { taskId } = req.params;
|
|
3643
3873
|
await taskBroker.cancel?.(taskId);
|
|
3644
3874
|
res.status(200).json({ status: "cancelled" });
|
|
3875
|
+
}).post("/v2/tasks/:taskId/retry", async (req, res) => {
|
|
3876
|
+
const credentials = await httpAuth.credentials(req);
|
|
3877
|
+
await checkPermission({
|
|
3878
|
+
credentials,
|
|
3879
|
+
permissions: [alpha$1.taskCreatePermission, alpha$1.taskReadPermission],
|
|
3880
|
+
permissionService: permissions
|
|
3881
|
+
});
|
|
3882
|
+
const { taskId } = req.params;
|
|
3883
|
+
await taskBroker.retry?.(taskId);
|
|
3884
|
+
res.status(201).json({ id: taskId });
|
|
3645
3885
|
}).get("/v2/tasks/:taskId/eventstream", async (req, res) => {
|
|
3646
3886
|
const credentials = await httpAuth.credentials(req);
|
|
3647
3887
|
await checkPermission({
|
|
@@ -3673,7 +3913,7 @@ data: ${JSON.stringify(event)}
|
|
|
3673
3913
|
|
|
3674
3914
|
`
|
|
3675
3915
|
);
|
|
3676
|
-
if (event.type === "completion") {
|
|
3916
|
+
if (event.type === "completion" && !event.isTaskRecoverable) {
|
|
3677
3917
|
shouldUnsubscribe = true;
|
|
3678
3918
|
}
|
|
3679
3919
|
}
|
|
@@ -3851,10 +4091,11 @@ exports.createFetchCatalogEntityAction = createFetchCatalogEntityAction;
|
|
|
3851
4091
|
exports.createFetchPlainAction = createFetchPlainAction;
|
|
3852
4092
|
exports.createFetchPlainFileAction = createFetchPlainFileAction;
|
|
3853
4093
|
exports.createFetchTemplateAction = createFetchTemplateAction;
|
|
4094
|
+
exports.createFetchTemplateFileAction = createFetchTemplateFileAction;
|
|
3854
4095
|
exports.createFilesystemDeleteAction = createFilesystemDeleteAction;
|
|
3855
4096
|
exports.createFilesystemRenameAction = createFilesystemRenameAction;
|
|
3856
4097
|
exports.createRouter = createRouter;
|
|
3857
4098
|
exports.createWaitAction = createWaitAction;
|
|
3858
4099
|
exports.scaffolderActionRules = scaffolderActionRules;
|
|
3859
4100
|
exports.scaffolderTemplateRules = scaffolderTemplateRules;
|
|
3860
|
-
//# sourceMappingURL=router-
|
|
4101
|
+
//# sourceMappingURL=router-CC-UhVkG.cjs.js.map
|