@backstage/plugin-scaffolder-backend 1.22.11 → 1.23.0-next.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -6,7 +6,7 @@ var config = require('@backstage/config');
6
6
  var errors = require('@backstage/errors');
7
7
  var integration = require('@backstage/integration');
8
8
  var pluginScaffolderCommon = require('@backstage/plugin-scaffolder-common');
9
- var alpha = require('@backstage/plugin-scaffolder-common/alpha');
9
+ var alpha$1 = require('@backstage/plugin-scaffolder-common/alpha');
10
10
  var express = require('express');
11
11
  var Router = require('express-promise-router');
12
12
  var jsonschema = require('jsonschema');
@@ -30,15 +30,13 @@ var gerrit = require('@backstage/plugin-scaffolder-backend-module-gerrit');
30
30
  var gitlab = require('@backstage/plugin-scaffolder-backend-module-gitlab');
31
31
  var pluginScaffolderBackendModuleGitea = require('@backstage/plugin-scaffolder-backend-module-gitea');
32
32
  var uuid = require('uuid');
33
- var tar = require('tar');
34
- var concatStream = require('concat-stream');
35
- var util = require('util');
36
- var stream = require('stream');
33
+ var alpha = require('@backstage/plugin-scaffolder-node/alpha');
37
34
  var ObservableImpl = require('zen-observable');
38
35
  var lodash = require('lodash');
39
36
  var PQueue = require('p-queue');
40
37
  var winston = require('winston');
41
38
  var nunjucks = require('nunjucks');
39
+ var stream = require('stream');
42
40
  var pluginPermissionNode = require('@backstage/plugin-permission-node');
43
41
  var promClient = require('prom-client');
44
42
  var pluginPermissionCommon = require('@backstage/plugin-permission-common');
@@ -74,8 +72,6 @@ var fs__default = /*#__PURE__*/_interopDefaultCompat(fs);
74
72
  var path__default = /*#__PURE__*/_interopDefaultCompat(path);
75
73
  var globby__default = /*#__PURE__*/_interopDefaultCompat(globby);
76
74
  var get__default = /*#__PURE__*/_interopDefaultCompat(get);
77
- var tar__default = /*#__PURE__*/_interopDefaultCompat(tar);
78
- var concatStream__default = /*#__PURE__*/_interopDefaultCompat(concatStream);
79
75
  var ObservableImpl__default = /*#__PURE__*/_interopDefaultCompat(ObservableImpl);
80
76
  var PQueue__default = /*#__PURE__*/_interopDefaultCompat(PQueue);
81
77
  var winston__namespace = /*#__PURE__*/_interopNamespaceCompat(winston);
@@ -1509,23 +1505,6 @@ const intervalFromNowTill = (timeoutS, knex) => {
1509
1505
  return heartbeatInterval;
1510
1506
  };
1511
1507
 
1512
- const pipeline = util.promisify(stream.pipeline);
1513
- const serializeWorkspace = async (path) => {
1514
- return await new Promise(async (resolve) => {
1515
- await pipeline(tar__default.default.create({ cwd: path }, [""]), concatStream__default.default(resolve));
1516
- });
1517
- };
1518
- const restoreWorkspace = async (path, buffer) => {
1519
- if (buffer) {
1520
- await pipeline(
1521
- stream.Readable.from(buffer),
1522
- tar__default.default.extract({
1523
- C: path
1524
- })
1525
- );
1526
- }
1527
- };
1528
-
1529
1508
  const migrationsDir = backendPluginApi.resolvePackagePath(
1530
1509
  "@backstage/plugin-scaffolder-backend",
1531
1510
  "migrations"
@@ -1837,7 +1816,10 @@ class DatabaseTaskStore {
1837
1816
  }
1838
1817
  async rehydrateWorkspace(options) {
1839
1818
  const [result] = await this.db("tasks").where({ id: options.taskId }).select("workspace");
1840
- await restoreWorkspace(options.targetPath, result.workspace);
1819
+ await alpha.restoreWorkspace({
1820
+ path: options.targetPath,
1821
+ buffer: result.workspace
1822
+ });
1841
1823
  }
1842
1824
  async cleanWorkspace({ taskId }) {
1843
1825
  await this.db("tasks").where({ id: taskId }).update({
@@ -1847,7 +1829,7 @@ class DatabaseTaskStore {
1847
1829
  async serializeWorkspace(options) {
1848
1830
  if (options.path) {
1849
1831
  await this.db("tasks").where({ id: options.taskId }).update({
1850
- workspace: await serializeWorkspace(options.path)
1832
+ workspace: (await alpha.serializeWorkspace(options)).contents
1851
1833
  });
1852
1834
  }
1853
1835
  }
@@ -1925,26 +1907,88 @@ const readDuration$1 = (config$1, key, defaultValue) => {
1925
1907
  return defaultValue;
1926
1908
  };
1927
1909
 
1910
+ class DatabaseWorkspaceProvider {
1911
+ constructor(storage) {
1912
+ this.storage = storage;
1913
+ }
1914
+ static create(storage) {
1915
+ return new DatabaseWorkspaceProvider(storage);
1916
+ }
1917
+ async serializeWorkspace(options) {
1918
+ this.storage.serializeWorkspace?.(options);
1919
+ }
1920
+ async rehydrateWorkspace(options) {
1921
+ return this.storage.rehydrateWorkspace?.(options);
1922
+ }
1923
+ async cleanWorkspace(options) {
1924
+ return this.storage.cleanWorkspace?.(options);
1925
+ }
1926
+ }
1927
+
1928
+ class DefaultWorkspaceService {
1929
+ constructor(task, workspaceProvider, config) {
1930
+ this.task = task;
1931
+ this.workspaceProvider = workspaceProvider;
1932
+ this.config = config;
1933
+ }
1934
+ static create(task, storage, additionalWorkspaceProviders, config) {
1935
+ const workspaceProviderName = config?.getOptionalString(
1936
+ "scaffolder.EXPERIMENTAL_workspaceSerializationProvider"
1937
+ ) ?? "database";
1938
+ const workspaceProvider = additionalWorkspaceProviders?.[workspaceProviderName] ?? DatabaseWorkspaceProvider.create(storage);
1939
+ return new DefaultWorkspaceService(task, workspaceProvider, config);
1940
+ }
1941
+ async serializeWorkspace(options) {
1942
+ if (this.isWorkspaceSerializationEnabled()) {
1943
+ await this.workspaceProvider.serializeWorkspace({
1944
+ path: options.path,
1945
+ taskId: this.task.taskId
1946
+ });
1947
+ }
1948
+ }
1949
+ async cleanWorkspace() {
1950
+ if (this.isWorkspaceSerializationEnabled()) {
1951
+ await this.workspaceProvider.cleanWorkspace({ taskId: this.task.taskId });
1952
+ }
1953
+ }
1954
+ async rehydrateWorkspace(options) {
1955
+ if (this.isWorkspaceSerializationEnabled()) {
1956
+ await this.workspaceProvider.rehydrateWorkspace(options);
1957
+ }
1958
+ }
1959
+ isWorkspaceSerializationEnabled() {
1960
+ return this.config?.getOptionalBoolean(
1961
+ "scaffolder.EXPERIMENTAL_workspaceSerialization"
1962
+ ) ?? false;
1963
+ }
1964
+ }
1965
+
1928
1966
  class TaskManager {
1929
1967
  // Runs heartbeat internally
1930
- constructor(task, storage, signal, logger, auth, config) {
1968
+ constructor(task, storage, signal, logger, workspaceService, auth) {
1931
1969
  this.task = task;
1932
1970
  this.storage = storage;
1933
1971
  this.signal = signal;
1934
1972
  this.logger = logger;
1973
+ this.workspaceService = workspaceService;
1935
1974
  this.auth = auth;
1936
- this.config = config;
1937
1975
  }
1938
1976
  isDone = false;
1939
1977
  heartbeatTimeoutId;
1940
- static create(task, storage, abortSignal, logger, auth, config) {
1978
+ static create(task, storage, abortSignal, logger, auth, config, additionalWorkspaceProviders) {
1979
+ const workspaceService = DefaultWorkspaceService.create(
1980
+ task,
1981
+ storage,
1982
+ additionalWorkspaceProviders,
1983
+ config
1984
+ );
1941
1985
  const agent = new TaskManager(
1942
1986
  task,
1943
1987
  storage,
1944
1988
  abortSignal,
1945
1989
  logger,
1946
- auth,
1947
- config
1990
+ workspaceService,
1991
+ auth
1948
1992
  );
1949
1993
  agent.startTimeout();
1950
1994
  return agent;
@@ -1965,9 +2009,7 @@ class TaskManager {
1965
2009
  return this.task.taskId;
1966
2010
  }
1967
2011
  async rehydrateWorkspace(options) {
1968
- if (this.isWorkspaceSerializationEnabled()) {
1969
- this.storage.rehydrateWorkspace?.(options);
1970
- }
2012
+ await this.workspaceService.rehydrateWorkspace(options);
1971
2013
  }
1972
2014
  get done() {
1973
2015
  return this.isDone;
@@ -1994,17 +2036,10 @@ class TaskManager {
1994
2036
  });
1995
2037
  }
1996
2038
  async serializeWorkspace(options) {
1997
- if (this.isWorkspaceSerializationEnabled()) {
1998
- await this.storage.serializeWorkspace?.({
1999
- path: options.path,
2000
- taskId: this.task.taskId
2001
- });
2002
- }
2039
+ await this.workspaceService.serializeWorkspace(options);
2003
2040
  }
2004
2041
  async cleanWorkspace() {
2005
- if (this.isWorkspaceSerializationEnabled()) {
2006
- await this.storage.cleanWorkspace?.({ taskId: this.task.taskId });
2007
- }
2042
+ await this.workspaceService.cleanWorkspace();
2008
2043
  }
2009
2044
  async complete(result, metadata) {
2010
2045
  await this.storage.completeTask({
@@ -2034,11 +2069,6 @@ class TaskManager {
2034
2069
  }
2035
2070
  }, 1e3);
2036
2071
  }
2037
- isWorkspaceSerializationEnabled() {
2038
- return this.config?.getOptionalBoolean(
2039
- "scaffolder.EXPERIMENTAL_workspaceSerialization"
2040
- ) ?? false;
2041
- }
2042
2072
  async getInitiatorCredentials() {
2043
2073
  const secrets = this.task.secrets;
2044
2074
  if (secrets && secrets.__initiatorCredentials) {
@@ -2061,11 +2091,12 @@ function defer() {
2061
2091
  return { promise, resolve };
2062
2092
  }
2063
2093
  class StorageTaskBroker {
2064
- constructor(storage, logger, config, auth) {
2094
+ constructor(storage, logger, config, auth, additionalWorkspaceProviders) {
2065
2095
  this.storage = storage;
2066
2096
  this.logger = logger;
2067
2097
  this.config = config;
2068
2098
  this.auth = auth;
2099
+ this.additionalWorkspaceProviders = additionalWorkspaceProviders;
2069
2100
  }
2070
2101
  async list(options) {
2071
2102
  if (!this.storage.list) {
@@ -2138,7 +2169,8 @@ class StorageTaskBroker {
2138
2169
  abortController.signal,
2139
2170
  this.logger,
2140
2171
  this.auth,
2141
- this.config
2172
+ this.config,
2173
+ this.additionalWorkspaceProviders
2142
2174
  );
2143
2175
  }
2144
2176
  await this.waitForDispatch();
@@ -2246,7 +2278,7 @@ function createHistogramMetric(config) {
2246
2278
  const createTemplatePermissionRule = pluginPermissionNode.makeCreatePermissionRule();
2247
2279
  const hasTag = createTemplatePermissionRule({
2248
2280
  name: "HAS_TAG",
2249
- resourceType: alpha.RESOURCE_TYPE_SCAFFOLDER_TEMPLATE,
2281
+ resourceType: alpha$1.RESOURCE_TYPE_SCAFFOLDER_TEMPLATE,
2250
2282
  description: `Match parameters or steps with the given tag`,
2251
2283
  paramsSchema: zod.z.object({
2252
2284
  tag: zod.z.string().describe("Name of the tag to match on")
@@ -2259,7 +2291,7 @@ const hasTag = createTemplatePermissionRule({
2259
2291
  const createActionPermissionRule = pluginPermissionNode.makeCreatePermissionRule();
2260
2292
  const hasActionId = createActionPermissionRule({
2261
2293
  name: "HAS_ACTION_ID",
2262
- resourceType: alpha.RESOURCE_TYPE_SCAFFOLDER_ACTION,
2294
+ resourceType: alpha$1.RESOURCE_TYPE_SCAFFOLDER_ACTION,
2263
2295
  description: `Match actions with the given actionId`,
2264
2296
  paramsSchema: zod.z.object({
2265
2297
  actionId: zod.z.string().describe("Name of the actionId to match on")
@@ -2294,7 +2326,7 @@ function buildHasProperty({
2294
2326
  return createActionPermissionRule({
2295
2327
  name,
2296
2328
  description: `Allow actions with the specified property`,
2297
- resourceType: alpha.RESOURCE_TYPE_SCAFFOLDER_ACTION,
2329
+ resourceType: alpha$1.RESOURCE_TYPE_SCAFFOLDER_ACTION,
2298
2330
  paramsSchema: zod.z.object({
2299
2331
  key: zod.z.string().describe(`Property within the action parameters to match on`),
2300
2332
  value: valueSchema.optional().describe(`Value of the given property to match on`)
@@ -2722,7 +2754,6 @@ class NunjucksWorkflowRunner {
2722
2754
  if (task.cancelSignal.aborted) {
2723
2755
  throw new Error(`Step ${step.name} has been cancelled.`);
2724
2756
  }
2725
- await task.cleanWorkspace?.();
2726
2757
  await stepTrack.markSuccessful();
2727
2758
  } catch (err) {
2728
2759
  await taskTrack.markFailed(step, err);
@@ -2758,7 +2789,7 @@ class NunjucksWorkflowRunner {
2758
2789
  user: task.spec.user
2759
2790
  };
2760
2791
  const [decision] = this.options.permissions && task.spec.steps.length ? await this.options.permissions.authorizeConditional(
2761
- [{ permission: alpha.actionExecutePermission }],
2792
+ [{ permission: alpha$1.actionExecutePermission }],
2762
2793
  { credentials: await task.getInitiatorCredentials() }
2763
2794
  ) : [{ result: pluginPermissionCommon.AuthorizeResult.ALLOW }];
2764
2795
  for (const step of task.spec.steps) {
@@ -2777,6 +2808,7 @@ class NunjucksWorkflowRunner {
2777
2808
  return { output };
2778
2809
  } finally {
2779
2810
  if (workspacePath) {
2811
+ await task.cleanWorkspace?.();
2780
2812
  await fs__default.default.remove(workspacePath);
2781
2813
  }
2782
2814
  }
@@ -3175,10 +3207,10 @@ async function checkPermission(options) {
3175
3207
  }
3176
3208
 
3177
3209
  function isTemplatePermissionRuleInput(permissionRule) {
3178
- return permissionRule.resourceType === alpha.RESOURCE_TYPE_SCAFFOLDER_TEMPLATE;
3210
+ return permissionRule.resourceType === alpha$1.RESOURCE_TYPE_SCAFFOLDER_TEMPLATE;
3179
3211
  }
3180
3212
  function isActionPermissionRuleInput(permissionRule) {
3181
- return permissionRule.resourceType === alpha.RESOURCE_TYPE_SCAFFOLDER_ACTION;
3213
+ return permissionRule.resourceType === alpha$1.RESOURCE_TYPE_SCAFFOLDER_ACTION;
3182
3214
  }
3183
3215
  function isSupportedTemplate(entity) {
3184
3216
  return entity.apiVersion === "scaffolder.backstage.io/v1beta3";
@@ -3246,10 +3278,12 @@ async function createRouter(options) {
3246
3278
  scheduler,
3247
3279
  additionalTemplateFilters,
3248
3280
  additionalTemplateGlobals,
3281
+ additionalWorkspaceProviders,
3249
3282
  permissions,
3250
3283
  permissionRules,
3251
3284
  discovery = backendCommon.HostDiscovery.fromConfig(config),
3252
- identity = buildDefaultIdentityClient(options)
3285
+ identity = buildDefaultIdentityClient(options),
3286
+ autocompleteHandlers = {}
3253
3287
  } = options;
3254
3288
  const { auth, httpAuth } = backendCommon.createLegacyAuthAdapters({
3255
3289
  ...options,
@@ -3263,7 +3297,13 @@ async function createRouter(options) {
3263
3297
  let taskBroker;
3264
3298
  if (!options.taskBroker) {
3265
3299
  const databaseTaskStore = await DatabaseTaskStore.create({ database });
3266
- taskBroker = new StorageTaskBroker(databaseTaskStore, logger, config, auth);
3300
+ taskBroker = new StorageTaskBroker(
3301
+ databaseTaskStore,
3302
+ logger,
3303
+ config,
3304
+ auth,
3305
+ additionalWorkspaceProviders
3306
+ );
3267
3307
  if (scheduler && databaseTaskStore.listStaleTasks) {
3268
3308
  await scheduler.scheduleTask({
3269
3309
  id: "close_stale_tasks",
@@ -3356,17 +3396,17 @@ async function createRouter(options) {
3356
3396
  const permissionIntegrationRouter = pluginPermissionNode.createPermissionIntegrationRouter({
3357
3397
  resources: [
3358
3398
  {
3359
- resourceType: alpha.RESOURCE_TYPE_SCAFFOLDER_TEMPLATE,
3360
- permissions: alpha.scaffolderTemplatePermissions,
3399
+ resourceType: alpha$1.RESOURCE_TYPE_SCAFFOLDER_TEMPLATE,
3400
+ permissions: alpha$1.scaffolderTemplatePermissions,
3361
3401
  rules: templateRules
3362
3402
  },
3363
3403
  {
3364
- resourceType: alpha.RESOURCE_TYPE_SCAFFOLDER_ACTION,
3365
- permissions: alpha.scaffolderActionPermissions,
3404
+ resourceType: alpha$1.RESOURCE_TYPE_SCAFFOLDER_ACTION,
3405
+ permissions: alpha$1.scaffolderActionPermissions,
3366
3406
  rules: actionRules
3367
3407
  }
3368
3408
  ],
3369
- permissions: alpha.scaffolderTaskPermissions
3409
+ permissions: alpha$1.scaffolderTaskPermissions
3370
3410
  });
3371
3411
  router.use(permissionIntegrationRouter);
3372
3412
  router.get(
@@ -3414,7 +3454,7 @@ async function createRouter(options) {
3414
3454
  const credentials = await httpAuth.credentials(req);
3415
3455
  await checkPermission({
3416
3456
  credentials,
3417
- permissions: [alpha.taskCreatePermission],
3457
+ permissions: [alpha$1.taskCreatePermission],
3418
3458
  permissionService: permissions
3419
3459
  });
3420
3460
  const { token } = await auth.getPluginRequestToken({
@@ -3479,7 +3519,7 @@ async function createRouter(options) {
3479
3519
  const credentials = await httpAuth.credentials(req);
3480
3520
  await checkPermission({
3481
3521
  credentials,
3482
- permissions: [alpha.taskReadPermission],
3522
+ permissions: [alpha$1.taskReadPermission],
3483
3523
  permissionService: permissions
3484
3524
  });
3485
3525
  const [userEntityRef] = [req.query.createdBy].flat();
@@ -3499,7 +3539,7 @@ async function createRouter(options) {
3499
3539
  const credentials = await httpAuth.credentials(req);
3500
3540
  await checkPermission({
3501
3541
  credentials,
3502
- permissions: [alpha.taskReadPermission],
3542
+ permissions: [alpha$1.taskReadPermission],
3503
3543
  permissionService: permissions
3504
3544
  });
3505
3545
  const { taskId } = req.params;
@@ -3513,7 +3553,7 @@ async function createRouter(options) {
3513
3553
  const credentials = await httpAuth.credentials(req);
3514
3554
  await checkPermission({
3515
3555
  credentials,
3516
- permissions: [alpha.taskCancelPermission, alpha.taskReadPermission],
3556
+ permissions: [alpha$1.taskCancelPermission, alpha$1.taskReadPermission],
3517
3557
  permissionService: permissions
3518
3558
  });
3519
3559
  const { taskId } = req.params;
@@ -3523,7 +3563,7 @@ async function createRouter(options) {
3523
3563
  const credentials = await httpAuth.credentials(req);
3524
3564
  await checkPermission({
3525
3565
  credentials,
3526
- permissions: [alpha.taskReadPermission],
3566
+ permissions: [alpha$1.taskReadPermission],
3527
3567
  permissionService: permissions
3528
3568
  });
3529
3569
  const { taskId } = req.params;
@@ -3569,7 +3609,7 @@ data: ${JSON.stringify(event)}
3569
3609
  const credentials = await httpAuth.credentials(req);
3570
3610
  await checkPermission({
3571
3611
  credentials,
3572
- permissions: [alpha.taskReadPermission],
3612
+ permissions: [alpha$1.taskReadPermission],
3573
3613
  permissionService: permissions
3574
3614
  });
3575
3615
  const { taskId } = req.params;
@@ -3597,7 +3637,7 @@ data: ${JSON.stringify(event)}
3597
3637
  const credentials = await httpAuth.credentials(req);
3598
3638
  await checkPermission({
3599
3639
  credentials,
3600
- permissions: [alpha.taskCreatePermission],
3640
+ permissions: [alpha$1.taskCreatePermission],
3601
3641
  permissionService: permissions
3602
3642
  });
3603
3643
  const bodySchema = zod.z.object({
@@ -3619,6 +3659,8 @@ data: ${JSON.stringify(event)}
3619
3659
  onBehalfOf: credentials,
3620
3660
  targetPluginId: "catalog"
3621
3661
  });
3662
+ const userEntityRef = auth.isPrincipal(credentials, "user") ? credentials.principal.userEntityRef : void 0;
3663
+ const userEntity = userEntityRef ? await catalogClient.getEntityByRef(userEntityRef, { token }) : void 0;
3622
3664
  for (const parameters of [template.spec.parameters ?? []].flat()) {
3623
3665
  const result2 = jsonschema.validate(body.values, parameters);
3624
3666
  if (!result2.valid) {
@@ -3636,7 +3678,11 @@ data: ${JSON.stringify(event)}
3636
3678
  apiVersion: template.apiVersion,
3637
3679
  steps,
3638
3680
  output: template.spec.output ?? {},
3639
- parameters: body.values
3681
+ parameters: body.values,
3682
+ user: {
3683
+ entity: userEntity,
3684
+ ref: userEntityRef
3685
+ }
3640
3686
  },
3641
3687
  directoryContents: (body.directoryContents ?? []).map((file) => ({
3642
3688
  path: file.path,
@@ -3657,6 +3703,19 @@ data: ${JSON.stringify(event)}
3657
3703
  base64Content: file.content.toString("base64")
3658
3704
  }))
3659
3705
  });
3706
+ }).post("/v2/autocomplete/:provider/:resource", async (req, res) => {
3707
+ const { token, context } = req.body;
3708
+ const { provider, resource } = req.params;
3709
+ if (!token) throw new errors.InputError("Missing token query parameter");
3710
+ if (!autocompleteHandlers[provider]) {
3711
+ throw new errors.InputError(`Unsupported provider: ${provider}`);
3712
+ }
3713
+ const { results } = await autocompleteHandlers[provider]({
3714
+ resource,
3715
+ token,
3716
+ context
3717
+ });
3718
+ res.status(200).json({ results });
3660
3719
  });
3661
3720
  const app = express__default.default();
3662
3721
  app.set("logger", logger);
@@ -3677,8 +3736,8 @@ data: ${JSON.stringify(event)}
3677
3736
  }
3678
3737
  const [parameterDecision, stepDecision] = await permissions.authorizeConditional(
3679
3738
  [
3680
- { permission: alpha.templateParameterReadPermission },
3681
- { permission: alpha.templateStepReadPermission }
3739
+ { permission: alpha$1.templateParameterReadPermission },
3740
+ { permission: alpha$1.templateStepReadPermission }
3682
3741
  ],
3683
3742
  { credentials }
3684
3743
  );
@@ -3715,4 +3774,4 @@ exports.createRouter = createRouter;
3715
3774
  exports.createWaitAction = createWaitAction;
3716
3775
  exports.scaffolderActionRules = scaffolderActionRules;
3717
3776
  exports.scaffolderTemplateRules = scaffolderTemplateRules;
3718
- //# sourceMappingURL=router-BtUx0eIt.cjs.js.map
3777
+ //# sourceMappingURL=router-BpffhBor.cjs.js.map