@backstage/plugin-scaffolder-backend 0.0.0-nightly-202192621851 → 0.0.0-nightly-202193122459

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CHANGELOG.md CHANGED
@@ -1,19 +1,27 @@
1
1
  # @backstage/plugin-scaffolder-backend
2
2
 
3
- ## 0.0.0-nightly-202192621851
3
+ ## 0.0.0-nightly-202193122459
4
+
5
+ ### Patch Changes
6
+
7
+ - 9990df8a1f: Expose some classes and interfaces public so TaskWorkers can run externally from the scaffolder API.
8
+
9
+ ## 0.15.11
4
10
 
5
11
  ### Patch Changes
6
12
 
7
13
  - 10615525f3: Switch to use the json and observable types from `@backstage/types`
8
14
  - 41c49884d2: Start using the new `@backstage/types` package. Initially, this means using the `Observable` and `Json*` types from there. The types also remain in their old places but deprecated, and will be removed in a future release.
15
+ - e55a5dea09: Fixed bug where the mode of an executable file was ignored
9
16
  - Updated dependencies
10
- - @backstage/backend-common@0.0.0-nightly-202192621851
11
- - @backstage/catalog-model@0.0.0-nightly-202192621851
12
- - @backstage/errors@0.0.0-nightly-202192621851
13
- - @backstage/plugin-catalog-backend@0.0.0-nightly-202192621851
14
- - @backstage/plugin-scaffolder-backend-module-cookiecutter@0.0.0-nightly-202192621851
15
- - @backstage/plugin-scaffolder-common@0.0.0-nightly-202192621851
16
- - @backstage/config@0.0.0-nightly-202192621851
17
+ - @backstage/plugin-catalog-backend@0.17.2
18
+ - @backstage/config@0.1.11
19
+ - @backstage/errors@0.1.4
20
+ - @backstage/integration@0.6.9
21
+ - @backstage/backend-common@0.9.8
22
+ - @backstage/catalog-model@0.9.6
23
+ - @backstage/plugin-scaffolder-backend-module-cookiecutter@0.1.3
24
+ - @backstage/plugin-scaffolder-common@0.1.1
17
25
 
18
26
  ## 0.15.10
19
27
 
package/dist/index.cjs.js CHANGED
@@ -22,14 +22,14 @@ var lodash = require('lodash');
22
22
  var octokitPluginCreatePullRequest = require('octokit-plugin-create-pull-request');
23
23
  var node = require('@gitbeaker/node');
24
24
  var webhooks = require('@octokit/webhooks');
25
- var express = require('express');
26
- var Router = require('express-promise-router');
27
- var jsonschema = require('jsonschema');
28
25
  var uuid = require('uuid');
29
26
  var luxon = require('luxon');
30
- var os = require('os');
31
27
  var Handlebars = require('handlebars');
32
28
  var winston = require('winston');
29
+ var jsonschema = require('jsonschema');
30
+ var express = require('express');
31
+ var Router = require('express-promise-router');
32
+ var os = require('os');
33
33
  var pluginCatalogBackend = require('@backstage/plugin-catalog-backend');
34
34
  var pluginScaffolderCommon = require('@backstage/plugin-scaffolder-common');
35
35
 
@@ -62,11 +62,11 @@ var yaml__namespace = /*#__PURE__*/_interopNamespace(yaml);
62
62
  var globby__default = /*#__PURE__*/_interopDefaultLegacy(globby);
63
63
  var nunjucks__default = /*#__PURE__*/_interopDefaultLegacy(nunjucks);
64
64
  var fetch__default = /*#__PURE__*/_interopDefaultLegacy(fetch);
65
+ var Handlebars__namespace = /*#__PURE__*/_interopNamespace(Handlebars);
66
+ var winston__namespace = /*#__PURE__*/_interopNamespace(winston);
65
67
  var express__default = /*#__PURE__*/_interopDefaultLegacy(express);
66
68
  var Router__default = /*#__PURE__*/_interopDefaultLegacy(Router);
67
69
  var os__default = /*#__PURE__*/_interopDefaultLegacy(os);
68
- var Handlebars__namespace = /*#__PURE__*/_interopNamespace(Handlebars);
69
- var winston__namespace = /*#__PURE__*/_interopNamespace(winston);
70
70
 
71
71
  const createTemplateAction = (templateAction) => {
72
72
  return templateAction;
@@ -1391,13 +1391,25 @@ const createPublishGithubPullRequestAction = ({
1391
1391
  gitignore: true,
1392
1392
  dot: true
1393
1393
  });
1394
- const fileContents = await Promise.all(localFilePaths.map((p) => fs.readFile(path__default['default'].resolve(fileRoot, p))));
1394
+ const fileContents = await Promise.all(localFilePaths.map((filePath) => {
1395
+ const absPath = path__default['default'].resolve(fileRoot, filePath);
1396
+ const base64EncodedContent = fs__default['default'].readFileSync(absPath).toString("base64");
1397
+ const fileStat = fs__default['default'].statSync(absPath);
1398
+ const isExecutable = fileStat.mode === 33277;
1399
+ const githubTreeItemMode = isExecutable ? "100755" : "100644";
1400
+ const encoding = "base64";
1401
+ return {
1402
+ encoding,
1403
+ content: base64EncodedContent,
1404
+ mode: githubTreeItemMode
1405
+ };
1406
+ }));
1395
1407
  const repoFilePaths = localFilePaths.map((repoFilePath) => {
1396
1408
  return targetPath ? `${targetPath}/${repoFilePath}` : repoFilePath;
1397
1409
  });
1398
1410
  const changes = [
1399
1411
  {
1400
- files: lodash.zipObject(repoFilePaths, fileContents.map((buf) => buf.toString())),
1412
+ files: lodash.zipObject(repoFilePaths, fileContents),
1401
1413
  commit: title
1402
1414
  }
1403
1415
  ];
@@ -1735,38 +1747,16 @@ class TemplateActionRegistry {
1735
1747
  }
1736
1748
  }
1737
1749
 
1738
- class CatalogEntityClient {
1739
- constructor(catalogClient) {
1740
- this.catalogClient = catalogClient;
1741
- }
1742
- async findTemplate(templateName, options) {
1743
- const {items: templates} = await this.catalogClient.getEntities({
1744
- filter: {
1745
- kind: "template",
1746
- "metadata.name": templateName
1747
- }
1748
- }, options);
1749
- if (templates.length !== 1) {
1750
- if (templates.length > 1) {
1751
- throw new errors.ConflictError("Templates lookup resulted in multiple matches");
1752
- } else {
1753
- throw new errors.NotFoundError("Template not found");
1754
- }
1755
- }
1756
- return templates[0];
1757
- }
1758
- }
1759
-
1760
1750
  const migrationsDir = backendCommon.resolvePackagePath("@backstage/plugin-scaffolder-backend", "migrations");
1761
1751
  class DatabaseTaskStore {
1762
- constructor(db) {
1763
- this.db = db;
1764
- }
1765
- static async create(knex) {
1766
- await knex.migrate.latest({
1752
+ static async create(options) {
1753
+ await options.database.migrate.latest({
1767
1754
  directory: migrationsDir
1768
1755
  });
1769
- return new DatabaseTaskStore(knex);
1756
+ return new DatabaseTaskStore(options);
1757
+ }
1758
+ constructor(options) {
1759
+ this.db = options.database;
1770
1760
  }
1771
1761
  async getTask(taskId) {
1772
1762
  const [result] = await this.db("tasks").where({id: taskId}).select();
@@ -1922,7 +1912,7 @@ class DatabaseTaskStore {
1922
1912
  }
1923
1913
  }
1924
1914
 
1925
- class TaskAgent {
1915
+ class TaskManager {
1926
1916
  constructor(state, storage, logger) {
1927
1917
  this.state = state;
1928
1918
  this.storage = storage;
@@ -1930,7 +1920,7 @@ class TaskAgent {
1930
1920
  this.isDone = false;
1931
1921
  }
1932
1922
  static create(state, storage, logger) {
1933
- const agent = new TaskAgent(state, storage, logger);
1923
+ const agent = new TaskManager(state, storage, logger);
1934
1924
  agent.startTimeout();
1935
1925
  return agent;
1936
1926
  }
@@ -1996,7 +1986,7 @@ class StorageTaskBroker {
1996
1986
  for (; ; ) {
1997
1987
  const pendingTask = await this.storage.claimTask();
1998
1988
  if (pendingTask) {
1999
- return TaskAgent.create({
1989
+ return TaskManager.create({
2000
1990
  taskId: pendingTask.id,
2001
1991
  spec: pendingTask.spec,
2002
1992
  secrets: pendingTask.secrets
@@ -2038,7 +2028,7 @@ class StorageTaskBroker {
2038
2028
  await new Promise((resolve) => setTimeout(resolve, 1e3));
2039
2029
  }
2040
2030
  })();
2041
- return unsubscribe;
2031
+ return {unsubscribe};
2042
2032
  }
2043
2033
  async vacuumTasks(timeoutS) {
2044
2034
  const {tasks} = await this.storage.listStaleTasks(timeoutS);
@@ -2065,70 +2055,12 @@ class StorageTaskBroker {
2065
2055
  }
2066
2056
  }
2067
2057
 
2068
- class TaskWorker {
2069
- constructor(options) {
2070
- this.options = options;
2071
- }
2072
- start() {
2073
- (async () => {
2074
- for (; ; ) {
2075
- const task = await this.options.taskBroker.claim();
2076
- await this.runOneTask(task);
2077
- }
2078
- })();
2079
- }
2080
- async runOneTask(task) {
2081
- try {
2082
- const {output} = task.spec.apiVersion === "scaffolder.backstage.io/v1beta3" ? await this.options.runners.workflowRunner.execute(task) : await this.options.runners.legacyWorkflowRunner.execute(task);
2083
- await task.complete("completed", {output});
2084
- } catch (error) {
2085
- errors.assertError(error);
2086
- await task.complete("failed", {
2087
- error: {name: error.name, message: error.message}
2088
- });
2089
- }
2090
- }
2091
- }
2092
-
2093
- async function getWorkingDirectory(config, logger) {
2094
- if (!config.has("backend.workingDirectory")) {
2095
- return os__default['default'].tmpdir();
2096
- }
2097
- const workingDirectory = config.getString("backend.workingDirectory");
2098
- try {
2099
- await fs__default['default'].access(workingDirectory, fs__default['default'].constants.F_OK | fs__default['default'].constants.W_OK);
2100
- logger.info(`using working directory: ${workingDirectory}`);
2101
- } catch (err) {
2102
- errors.assertError(err);
2103
- logger.error(`working directory ${workingDirectory} ${err.code === "ENOENT" ? "does not exist" : "is not writable"}`);
2104
- throw err;
2105
- }
2106
- return workingDirectory;
2107
- }
2108
- function getEntityBaseUrl(entity) {
2109
- var _a, _b;
2110
- let location = (_a = entity.metadata.annotations) == null ? void 0 : _a[catalogModel.SOURCE_LOCATION_ANNOTATION];
2111
- if (!location) {
2112
- location = (_b = entity.metadata.annotations) == null ? void 0 : _b[catalogModel.LOCATION_ANNOTATION];
2113
- }
2114
- if (!location) {
2115
- return void 0;
2116
- }
2117
- const {type, target} = catalogModel.parseLocationReference(location);
2118
- if (type === "url") {
2119
- return target;
2120
- } else if (type === "file") {
2121
- return `file://${target}`;
2122
- }
2123
- return void 0;
2124
- }
2125
-
2126
2058
  function isTruthy(value) {
2127
2059
  return lodash.isArray(value) ? value.length > 0 : !!value;
2128
2060
  }
2129
2061
 
2130
2062
  const isValidTaskSpec$1 = (taskSpec) => taskSpec.apiVersion === "backstage.io/v1beta2";
2131
- class LegacyWorkflowRunner {
2063
+ class HandlebarsWorkflowRunner {
2132
2064
  constructor(options) {
2133
2065
  this.options = options;
2134
2066
  this.handlebars = Handlebars__namespace.create();
@@ -2304,7 +2236,10 @@ class LegacyWorkflowRunner {
2304
2236
  const isValidTaskSpec = (taskSpec) => {
2305
2237
  return taskSpec.apiVersion === "scaffolder.backstage.io/v1beta3";
2306
2238
  };
2307
- const createStepLogger = ({task, step}) => {
2239
+ const createStepLogger = ({
2240
+ task,
2241
+ step
2242
+ }) => {
2308
2243
  const metadata = {stepId: step.id};
2309
2244
  const taskLogger = winston__namespace.createLogger({
2310
2245
  level: process.env.LOG_LEVEL || "info",
@@ -2321,7 +2256,7 @@ const createStepLogger = ({task, step}) => {
2321
2256
  taskLogger.add(new winston__namespace.transports.Stream({stream: streamLogger}));
2322
2257
  return {taskLogger, streamLogger};
2323
2258
  };
2324
- class DefaultWorkflowRunner {
2259
+ class NunjucksWorkflowRunner {
2325
2260
  constructor(options) {
2326
2261
  this.options = options;
2327
2262
  this.nunjucksOptions = {
@@ -2452,6 +2387,111 @@ class DefaultWorkflowRunner {
2452
2387
  }
2453
2388
  }
2454
2389
 
2390
+ class TaskWorker {
2391
+ constructor(options) {
2392
+ this.options = options;
2393
+ }
2394
+ static async create(options) {
2395
+ const {
2396
+ taskBroker,
2397
+ logger,
2398
+ actionRegistry,
2399
+ integrations,
2400
+ workingDirectory
2401
+ } = options;
2402
+ const legacyWorkflowRunner = new HandlebarsWorkflowRunner({
2403
+ logger,
2404
+ actionRegistry,
2405
+ integrations,
2406
+ workingDirectory
2407
+ });
2408
+ const workflowRunner = new NunjucksWorkflowRunner({
2409
+ actionRegistry,
2410
+ integrations,
2411
+ logger,
2412
+ workingDirectory
2413
+ });
2414
+ return new TaskWorker({
2415
+ taskBroker,
2416
+ runners: {legacyWorkflowRunner, workflowRunner}
2417
+ });
2418
+ }
2419
+ start() {
2420
+ (async () => {
2421
+ for (; ; ) {
2422
+ const task = await this.options.taskBroker.claim();
2423
+ await this.runOneTask(task);
2424
+ }
2425
+ })();
2426
+ }
2427
+ async runOneTask(task) {
2428
+ try {
2429
+ const {output} = task.spec.apiVersion === "scaffolder.backstage.io/v1beta3" ? await this.options.runners.workflowRunner.execute(task) : await this.options.runners.legacyWorkflowRunner.execute(task);
2430
+ await task.complete("completed", {output});
2431
+ } catch (error) {
2432
+ errors.assertError(error);
2433
+ await task.complete("failed", {
2434
+ error: {name: error.name, message: error.message}
2435
+ });
2436
+ }
2437
+ }
2438
+ }
2439
+
2440
+ class CatalogEntityClient {
2441
+ constructor(catalogClient) {
2442
+ this.catalogClient = catalogClient;
2443
+ }
2444
+ async findTemplate(templateName, options) {
2445
+ const {items: templates} = await this.catalogClient.getEntities({
2446
+ filter: {
2447
+ kind: "template",
2448
+ "metadata.name": templateName
2449
+ }
2450
+ }, options);
2451
+ if (templates.length !== 1) {
2452
+ if (templates.length > 1) {
2453
+ throw new errors.ConflictError("Templates lookup resulted in multiple matches");
2454
+ } else {
2455
+ throw new errors.NotFoundError("Template not found");
2456
+ }
2457
+ }
2458
+ return templates[0];
2459
+ }
2460
+ }
2461
+
2462
+ async function getWorkingDirectory(config, logger) {
2463
+ if (!config.has("backend.workingDirectory")) {
2464
+ return os__default['default'].tmpdir();
2465
+ }
2466
+ const workingDirectory = config.getString("backend.workingDirectory");
2467
+ try {
2468
+ await fs__default['default'].access(workingDirectory, fs__default['default'].constants.F_OK | fs__default['default'].constants.W_OK);
2469
+ logger.info(`using working directory: ${workingDirectory}`);
2470
+ } catch (err) {
2471
+ errors.assertError(err);
2472
+ logger.error(`working directory ${workingDirectory} ${err.code === "ENOENT" ? "does not exist" : "is not writable"}`);
2473
+ throw err;
2474
+ }
2475
+ return workingDirectory;
2476
+ }
2477
+ function getEntityBaseUrl(entity) {
2478
+ var _a, _b;
2479
+ let location = (_a = entity.metadata.annotations) == null ? void 0 : _a[catalogModel.SOURCE_LOCATION_ANNOTATION];
2480
+ if (!location) {
2481
+ location = (_b = entity.metadata.annotations) == null ? void 0 : _b[catalogModel.LOCATION_ANNOTATION];
2482
+ }
2483
+ if (!location) {
2484
+ return void 0;
2485
+ }
2486
+ const {type, target} = catalogModel.parseLocationReference(location);
2487
+ if (type === "url") {
2488
+ return target;
2489
+ } else if (type === "file") {
2490
+ return `file://${target}`;
2491
+ }
2492
+ return void 0;
2493
+ }
2494
+
2455
2495
  function isSupportedTemplate(entity) {
2456
2496
  return entity.apiVersion === "backstage.io/v1beta2" || entity.apiVersion === "scaffolder.backstage.io/v1beta3";
2457
2497
  }
@@ -2472,29 +2512,24 @@ async function createRouter(options) {
2472
2512
  const workingDirectory = await getWorkingDirectory(config, logger);
2473
2513
  const entityClient = new CatalogEntityClient(catalogClient);
2474
2514
  const integrations = integration.ScmIntegrations.fromConfig(config);
2475
- const databaseTaskStore = await DatabaseTaskStore.create(await database.getClient());
2476
- const taskBroker = new StorageTaskBroker(databaseTaskStore, logger);
2515
+ let taskBroker;
2516
+ if (!options.taskBroker) {
2517
+ const databaseTaskStore = await DatabaseTaskStore.create({
2518
+ database: await database.getClient()
2519
+ });
2520
+ taskBroker = new StorageTaskBroker(databaseTaskStore, logger);
2521
+ } else {
2522
+ taskBroker = options.taskBroker;
2523
+ }
2477
2524
  const actionRegistry = new TemplateActionRegistry();
2478
- const legacyWorkflowRunner = new LegacyWorkflowRunner({
2479
- logger,
2480
- actionRegistry,
2481
- integrations,
2482
- workingDirectory
2483
- });
2484
- const workflowRunner = new DefaultWorkflowRunner({
2485
- actionRegistry,
2486
- integrations,
2487
- logger,
2488
- workingDirectory
2489
- });
2490
2525
  const workers = [];
2491
2526
  for (let i = 0; i < (taskWorkers || 1); i++) {
2492
- const worker = new TaskWorker({
2527
+ const worker = await TaskWorker.create({
2493
2528
  taskBroker,
2494
- runners: {
2495
- legacyWorkflowRunner,
2496
- workflowRunner
2497
- }
2529
+ actionRegistry,
2530
+ integrations,
2531
+ logger,
2532
+ workingDirectory
2498
2533
  });
2499
2534
  workers.push(worker);
2500
2535
  }
@@ -2612,7 +2647,7 @@ async function createRouter(options) {
2612
2647
  "Cache-Control": "no-cache",
2613
2648
  "Content-Type": "text/event-stream"
2614
2649
  });
2615
- const unsubscribe = taskBroker.observe({taskId, after}, (error, {events}) => {
2650
+ const {unsubscribe} = taskBroker.observe({taskId, after}, (error, {events}) => {
2616
2651
  if (error) {
2617
2652
  logger.error(`Received error from event stream when observing taskId '${taskId}', ${error}`);
2618
2653
  }
@@ -2700,8 +2735,11 @@ Object.defineProperty(exports, 'createFetchCookiecutterAction', {
2700
2735
  }
2701
2736
  });
2702
2737
  exports.CatalogEntityClient = CatalogEntityClient;
2738
+ exports.DatabaseTaskStore = DatabaseTaskStore;
2703
2739
  exports.OctokitProvider = OctokitProvider;
2704
2740
  exports.ScaffolderEntitiesProcessor = ScaffolderEntitiesProcessor;
2741
+ exports.TaskManager = TaskManager;
2742
+ exports.TaskWorker = TaskWorker;
2705
2743
  exports.TemplateActionRegistry = TemplateActionRegistry;
2706
2744
  exports.createBuiltinActions = createBuiltinActions;
2707
2745
  exports.createCatalogRegisterAction = createCatalogRegisterAction;