@backstage/plugin-scaffolder-backend 1.7.0-next.2 → 1.8.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 CHANGED
@@ -1,5 +1,94 @@
1
1
  # @backstage/plugin-scaffolder-backend
2
2
 
3
+ ## 1.8.0-next.0
4
+
5
+ ### Minor Changes
6
+
7
+ - ea14eb62a2: Added a set of default Prometheus metrics around scaffolding. See below for a list of metrics and an explanation of their labels:
8
+
9
+ - `scaffolder_task_count`: Tracks successful task runs.
10
+
11
+ Labels:
12
+
13
+ - `template`: The entity ref of the scaffolded template
14
+ - `user`: The entity ref of the user that invoked the template run
15
+ - `result`: A string describing whether the task ran successfully, failed, or was skipped
16
+
17
+ - `scaffolder_task_duration`: a histogram which tracks the duration of a task run
18
+
19
+ Labels:
20
+
21
+ - `template`: The entity ref of the scaffolded template
22
+ - `result`: A boolean describing whether the task ran successfully
23
+
24
+ - `scaffolder_step_count`: a count that tracks each step run
25
+
26
+ Labels:
27
+
28
+ - `template`: The entity ref of the scaffolded template
29
+ - `step`: The name of the step that was run
30
+ - `result`: A string describing whether the task ran successfully, failed, or was skipped
31
+
32
+ - `scaffolder_step_duration`: a histogram which tracks the duration of each step run
33
+
34
+ Labels:
35
+
36
+ - `template`: The entity ref of the scaffolded template
37
+ - `step`: The name of the step that was run
38
+ - `result`: A string describing whether the task ran successfully, failed, or was skipped
39
+
40
+ You can find a guide for running Prometheus metrics here: https://github.com/backstage/backstage/blob/master/contrib/docs/tutorials/prometheus-metrics.md
41
+
42
+ ### Patch Changes
43
+
44
+ - 7573b65232: Internal refactor of imports to avoid circular dependencies
45
+ - Updated dependencies
46
+ - @backstage/backend-common@0.16.0-next.0
47
+ - @backstage/plugin-catalog-backend@1.5.1-next.0
48
+ - @backstage/integration@1.4.0-next.0
49
+ - @backstage/backend-tasks@0.3.7-next.0
50
+ - @backstage/catalog-model@1.1.3-next.0
51
+ - @backstage/plugin-auth-node@0.2.7-next.0
52
+ - @backstage/types@1.0.1-next.0
53
+ - @backstage/backend-plugin-api@0.1.4-next.0
54
+ - @backstage/plugin-catalog-node@1.2.1-next.0
55
+ - @backstage/catalog-client@1.1.2-next.0
56
+ - @backstage/config@1.0.4-next.0
57
+ - @backstage/errors@1.1.3-next.0
58
+ - @backstage/plugin-scaffolder-common@1.2.2-next.0
59
+
60
+ ## 1.7.0
61
+
62
+ ### Minor Changes
63
+
64
+ - 253453fa14: Added a new property called `additionalTemplateGlobals` which allows you to add global functions to the scaffolder nunjucks templates.
65
+ - 17ff77154c: Update the `github:publish` action to allow passing whether pull
66
+ requests must be up to date with the default branch before merging.
67
+ - 304305dd20: Add `allowAutoMerge` option for `publish:github` action
68
+ - 694bfe2d61: Add functionality to shutdown scaffolder tasks if they are stale
69
+ - a8e9848479: Added optional `sourcePath` parameter to `publish:gitlab:merge-request` action, `targetPath` is now optional and falls back to current workspace path.
70
+
71
+ ### Patch Changes
72
+
73
+ - 489621f613: Switching off duplicated timestamp in case of logging via task logger in a custom action
74
+ - 4880d43e25: Fixed setting default branch for Bitbucket Server
75
+ - b681275e69: Ignore .git directories in Template Editor, increase upload limit for dry-runs to 10MB.
76
+ - a35a27df70: Updated the `moduleId` of the experimental module export.
77
+ - Updated dependencies
78
+ - @backstage/plugin-catalog-node@1.2.0
79
+ - @backstage/catalog-model@1.1.2
80
+ - @backstage/backend-common@0.15.2
81
+ - @backstage/plugin-catalog-backend@1.5.0
82
+ - @backstage/plugin-auth-node@0.2.6
83
+ - @backstage/backend-tasks@0.3.6
84
+ - @backstage/backend-plugin-api@0.1.3
85
+ - @backstage/catalog-client@1.1.1
86
+ - @backstage/plugin-scaffolder-common@1.2.1
87
+ - @backstage/config@1.0.3
88
+ - @backstage/errors@1.1.2
89
+ - @backstage/integration@1.3.2
90
+ - @backstage/types@1.0.0
91
+
3
92
  ## 1.7.0-next.2
4
93
 
5
94
  ### Minor Changes
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@backstage/plugin-scaffolder-backend",
3
- "version": "1.7.0-next.2",
3
+ "version": "1.8.0-next.0",
4
4
  "main": "../dist/index.cjs.js",
5
5
  "types": "../dist/index.alpha.d.ts"
6
6
  }
package/dist/index.cjs.js CHANGED
@@ -30,6 +30,7 @@ var winston = require('winston');
30
30
  var nunjucks = require('nunjucks');
31
31
  var lodash = require('lodash');
32
32
  var jsonschema = require('jsonschema');
33
+ var promClient = require('prom-client');
33
34
  var pluginScaffolderCommon = require('@backstage/plugin-scaffolder-common');
34
35
  var express = require('express');
35
36
  var Router = require('express-promise-router');
@@ -4188,6 +4189,23 @@ function generateExampleOutput(schema) {
4188
4189
  return "<unknown>";
4189
4190
  }
4190
4191
 
4192
+ function createCounterMetric(config) {
4193
+ let metric = promClient.register.getSingleMetric(config.name);
4194
+ if (!metric) {
4195
+ metric = new promClient.Counter(config);
4196
+ promClient.register.registerMetric(metric);
4197
+ }
4198
+ return metric;
4199
+ }
4200
+ function createHistogramMetric(config) {
4201
+ let metric = promClient.register.getSingleMetric(config.name);
4202
+ if (!metric) {
4203
+ metric = new promClient.Histogram(config);
4204
+ promClient.register.registerMetric(metric);
4205
+ }
4206
+ return metric;
4207
+ }
4208
+
4191
4209
  const isValidTaskSpec = (taskSpec) => {
4192
4210
  return taskSpec.apiVersion === "scaffolder.backstage.io/v1beta3";
4193
4211
  };
@@ -4217,6 +4235,7 @@ const createStepLogger = ({
4217
4235
  class NunjucksWorkflowRunner {
4218
4236
  constructor(options) {
4219
4237
  this.options = options;
4238
+ this.tracker = scaffoldingTracker();
4220
4239
  }
4221
4240
  isSingleTemplateString(input) {
4222
4241
  var _a, _b;
@@ -4287,16 +4306,15 @@ class NunjucksWorkflowRunner {
4287
4306
  additionalTemplateGlobals: this.options.additionalTemplateGlobals
4288
4307
  });
4289
4308
  try {
4309
+ const taskTrack = await this.tracker.taskStart(task);
4290
4310
  await fs__default["default"].ensureDir(workspacePath);
4291
- await task.emitLog(
4292
- `Starting up task with ${task.spec.steps.length} steps`
4293
- );
4294
4311
  const context = {
4295
4312
  parameters: task.spec.parameters,
4296
4313
  steps: {},
4297
4314
  user: task.spec.user
4298
4315
  };
4299
4316
  for (const step of task.spec.steps) {
4317
+ const stepTrack = await this.tracker.stepStart(task, step);
4300
4318
  try {
4301
4319
  if (step.if) {
4302
4320
  const ifResult = await this.render(
@@ -4305,17 +4323,10 @@ class NunjucksWorkflowRunner {
4305
4323
  renderTemplate
4306
4324
  );
4307
4325
  if (!isTruthy(ifResult)) {
4308
- await task.emitLog(
4309
- `Skipping step ${step.id} because it's if condition was false`,
4310
- { stepId: step.id, status: "skipped" }
4311
- );
4326
+ await stepTrack.skipFalsy();
4312
4327
  continue;
4313
4328
  }
4314
4329
  }
4315
- await task.emitLog(`Beginning step ${step.name}`, {
4316
- stepId: step.id,
4317
- status: "processing"
4318
- });
4319
4330
  const action = this.options.actionRegistry.get(step.action);
4320
4331
  const { taskLogger, streamLogger } = createStepLogger({ task, step });
4321
4332
  if (task.isDryRun) {
@@ -4341,13 +4352,7 @@ class NunjucksWorkflowRunner {
4341
4352
  )}`
4342
4353
  );
4343
4354
  if (!action.supportsDryRun) {
4344
- task.emitLog(
4345
- `Skipping because ${action.id} does not support dry-run`,
4346
- {
4347
- stepId: step.id,
4348
- status: "skipped"
4349
- }
4350
- );
4355
+ await taskTrack.skipDryRun(step, action);
4351
4356
  const outputSchema = (_c = action.schema) == null ? void 0 : _c.output;
4352
4357
  if (outputSchema) {
4353
4358
  context.steps[step.id] = {
@@ -4402,19 +4407,15 @@ class NunjucksWorkflowRunner {
4402
4407
  await fs__default["default"].remove(tmpDir);
4403
4408
  }
4404
4409
  context.steps[step.id] = { output: stepOutput };
4405
- await task.emitLog(`Finished step ${step.name}`, {
4406
- stepId: step.id,
4407
- status: "completed"
4408
- });
4410
+ await stepTrack.markSuccessful();
4409
4411
  } catch (err) {
4410
- await task.emitLog(String(err.stack), {
4411
- stepId: step.id,
4412
- status: "failed"
4413
- });
4412
+ await taskTrack.markFailed(step, err);
4413
+ await stepTrack.markFailed();
4414
4414
  throw err;
4415
4415
  }
4416
4416
  }
4417
4417
  const output = this.render(task.spec.output, context, renderTemplate);
4418
+ await taskTrack.markSuccessful();
4418
4419
  return { output };
4419
4420
  } finally {
4420
4421
  if (workspacePath) {
@@ -4423,6 +4424,116 @@ class NunjucksWorkflowRunner {
4423
4424
  }
4424
4425
  }
4425
4426
  }
4427
+ function scaffoldingTracker() {
4428
+ const taskCount = createCounterMetric({
4429
+ name: "scaffolder_task_count",
4430
+ help: "Count of task runs",
4431
+ labelNames: ["template", "user", "result"]
4432
+ });
4433
+ const taskDuration = createHistogramMetric({
4434
+ name: "scaffolder_task_duration",
4435
+ help: "Duration of a task run",
4436
+ labelNames: ["template", "result"]
4437
+ });
4438
+ const stepCount = createCounterMetric({
4439
+ name: "scaffolder_step_count",
4440
+ help: "Count of step runs",
4441
+ labelNames: ["template", "step", "result"]
4442
+ });
4443
+ const stepDuration = createHistogramMetric({
4444
+ name: "scaffolder_step_duration",
4445
+ help: "Duration of a step runs",
4446
+ labelNames: ["template", "step", "result"]
4447
+ });
4448
+ async function taskStart(task) {
4449
+ var _a, _b;
4450
+ await task.emitLog(`Starting up task with ${task.spec.steps.length} steps`);
4451
+ const template = ((_a = task.spec.templateInfo) == null ? void 0 : _a.entityRef) || "";
4452
+ const user = ((_b = task.spec.user) == null ? void 0 : _b.ref) || "";
4453
+ const taskTimer = taskDuration.startTimer({
4454
+ template
4455
+ });
4456
+ async function skipDryRun(step, action) {
4457
+ task.emitLog(`Skipping because ${action.id} does not support dry-run`, {
4458
+ stepId: step.id,
4459
+ status: "skipped"
4460
+ });
4461
+ }
4462
+ async function markSuccessful() {
4463
+ taskCount.inc({
4464
+ template,
4465
+ user,
4466
+ result: "ok"
4467
+ });
4468
+ taskTimer({ result: "ok" });
4469
+ }
4470
+ async function markFailed(step, err) {
4471
+ await task.emitLog(String(err.stack), {
4472
+ stepId: step.id,
4473
+ status: "failed"
4474
+ });
4475
+ taskCount.inc({
4476
+ template,
4477
+ user,
4478
+ result: "failed"
4479
+ });
4480
+ taskTimer({ result: "failed" });
4481
+ }
4482
+ return {
4483
+ skipDryRun,
4484
+ markSuccessful,
4485
+ markFailed
4486
+ };
4487
+ }
4488
+ async function stepStart(task, step) {
4489
+ var _a;
4490
+ await task.emitLog(`Beginning step ${step.name}`, {
4491
+ stepId: step.id,
4492
+ status: "processing"
4493
+ });
4494
+ const template = ((_a = task.spec.templateInfo) == null ? void 0 : _a.entityRef) || "";
4495
+ const stepTimer = stepDuration.startTimer({
4496
+ template,
4497
+ step: step.name
4498
+ });
4499
+ async function markSuccessful() {
4500
+ await task.emitLog(`Finished step ${step.name}`, {
4501
+ stepId: step.id,
4502
+ status: "completed"
4503
+ });
4504
+ stepCount.inc({
4505
+ template,
4506
+ step: step.name,
4507
+ result: "ok"
4508
+ });
4509
+ stepTimer({ result: "ok" });
4510
+ }
4511
+ async function markFailed() {
4512
+ stepCount.inc({
4513
+ template,
4514
+ step: step.name,
4515
+ result: "failed"
4516
+ });
4517
+ stepTimer({ result: "failed" });
4518
+ }
4519
+ async function skipFalsy() {
4520
+ await task.emitLog(
4521
+ `Skipping step ${step.id} because its if condition was false`,
4522
+ { stepId: step.id, status: "skipped" }
4523
+ );
4524
+ stepTimer({ result: "skipped" });
4525
+ }
4526
+ return {
4527
+ markSuccessful,
4528
+ markFailed,
4529
+ skipFalsy
4530
+ };
4531
+ }
4532
+ return {
4533
+ taskStart,
4534
+ stepStart
4535
+ };
4536
+ }
4426
4537
 
4427
4538
  class TaskWorker {
4428
4539
  constructor(options) {