@backstage/plugin-scaffolder-backend 0.15.21-next.0 → 0.15.23-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 CHANGED
@@ -1,5 +1,54 @@
1
1
  # @backstage/plugin-scaffolder-backend
2
2
 
3
+ ## 0.15.23-next.1
4
+
5
+ ### Patch Changes
6
+
7
+ - Updated dependencies
8
+ - @backstage/backend-common@0.10.6-next.0
9
+ - @backstage/plugin-catalog-backend@0.21.2-next.1
10
+ - @backstage/plugin-scaffolder-backend-module-cookiecutter@0.1.10-next.1
11
+
12
+ ## 0.15.23-next.0
13
+
14
+ ### Patch Changes
15
+
16
+ - 2e0dbb0e50: Migrate from deprecated package @octokit/rest to octokit
17
+ - Updated dependencies
18
+ - @backstage/plugin-catalog-backend@0.21.2-next.0
19
+ - @backstage/plugin-scaffolder-backend-module-cookiecutter@0.1.10-next.0
20
+
21
+ ## 0.15.22
22
+
23
+ ### Patch Changes
24
+
25
+ - b09dd8f43b: chore(deps): bump `@gitbeaker/node` from 34.6.0 to 35.1.0
26
+ - ac2f1eeec0: This change is for adding the option of inputs on the `github:actions:dispatch` Backstage Action. This will allow users to pass data from Backstage to the GitHub Action.
27
+ - 0d5e846a78: Expose a new option to provide additional template filters via `@backstage/scaffolder-backend`'s `createRouter()` function.
28
+ - Updated dependencies
29
+ - @backstage/plugin-catalog-backend@0.21.1
30
+ - @backstage/backend-common@0.10.5
31
+
32
+ ## 0.15.21
33
+
34
+ ### Patch Changes
35
+
36
+ - b05d303226: Added the ability to support supplying secrets when creating tasks in the `scaffolder-backend`.
37
+
38
+ **deprecation**: Deprecated `ctx.token` from actions in the `scaffolder-backend`. Please move to using `ctx.secrets.backstageToken` instead.
39
+
40
+ **deprecation**: Deprecated `task.token` in `TaskSpec` in the `scaffolder-backend`. Please move to using `task.secrets.backstageToken` instead.
41
+
42
+ - Updated dependencies
43
+ - @backstage/plugin-catalog-backend@0.21.0
44
+ - @backstage/integration@0.7.2
45
+ - @backstage/backend-common@0.10.4
46
+ - @backstage/config@0.1.13
47
+ - @backstage/catalog-model@0.9.10
48
+ - @backstage/catalog-client@0.5.5
49
+ - @backstage/plugin-scaffolder-backend-module-cookiecutter@0.1.9
50
+ - @backstage/plugin-scaffolder-common@0.1.3
51
+
3
52
  ## 0.15.21-next.0
4
53
 
5
54
  ### Patch Changes
package/dist/index.cjs.js CHANGED
@@ -17,7 +17,7 @@ var child_process = require('child_process');
17
17
  var stream = require('stream');
18
18
  var azureDevopsNodeApi = require('azure-devops-node-api');
19
19
  var fetch = require('node-fetch');
20
- var rest = require('@octokit/rest');
20
+ var octokit = require('octokit');
21
21
  var lodash = require('lodash');
22
22
  var octokitPluginCreatePullRequest = require('octokit-plugin-create-pull-request');
23
23
  var node = require('@gitbeaker/node');
@@ -119,6 +119,7 @@ function createCatalogRegisterAction(options) {
119
119
  }
120
120
  },
121
121
  async handler(ctx) {
122
+ var _a, _b;
122
123
  const { input } = ctx;
123
124
  let catalogInfoUrl;
124
125
  if ("catalogInfoUrl" in input) {
@@ -138,13 +139,13 @@ function createCatalogRegisterAction(options) {
138
139
  await catalogClient.addLocation({
139
140
  type: "url",
140
141
  target: catalogInfoUrl
141
- }, ctx.token ? { token: ctx.token } : {});
142
+ }, ((_a = ctx.secrets) == null ? void 0 : _a.backstageToken) ? { token: ctx.secrets.backstageToken } : {});
142
143
  try {
143
144
  const result = await catalogClient.addLocation({
144
145
  dryRun: true,
145
146
  type: "url",
146
147
  target: catalogInfoUrl
147
- }, ctx.token ? { token: ctx.token } : {});
148
+ }, ((_b = ctx.secrets) == null ? void 0 : _b.backstageToken) ? { token: ctx.secrets.backstageToken } : {});
148
149
  if (result.entities.length > 0) {
149
150
  const { entities } = result;
150
151
  let entity;
@@ -360,6 +361,12 @@ const { render, renderCompat } = (() => {
360
361
  });
361
362
  }
362
363
 
364
+ if (typeof additionalTemplateFilters !== 'undefined') {
365
+ for (const [filterName, filterFn] of Object.entries(additionalTemplateFilters)) {
366
+ env.addFilter(filterName, (...args) => JSON.parse(filterFn(...args)));
367
+ }
368
+ }
369
+
363
370
  let uninstallCompat = undefined;
364
371
 
365
372
  function render(str, values) {
@@ -392,12 +399,16 @@ const { render, renderCompat } = (() => {
392
399
  `;
393
400
  class SecureTemplater {
394
401
  static async loadRenderer(options = {}) {
395
- const { parseRepoUrl, cookiecutterCompat } = options;
396
- let sandbox = void 0;
402
+ const { parseRepoUrl, cookiecutterCompat, additionalTemplateFilters } = options;
403
+ const sandbox = {};
397
404
  if (parseRepoUrl) {
398
- sandbox = {
399
- parseRepoUrl: (url) => JSON.stringify(parseRepoUrl(url))
400
- };
405
+ sandbox.parseRepoUrl = (url) => JSON.stringify(parseRepoUrl(url));
406
+ }
407
+ if (additionalTemplateFilters) {
408
+ sandbox.additionalTemplateFilters = Object.fromEntries(Object.entries(additionalTemplateFilters).filter(([_, filterFunction]) => !!filterFunction).map(([filterName, filterFunction]) => [
409
+ filterName,
410
+ (...args) => JSON.stringify(filterFunction(...args))
411
+ ]));
401
412
  }
402
413
  const vm = new vm2.VM({ sandbox });
403
414
  const nunjucksSource = await fs__default["default"].readFile(backendCommon.resolvePackagePath("@backstage/plugin-scaffolder-backend", "assets/nunjucks.js.txt"), "utf-8");
@@ -418,7 +429,7 @@ class SecureTemplater {
418
429
  }
419
430
 
420
431
  function createFetchTemplateAction(options) {
421
- const { reader, integrations } = options;
432
+ const { reader, integrations, additionalTemplateFilters } = options;
422
433
  return createTemplateAction({
423
434
  id: "fetch:template",
424
435
  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.",
@@ -509,7 +520,8 @@ function createFetchTemplateAction(options) {
509
520
  };
510
521
  ctx.logger.info(`Processing ${allEntriesInTemplate.length} template files/directories with input values`, ctx.input.values);
511
522
  const renderTemplate = await SecureTemplater.loadRenderer({
512
- cookiecutterCompat: ctx.input.cookiecutterCompat
523
+ cookiecutterCompat: ctx.input.cookiecutterCompat,
524
+ additionalTemplateFilters
513
525
  });
514
526
  for (const location of allEntriesInTemplate) {
515
527
  let renderFilename;
@@ -729,7 +741,7 @@ const enableBranchProtectionOnDefaultRepoBranch = async ({
729
741
  }) => {
730
742
  const tryOnce = async () => {
731
743
  try {
732
- await client.repos.updateBranchProtection({
744
+ await client.rest.repos.updateBranchProtection({
733
745
  mediaType: {
734
746
  previews: ["luke-cage-preview"]
735
747
  },
@@ -1176,7 +1188,7 @@ class OctokitProvider {
1176
1188
  if (!token) {
1177
1189
  throw new errors.InputError(`No token available for host: ${host}, with owner ${owner}, and repo ${repo}`);
1178
1190
  }
1179
- const client = new rest.Octokit({
1191
+ const client = new octokit.Octokit({
1180
1192
  auth: token,
1181
1193
  baseUrl: integrationConfig.apiBaseUrl,
1182
1194
  previews: ["nebula-preview"]
@@ -1283,16 +1295,16 @@ function createPublishGithubAction(options) {
1283
1295
  topics
1284
1296
  } = ctx.input;
1285
1297
  const { client, token, owner, repo } = await octokitProvider.getOctokit(repoUrl);
1286
- const user = await client.users.getByUsername({
1298
+ const user = await client.rest.users.getByUsername({
1287
1299
  username: owner
1288
1300
  });
1289
- const repoCreationPromise = user.data.type === "Organization" ? client.repos.createInOrg({
1301
+ const repoCreationPromise = user.data.type === "Organization" ? client.rest.repos.createInOrg({
1290
1302
  name: repo,
1291
1303
  org: owner,
1292
1304
  private: repoVisibility === "private",
1293
1305
  visibility: repoVisibility,
1294
1306
  description
1295
- }) : client.repos.createForAuthenticatedUser({
1307
+ }) : client.rest.repos.createForAuthenticatedUser({
1296
1308
  name: repo,
1297
1309
  private: repoVisibility === "private",
1298
1310
  description
@@ -1300,7 +1312,7 @@ function createPublishGithubAction(options) {
1300
1312
  const { data: newRepo } = await repoCreationPromise;
1301
1313
  if (access == null ? void 0 : access.startsWith(`${owner}/`)) {
1302
1314
  const [, team] = access.split("/");
1303
- await client.teams.addOrUpdateRepoPermissionsInOrg({
1315
+ await client.rest.teams.addOrUpdateRepoPermissionsInOrg({
1304
1316
  org: owner,
1305
1317
  team_slug: team,
1306
1318
  owner,
@@ -1308,7 +1320,7 @@ function createPublishGithubAction(options) {
1308
1320
  permission: "admin"
1309
1321
  });
1310
1322
  } else if (access && access !== owner) {
1311
- await client.repos.addCollaborator({
1323
+ await client.rest.repos.addCollaborator({
1312
1324
  owner,
1313
1325
  repo,
1314
1326
  username: access,
@@ -1321,7 +1333,7 @@ function createPublishGithubAction(options) {
1321
1333
  username: team_slug
1322
1334
  } of collaborators) {
1323
1335
  try {
1324
- await client.teams.addOrUpdateRepoPermissionsInOrg({
1336
+ await client.rest.teams.addOrUpdateRepoPermissionsInOrg({
1325
1337
  org: owner,
1326
1338
  team_slug,
1327
1339
  owner,
@@ -1336,7 +1348,7 @@ function createPublishGithubAction(options) {
1336
1348
  }
1337
1349
  if (topics) {
1338
1350
  try {
1339
- await client.repos.replaceAllTopics({
1351
+ await client.rest.repos.replaceAllTopics({
1340
1352
  owner,
1341
1353
  repo,
1342
1354
  names: topics.map((t) => t.toLowerCase())
@@ -1404,7 +1416,7 @@ const defaultClientFactory = async ({
1404
1416
  if (!token) {
1405
1417
  throw new errors.InputError(`No token available for host: ${host}, with owner ${owner}, and repo ${repo}`);
1406
1418
  }
1407
- const OctokitPR = rest.Octokit.plugin(octokitPluginCreatePullRequest.createPullRequest);
1419
+ const OctokitPR = octokit.Octokit.plugin(octokitPluginCreatePullRequest.createPullRequest);
1408
1420
  return new OctokitPR({
1409
1421
  auth: token,
1410
1422
  baseUrl: integrationConfig.apiBaseUrl
@@ -1771,19 +1783,25 @@ function createGithubActionsDispatchAction(options) {
1771
1783
  title: "Branch or Tag name",
1772
1784
  description: "The git branch or tag name used to dispatch the workflow",
1773
1785
  type: "string"
1786
+ },
1787
+ workflowInputs: {
1788
+ title: "Workflow Inputs",
1789
+ description: "Inputs keys and values to send to GitHub Action configured on the workflow file. The maximum number of properties is 10. ",
1790
+ type: "object"
1774
1791
  }
1775
1792
  }
1776
1793
  }
1777
1794
  },
1778
1795
  async handler(ctx) {
1779
- const { repoUrl, workflowId, branchOrTagName } = ctx.input;
1796
+ const { repoUrl, workflowId, branchOrTagName, workflowInputs } = ctx.input;
1780
1797
  ctx.logger.info(`Dispatching workflow ${workflowId} for repo ${repoUrl} on ${branchOrTagName}`);
1781
1798
  const { client, owner, repo } = await octokitProvider.getOctokit(repoUrl);
1782
1799
  await client.rest.actions.createWorkflowDispatch({
1783
1800
  owner,
1784
1801
  repo,
1785
1802
  workflow_id: workflowId,
1786
- ref: branchOrTagName
1803
+ ref: branchOrTagName,
1804
+ inputs: workflowInputs
1787
1805
  });
1788
1806
  ctx.logger.info(`Workflow ${workflowId} dispatched successfully`);
1789
1807
  }
@@ -1869,7 +1887,7 @@ function createGithubWebhookAction(options) {
1869
1887
  const { client, owner, repo } = await octokitProvider.getOctokit(repoUrl);
1870
1888
  try {
1871
1889
  const insecure_ssl = insecureSsl ? "1" : "0";
1872
- await client.repos.createWebhook({
1890
+ await client.rest.repos.createWebhook({
1873
1891
  owner,
1874
1892
  repo,
1875
1893
  config: {
@@ -1891,7 +1909,14 @@ function createGithubWebhookAction(options) {
1891
1909
  }
1892
1910
 
1893
1911
  const createBuiltinActions = (options) => {
1894
- const { reader, integrations, containerRunner, catalogClient, config } = options;
1912
+ const {
1913
+ reader,
1914
+ integrations,
1915
+ containerRunner,
1916
+ catalogClient,
1917
+ config,
1918
+ additionalTemplateFilters
1919
+ } = options;
1895
1920
  const githubCredentialsProvider = integration.DefaultGithubCredentialsProvider.fromIntegrations(integrations);
1896
1921
  const actions = [
1897
1922
  createFetchPlainAction({
@@ -1900,7 +1925,8 @@ const createBuiltinActions = (options) => {
1900
1925
  }),
1901
1926
  createFetchTemplateAction({
1902
1927
  integrations,
1903
- reader
1928
+ reader,
1929
+ additionalTemplateFilters
1904
1930
  }),
1905
1931
  createPublishGithubAction({
1906
1932
  integrations,
@@ -2023,7 +2049,8 @@ class DatabaseTaskStore {
2023
2049
  }
2024
2050
  const updateCount = await tx("tasks").where({ id: task.id, status: "open" }).update({
2025
2051
  status: "processing",
2026
- last_heartbeat_at: this.db.fn.now()
2052
+ last_heartbeat_at: this.db.fn.now(),
2053
+ secrets: null
2027
2054
  });
2028
2055
  if (updateCount < 1) {
2029
2056
  return void 0;
@@ -2087,8 +2114,7 @@ class DatabaseTaskStore {
2087
2114
  id: taskId,
2088
2115
  status: oldStatus
2089
2116
  }).update({
2090
- status,
2091
- secrets: null
2117
+ status
2092
2118
  });
2093
2119
  if (updateCount !== 1) {
2094
2120
  throw new errors.ConflictError(`Failed to update status to '${status}' for taskId ${taskId}`);
@@ -2301,7 +2327,7 @@ class HandlebarsWorkflowRunner {
2301
2327
  this.handlebars.registerHelper("eq", (a, b) => a === b);
2302
2328
  }
2303
2329
  async execute(task) {
2304
- var _a, _b;
2330
+ var _a, _b, _c;
2305
2331
  if (!isValidTaskSpec$1(task.spec)) {
2306
2332
  throw new errors.InputError(`Task spec is not a valid v1beta2 task spec`);
2307
2333
  }
@@ -2405,6 +2431,7 @@ class HandlebarsWorkflowRunner {
2405
2431
  logStream: stream$1,
2406
2432
  input,
2407
2433
  token: (_b = task.secrets) == null ? void 0 : _b.token,
2434
+ secrets: (_c = task.secrets) != null ? _c : {},
2408
2435
  workspacePath,
2409
2436
  async createTemporaryDirectory() {
2410
2437
  const tmpDir = await fs__default["default"].mkdtemp(`${workspacePath}_step-${step.id}-`);
@@ -2530,7 +2557,7 @@ class NunjucksWorkflowRunner {
2530
2557
  });
2531
2558
  }
2532
2559
  async execute(task) {
2533
- var _a, _b, _c;
2560
+ var _a, _b, _c, _d;
2534
2561
  if (!isValidTaskSpec(task.spec)) {
2535
2562
  throw new errors.InputError("Wrong template version executed with the workflow engine");
2536
2563
  }
@@ -2539,7 +2566,8 @@ class NunjucksWorkflowRunner {
2539
2566
  const renderTemplate = await SecureTemplater.loadRenderer({
2540
2567
  parseRepoUrl(url) {
2541
2568
  return parseRepoUrl(url, integrations);
2542
- }
2569
+ },
2570
+ additionalTemplateFilters: this.options.additionalTemplateFilters
2543
2571
  });
2544
2572
  try {
2545
2573
  await fs__default["default"].ensureDir(workspacePath);
@@ -2580,6 +2608,7 @@ class NunjucksWorkflowRunner {
2580
2608
  baseUrl: task.spec.baseUrl,
2581
2609
  input,
2582
2610
  token: (_c = task.secrets) == null ? void 0 : _c.token,
2611
+ secrets: (_d = task.secrets) != null ? _d : {},
2583
2612
  logger: taskLogger,
2584
2613
  logStream: streamLogger,
2585
2614
  workspacePath,
@@ -2629,7 +2658,8 @@ class TaskWorker {
2629
2658
  logger,
2630
2659
  actionRegistry,
2631
2660
  integrations,
2632
- workingDirectory
2661
+ workingDirectory,
2662
+ additionalTemplateFilters
2633
2663
  } = options;
2634
2664
  const legacyWorkflowRunner = new HandlebarsWorkflowRunner({
2635
2665
  logger,
@@ -2641,7 +2671,8 @@ class TaskWorker {
2641
2671
  actionRegistry,
2642
2672
  integrations,
2643
2673
  logger,
2644
- workingDirectory
2674
+ workingDirectory,
2675
+ additionalTemplateFilters
2645
2676
  });
2646
2677
  return new TaskWorker({
2647
2678
  taskBroker,
@@ -2738,7 +2769,8 @@ async function createRouter(options) {
2738
2769
  catalogClient,
2739
2770
  actions,
2740
2771
  containerRunner,
2741
- taskWorkers
2772
+ taskWorkers,
2773
+ additionalTemplateFilters
2742
2774
  } = options;
2743
2775
  const logger = parentLogger.child({ plugin: "scaffolder" });
2744
2776
  const workingDirectory = await getWorkingDirectory(config, logger);
@@ -2761,7 +2793,8 @@ async function createRouter(options) {
2761
2793
  actionRegistry,
2762
2794
  integrations,
2763
2795
  logger,
2764
- workingDirectory
2796
+ workingDirectory,
2797
+ additionalTemplateFilters
2765
2798
  });
2766
2799
  workers.push(worker);
2767
2800
  }
@@ -2770,7 +2803,8 @@ async function createRouter(options) {
2770
2803
  catalogClient,
2771
2804
  containerRunner,
2772
2805
  reader,
2773
- config
2806
+ config,
2807
+ additionalTemplateFilters
2774
2808
  });
2775
2809
  actionsToRegister.forEach((action) => actionRegistry.register(action));
2776
2810
  workers.forEach((worker) => worker.start());
@@ -2861,6 +2895,8 @@ async function createRouter(options) {
2861
2895
  throw new errors.InputError(`Unsupported apiVersion field in schema entity, ${template.apiVersion}`);
2862
2896
  }
2863
2897
  const result = await taskBroker.dispatch(taskSpec, {
2898
+ ...req.body.secrets,
2899
+ backstageToken: token,
2864
2900
  token
2865
2901
  });
2866
2902
  res.status(201).json({ id: result.taskId });