@backstage/plugin-scaffolder-backend 1.20.0 → 1.21.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 +43 -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-842a762b.cjs.js} +197 -71
- package/dist/cjs/router-842a762b.cjs.js.map +1 -0
- package/dist/index.cjs.js +2 -2
- package/dist/index.d.ts +25 -2
- package/package.json +18 -18
- package/dist/cjs/router-e667d04e.cjs.js.map +0 -1
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,48 @@
|
|
|
1
1
|
# @backstage/plugin-scaffolder-backend
|
|
2
2
|
|
|
3
|
+
## 1.21.0-next.0
|
|
4
|
+
|
|
5
|
+
### Minor Changes
|
|
6
|
+
|
|
7
|
+
- 11b9a08: Introduced the first version of recoverable tasks.
|
|
8
|
+
- e9a5228: The built-in module list has been trimmed down when using the new Backend System. Provider specific modules should now be installed with `backend.add` to provide additional actions to the scaffolder. These modules are as follows:
|
|
9
|
+
|
|
10
|
+
- `@backstage/plugin-scaffolder-backend-module-github`
|
|
11
|
+
- `@backstage/plugin-scaffolder-backend-module-gitlab`
|
|
12
|
+
- `@backstage/plugin-scaffolder-backend-module-bitbucket`
|
|
13
|
+
- `@backstage/plugin-scaffolder-backend-module-gitea`
|
|
14
|
+
- `@backstage/plugin-scaffolder-backend-module-gerrit`
|
|
15
|
+
- `@backstage/plugin-scaffolder-backend-module-confluence-to-markdown`
|
|
16
|
+
- `@backstage/plugin-scaffolder-backend-module-cookiecutter`
|
|
17
|
+
- `@backstage/plugin-scaffolder-backend-module-rails`
|
|
18
|
+
- `@backstage/plugin-scaffolder-backend-module-sentry`
|
|
19
|
+
- `@backstage/plugin-scaffolder-backend-module-yeoman`
|
|
20
|
+
|
|
21
|
+
### Patch Changes
|
|
22
|
+
|
|
23
|
+
- Updated dependencies
|
|
24
|
+
- @backstage/backend-common@0.21.0-next.0
|
|
25
|
+
- @backstage/plugin-scaffolder-backend-module-bitbucket@0.1.2-next.0
|
|
26
|
+
- @backstage/plugin-scaffolder-backend-module-gerrit@0.1.2-next.0
|
|
27
|
+
- @backstage/plugin-scaffolder-backend-module-github@0.1.2-next.0
|
|
28
|
+
- @backstage/plugin-scaffolder-backend-module-gitlab@0.2.13-next.0
|
|
29
|
+
- @backstage/plugin-scaffolder-backend-module-azure@0.1.2-next.0
|
|
30
|
+
- @backstage/catalog-client@1.6.0-next.0
|
|
31
|
+
- @backstage/plugin-scaffolder-node@0.3.0-next.0
|
|
32
|
+
- @backstage/plugin-scaffolder-common@1.5.0-next.0
|
|
33
|
+
- @backstage/backend-tasks@0.5.15-next.0
|
|
34
|
+
- @backstage/plugin-auth-node@0.4.4-next.0
|
|
35
|
+
- @backstage/plugin-catalog-backend-module-scaffolder-entity-model@0.1.7-next.0
|
|
36
|
+
- @backstage/plugin-catalog-node@1.6.2-next.0
|
|
37
|
+
- @backstage/plugin-permission-node@0.7.21-next.0
|
|
38
|
+
- @backstage/backend-plugin-api@0.6.10-next.0
|
|
39
|
+
- @backstage/catalog-model@1.4.3
|
|
40
|
+
- @backstage/config@1.1.1
|
|
41
|
+
- @backstage/errors@1.2.3
|
|
42
|
+
- @backstage/integration@1.8.0
|
|
43
|
+
- @backstage/types@1.1.1
|
|
44
|
+
- @backstage/plugin-permission-common@0.7.12
|
|
45
|
+
|
|
3
46
|
## 1.20.0
|
|
4
47
|
|
|
5
48
|
### Minor Changes
|
package/alpha/package.json
CHANGED
package/config.d.ts
CHANGED
|
@@ -40,6 +40,22 @@ export interface Config {
|
|
|
40
40
|
*/
|
|
41
41
|
concurrentTasksLimit?: number;
|
|
42
42
|
|
|
43
|
+
/**
|
|
44
|
+
* Sets the tasks recoverability on system start up.
|
|
45
|
+
*
|
|
46
|
+
* If not specified, the default value is false.
|
|
47
|
+
*/
|
|
48
|
+
EXPERIMENTAL_recoverTasks?: boolean;
|
|
49
|
+
|
|
50
|
+
/**
|
|
51
|
+
* Every task which is in progress state and having a last heartbeat longer than a specified timeout is going to
|
|
52
|
+
* be attempted to recover.
|
|
53
|
+
*
|
|
54
|
+
* If not specified, the default value is 5 seconds.
|
|
55
|
+
*
|
|
56
|
+
*/
|
|
57
|
+
EXPERIMENTAL_recoverTasksTimeout?: HumanDuration;
|
|
58
|
+
|
|
43
59
|
/**
|
|
44
60
|
* Makes sure to auto-expire and clean up things that time out or for other reasons should not be left lingering.
|
|
45
61
|
*
|
package/dist/alpha.cjs.js
CHANGED
|
@@ -4,41 +4,41 @@ Object.defineProperty(exports, '__esModule', { value: true });
|
|
|
4
4
|
|
|
5
5
|
var alpha = require('@backstage/plugin-scaffolder-common/alpha');
|
|
6
6
|
var pluginPermissionNode = require('@backstage/plugin-permission-node');
|
|
7
|
-
var router = require('./cjs/router-
|
|
7
|
+
var router = require('./cjs/router-842a762b.cjs.js');
|
|
8
8
|
var backendPluginApi = require('@backstage/backend-plugin-api');
|
|
9
9
|
var backendCommon = require('@backstage/backend-common');
|
|
10
10
|
var integration = require('@backstage/integration');
|
|
11
11
|
var alpha$2 = require('@backstage/plugin-catalog-node/alpha');
|
|
12
12
|
var alpha$1 = require('@backstage/plugin-scaffolder-node/alpha');
|
|
13
|
-
require('@backstage/errors');
|
|
14
|
-
require('@backstage/catalog-model');
|
|
15
|
-
require('@backstage/plugin-scaffolder-node');
|
|
16
|
-
require('fs-extra');
|
|
17
|
-
require('yaml');
|
|
18
|
-
require('zod');
|
|
19
|
-
require('path');
|
|
20
|
-
require('luxon');
|
|
21
|
-
require('globby');
|
|
22
|
-
require('isbinaryfile');
|
|
23
|
-
require('isolated-vm');
|
|
24
|
-
require('lodash/get');
|
|
25
13
|
require('@backstage/plugin-scaffolder-backend-module-github');
|
|
26
|
-
require('@backstage/plugin-scaffolder-backend-module-gitlab');
|
|
27
14
|
require('@backstage/plugin-scaffolder-backend-module-azure');
|
|
28
15
|
require('@backstage/plugin-scaffolder-backend-module-bitbucket');
|
|
29
16
|
require('@backstage/plugin-scaffolder-backend-module-gerrit');
|
|
17
|
+
require('@backstage/plugin-scaffolder-backend-module-gitlab');
|
|
18
|
+
require('@backstage/errors');
|
|
30
19
|
require('zen-observable');
|
|
31
|
-
require('p-queue');
|
|
32
20
|
require('@backstage/config');
|
|
21
|
+
require('lodash');
|
|
22
|
+
require('p-queue');
|
|
23
|
+
require('@backstage/catalog-model');
|
|
33
24
|
require('@backstage/plugin-scaffolder-common');
|
|
34
25
|
require('express');
|
|
35
26
|
require('express-promise-router');
|
|
36
27
|
require('jsonschema');
|
|
28
|
+
require('zod');
|
|
29
|
+
require('@backstage/plugin-scaffolder-node');
|
|
30
|
+
require('yaml');
|
|
31
|
+
require('fs-extra');
|
|
32
|
+
require('path');
|
|
33
|
+
require('luxon');
|
|
34
|
+
require('globby');
|
|
35
|
+
require('isbinaryfile');
|
|
36
|
+
require('isolated-vm');
|
|
37
|
+
require('lodash/get');
|
|
37
38
|
require('uuid');
|
|
38
39
|
require('winston');
|
|
39
40
|
require('nunjucks');
|
|
40
41
|
require('stream');
|
|
41
|
-
require('lodash');
|
|
42
42
|
require('prom-client');
|
|
43
43
|
require('@backstage/plugin-permission-common');
|
|
44
44
|
require('url');
|
|
@@ -91,6 +91,7 @@ const scaffolderPlugin = backendPluginApi.createBackendPlugin({
|
|
|
91
91
|
deps: {
|
|
92
92
|
logger: backendPluginApi.coreServices.logger,
|
|
93
93
|
config: backendPluginApi.coreServices.rootConfig,
|
|
94
|
+
lifecycle: backendPluginApi.coreServices.rootLifecycle,
|
|
94
95
|
reader: backendPluginApi.coreServices.urlReader,
|
|
95
96
|
permissions: backendPluginApi.coreServices.permissions,
|
|
96
97
|
database: backendPluginApi.coreServices.database,
|
|
@@ -100,6 +101,7 @@ const scaffolderPlugin = backendPluginApi.createBackendPlugin({
|
|
|
100
101
|
async init({
|
|
101
102
|
logger,
|
|
102
103
|
config,
|
|
104
|
+
lifecycle,
|
|
103
105
|
reader,
|
|
104
106
|
database,
|
|
105
107
|
httpRouter,
|
|
@@ -107,16 +109,33 @@ const scaffolderPlugin = backendPluginApi.createBackendPlugin({
|
|
|
107
109
|
permissions
|
|
108
110
|
}) {
|
|
109
111
|
const log = backendCommon.loggerToWinstonLogger(logger);
|
|
112
|
+
const integrations = integration.ScmIntegrations.fromConfig(config);
|
|
110
113
|
const actions = [
|
|
114
|
+
// actions provided from other modules
|
|
111
115
|
...addedActions,
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
116
|
+
// built-in actions for the scaffolder
|
|
117
|
+
router.createFetchPlainAction({
|
|
118
|
+
reader,
|
|
119
|
+
integrations
|
|
120
|
+
}),
|
|
121
|
+
router.createFetchPlainFileAction({
|
|
122
|
+
reader,
|
|
123
|
+
integrations
|
|
124
|
+
}),
|
|
125
|
+
router.createFetchTemplateAction({
|
|
126
|
+
integrations,
|
|
115
127
|
reader,
|
|
116
|
-
config,
|
|
117
128
|
additionalTemplateFilters,
|
|
118
129
|
additionalTemplateGlobals
|
|
119
|
-
})
|
|
130
|
+
}),
|
|
131
|
+
router.createDebugLogAction(),
|
|
132
|
+
router.createWaitAction(),
|
|
133
|
+
// todo(blam): maybe these should be a -catalog module?
|
|
134
|
+
router.createCatalogRegisterAction({ catalogClient, integrations }),
|
|
135
|
+
router.createFetchCatalogEntityAction({ catalogClient }),
|
|
136
|
+
router.createCatalogWriteAction(),
|
|
137
|
+
router.createFilesystemDeleteAction(),
|
|
138
|
+
router.createFilesystemRenameAction()
|
|
120
139
|
];
|
|
121
140
|
const actionIds = actions.map((action) => action.id).join(", ");
|
|
122
141
|
log.info(
|
|
@@ -128,6 +147,7 @@ const scaffolderPlugin = backendPluginApi.createBackendPlugin({
|
|
|
128
147
|
database,
|
|
129
148
|
catalogClient,
|
|
130
149
|
reader,
|
|
150
|
+
lifecycle,
|
|
131
151
|
actions,
|
|
132
152
|
taskBroker,
|
|
133
153
|
additionalTemplateFilters,
|
package/dist/alpha.cjs.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"alpha.cjs.js","sources":["../src/service/conditionExports.ts","../src/ScaffolderPlugin.ts"],"sourcesContent":["/*\n * Copyright 2022 The Backstage Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport {\n RESOURCE_TYPE_SCAFFOLDER_TEMPLATE,\n RESOURCE_TYPE_SCAFFOLDER_ACTION,\n} from '@backstage/plugin-scaffolder-common/alpha';\nimport { createConditionExports } from '@backstage/plugin-permission-node';\nimport { scaffolderTemplateRules, scaffolderActionRules } from './rules';\n\nconst templateConditionExports = createConditionExports({\n pluginId: 'scaffolder',\n resourceType: RESOURCE_TYPE_SCAFFOLDER_TEMPLATE,\n rules: scaffolderTemplateRules,\n});\n\nconst actionsConditionExports = createConditionExports({\n pluginId: 'scaffolder',\n resourceType: RESOURCE_TYPE_SCAFFOLDER_ACTION,\n rules: scaffolderActionRules,\n});\n\n/**\n * `createScaffolderTemplateConditionalDecision` can be used when authoring policies to\n * create conditional decisions. It requires a permission of type\n * `ResourcePermission<'scaffolder-template'>` to be passed as the first parameter.\n * It's recommended that you use the provided `isResourcePermission` and\n * `isPermission` helper methods to narrow the type of the permission passed to\n * the handle method as shown below.\n *\n * ```\n * // MyAuthorizationPolicy.ts\n * ...\n * import { createScaffolderPolicyDecision } from '@backstage/plugin-scaffolder-backend';\n * import { RESOURCE_TYPE_SCAFFOLDER_TEMPLATE } from '@backstage/plugin-scaffolder-common';\n *\n * class MyAuthorizationPolicy implements PermissionPolicy {\n * async handle(request, user) {\n * ...\n *\n * if (isResourcePermission(request.permission, RESOURCE_TYPE_SCAFFOLDER_TEMPLATE)) {\n * return createScaffolderConditionalDecision(\n * request.permission,\n * { anyOf: [...insert conditions here...] }\n * );\n * }\n *\n * ...\n * }\n *\n * ```\n *\n * @alpha\n */\nexport const createScaffolderTemplateConditionalDecision =\n templateConditionExports.createConditionalDecision;\n\n/**\n * These conditions are used when creating conditional decisions for scaffolder\n * templates that are returned by authorization policies.\n *\n * @alpha\n */\nexport const scaffolderTemplateConditions = templateConditionExports.conditions;\n\n/**\n * @alpha\n */\nexport const createScaffolderActionConditionalDecision =\n actionsConditionExports.createConditionalDecision;\n\n/**\n *\n * These conditions are used when creating conditional decisions for scaffolder\n * actions that are returned by authorization policies.\n *\n * @alpha\n */\nexport const scaffolderActionConditions = actionsConditionExports.conditions;\n","/*\n * Copyright 2022 The Backstage Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport {\n createBackendPlugin,\n coreServices,\n} from '@backstage/backend-plugin-api';\nimport { loggerToWinstonLogger } from '@backstage/backend-common';\nimport { ScmIntegrations } from '@backstage/integration';\nimport { catalogServiceRef } from '@backstage/plugin-catalog-node/alpha';\nimport {\n TaskBroker,\n TemplateAction,\n TemplateFilter,\n TemplateGlobal,\n} from '@backstage/plugin-scaffolder-node';\nimport {\n scaffolderActionsExtensionPoint,\n scaffolderTaskBrokerExtensionPoint,\n scaffolderTemplatingExtensionPoint,\n} from '@backstage/plugin-scaffolder-node/alpha';\nimport { createBuiltinActions } from './scaffolder';\nimport { createRouter } from './service/router';\n\n/**\n * Scaffolder plugin\n *\n * @alpha\n */\nexport const scaffolderPlugin = createBackendPlugin({\n pluginId: 'scaffolder',\n register(env) {\n const addedActions = new Array<TemplateAction<any, any>>();\n env.registerExtensionPoint(scaffolderActionsExtensionPoint, {\n addActions(...newActions: TemplateAction<any>[]) {\n addedActions.push(...newActions);\n },\n });\n\n let taskBroker: TaskBroker | undefined;\n env.registerExtensionPoint(scaffolderTaskBrokerExtensionPoint, {\n setTaskBroker(newTaskBroker) {\n if (taskBroker) {\n throw new Error('Task broker may only be set once');\n }\n taskBroker = newTaskBroker;\n },\n });\n\n const additionalTemplateFilters: Record<string, TemplateFilter> = {};\n const additionalTemplateGlobals: Record<string, TemplateGlobal> = {};\n env.registerExtensionPoint(scaffolderTemplatingExtensionPoint, {\n addTemplateFilters(newFilters) {\n Object.assign(additionalTemplateFilters, newFilters);\n },\n addTemplateGlobals(newGlobals) {\n Object.assign(additionalTemplateGlobals, newGlobals);\n },\n });\n\n env.registerInit({\n deps: {\n logger: coreServices.logger,\n config: coreServices.rootConfig,\n reader: coreServices.urlReader,\n permissions: coreServices.permissions,\n database: coreServices.database,\n httpRouter: coreServices.httpRouter,\n catalogClient: catalogServiceRef,\n },\n async init({\n logger,\n config,\n reader,\n database,\n httpRouter,\n catalogClient,\n permissions,\n }) {\n const log = loggerToWinstonLogger(logger);\n\n const actions = [\n ...addedActions,\n ...createBuiltinActions({\n integrations: ScmIntegrations.fromConfig(config),\n catalogClient,\n reader,\n config,\n additionalTemplateFilters,\n additionalTemplateGlobals,\n }),\n ];\n\n const actionIds = actions.map(action => action.id).join(', ');\n log.info(\n `Starting scaffolder with the following actions enabled ${actionIds}`,\n );\n\n const router = await createRouter({\n logger: log,\n config,\n database,\n catalogClient,\n reader,\n actions,\n taskBroker,\n additionalTemplateFilters,\n additionalTemplateGlobals,\n permissions,\n });\n httpRouter.use(router);\n },\n });\n },\n});\n"],"names":["createConditionExports","RESOURCE_TYPE_SCAFFOLDER_TEMPLATE","scaffolderTemplateRules","RESOURCE_TYPE_SCAFFOLDER_ACTION","scaffolderActionRules","createBackendPlugin","scaffolderActionsExtensionPoint","scaffolderTaskBrokerExtensionPoint","scaffolderTemplatingExtensionPoint","coreServices","catalogServiceRef","loggerToWinstonLogger","createBuiltinActions","ScmIntegrations","router","createRouter"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"alpha.cjs.js","sources":["../src/service/conditionExports.ts","../src/ScaffolderPlugin.ts"],"sourcesContent":["/*\n * Copyright 2022 The Backstage Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport {\n RESOURCE_TYPE_SCAFFOLDER_TEMPLATE,\n RESOURCE_TYPE_SCAFFOLDER_ACTION,\n} from '@backstage/plugin-scaffolder-common/alpha';\nimport { createConditionExports } from '@backstage/plugin-permission-node';\nimport { scaffolderTemplateRules, scaffolderActionRules } from './rules';\n\nconst templateConditionExports = createConditionExports({\n pluginId: 'scaffolder',\n resourceType: RESOURCE_TYPE_SCAFFOLDER_TEMPLATE,\n rules: scaffolderTemplateRules,\n});\n\nconst actionsConditionExports = createConditionExports({\n pluginId: 'scaffolder',\n resourceType: RESOURCE_TYPE_SCAFFOLDER_ACTION,\n rules: scaffolderActionRules,\n});\n\n/**\n * `createScaffolderTemplateConditionalDecision` can be used when authoring policies to\n * create conditional decisions. It requires a permission of type\n * `ResourcePermission<'scaffolder-template'>` to be passed as the first parameter.\n * It's recommended that you use the provided `isResourcePermission` and\n * `isPermission` helper methods to narrow the type of the permission passed to\n * the handle method as shown below.\n *\n * ```\n * // MyAuthorizationPolicy.ts\n * ...\n * import { createScaffolderPolicyDecision } from '@backstage/plugin-scaffolder-backend';\n * import { RESOURCE_TYPE_SCAFFOLDER_TEMPLATE } from '@backstage/plugin-scaffolder-common';\n *\n * class MyAuthorizationPolicy implements PermissionPolicy {\n * async handle(request, user) {\n * ...\n *\n * if (isResourcePermission(request.permission, RESOURCE_TYPE_SCAFFOLDER_TEMPLATE)) {\n * return createScaffolderConditionalDecision(\n * request.permission,\n * { anyOf: [...insert conditions here...] }\n * );\n * }\n *\n * ...\n * }\n *\n * ```\n *\n * @alpha\n */\nexport const createScaffolderTemplateConditionalDecision =\n templateConditionExports.createConditionalDecision;\n\n/**\n * These conditions are used when creating conditional decisions for scaffolder\n * templates that are returned by authorization policies.\n *\n * @alpha\n */\nexport const scaffolderTemplateConditions = templateConditionExports.conditions;\n\n/**\n * @alpha\n */\nexport const createScaffolderActionConditionalDecision =\n actionsConditionExports.createConditionalDecision;\n\n/**\n *\n * These conditions are used when creating conditional decisions for scaffolder\n * actions that are returned by authorization policies.\n *\n * @alpha\n */\nexport const scaffolderActionConditions = actionsConditionExports.conditions;\n","/*\n * Copyright 2022 The Backstage Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport {\n createBackendPlugin,\n coreServices,\n} from '@backstage/backend-plugin-api';\nimport { loggerToWinstonLogger } from '@backstage/backend-common';\nimport { ScmIntegrations } from '@backstage/integration';\nimport { catalogServiceRef } from '@backstage/plugin-catalog-node/alpha';\nimport {\n TaskBroker,\n TemplateAction,\n TemplateFilter,\n TemplateGlobal,\n} from '@backstage/plugin-scaffolder-node';\nimport {\n scaffolderActionsExtensionPoint,\n scaffolderTaskBrokerExtensionPoint,\n scaffolderTemplatingExtensionPoint,\n} from '@backstage/plugin-scaffolder-node/alpha';\nimport {\n createCatalogRegisterAction,\n createCatalogWriteAction,\n createDebugLogAction,\n createFetchCatalogEntityAction,\n createFetchPlainAction,\n createFetchPlainFileAction,\n createFetchTemplateAction,\n createFilesystemDeleteAction,\n createFilesystemRenameAction,\n createWaitAction,\n} from './scaffolder';\nimport { createRouter } from './service/router';\n\n/**\n * Scaffolder plugin\n *\n * @alpha\n */\nexport const scaffolderPlugin = createBackendPlugin({\n pluginId: 'scaffolder',\n register(env) {\n const addedActions = new Array<TemplateAction<any, any>>();\n env.registerExtensionPoint(scaffolderActionsExtensionPoint, {\n addActions(...newActions: TemplateAction<any>[]) {\n addedActions.push(...newActions);\n },\n });\n\n let taskBroker: TaskBroker | undefined;\n env.registerExtensionPoint(scaffolderTaskBrokerExtensionPoint, {\n setTaskBroker(newTaskBroker) {\n if (taskBroker) {\n throw new Error('Task broker may only be set once');\n }\n taskBroker = newTaskBroker;\n },\n });\n\n const additionalTemplateFilters: Record<string, TemplateFilter> = {};\n const additionalTemplateGlobals: Record<string, TemplateGlobal> = {};\n env.registerExtensionPoint(scaffolderTemplatingExtensionPoint, {\n addTemplateFilters(newFilters) {\n Object.assign(additionalTemplateFilters, newFilters);\n },\n addTemplateGlobals(newGlobals) {\n Object.assign(additionalTemplateGlobals, newGlobals);\n },\n });\n\n env.registerInit({\n deps: {\n logger: coreServices.logger,\n config: coreServices.rootConfig,\n lifecycle: coreServices.rootLifecycle,\n reader: coreServices.urlReader,\n permissions: coreServices.permissions,\n database: coreServices.database,\n httpRouter: coreServices.httpRouter,\n catalogClient: catalogServiceRef,\n },\n async init({\n logger,\n config,\n lifecycle,\n reader,\n database,\n httpRouter,\n catalogClient,\n permissions,\n }) {\n const log = loggerToWinstonLogger(logger);\n const integrations = ScmIntegrations.fromConfig(config);\n\n const actions = [\n // actions provided from other modules\n ...addedActions,\n\n // built-in actions for the scaffolder\n createFetchPlainAction({\n reader,\n integrations,\n }),\n createFetchPlainFileAction({\n reader,\n integrations,\n }),\n createFetchTemplateAction({\n integrations,\n reader,\n additionalTemplateFilters,\n additionalTemplateGlobals,\n }),\n createDebugLogAction(),\n createWaitAction(),\n // todo(blam): maybe these should be a -catalog module?\n createCatalogRegisterAction({ catalogClient, integrations }),\n createFetchCatalogEntityAction({ catalogClient }),\n createCatalogWriteAction(),\n createFilesystemDeleteAction(),\n createFilesystemRenameAction(),\n ];\n\n const actionIds = actions.map(action => action.id).join(', ');\n\n log.info(\n `Starting scaffolder with the following actions enabled ${actionIds}`,\n );\n\n const router = await createRouter({\n logger: log,\n config,\n database,\n catalogClient,\n reader,\n lifecycle,\n actions,\n taskBroker,\n additionalTemplateFilters,\n additionalTemplateGlobals,\n permissions,\n });\n httpRouter.use(router);\n },\n });\n },\n});\n"],"names":["createConditionExports","RESOURCE_TYPE_SCAFFOLDER_TEMPLATE","scaffolderTemplateRules","RESOURCE_TYPE_SCAFFOLDER_ACTION","scaffolderActionRules","createBackendPlugin","scaffolderActionsExtensionPoint","scaffolderTaskBrokerExtensionPoint","scaffolderTemplatingExtensionPoint","coreServices","catalogServiceRef","loggerToWinstonLogger","ScmIntegrations","createFetchPlainAction","createFetchPlainFileAction","createFetchTemplateAction","createDebugLogAction","createWaitAction","createCatalogRegisterAction","createFetchCatalogEntityAction","createCatalogWriteAction","createFilesystemDeleteAction","createFilesystemRenameAction","router","createRouter"],"mappings":"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"}
|
|
@@ -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');
|
|
@@ -1419,6 +1419,36 @@ class TemplateActionRegistry {
|
|
|
1419
1419
|
}
|
|
1420
1420
|
}
|
|
1421
1421
|
|
|
1422
|
+
const trimEventsTillLastRecovery = (events) => {
|
|
1423
|
+
const recoveredEventInd = events.slice().reverse().findIndex((event) => event.type === "recovered");
|
|
1424
|
+
if (recoveredEventInd >= 0) {
|
|
1425
|
+
const ind = events.length - recoveredEventInd - 1;
|
|
1426
|
+
const { recoverStrategy } = events[ind].body;
|
|
1427
|
+
if (recoverStrategy === "startOver") {
|
|
1428
|
+
return {
|
|
1429
|
+
events: recoveredEventInd === 0 ? [] : events.slice(ind)
|
|
1430
|
+
};
|
|
1431
|
+
}
|
|
1432
|
+
}
|
|
1433
|
+
return { events };
|
|
1434
|
+
};
|
|
1435
|
+
|
|
1436
|
+
const intervalFromNowTill = (timeoutS, knex) => {
|
|
1437
|
+
let heartbeatInterval = knex.raw(`? - interval '${timeoutS} seconds'`, [
|
|
1438
|
+
knex.fn.now()
|
|
1439
|
+
]);
|
|
1440
|
+
if (knex.client.config.client.includes("mysql")) {
|
|
1441
|
+
heartbeatInterval = knex.raw(
|
|
1442
|
+
`date_sub(now(), interval ${timeoutS} second)`
|
|
1443
|
+
);
|
|
1444
|
+
} else if (knex.client.config.client.includes("sqlite3")) {
|
|
1445
|
+
heartbeatInterval = knex.raw(`datetime('now', ?)`, [
|
|
1446
|
+
`-${timeoutS} seconds`
|
|
1447
|
+
]);
|
|
1448
|
+
}
|
|
1449
|
+
return heartbeatInterval;
|
|
1450
|
+
};
|
|
1451
|
+
|
|
1422
1452
|
var __defProp$3 = Object.defineProperty;
|
|
1423
1453
|
var __defNormalProp$3 = (obj, key, value) => key in obj ? __defProp$3(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value;
|
|
1424
1454
|
var __publicField$3 = (obj, key, value) => {
|
|
@@ -1455,6 +1485,28 @@ class DatabaseTaskStore {
|
|
|
1455
1485
|
await this.runMigrations(database, client);
|
|
1456
1486
|
return new DatabaseTaskStore(client);
|
|
1457
1487
|
}
|
|
1488
|
+
isRecoverableTask(spec) {
|
|
1489
|
+
var _a, _b;
|
|
1490
|
+
return ["startOver"].includes(
|
|
1491
|
+
(_b = (_a = spec.EXPERIMENTAL_recovery) == null ? void 0 : _a.EXPERIMENTAL_strategy) != null ? _b : "none"
|
|
1492
|
+
);
|
|
1493
|
+
}
|
|
1494
|
+
parseSpec({ spec, id }) {
|
|
1495
|
+
try {
|
|
1496
|
+
return JSON.parse(spec);
|
|
1497
|
+
} catch (error) {
|
|
1498
|
+
throw new Error(`Failed to parse spec of task '${id}', ${error}`);
|
|
1499
|
+
}
|
|
1500
|
+
}
|
|
1501
|
+
parseTaskSecrets(taskRow) {
|
|
1502
|
+
try {
|
|
1503
|
+
return taskRow.secrets ? JSON.parse(taskRow.secrets) : void 0;
|
|
1504
|
+
} catch (error) {
|
|
1505
|
+
throw new Error(
|
|
1506
|
+
`Failed to parse secrets of task '${taskRow.id}', ${error}`
|
|
1507
|
+
);
|
|
1508
|
+
}
|
|
1509
|
+
}
|
|
1458
1510
|
static async getClient(database) {
|
|
1459
1511
|
if (isPluginDatabaseManager(database)) {
|
|
1460
1512
|
return database.getClient();
|
|
@@ -1539,30 +1591,26 @@ class DatabaseTaskStore {
|
|
|
1539
1591
|
if (!task) {
|
|
1540
1592
|
return void 0;
|
|
1541
1593
|
}
|
|
1594
|
+
const spec = this.parseSpec(task);
|
|
1542
1595
|
const updateCount = await tx("tasks").where({ id: task.id, status: "open" }).update({
|
|
1543
1596
|
status: "processing",
|
|
1544
1597
|
last_heartbeat_at: this.db.fn.now(),
|
|
1545
|
-
// remove the secrets when moving to processing state.
|
|
1546
|
-
secrets: null
|
|
1598
|
+
// remove the secrets for non-recoverable tasks when moving to processing state.
|
|
1599
|
+
secrets: this.isRecoverableTask(spec) ? task.secrets : null
|
|
1547
1600
|
});
|
|
1548
1601
|
if (updateCount < 1) {
|
|
1549
1602
|
return void 0;
|
|
1550
1603
|
}
|
|
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
|
-
}
|
|
1604
|
+
const secrets = this.parseTaskSecrets(task);
|
|
1605
|
+
return {
|
|
1606
|
+
id: task.id,
|
|
1607
|
+
spec,
|
|
1608
|
+
status: "processing",
|
|
1609
|
+
lastHeartbeatAt: task.last_heartbeat_at,
|
|
1610
|
+
createdAt: task.created_at,
|
|
1611
|
+
createdBy: (_a = task.created_by) != null ? _a : void 0,
|
|
1612
|
+
secrets
|
|
1613
|
+
};
|
|
1566
1614
|
});
|
|
1567
1615
|
}
|
|
1568
1616
|
async heartbeatTask(taskId) {
|
|
@@ -1575,20 +1623,10 @@ class DatabaseTaskStore {
|
|
|
1575
1623
|
}
|
|
1576
1624
|
async listStaleTasks(options) {
|
|
1577
1625
|
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
|
-
}
|
|
1626
|
+
const heartbeatInterval = intervalFromNowTill(timeoutS, this.db);
|
|
1590
1627
|
const rawRows = await this.db("tasks").where("status", "processing").andWhere("last_heartbeat_at", "<=", heartbeatInterval);
|
|
1591
1628
|
const tasks = rawRows.map((row) => ({
|
|
1629
|
+
recovery: JSON.parse(row.spec).EXPERIMENTAL_recovery,
|
|
1592
1630
|
taskId: row.id
|
|
1593
1631
|
}));
|
|
1594
1632
|
return { tasks };
|
|
@@ -1609,7 +1647,8 @@ class DatabaseTaskStore {
|
|
|
1609
1647
|
}).limit(1).select();
|
|
1610
1648
|
const updateTask = async (criteria) => {
|
|
1611
1649
|
const updateCount = await tx("tasks").where(criteria).update({
|
|
1612
|
-
status
|
|
1650
|
+
status,
|
|
1651
|
+
secrets: null
|
|
1613
1652
|
});
|
|
1614
1653
|
if (updateCount !== 1) {
|
|
1615
1654
|
throw new errors.ConflictError(
|
|
@@ -1679,7 +1718,7 @@ class DatabaseTaskStore {
|
|
|
1679
1718
|
);
|
|
1680
1719
|
}
|
|
1681
1720
|
});
|
|
1682
|
-
return
|
|
1721
|
+
return trimEventsTillLastRecovery(events);
|
|
1683
1722
|
}
|
|
1684
1723
|
async shutdownTask(options) {
|
|
1685
1724
|
const { taskId } = options;
|
|
@@ -1718,7 +1757,72 @@ class DatabaseTaskStore {
|
|
|
1718
1757
|
body: serializedBody
|
|
1719
1758
|
});
|
|
1720
1759
|
}
|
|
1760
|
+
async recoverTasks(options) {
|
|
1761
|
+
const taskIdsToRecover = [];
|
|
1762
|
+
const timeoutS = luxon.Duration.fromObject(options.timeout).as("seconds");
|
|
1763
|
+
await this.db.transaction(async (tx) => {
|
|
1764
|
+
var _a, _b;
|
|
1765
|
+
const heartbeatInterval = intervalFromNowTill(timeoutS, this.db);
|
|
1766
|
+
const result = await tx("tasks").where("status", "processing").andWhere("last_heartbeat_at", "<=", heartbeatInterval).update(
|
|
1767
|
+
{
|
|
1768
|
+
status: "open",
|
|
1769
|
+
last_heartbeat_at: this.db.fn.now()
|
|
1770
|
+
},
|
|
1771
|
+
["id", "spec"]
|
|
1772
|
+
);
|
|
1773
|
+
taskIdsToRecover.push(...result.map((i) => i.id));
|
|
1774
|
+
for (const { id, spec } of result) {
|
|
1775
|
+
const taskSpec = JSON.parse(spec);
|
|
1776
|
+
await this.db("task_events").insert({
|
|
1777
|
+
task_id: id,
|
|
1778
|
+
event_type: "recovered",
|
|
1779
|
+
body: JSON.stringify({
|
|
1780
|
+
recoverStrategy: (_b = (_a = taskSpec.EXPERIMENTAL_recovery) == null ? void 0 : _a.EXPERIMENTAL_strategy) != null ? _b : "none"
|
|
1781
|
+
})
|
|
1782
|
+
});
|
|
1783
|
+
}
|
|
1784
|
+
});
|
|
1785
|
+
return { ids: taskIdsToRecover };
|
|
1786
|
+
}
|
|
1787
|
+
}
|
|
1788
|
+
|
|
1789
|
+
function isTruthy(value) {
|
|
1790
|
+
return lodash.isArray(value) ? value.length > 0 : !!value;
|
|
1791
|
+
}
|
|
1792
|
+
function generateExampleOutput(schema) {
|
|
1793
|
+
var _a, _b;
|
|
1794
|
+
const { examples } = schema;
|
|
1795
|
+
if (examples && Array.isArray(examples)) {
|
|
1796
|
+
return examples[0];
|
|
1797
|
+
}
|
|
1798
|
+
if (schema.type === "object") {
|
|
1799
|
+
return Object.fromEntries(
|
|
1800
|
+
Object.entries((_a = schema.properties) != null ? _a : {}).map(([key, value]) => [
|
|
1801
|
+
key,
|
|
1802
|
+
generateExampleOutput(value)
|
|
1803
|
+
])
|
|
1804
|
+
);
|
|
1805
|
+
} else if (schema.type === "array") {
|
|
1806
|
+
const [firstSchema] = (_b = [schema.items]) == null ? void 0 : _b.flat();
|
|
1807
|
+
if (firstSchema) {
|
|
1808
|
+
return [generateExampleOutput(firstSchema)];
|
|
1809
|
+
}
|
|
1810
|
+
return [];
|
|
1811
|
+
} else if (schema.type === "string") {
|
|
1812
|
+
return "<example>";
|
|
1813
|
+
} else if (schema.type === "number") {
|
|
1814
|
+
return 0;
|
|
1815
|
+
} else if (schema.type === "boolean") {
|
|
1816
|
+
return false;
|
|
1817
|
+
}
|
|
1818
|
+
return "<unknown>";
|
|
1721
1819
|
}
|
|
1820
|
+
const readDuration$1 = (config$1, key, defaultValue) => {
|
|
1821
|
+
if (config$1 == null ? void 0 : config$1.has(key)) {
|
|
1822
|
+
return config.readDurationFromConfig(config$1, { key });
|
|
1823
|
+
}
|
|
1824
|
+
return defaultValue;
|
|
1825
|
+
};
|
|
1722
1826
|
|
|
1723
1827
|
var __defProp$2 = Object.defineProperty;
|
|
1724
1828
|
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 +1907,10 @@ function defer() {
|
|
|
1803
1907
|
return { promise, resolve };
|
|
1804
1908
|
}
|
|
1805
1909
|
class StorageTaskBroker {
|
|
1806
|
-
constructor(storage, logger) {
|
|
1910
|
+
constructor(storage, logger, config) {
|
|
1807
1911
|
this.storage = storage;
|
|
1808
1912
|
this.logger = logger;
|
|
1913
|
+
this.config = config;
|
|
1809
1914
|
__publicField$2(this, "deferredDispatch", defer());
|
|
1810
1915
|
}
|
|
1811
1916
|
async list(options) {
|
|
@@ -1838,6 +1943,26 @@ class StorageTaskBroker {
|
|
|
1838
1943
|
}
|
|
1839
1944
|
});
|
|
1840
1945
|
}
|
|
1946
|
+
async recoverTasks() {
|
|
1947
|
+
var _a, _b, _c, _d;
|
|
1948
|
+
const enabled = (_a = this.config && this.config.getOptionalBoolean(
|
|
1949
|
+
"scaffolder.EXPERIMENTAL_recoverTasks"
|
|
1950
|
+
)) != null ? _a : false;
|
|
1951
|
+
if (enabled) {
|
|
1952
|
+
const defaultTimeout = { seconds: 30 };
|
|
1953
|
+
const timeout = readDuration$1(
|
|
1954
|
+
this.config,
|
|
1955
|
+
"scaffolder.EXPERIMENTAL_recoverTasksTimeout",
|
|
1956
|
+
defaultTimeout
|
|
1957
|
+
);
|
|
1958
|
+
const { ids: recoveredTaskIds } = (_d = await ((_c = (_b = this.storage).recoverTasks) == null ? void 0 : _c.call(_b, {
|
|
1959
|
+
timeout
|
|
1960
|
+
}))) != null ? _d : { ids: [] };
|
|
1961
|
+
if (recoveredTaskIds.length > 0) {
|
|
1962
|
+
this.signalDispatch();
|
|
1963
|
+
}
|
|
1964
|
+
}
|
|
1965
|
+
}
|
|
1841
1966
|
/**
|
|
1842
1967
|
* {@inheritdoc TaskBroker.claim}
|
|
1843
1968
|
*/
|
|
@@ -1945,38 +2070,6 @@ class StorageTaskBroker {
|
|
|
1945
2070
|
}
|
|
1946
2071
|
}
|
|
1947
2072
|
|
|
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
2073
|
function createCounterMetric(config) {
|
|
1981
2074
|
let metric = promClient.register.getSingleMetric(config.name);
|
|
1982
2075
|
if (!metric) {
|
|
@@ -2510,6 +2603,10 @@ class TaskWorker {
|
|
|
2510
2603
|
constructor(options) {
|
|
2511
2604
|
this.options = options;
|
|
2512
2605
|
__publicField(this, "taskQueue");
|
|
2606
|
+
__publicField(this, "logger");
|
|
2607
|
+
__publicField(this, "stopWorkers");
|
|
2608
|
+
this.stopWorkers = false;
|
|
2609
|
+
this.logger = options.logger;
|
|
2513
2610
|
this.taskQueue = new PQueue__default["default"]({
|
|
2514
2611
|
concurrency: options.concurrentTasksLimit
|
|
2515
2612
|
});
|
|
@@ -2543,15 +2640,34 @@ class TaskWorker {
|
|
|
2543
2640
|
permissions
|
|
2544
2641
|
});
|
|
2545
2642
|
}
|
|
2643
|
+
async recoverTasks() {
|
|
2644
|
+
var _a, _b, _c;
|
|
2645
|
+
try {
|
|
2646
|
+
await ((_b = (_a = this.options.taskBroker).recoverTasks) == null ? void 0 : _b.call(_a));
|
|
2647
|
+
} catch (err) {
|
|
2648
|
+
(_c = this.logger) == null ? void 0 : _c.error(errors.stringifyError(err));
|
|
2649
|
+
}
|
|
2650
|
+
}
|
|
2546
2651
|
start() {
|
|
2547
2652
|
(async () => {
|
|
2548
|
-
|
|
2653
|
+
while (!this.stopWorkers) {
|
|
2654
|
+
await new Promise((resolve) => setTimeout(resolve, 1e4));
|
|
2655
|
+
await this.recoverTasks();
|
|
2656
|
+
}
|
|
2657
|
+
})();
|
|
2658
|
+
(async () => {
|
|
2659
|
+
while (!this.stopWorkers) {
|
|
2549
2660
|
await this.onReadyToClaimTask();
|
|
2550
|
-
|
|
2551
|
-
|
|
2661
|
+
if (!this.stopWorkers) {
|
|
2662
|
+
const task = await this.options.taskBroker.claim();
|
|
2663
|
+
void this.taskQueue.add(() => this.runOneTask(task));
|
|
2664
|
+
}
|
|
2552
2665
|
}
|
|
2553
2666
|
})();
|
|
2554
2667
|
}
|
|
2668
|
+
stop() {
|
|
2669
|
+
this.stopWorkers = true;
|
|
2670
|
+
}
|
|
2555
2671
|
onReadyToClaimTask() {
|
|
2556
2672
|
if (this.taskQueue.pending < this.options.concurrentTasksLimit) {
|
|
2557
2673
|
return Promise.resolve();
|
|
@@ -2812,7 +2928,7 @@ async function createRouter(options) {
|
|
|
2812
2928
|
let taskBroker;
|
|
2813
2929
|
if (!options.taskBroker) {
|
|
2814
2930
|
const databaseTaskStore = await DatabaseTaskStore.create({ database });
|
|
2815
|
-
taskBroker = new StorageTaskBroker(databaseTaskStore, logger);
|
|
2931
|
+
taskBroker = new StorageTaskBroker(databaseTaskStore, logger, config);
|
|
2816
2932
|
if (scheduler && databaseTaskStore.listStaleTasks) {
|
|
2817
2933
|
await scheduler.scheduleTask({
|
|
2818
2934
|
id: "close_stale_tasks",
|
|
@@ -2869,7 +2985,16 @@ async function createRouter(options) {
|
|
|
2869
2985
|
additionalTemplateGlobals
|
|
2870
2986
|
});
|
|
2871
2987
|
actionsToRegister.forEach((action) => actionRegistry.register(action));
|
|
2872
|
-
workers.forEach((worker) => worker.start());
|
|
2988
|
+
const launchWorkers = () => workers.forEach((worker) => worker.start());
|
|
2989
|
+
const shutdownWorkers = () => {
|
|
2990
|
+
workers.forEach((worker) => worker.stop());
|
|
2991
|
+
};
|
|
2992
|
+
if (options.lifecycle) {
|
|
2993
|
+
options.lifecycle.addStartupHook(launchWorkers);
|
|
2994
|
+
options.lifecycle.addShutdownHook(shutdownWorkers);
|
|
2995
|
+
} else {
|
|
2996
|
+
launchWorkers();
|
|
2997
|
+
}
|
|
2873
2998
|
const dryRunner = createDryRunner({
|
|
2874
2999
|
actionRegistry,
|
|
2875
3000
|
integrations,
|
|
@@ -2983,6 +3108,7 @@ async function createRouter(options) {
|
|
|
2983
3108
|
name: (_b2 = step.name) != null ? _b2 : step.action
|
|
2984
3109
|
};
|
|
2985
3110
|
}),
|
|
3111
|
+
EXPERIMENTAL_recovery: template.spec.EXPERIMENTAL_recovery,
|
|
2986
3112
|
output: (_b = template.spec.output) != null ? _b : {},
|
|
2987
3113
|
parameters: values,
|
|
2988
3114
|
user: {
|
|
@@ -3214,4 +3340,4 @@ exports.createRouter = createRouter;
|
|
|
3214
3340
|
exports.createWaitAction = createWaitAction;
|
|
3215
3341
|
exports.scaffolderActionRules = scaffolderActionRules;
|
|
3216
3342
|
exports.scaffolderTemplateRules = scaffolderTemplateRules;
|
|
3217
|
-
//# sourceMappingURL=router-
|
|
3343
|
+
//# sourceMappingURL=router-842a762b.cjs.js.map
|