@secustor/backstage-plugin-renovate-backend 0.1.0 → 0.1.2

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 ADDED
@@ -0,0 +1,15 @@
1
+ # @secustor/backstage-plugin-renovate-backend
2
+
3
+ ## 0.1.2
4
+
5
+ ### Patch Changes
6
+
7
+ - 98796af: Publish migrations
8
+
9
+ ## 0.1.1
10
+
11
+ ### Patch Changes
12
+
13
+ - 45edddc: Fix release process
14
+ - Updated dependencies [45edddc]
15
+ - @secustor/backstage-plugin-renovate-common@0.1.1
@@ -0,0 +1,718 @@
1
+ 'use strict';
2
+
3
+ Object.defineProperty(exports, '__esModule', { value: true });
4
+
5
+ var backendCommon = require('@backstage/backend-common');
6
+ var express = require('express');
7
+ var backendOpenapiUtils = require('@backstage/backend-openapi-utils');
8
+ var backstagePluginRenovateCommon = require('@secustor/backstage-plugin-renovate-common');
9
+ var zod = require('zod');
10
+ var fetch = require('node-fetch');
11
+ var backendPluginApi = require('@backstage/backend-plugin-api');
12
+ var catalogClient = require('@backstage/catalog-client');
13
+ var catalogModel = require('@backstage/catalog-model');
14
+ var is = require('@sindresorhus/is');
15
+ var integration = require('@backstage/integration');
16
+ var backendTasks = require('@backstage/backend-tasks');
17
+
18
+ function _interopDefaultCompat (e) { return e && typeof e === 'object' && 'default' in e ? e : { default: e }; }
19
+
20
+ var express__default = /*#__PURE__*/_interopDefaultCompat(express);
21
+ var fetch__default = /*#__PURE__*/_interopDefaultCompat(fetch);
22
+ var is__default = /*#__PURE__*/_interopDefaultCompat(is);
23
+
24
+ const spec = {
25
+ openapi: "3.0.0",
26
+ info: {
27
+ title: "renovate",
28
+ description: "Backstage Renovate API",
29
+ version: "0.1.0"
30
+ },
31
+ servers: [
32
+ {
33
+ description: "local test setup",
34
+ url: "http://localhost:7007"
35
+ }
36
+ ],
37
+ paths: {
38
+ "/health": {
39
+ get: {
40
+ summary: "Get health status of the plugin",
41
+ responses: {
42
+ "200": {
43
+ description: "health status is green",
44
+ content: {
45
+ "application/json": {
46
+ schema: {
47
+ type: "string",
48
+ example: "ok"
49
+ }
50
+ }
51
+ }
52
+ }
53
+ }
54
+ }
55
+ },
56
+ "/reports": {
57
+ get: {
58
+ summary: "Get all reports",
59
+ responses: {
60
+ "200": {
61
+ $ref: "#/components/responses/reports"
62
+ }
63
+ }
64
+ }
65
+ },
66
+ "/reports/{host}": {
67
+ get: {
68
+ summary: "Get reports for host",
69
+ parameters: [
70
+ {
71
+ name: "host",
72
+ in: "path",
73
+ required: true,
74
+ schema: {
75
+ type: "string",
76
+ example: "github.com"
77
+ }
78
+ }
79
+ ],
80
+ responses: {
81
+ "200": {
82
+ $ref: "#/components/responses/reports"
83
+ },
84
+ "404": {
85
+ description: "unknown host"
86
+ }
87
+ }
88
+ }
89
+ },
90
+ "/reports/{host}/{repository}": {
91
+ get: {
92
+ summary: "Get reports for repository",
93
+ parameters: [
94
+ {
95
+ name: "host",
96
+ in: "path",
97
+ required: true,
98
+ schema: {
99
+ type: "string",
100
+ example: "github.com"
101
+ }
102
+ },
103
+ {
104
+ name: "repository",
105
+ in: "path",
106
+ required: true,
107
+ schema: {
108
+ type: "string",
109
+ example: "myOrg/myRepository"
110
+ }
111
+ }
112
+ ],
113
+ responses: {
114
+ "200": {
115
+ $ref: "#/components/responses/reports"
116
+ },
117
+ "404": {
118
+ description: "unknown repository"
119
+ }
120
+ }
121
+ }
122
+ },
123
+ "/runs": {
124
+ post: {
125
+ summary: "Start or get Renovate runs",
126
+ requestBody: {
127
+ content: {
128
+ "application/json": {
129
+ schema: {
130
+ type: "object",
131
+ required: ["target"],
132
+ properties: {
133
+ target: {
134
+ $ref: "#/components/schemas/target"
135
+ },
136
+ callBackURL: {
137
+ type: "string",
138
+ example: "https://localhost:8080/my-webhook-endpoint"
139
+ }
140
+ }
141
+ }
142
+ }
143
+ }
144
+ },
145
+ responses: {
146
+ "202": {
147
+ description: "Run has been scheduled",
148
+ content: {
149
+ "application/json": {
150
+ schema: {
151
+ type: "object",
152
+ properties: {
153
+ runID: {
154
+ description: "unique ID for the run",
155
+ type: "string",
156
+ example: "9-d_CO9JlaEmd-OM9QfkI"
157
+ }
158
+ }
159
+ }
160
+ }
161
+ }
162
+ },
163
+ "400": {
164
+ description: "Unexpected incoming data",
165
+ content: {
166
+ "application/json": {
167
+ schema: {
168
+ type: "object",
169
+ properties: {
170
+ error: {
171
+ $ref: "#/components/schemas/error"
172
+ }
173
+ }
174
+ }
175
+ }
176
+ }
177
+ }
178
+ }
179
+ }
180
+ }
181
+ },
182
+ components: {
183
+ responses: {
184
+ reports: {
185
+ description: "Returns reports",
186
+ content: {
187
+ "application/json": {
188
+ schema: {
189
+ type: "array",
190
+ items: {
191
+ type: "object",
192
+ additionalProperties: false,
193
+ required: [
194
+ "taskID",
195
+ "repository",
196
+ "host",
197
+ "lastUpdated",
198
+ "report"
199
+ ],
200
+ properties: {
201
+ taskID: {
202
+ type: "string"
203
+ },
204
+ lastUpdated: {
205
+ type: "string",
206
+ format: "date-time"
207
+ },
208
+ host: {
209
+ type: "string"
210
+ },
211
+ repository: {
212
+ type: "string"
213
+ },
214
+ report: {
215
+ $ref: "#/components/schemas/repositoryReport"
216
+ }
217
+ }
218
+ }
219
+ }
220
+ }
221
+ }
222
+ }
223
+ },
224
+ schemas: {
225
+ error: {
226
+ anyOf: [
227
+ {
228
+ type: "object",
229
+ example: {
230
+ message: "I'm an error",
231
+ code: 1111
232
+ }
233
+ },
234
+ {
235
+ type: "string",
236
+ example: "I'm an error"
237
+ }
238
+ ]
239
+ },
240
+ target: {
241
+ oneOf: [
242
+ {
243
+ type: "string",
244
+ description: "URL to an repository",
245
+ example: "https://github.com/secustor/renovate-test"
246
+ },
247
+ {
248
+ type: "object",
249
+ description: "Entity with SourceLocation URL annotation",
250
+ required: ["metadata"],
251
+ properties: {
252
+ metadata: {
253
+ type: "object",
254
+ required: ["annotations"],
255
+ properties: {
256
+ annotations: {
257
+ type: "object",
258
+ additionalProperties: false,
259
+ required: ["backstage.io/source-location"],
260
+ properties: {
261
+ "backstage.io/source-location": {
262
+ type: "string",
263
+ example: "https://github.com/secustor/renovate-meetup/blob/master/renovate.json"
264
+ }
265
+ }
266
+ }
267
+ }
268
+ }
269
+ }
270
+ }
271
+ ]
272
+ },
273
+ repositoryReportList: {
274
+ type: "array",
275
+ items: {
276
+ $ref: "#/components/schemas/repositoryReport"
277
+ }
278
+ },
279
+ repositoryReport: {
280
+ description: "report for a specific repository",
281
+ type: "object",
282
+ additionalProperties: false,
283
+ required: ["branches", "packageFiles", "problems"],
284
+ properties: {
285
+ branches: {
286
+ type: "array",
287
+ items: {
288
+ type: "object"
289
+ }
290
+ },
291
+ packageFiles: {
292
+ type: "object"
293
+ },
294
+ problems: {
295
+ type: "array",
296
+ items: {
297
+ type: "object"
298
+ }
299
+ }
300
+ }
301
+ }
302
+ }
303
+ }
304
+ };
305
+ const createOpenApiRouter = async (options) => backendOpenapiUtils.createValidatedOpenApiRouter(spec, options);
306
+
307
+ const target = zod.z.union([zod.z.string(), backstagePluginRenovateCommon.entityWithAnnotations]);
308
+ const runRequestBody = zod.z.object({
309
+ target,
310
+ callBackURL: zod.z.string().url().optional()
311
+ });
312
+
313
+ async function createRouter(runner, options) {
314
+ const { logger, pluginConfig, databaseHandler } = options;
315
+ const router = await createOpenApiRouter();
316
+ router.use(express__default.default.json());
317
+ router.get("/health", (_, response) => {
318
+ logger.debug("healthcheck request");
319
+ response.json("ok");
320
+ });
321
+ router.get("/reports", async (_request, response) => {
322
+ const reports = await databaseHandler.getReports();
323
+ response.status(200).json(reports);
324
+ });
325
+ router.get("/reports/:host", async (request, response) => {
326
+ const reports = await databaseHandler.getReports({
327
+ ...request.params
328
+ });
329
+ response.status(200).json(reports);
330
+ });
331
+ router.get("/reports/:host/:repository", async (request, response) => {
332
+ const reports = await databaseHandler.getReports({
333
+ ...request.params
334
+ });
335
+ response.status(200).json(reports);
336
+ });
337
+ router.post("/runs", async (request, response) => {
338
+ const body = runRequestBody.safeParse(request.body);
339
+ if (!body.success) {
340
+ response.status(400).json({ error: body.error });
341
+ return;
342
+ }
343
+ const data = body.data;
344
+ const targetRepo = backstagePluginRenovateCommon.getTargetRepo(data.target);
345
+ const promise = runner.schedule(targetRepo);
346
+ const parsedUrl = backstagePluginRenovateCommon.parseUrl(data.callBackURL);
347
+ if (parsedUrl) {
348
+ const allowedHosts = pluginConfig.getOptionalStringArray(
349
+ "callBacks.allowedHosts"
350
+ );
351
+ if (
352
+ // by default, allow only localhost
353
+ !allowedHosts && parsedUrl.host !== "localhost" || // else check if host is in the allowed list
354
+ allowedHosts && !allowedHosts.includes(parsedUrl.host)
355
+ ) {
356
+ response.status(400).json({
357
+ error: `callBackUrl '${parsedUrl.host}' is not allowed`
358
+ });
359
+ return;
360
+ }
361
+ promise.then((report) => {
362
+ fetch__default.default(data.callBackURL, {
363
+ method: "POST",
364
+ body: JSON.stringify(report)
365
+ });
366
+ });
367
+ promise.catch((reason) => {
368
+ fetch__default.default(data.callBackURL, {
369
+ method: "POST",
370
+ body: JSON.stringify(reason)
371
+ });
372
+ });
373
+ }
374
+ response.status(202).json({ runID: backstagePluginRenovateCommon.getTaskID(targetRepo) });
375
+ });
376
+ router.use(backendCommon.errorHandler());
377
+ return router;
378
+ }
379
+
380
+ const renovateRuntimeExtensionPoint = backendPluginApi.createExtensionPoint({
381
+ id: "renovate.runtimes"
382
+ });
383
+
384
+ function getPlatformEnvs(target, context) {
385
+ var _a, _b, _c, _d;
386
+ const { rootConfig, logger } = context;
387
+ const env = {};
388
+ const integrations = integration.ScmIntegrations.fromConfig(rootConfig);
389
+ const integration$1 = integrations.byHost(target.host);
390
+ if (is__default.default.nullOrUndefined(integration$1)) {
391
+ throw new Error(
392
+ `Could not identify platform for target ${target.host}/${target.repository}`
393
+ );
394
+ }
395
+ const errMsg = `Could no integration found for url and '${integration$1.type}' type for host ${target.host}`;
396
+ switch (integration$1.type) {
397
+ case "github":
398
+ env.RENOVATE_PLATFORM = integration$1.type;
399
+ env.RENOVATE_TOKEN = requireConfigVariable(
400
+ (_a = integrations.github.byHost(target.host)) == null ? void 0 : _a.config.token,
401
+ errMsg
402
+ );
403
+ env.RENOVATE_REPOSITORIES = target.repository;
404
+ break;
405
+ case "gitlab":
406
+ {
407
+ const gitLabIntegrationConfig = requireConfigVariable(
408
+ (_b = integrations.gitlab.byHost(target.host)) == null ? void 0 : _b.config,
409
+ errMsg
410
+ );
411
+ env.RENOVATE_PLATFORM = integration$1.type;
412
+ env.RENOVATE_ENDPOINT = (_c = gitLabIntegrationConfig.apiBaseUrl) != null ? _c : `https://${target.host}/api/v4`;
413
+ env.RENOVATE_TOKEN = requireConfigVariable(
414
+ gitLabIntegrationConfig.token,
415
+ "Could not get Gitlab token"
416
+ );
417
+ env.RENOVATE_REPOSITORIES = target.repository;
418
+ }
419
+ break;
420
+ default:
421
+ throw new Error(`Unsupported platform type ${integration$1.type}`);
422
+ }
423
+ const githubComIntegration = integrations.github.byHost("github.com");
424
+ if (is__default.default.nullOrUndefined(githubComIntegration)) {
425
+ logger.warn(`No Github.com integration has been found`);
426
+ } else {
427
+ env.RENOVATE_GITHUB_COM = requireConfigVariable(
428
+ (_d = integrations.github.byHost("github.com")) == null ? void 0 : _d.config.token,
429
+ "Could not get token for Github.com token in the defined integration"
430
+ );
431
+ }
432
+ return env;
433
+ }
434
+ function requireConfigVariable(input, errMessage) {
435
+ if (is__default.default.nullOrUndefined(input)) {
436
+ throw new Error(errMessage);
437
+ }
438
+ return input;
439
+ }
440
+
441
+ async function extractReport(opts) {
442
+ const { logStream, logger } = opts;
443
+ return new Promise((resolve) => {
444
+ let uncompletedText = "";
445
+ logStream.on("data", (chunk) => {
446
+ var _a;
447
+ const text = uncompletedText.concat(chunk.toString());
448
+ const logLines = text.split("\n");
449
+ uncompletedText = (_a = logLines.pop()) != null ? _a : "";
450
+ for (const logLine of logLines) {
451
+ const log = JSON.parse(logLine);
452
+ if (log.report) {
453
+ const report = log.report;
454
+ resolve(report);
455
+ }
456
+ const msg = log.msg;
457
+ delete log.msg;
458
+ delete log.logContext;
459
+ logger.debug(msg, log);
460
+ }
461
+ });
462
+ });
463
+ }
464
+
465
+ function getRuntime(pluginConfig) {
466
+ return pluginConfig.getString("runtime.type");
467
+ }
468
+ function getScheduleDefinition(pluginConfig) {
469
+ const scheduleConfig = pluginConfig.getConfig("schedule");
470
+ return backendTasks.readTaskScheduleDefinitionFromConfig(scheduleConfig);
471
+ }
472
+
473
+ var __defProp = Object.defineProperty;
474
+ var __defNormalProp = (obj, key, value) => key in obj ? __defProp(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value;
475
+ var __publicField = (obj, key, value) => {
476
+ __defNormalProp(obj, typeof key !== "symbol" ? key + "" : key, value);
477
+ return value;
478
+ };
479
+ class RenovateRunner {
480
+ constructor(databaseHandler, rootConfig, pluginConfig, logger, runtimes, runner, scheduler) {
481
+ __publicField(this, "scheduler");
482
+ __publicField(this, "rootConfig");
483
+ __publicField(this, "databaseHandler");
484
+ __publicField(this, "pluginConfig");
485
+ __publicField(this, "logger");
486
+ __publicField(this, "runtimes");
487
+ __publicField(this, "runner");
488
+ this.databaseHandler = databaseHandler;
489
+ this.rootConfig = rootConfig;
490
+ this.pluginConfig = pluginConfig;
491
+ this.logger = logger;
492
+ this.runtimes = runtimes;
493
+ this.runner = runner;
494
+ this.scheduler = scheduler;
495
+ }
496
+ static async from(options) {
497
+ const {
498
+ databaseHandler,
499
+ rootConfig,
500
+ pluginConfig,
501
+ runtimes,
502
+ logger,
503
+ scheduler
504
+ } = options;
505
+ const scheduleConfig = getScheduleDefinition(pluginConfig);
506
+ const runner = scheduler.createScheduledTaskRunner(scheduleConfig);
507
+ return new RenovateRunner(
508
+ databaseHandler,
509
+ rootConfig,
510
+ pluginConfig,
511
+ logger,
512
+ runtimes,
513
+ runner,
514
+ scheduler
515
+ );
516
+ }
517
+ async renovate(id, target, { logger }) {
518
+ var _a, _b;
519
+ const runtime = getRuntime(this.pluginConfig);
520
+ const wrapperRuntime = this.runtimes.get(runtime);
521
+ if (is__default.default.nullOrUndefined(wrapperRuntime)) {
522
+ throw new Error(`Unknown runtime type '${runtime}'`);
523
+ }
524
+ const env = {
525
+ // setup logging
526
+ LOG_FORMAT: "json",
527
+ LOG_LEVEL: "debug",
528
+ LOG_CONTEXT: id,
529
+ RENOVATE_REPORT_TYPE: "logging",
530
+ // setup platform specifics
531
+ ...getPlatformEnvs(target, {
532
+ logger,
533
+ rootConfig: this.rootConfig
534
+ })
535
+ };
536
+ const renovateConfig = (_a = this.pluginConfig.getOptional("config")) != null ? _a : {};
537
+ const runtimeConfig = (_b = this.pluginConfig.getOptionalConfig(`runtime.${runtime}`)) != null ? _b : null;
538
+ const promise = wrapperRuntime.run({
539
+ env,
540
+ renovateConfig,
541
+ runtimeConfig
542
+ });
543
+ return await promise.then((result) => {
544
+ return extractReport({
545
+ logger,
546
+ logStream: result.stdout
547
+ });
548
+ });
549
+ }
550
+ async run(id, target) {
551
+ const logger = this.logger.child({ runID: id, ...target });
552
+ logger.info("Renovate run starting");
553
+ const report = await this.renovate(id, target, { logger });
554
+ await this.databaseHandler.addReport({
555
+ taskID: id,
556
+ report,
557
+ target,
558
+ logger
559
+ });
560
+ logger.info("Renovate run finished");
561
+ }
562
+ async schedule(target) {
563
+ const id = backstagePluginRenovateCommon.getTaskID(target);
564
+ const targetRepo = backstagePluginRenovateCommon.getTargetRepo(target);
565
+ try {
566
+ await this.scheduler.triggerTask(id);
567
+ return;
568
+ } catch (e) {
569
+ await this.runner.run({
570
+ id,
571
+ fn: () => this.run(id, targetRepo)
572
+ });
573
+ }
574
+ }
575
+ }
576
+
577
+ const migrationsDir = backendCommon.resolvePackagePath(
578
+ "@secustor/backstage-plugin-renovate-backend",
579
+ "migrations"
580
+ );
581
+ class DatabaseHandler {
582
+ constructor(client, logger) {
583
+ this.client = client;
584
+ this.logger = logger;
585
+ }
586
+ static async create(options) {
587
+ var _a;
588
+ const { database, logger } = options;
589
+ const client = await database.getClient();
590
+ if (!((_a = database.migrations) == null ? void 0 : _a.skip)) {
591
+ await client.migrate.latest({
592
+ directory: migrationsDir
593
+ });
594
+ }
595
+ return new DatabaseHandler(client, logger);
596
+ }
597
+ async addReport(options) {
598
+ var _a;
599
+ const { taskID, report, target } = options;
600
+ const logger = (_a = options.logger) != null ? _a : this.logger;
601
+ const inserts = [];
602
+ for (const [repository, value] of Object.entries(report.repositories)) {
603
+ inserts.push({
604
+ task_id: taskID,
605
+ last_updated: Date.now(),
606
+ host: target.host,
607
+ repository,
608
+ report: value
609
+ });
610
+ }
611
+ await this.client("reports").insert(inserts).onConflict("task_id").merge().catch((reason) => logger.error("Failed insert data", reason));
612
+ }
613
+ async getReports(query) {
614
+ const builder = this.client.select();
615
+ if (query) {
616
+ builder.where(query);
617
+ }
618
+ const rows = await builder.from("reports");
619
+ return rows.map((row) => {
620
+ return {
621
+ taskID: row.task_id,
622
+ lastUpdated: new Date(row.last_updated).toISOString(),
623
+ host: row.host,
624
+ repository: row.repository,
625
+ // if the JSON field has not been auto-parsed do it manually
626
+ report: is__default.default.string(row.report) ? JSON.parse(row.report) : row.report
627
+ };
628
+ });
629
+ }
630
+ }
631
+
632
+ const RENOVATE_ANNOTATION_KEEP_UPDATED = "renovate.secustor.dev/keep-updated";
633
+ const renovatePlugin = backendPluginApi.createBackendPlugin({
634
+ pluginId: "renovate",
635
+ register(env) {
636
+ const runtimes = /* @__PURE__ */ new Map();
637
+ env.registerExtensionPoint(renovateRuntimeExtensionPoint, {
638
+ addRuntime(id, runtime) {
639
+ if (runtimes.has(id)) {
640
+ throw new Error(
641
+ ` ${id} has been already registered. Only one wrapper with the same ID is allowed to be registered`
642
+ );
643
+ }
644
+ runtimes.set(id, runtime);
645
+ }
646
+ });
647
+ env.registerInit({
648
+ deps: {
649
+ rootConfig: backendPluginApi.coreServices.rootConfig,
650
+ logger: backendPluginApi.coreServices.logger,
651
+ httpRouter: backendPluginApi.coreServices.httpRouter,
652
+ database: backendPluginApi.coreServices.database,
653
+ scheduler: backendPluginApi.coreServices.scheduler,
654
+ discovery: backendPluginApi.coreServices.discovery,
655
+ auth: backendPluginApi.coreServices.auth
656
+ },
657
+ async init(options) {
658
+ const {
659
+ auth,
660
+ database,
661
+ discovery,
662
+ httpRouter,
663
+ rootConfig,
664
+ scheduler,
665
+ logger
666
+ } = options;
667
+ const routerOptions = {
668
+ ...options,
669
+ databaseHandler: await DatabaseHandler.create({ database, logger }),
670
+ pluginConfig: rootConfig.getConfig("renovate"),
671
+ runtimes
672
+ };
673
+ const renovateRunner = await RenovateRunner.from(routerOptions);
674
+ const client = new catalogClient.CatalogClient({ discoveryApi: discovery });
675
+ const schedule = getScheduleDefinition(routerOptions.pluginConfig);
676
+ await scheduler.scheduleTask({
677
+ id: `renovate/job-sync`,
678
+ ...schedule,
679
+ fn: async () => {
680
+ const { token } = await auth.getPluginRequestToken({
681
+ onBehalfOf: await auth.getOwnServiceCredentials(),
682
+ targetPluginId: "catalog"
683
+ });
684
+ const { items: entities } = await client.getEntities(
685
+ {
686
+ filter: {
687
+ [`metadata.annotations.${RENOVATE_ANNOTATION_KEEP_UPDATED}`]: catalogClient.CATALOG_FILTER_EXISTS,
688
+ [`metadata.annotations.${catalogModel.ANNOTATION_SOURCE_LOCATION}`]: catalogClient.CATALOG_FILTER_EXISTS
689
+ },
690
+ fields: [
691
+ "kind",
692
+ "metadata.annotations",
693
+ "metadata.name",
694
+ "metadata.namespace",
695
+ "metadata.title"
696
+ ]
697
+ },
698
+ { token }
699
+ );
700
+ for (const entity of entities) {
701
+ renovateRunner.schedule(entity);
702
+ }
703
+ }
704
+ });
705
+ httpRouter.use(await createRouter(renovateRunner, routerOptions));
706
+ httpRouter.addAuthPolicy({
707
+ path: "/health",
708
+ allow: "unauthenticated"
709
+ });
710
+ }
711
+ });
712
+ }
713
+ });
714
+
715
+ exports.createRouter = createRouter;
716
+ exports.default = renovatePlugin;
717
+ exports.renovateRuntimeExtensionPoint = renovateRuntimeExtensionPoint;
718
+ //# sourceMappingURL=index.cjs.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.cjs.js","sources":["../src/schema/openapi.generated.ts","../src/service/schema.ts","../src/service/router.ts","../src/extensionPoints.ts","../src/wrapper/platforms/index.ts","../src/wrapper/utils.ts","../src/config/index.ts","../src/wrapper/index.ts","../src/service/databaseHandler.ts","../src/plugin.ts"],"sourcesContent":["//\n\n// ******************************************************************\n// * THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY. *\n// ******************************************************************\nimport { createValidatedOpenApiRouter } from '@backstage/backend-openapi-utils';\n\nexport const spec = {\n openapi: '3.0.0',\n info: {\n title: 'renovate',\n description: 'Backstage Renovate API',\n version: '0.1.0',\n },\n servers: [\n {\n description: 'local test setup',\n url: 'http://localhost:7007',\n },\n ],\n paths: {\n '/health': {\n get: {\n summary: 'Get health status of the plugin',\n responses: {\n '200': {\n description: 'health status is green',\n content: {\n 'application/json': {\n schema: {\n type: 'string',\n example: 'ok',\n },\n },\n },\n },\n },\n },\n },\n '/reports': {\n get: {\n summary: 'Get all reports',\n responses: {\n '200': {\n $ref: '#/components/responses/reports',\n },\n },\n },\n },\n '/reports/{host}': {\n get: {\n summary: 'Get reports for host',\n parameters: [\n {\n name: 'host',\n in: 'path',\n required: true,\n schema: {\n type: 'string',\n example: 'github.com',\n },\n },\n ],\n responses: {\n '200': {\n $ref: '#/components/responses/reports',\n },\n '404': {\n description: 'unknown host',\n },\n },\n },\n },\n '/reports/{host}/{repository}': {\n get: {\n summary: 'Get reports for repository',\n parameters: [\n {\n name: 'host',\n in: 'path',\n required: true,\n schema: {\n type: 'string',\n example: 'github.com',\n },\n },\n {\n name: 'repository',\n in: 'path',\n required: true,\n schema: {\n type: 'string',\n example: 'myOrg/myRepository',\n },\n },\n ],\n responses: {\n '200': {\n $ref: '#/components/responses/reports',\n },\n '404': {\n description: 'unknown repository',\n },\n },\n },\n },\n '/runs': {\n post: {\n summary: 'Start or get Renovate runs',\n requestBody: {\n content: {\n 'application/json': {\n schema: {\n type: 'object',\n required: ['target'],\n properties: {\n target: {\n $ref: '#/components/schemas/target',\n },\n callBackURL: {\n type: 'string',\n example: 'https://localhost:8080/my-webhook-endpoint',\n },\n },\n },\n },\n },\n },\n responses: {\n '202': {\n description: 'Run has been scheduled',\n content: {\n 'application/json': {\n schema: {\n type: 'object',\n properties: {\n runID: {\n description: 'unique ID for the run',\n type: 'string',\n example: '9-d_CO9JlaEmd-OM9QfkI',\n },\n },\n },\n },\n },\n },\n '400': {\n description: 'Unexpected incoming data',\n content: {\n 'application/json': {\n schema: {\n type: 'object',\n properties: {\n error: {\n $ref: '#/components/schemas/error',\n },\n },\n },\n },\n },\n },\n },\n },\n },\n },\n components: {\n responses: {\n reports: {\n description: 'Returns reports',\n content: {\n 'application/json': {\n schema: {\n type: 'array',\n items: {\n type: 'object',\n additionalProperties: false,\n required: [\n 'taskID',\n 'repository',\n 'host',\n 'lastUpdated',\n 'report',\n ],\n properties: {\n taskID: {\n type: 'string',\n },\n lastUpdated: {\n type: 'string',\n format: 'date-time',\n },\n host: {\n type: 'string',\n },\n repository: {\n type: 'string',\n },\n report: {\n $ref: '#/components/schemas/repositoryReport',\n },\n },\n },\n },\n },\n },\n },\n },\n schemas: {\n error: {\n anyOf: [\n {\n type: 'object',\n example: {\n message: \"I'm an error\",\n code: 1111,\n },\n },\n {\n type: 'string',\n example: \"I'm an error\",\n },\n ],\n },\n target: {\n oneOf: [\n {\n type: 'string',\n description: 'URL to an repository',\n example: 'https://github.com/secustor/renovate-test',\n },\n {\n type: 'object',\n description: 'Entity with SourceLocation URL annotation',\n required: ['metadata'],\n properties: {\n metadata: {\n type: 'object',\n required: ['annotations'],\n properties: {\n annotations: {\n type: 'object',\n additionalProperties: false,\n required: ['backstage.io/source-location'],\n properties: {\n 'backstage.io/source-location': {\n type: 'string',\n example:\n 'https://github.com/secustor/renovate-meetup/blob/master/renovate.json',\n },\n },\n },\n },\n },\n },\n },\n ],\n },\n repositoryReportList: {\n type: 'array',\n items: {\n $ref: '#/components/schemas/repositoryReport',\n },\n },\n repositoryReport: {\n description: 'report for a specific repository',\n type: 'object',\n additionalProperties: false,\n required: ['branches', 'packageFiles', 'problems'],\n properties: {\n branches: {\n type: 'array',\n items: {\n type: 'object',\n },\n },\n packageFiles: {\n type: 'object',\n },\n problems: {\n type: 'array',\n items: {\n type: 'object',\n },\n },\n },\n },\n },\n },\n} as const;\nexport const createOpenApiRouter = async (\n options?: Parameters<typeof createValidatedOpenApiRouter>['1'],\n) => createValidatedOpenApiRouter<typeof spec>(spec, options);\n","import { entityWithAnnotations } from '@secustor/backstage-plugin-renovate-common';\nimport { z } from 'zod';\n\nexport const target = z.union([z.string(), entityWithAnnotations]);\n\nexport const runRequestBody = z.object({\n target,\n callBackURL: z.string().url().optional(),\n});\n","import { errorHandler } from '@backstage/backend-common';\nimport express from 'express';\nimport { createOpenApiRouter } from '../schema/openapi.generated';\nimport { runRequestBody } from './schema';\nimport fetch from 'node-fetch';\nimport type { RouterOptions } from './types';\nimport {\n getTargetRepo,\n getTaskID,\n parseUrl,\n} from '@secustor/backstage-plugin-renovate-common';\nimport { RenovateRunner } from '../wrapper';\n\nexport async function createRouter(\n runner: RenovateRunner,\n options: RouterOptions,\n): Promise<express.Router> {\n const { logger, pluginConfig, databaseHandler } = options;\n\n const router = await createOpenApiRouter();\n router.use(express.json());\n\n router.get('/health', (_, response) => {\n logger.debug('healthcheck request');\n response.json('ok');\n });\n\n router.get('/reports', async (_request, response) => {\n const reports = await databaseHandler.getReports();\n response.status(200).json(reports);\n });\n\n router.get('/reports/:host', async (request, response) => {\n const reports = await databaseHandler.getReports({\n ...request.params,\n });\n response.status(200).json(reports);\n });\n\n router.get('/reports/:host/:repository', async (request, response) => {\n const reports = await databaseHandler.getReports({\n ...request.params,\n });\n response.status(200).json(reports);\n });\n\n router.post('/runs', async (request, response) => {\n const body = runRequestBody.safeParse(request.body);\n if (!body.success) {\n response.status(400).json({ error: body.error });\n return;\n }\n const data = body.data;\n\n // trigger Renovate run\n const targetRepo = getTargetRepo(data.target);\n const promise = runner.schedule(targetRepo);\n\n // in case of a callBackURL, set up a completion message\n const parsedUrl = parseUrl(data.callBackURL);\n if (parsedUrl) {\n // if allowedHosts are defined, check if callBackUrl is in the list\n const allowedHosts = pluginConfig.getOptionalStringArray(\n 'callBacks.allowedHosts',\n );\n if (\n // by default, allow only localhost\n (!allowedHosts && parsedUrl.host !== 'localhost') ||\n // else check if host is in the allowed list\n (allowedHosts && !allowedHosts.includes(parsedUrl.host))\n ) {\n response.status(400).json({\n error: `callBackUrl '${parsedUrl.host}' is not allowed`,\n });\n return;\n }\n\n promise.then(report => {\n fetch(data.callBackURL!, {\n method: 'POST',\n body: JSON.stringify(report),\n });\n });\n\n promise.catch(reason => {\n fetch(data.callBackURL!, {\n method: 'POST',\n body: JSON.stringify(reason),\n });\n });\n }\n\n response.status(202).json({ runID: getTaskID(targetRepo) });\n });\n router.use(errorHandler());\n return router;\n}\n","import { createExtensionPoint } from '@backstage/backend-plugin-api';\nimport { RenovateWrapper } from '@secustor/backstage-plugin-renovate-common';\n\nexport interface RenovateRuntimeExtensionPoint {\n addRuntime(id: string, runtime: RenovateWrapper): void;\n}\n\nexport const renovateRuntimeExtensionPoint =\n createExtensionPoint<RenovateRuntimeExtensionPoint>({\n id: 'renovate.runtimes',\n });\n","import { ScmIntegrations } from '@backstage/integration';\nimport is from '@sindresorhus/is';\nimport { PlatformEnvsOptions } from './types';\nimport { TargetRepo } from '@secustor/backstage-plugin-renovate-common';\n\n/*\n Returns record of Renovate environment variables specific for the platform of targetUrl\n */\nexport function getPlatformEnvs(\n target: TargetRepo,\n context: PlatformEnvsOptions,\n): Record<string, string> {\n const { rootConfig, logger } = context;\n\n const env: Record<string, string> = {};\n // add Renovate platform and tokens\n const integrations = ScmIntegrations.fromConfig(rootConfig);\n const integration = integrations.byHost(target.host);\n if (is.nullOrUndefined(integration)) {\n throw new Error(\n `Could not identify platform for target ${target.host}/${target.repository}`,\n );\n }\n\n const errMsg = `Could no integration found for url and '${integration.type}' type for host ${target.host}`;\n switch (integration.type) {\n case 'github':\n env.RENOVATE_PLATFORM = integration.type;\n env.RENOVATE_TOKEN = requireConfigVariable(\n integrations.github.byHost(target.host)?.config.token,\n errMsg,\n );\n env.RENOVATE_REPOSITORIES = target.repository;\n\n break;\n case 'gitlab':\n {\n const gitLabIntegrationConfig = requireConfigVariable(\n integrations.gitlab.byHost(target.host)?.config,\n errMsg,\n );\n env.RENOVATE_PLATFORM = integration.type;\n env.RENOVATE_ENDPOINT =\n gitLabIntegrationConfig.apiBaseUrl ?? `https://${target.host}/api/v4`;\n env.RENOVATE_TOKEN = requireConfigVariable(\n gitLabIntegrationConfig.token,\n 'Could not get Gitlab token',\n );\n env.RENOVATE_REPOSITORIES = target.repository;\n }\n break;\n default:\n throw new Error(`Unsupported platform type ${integration.type}`);\n }\n\n const githubComIntegration = integrations.github.byHost('github.com');\n if (is.nullOrUndefined(githubComIntegration)) {\n logger.warn(`No Github.com integration has been found`);\n } else {\n env.RENOVATE_GITHUB_COM = requireConfigVariable(\n integrations.github.byHost('github.com')?.config.token,\n 'Could not get token for Github.com token in the defined integration',\n );\n }\n\n return env;\n}\n\nfunction requireConfigVariable<T>(\n input: T | undefined | null,\n errMessage: string,\n): T {\n if (is.nullOrUndefined(input)) {\n throw new Error(errMessage);\n }\n return input;\n}\n","import { RenovateReport } from '@secustor/backstage-plugin-renovate-common';\nimport { ExtractReportOptions } from './types';\n\nexport async function extractReport(\n opts: ExtractReportOptions,\n): Promise<RenovateReport> {\n const { logStream, logger } = opts;\n return new Promise(resolve => {\n let uncompletedText = '';\n logStream.on('data', (chunk: Buffer) => {\n const text = uncompletedText.concat(chunk.toString());\n const logLines = text.split('\\n');\n\n // if the last element is an empty string, then we have a complete json line so we reset it.\n // else we save it to\n uncompletedText = logLines.pop() ?? '';\n\n for (const logLine of logLines) {\n const log = JSON.parse(logLine);\n if (log.report) {\n // TODO use schema and reject if report does not fit expectation\n const report = log.report as RenovateReport;\n // do not forward the report to logging\n resolve(report);\n }\n const msg = log.msg;\n delete log.msg;\n // delete logContext as it is the same as runID\n delete log.logContext;\n logger.debug(msg, log);\n }\n });\n });\n}\n","import { Config } from '@backstage/config';\nimport {\n readTaskScheduleDefinitionFromConfig,\n TaskScheduleDefinition,\n} from '@backstage/backend-tasks';\n\nexport function getRuntime(pluginConfig: Config): string {\n return pluginConfig.getString('runtime.type');\n}\n\nexport function getScheduleDefinition(\n pluginConfig: Config,\n): TaskScheduleDefinition {\n const scheduleConfig = pluginConfig.getConfig('schedule');\n return readTaskScheduleDefinitionFromConfig(scheduleConfig);\n}\n","/***/\n/**\n * Node.js library for the renovate-wrapper plugin.\n *\n * @packageDocumentation\n */\n\nimport is from '@sindresorhus/is';\nimport { getPlatformEnvs } from './platforms';\nimport { RouterOptions } from '../service/types';\nimport { extractReport } from './utils';\nimport {\n EntityWithAnnotations,\n getTargetRepo,\n getTaskID,\n RenovateReport,\n RenovateWrapper,\n TargetRepo,\n} from '@secustor/backstage-plugin-renovate-common';\nimport { Config } from '@backstage/config';\nimport { LoggerService, SchedulerService } from '@backstage/backend-plugin-api';\nimport { getRuntime, getScheduleDefinition } from '../config';\nimport { DatabaseHandler } from '../service/databaseHandler';\nimport { TaskRunner } from '@backstage/backend-tasks';\nimport { RunOptions } from './types';\n\nexport class RenovateRunner {\n private scheduler: SchedulerService;\n private rootConfig: Config;\n private databaseHandler: DatabaseHandler;\n private pluginConfig: Config;\n private logger: LoggerService;\n private runtimes: Map<string, RenovateWrapper>;\n private runner: TaskRunner;\n\n constructor(\n databaseHandler: DatabaseHandler,\n rootConfig: Config,\n pluginConfig: Config,\n logger: LoggerService,\n runtimes: Map<string, RenovateWrapper>,\n runner: TaskRunner,\n scheduler: SchedulerService,\n ) {\n this.databaseHandler = databaseHandler;\n this.rootConfig = rootConfig;\n this.pluginConfig = pluginConfig;\n this.logger = logger;\n this.runtimes = runtimes;\n this.runner = runner;\n this.scheduler = scheduler;\n }\n\n static async from(options: RouterOptions): Promise<RenovateRunner> {\n const {\n databaseHandler,\n rootConfig,\n pluginConfig,\n runtimes,\n logger,\n scheduler,\n } = options;\n\n const scheduleConfig = getScheduleDefinition(pluginConfig);\n const runner = scheduler.createScheduledTaskRunner(scheduleConfig);\n\n return new RenovateRunner(\n databaseHandler,\n rootConfig,\n pluginConfig,\n logger,\n runtimes,\n runner,\n scheduler,\n );\n }\n\n private async renovate(\n id: string,\n target: TargetRepo,\n { logger }: RunOptions,\n ): Promise<RenovateReport> {\n const runtime = getRuntime(this.pluginConfig);\n const wrapperRuntime = this.runtimes.get(runtime);\n if (is.nullOrUndefined(wrapperRuntime)) {\n throw new Error(`Unknown runtime type '${runtime}'`);\n }\n\n const env: Record<string, string> = {\n // setup logging\n LOG_FORMAT: 'json',\n LOG_LEVEL: 'debug',\n LOG_CONTEXT: id,\n RENOVATE_REPORT_TYPE: 'logging',\n // setup platform specifics\n ...getPlatformEnvs(target, {\n logger,\n rootConfig: this.rootConfig,\n }),\n };\n\n // read out renovate.config and write out to json file for consumption by Renovate\n // we are reading it at this place to allow dynamic configuration changes\n const renovateConfig = this.pluginConfig.getOptional('config') ?? {};\n const runtimeConfig =\n this.pluginConfig.getOptionalConfig(`runtime.${runtime}`) ?? null;\n\n const promise = wrapperRuntime.run({\n env,\n renovateConfig,\n runtimeConfig,\n });\n\n return await promise.then(result => {\n return extractReport({\n logger,\n logStream: result.stdout,\n });\n });\n }\n\n async run(id: string, target: TargetRepo): Promise<void> {\n const logger = this.logger.child({ runID: id, ...target });\n logger.info('Renovate run starting');\n const report = await this.renovate(id, target, { logger });\n await this.databaseHandler.addReport({\n taskID: id,\n report,\n target,\n logger,\n });\n logger.info('Renovate run finished');\n }\n\n async schedule(\n target: string | EntityWithAnnotations | TargetRepo,\n ): Promise<void> {\n const id = getTaskID(target);\n const targetRepo = getTargetRepo(target);\n\n // try to trigger existing schedule and if this fails, start a run.\n try {\n await this.scheduler.triggerTask(id);\n return;\n } catch (e) {\n await this.runner.run({\n id,\n fn: () => this.run(id, targetRepo),\n });\n }\n }\n}\n","import { resolvePackagePath } from '@backstage/backend-common';\nimport { Knex } from 'knex';\nimport {\n AddReportParameters,\n DatabaseCreationParameters,\n ReportQueryParameters,\n ReportsRow,\n} from './types';\nimport { LoggerService } from '@backstage/backend-plugin-api';\nimport is from '@sindresorhus/is';\nimport { RepositoryReportResponse } from '@secustor/backstage-plugin-renovate-common';\n\nconst migrationsDir = resolvePackagePath(\n '@secustor/backstage-plugin-renovate-backend',\n 'migrations',\n);\n\nexport class DatabaseHandler {\n static async create(\n options: DatabaseCreationParameters,\n ): Promise<DatabaseHandler> {\n const { database, logger } = options;\n const client = await database.getClient();\n\n if (!database.migrations?.skip) {\n await client.migrate.latest({\n directory: migrationsDir,\n });\n }\n\n return new DatabaseHandler(client, logger);\n }\n\n private constructor(\n private client: Knex,\n private logger: LoggerService,\n ) {}\n\n async addReport(options: AddReportParameters): Promise<void> {\n const { taskID, report, target } = options;\n const logger = options.logger ?? this.logger;\n\n const inserts: ReportsRow[] = [];\n for (const [repository, value] of Object.entries(report.repositories)) {\n inserts.push({\n task_id: taskID,\n last_updated: Date.now(),\n host: target.host,\n repository,\n report: value,\n });\n }\n // this.client.batchInsert<ReportsRow>('reports', inserts);\n await this.client('reports')\n .insert(inserts)\n .onConflict('task_id')\n .merge()\n .catch(reason => logger.error('Failed insert data', reason));\n }\n\n async getReports(\n query?: ReportQueryParameters,\n ): Promise<RepositoryReportResponse> {\n const builder = this.client.select<ReportsRow[]>();\n if (query) {\n builder.where(query);\n }\n const rows = await builder.from<ReportsRow[]>('reports');\n return rows.map(row => {\n return {\n taskID: row.task_id,\n lastUpdated: new Date(row.last_updated).toISOString(),\n host: row.host,\n repository: row.repository,\n // if the JSON field has not been auto-parsed do it manually\n report: is.string(row.report) ? JSON.parse(row.report) : row.report,\n };\n });\n }\n}\n","import {\n coreServices,\n createBackendPlugin,\n} from '@backstage/backend-plugin-api';\nimport { createRouter } from './service/router';\nimport { renovateRuntimeExtensionPoint } from './extensionPoints';\nimport {\n EntityWithAnnotations,\n RenovateWrapper,\n} from '@secustor/backstage-plugin-renovate-common';\nimport {\n CatalogClient,\n CATALOG_FILTER_EXISTS,\n} from '@backstage/catalog-client';\nimport { ANNOTATION_SOURCE_LOCATION } from '@backstage/catalog-model';\nimport { RenovateRunner } from './wrapper';\nimport { RouterOptions } from './service/types';\nimport { DatabaseHandler } from './service/databaseHandler';\nimport { getScheduleDefinition } from './config';\n\nconst RENOVATE_ANNOTATION_KEEP_UPDATED = 'renovate.secustor.dev/keep-updated';\n\n/**\n * Renovate backend plugin\n *\n * @public\n */\nexport const renovatePlugin = createBackendPlugin({\n pluginId: 'renovate',\n register(env) {\n // allow modules provide additional runtimes\n const runtimes = new Map<string, RenovateWrapper>();\n env.registerExtensionPoint(renovateRuntimeExtensionPoint, {\n addRuntime(id: string, runtime: RenovateWrapper) {\n if (runtimes.has(id)) {\n throw new Error(\n ` ${id} has been already registered. Only one wrapper with the same ID is allowed to be registered`,\n );\n }\n runtimes.set(id, runtime);\n },\n });\n\n env.registerInit({\n deps: {\n rootConfig: coreServices.rootConfig,\n logger: coreServices.logger,\n httpRouter: coreServices.httpRouter,\n database: coreServices.database,\n scheduler: coreServices.scheduler,\n discovery: coreServices.discovery,\n auth: coreServices.auth,\n },\n async init(options) {\n const {\n auth,\n database,\n discovery,\n httpRouter,\n rootConfig,\n scheduler,\n logger,\n } = options;\n\n const routerOptions: RouterOptions = {\n ...options,\n databaseHandler: await DatabaseHandler.create({ database, logger }),\n pluginConfig: rootConfig.getConfig('renovate'),\n runtimes,\n };\n const renovateRunner = await RenovateRunner.from(routerOptions);\n const client = new CatalogClient({ discoveryApi: discovery });\n\n const schedule = getScheduleDefinition(routerOptions.pluginConfig);\n\n await scheduler.scheduleTask({\n id: `renovate/job-sync`,\n ...schedule,\n fn: async () => {\n const { token } = await auth.getPluginRequestToken({\n onBehalfOf: await auth.getOwnServiceCredentials(),\n targetPluginId: 'catalog',\n });\n const { items: entities } = await client.getEntities(\n {\n filter: {\n [`metadata.annotations.${RENOVATE_ANNOTATION_KEEP_UPDATED}`]:\n CATALOG_FILTER_EXISTS,\n [`metadata.annotations.${ANNOTATION_SOURCE_LOCATION}`]:\n CATALOG_FILTER_EXISTS,\n },\n fields: [\n 'kind',\n 'metadata.annotations',\n 'metadata.name',\n 'metadata.namespace',\n 'metadata.title',\n ],\n },\n { token },\n );\n\n for (const entity of entities) {\n renovateRunner.schedule(entity as EntityWithAnnotations);\n }\n },\n });\n\n httpRouter.use(await createRouter(renovateRunner, routerOptions));\n httpRouter.addAuthPolicy({\n path: '/health',\n allow: 'unauthenticated',\n });\n },\n });\n },\n});\n"],"names":["createValidatedOpenApiRouter","z","entityWithAnnotations","express","getTargetRepo","parseUrl","fetch","getTaskID","errorHandler","createExtensionPoint","ScmIntegrations","integration","is","readTaskScheduleDefinitionFromConfig","resolvePackagePath","createBackendPlugin","coreServices","CatalogClient","CATALOG_FILTER_EXISTS","ANNOTATION_SOURCE_LOCATION"],"mappings":"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"}
@@ -0,0 +1,78 @@
1
+ import express from 'express';
2
+ import { Config } from '@backstage/config';
3
+ import * as _backstage_backend_plugin_api from '@backstage/backend-plugin-api';
4
+ import { LoggerService, SchedulerService, DatabaseService } from '@backstage/backend-plugin-api';
5
+ import { RepositoryReportResponse, RenovateWrapper, RenovateReport, TargetRepo, EntityWithAnnotations } from '@secustor/backstage-plugin-renovate-common';
6
+ import { TaskRunner } from '@backstage/backend-tasks';
7
+
8
+ declare class DatabaseHandler {
9
+ private client;
10
+ private logger;
11
+ static create(options: DatabaseCreationParameters): Promise<DatabaseHandler>;
12
+ private constructor();
13
+ addReport(options: AddReportParameters): Promise<void>;
14
+ getReports(query?: ReportQueryParameters): Promise<RepositoryReportResponse>;
15
+ }
16
+
17
+ interface RouterOptions {
18
+ rootConfig: Config;
19
+ pluginConfig: Config;
20
+ logger: LoggerService;
21
+ databaseHandler: DatabaseHandler;
22
+ runtimes: Map<string, RenovateWrapper>;
23
+ scheduler: SchedulerService;
24
+ }
25
+ interface Context extends RouterOptions {
26
+ }
27
+ interface DatabaseCreationParameters {
28
+ database: DatabaseService;
29
+ logger: LoggerService;
30
+ }
31
+ interface ReportQueryParameters {
32
+ host?: string;
33
+ repository?: string;
34
+ }
35
+ interface AddReportParameters {
36
+ taskID: string;
37
+ report: RenovateReport;
38
+ target: TargetRepo;
39
+ logger?: LoggerService;
40
+ }
41
+
42
+ /***/
43
+ /**
44
+ * Node.js library for the renovate-wrapper plugin.
45
+ *
46
+ * @packageDocumentation
47
+ */
48
+
49
+ declare class RenovateRunner {
50
+ private scheduler;
51
+ private rootConfig;
52
+ private databaseHandler;
53
+ private pluginConfig;
54
+ private logger;
55
+ private runtimes;
56
+ private runner;
57
+ constructor(databaseHandler: DatabaseHandler, rootConfig: Config, pluginConfig: Config, logger: LoggerService, runtimes: Map<string, RenovateWrapper>, runner: TaskRunner, scheduler: SchedulerService);
58
+ static from(options: RouterOptions): Promise<RenovateRunner>;
59
+ private renovate;
60
+ run(id: string, target: TargetRepo): Promise<void>;
61
+ schedule(target: string | EntityWithAnnotations | TargetRepo): Promise<void>;
62
+ }
63
+
64
+ declare function createRouter(runner: RenovateRunner, options: RouterOptions): Promise<express.Router>;
65
+
66
+ /**
67
+ * Renovate backend plugin
68
+ *
69
+ * @public
70
+ */
71
+ declare const renovatePlugin: () => _backstage_backend_plugin_api.BackendFeature;
72
+
73
+ interface RenovateRuntimeExtensionPoint {
74
+ addRuntime(id: string, runtime: RenovateWrapper): void;
75
+ }
76
+ declare const renovateRuntimeExtensionPoint: _backstage_backend_plugin_api.ExtensionPoint<RenovateRuntimeExtensionPoint>;
77
+
78
+ export { type Context, type RenovateRuntimeExtensionPoint, createRouter, renovatePlugin as default, renovateRuntimeExtensionPoint };
@@ -0,0 +1,40 @@
1
+ // @ts-check
2
+
3
+ /**
4
+ * @param {import('knex').Knex} knex
5
+ */
6
+ exports.up = async function up(knex) {
7
+ await knex.schema.createTable('reports', table => {
8
+ table.comment('Table containing Renovate reports');
9
+ table
10
+ .string('task_id')
11
+ .notNullable()
12
+ .unique()
13
+ .comment('unique id of the Repository reoccurring task');
14
+ table
15
+ .time('last_updated')
16
+ .notNullable()
17
+ .comment('Time when the last report has been pushed');
18
+ table
19
+ .text('host')
20
+ .notNullable()
21
+ .comment('host of the git service e.g. github.com');
22
+ table
23
+ .text('repository')
24
+ .notNullable()
25
+ .comment(
26
+ 'organization or full group with repository e.g. "myOrg/myRepository" for github',
27
+ );
28
+ table
29
+ .json('report')
30
+ .notNullable()
31
+ .comment('Report of this repository as JSON blob');
32
+ });
33
+ };
34
+
35
+ /**
36
+ * @param {import('knex').Knex} knex
37
+ */
38
+ exports.down = async function down(knex) {
39
+ await knex.schema.dropTable('reports');
40
+ };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@secustor/backstage-plugin-renovate-backend",
3
- "version": "0.1.0",
3
+ "version": "0.1.2",
4
4
  "main": "dist/index.cjs.js",
5
5
  "types": "dist/index.d.ts",
6
6
  "type": "commonjs",
@@ -35,7 +35,7 @@
35
35
  "@backstage/config": "^1.2.0",
36
36
  "@backstage/integration": "^1.9.1",
37
37
  "@backstage/types": "^1.1.1",
38
- "@secustor/backstage-plugin-renovate-common": "^0.1.0",
38
+ "@secustor/backstage-plugin-renovate-common": "^0.1.1",
39
39
  "@sindresorhus/is": "^4.6.0",
40
40
  "@types/express": "*",
41
41
  "express": "^4.17.1",
@@ -58,6 +58,8 @@
58
58
  "supertest": "^6.2.4"
59
59
  },
60
60
  "files": [
61
- "dist"
61
+ "dist",
62
+ "migrations/**/*.{js,d.ts}",
63
+ "config.d.ts"
62
64
  ]
63
65
  }