@backstage/plugin-scaffolder-backend 1.4.0-next.2 → 1.5.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 +101 -4
- package/alpha/package.json +6 -0
- package/dist/index.alpha.d.ts +984 -0
- package/dist/index.beta.d.ts +980 -0
- package/dist/index.cjs.js +990 -314
- package/dist/index.cjs.js.map +1 -1
- package/dist/index.d.ts +669 -601
- package/package.json +23 -19
package/dist/index.cjs.js
CHANGED
|
@@ -36,6 +36,8 @@ var zod = require('zod');
|
|
|
36
36
|
var url = require('url');
|
|
37
37
|
var os = require('os');
|
|
38
38
|
var pluginCatalogBackend = require('@backstage/plugin-catalog-backend');
|
|
39
|
+
var backendPluginApi = require('@backstage/backend-plugin-api');
|
|
40
|
+
var pluginCatalogNode = require('@backstage/plugin-catalog-node');
|
|
39
41
|
|
|
40
42
|
function _interopDefaultLegacy (e) { return e && typeof e === 'object' && 'default' in e ? e : { 'default': e }; }
|
|
41
43
|
|
|
@@ -145,7 +147,9 @@ function createCatalogRegisterAction(options) {
|
|
|
145
147
|
const { repoContentsUrl, catalogInfoPath = "/catalog-info.yaml" } = input;
|
|
146
148
|
const integration = integrations.byUrl(repoContentsUrl);
|
|
147
149
|
if (!integration) {
|
|
148
|
-
throw new errors.InputError(
|
|
150
|
+
throw new errors.InputError(
|
|
151
|
+
`No integration found for host ${repoContentsUrl}`
|
|
152
|
+
);
|
|
149
153
|
}
|
|
150
154
|
catalogInfoUrl = integration.resolveUrl({
|
|
151
155
|
base: repoContentsUrl,
|
|
@@ -153,22 +157,32 @@ function createCatalogRegisterAction(options) {
|
|
|
153
157
|
});
|
|
154
158
|
}
|
|
155
159
|
ctx.logger.info(`Registering ${catalogInfoUrl} in the catalog`);
|
|
156
|
-
await catalogClient.addLocation(
|
|
157
|
-
|
|
158
|
-
target: catalogInfoUrl
|
|
159
|
-
}, ((_a = ctx.secrets) == null ? void 0 : _a.backstageToken) ? { token: ctx.secrets.backstageToken } : {});
|
|
160
|
-
try {
|
|
161
|
-
const result = await catalogClient.addLocation({
|
|
162
|
-
dryRun: true,
|
|
160
|
+
await catalogClient.addLocation(
|
|
161
|
+
{
|
|
163
162
|
type: "url",
|
|
164
163
|
target: catalogInfoUrl
|
|
165
|
-
},
|
|
164
|
+
},
|
|
165
|
+
((_a = ctx.secrets) == null ? void 0 : _a.backstageToken) ? { token: ctx.secrets.backstageToken } : {}
|
|
166
|
+
);
|
|
167
|
+
try {
|
|
168
|
+
const result = await catalogClient.addLocation(
|
|
169
|
+
{
|
|
170
|
+
dryRun: true,
|
|
171
|
+
type: "url",
|
|
172
|
+
target: catalogInfoUrl
|
|
173
|
+
},
|
|
174
|
+
((_b = ctx.secrets) == null ? void 0 : _b.backstageToken) ? { token: ctx.secrets.backstageToken } : {}
|
|
175
|
+
);
|
|
166
176
|
if (result.entities.length > 0) {
|
|
167
177
|
const { entities } = result;
|
|
168
178
|
let entity;
|
|
169
|
-
entity = entities.find(
|
|
179
|
+
entity = entities.find(
|
|
180
|
+
(e) => !e.metadata.name.startsWith("generated-") && e.kind === "Component"
|
|
181
|
+
);
|
|
170
182
|
if (!entity) {
|
|
171
|
-
entity = entities.find(
|
|
183
|
+
entity = entities.find(
|
|
184
|
+
(e) => !e.metadata.name.startsWith("generated-")
|
|
185
|
+
);
|
|
172
186
|
}
|
|
173
187
|
if (!entity) {
|
|
174
188
|
entity = entities[0];
|
|
@@ -211,7 +225,10 @@ function createCatalogWriteAction() {
|
|
|
211
225
|
ctx.logStream.write(`Writing catalog-info.yaml`);
|
|
212
226
|
const { filePath, entity } = ctx.input;
|
|
213
227
|
const path = filePath != null ? filePath : "catalog-info.yaml";
|
|
214
|
-
await fs__default["default"].writeFile(
|
|
228
|
+
await fs__default["default"].writeFile(
|
|
229
|
+
backendCommon.resolveSafeChildPath(ctx.workspacePath, path),
|
|
230
|
+
yaml__namespace.stringify(entity)
|
|
231
|
+
);
|
|
215
232
|
}
|
|
216
233
|
});
|
|
217
234
|
}
|
|
@@ -247,18 +264,22 @@ function createDebugLogAction() {
|
|
|
247
264
|
}
|
|
248
265
|
if ((_b = ctx.input) == null ? void 0 : _b.listWorkspace) {
|
|
249
266
|
const files = await recursiveReadDir(ctx.workspacePath);
|
|
250
|
-
ctx.logStream.write(
|
|
251
|
-
|
|
267
|
+
ctx.logStream.write(
|
|
268
|
+
`Workspace:
|
|
269
|
+
${files.map((f) => ` - ${path.relative(ctx.workspacePath, f)}`).join("\n")}`
|
|
270
|
+
);
|
|
252
271
|
}
|
|
253
272
|
}
|
|
254
273
|
});
|
|
255
274
|
}
|
|
256
275
|
async function recursiveReadDir(dir) {
|
|
257
276
|
const subdirs = await fs.readdir(dir);
|
|
258
|
-
const files = await Promise.all(
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
277
|
+
const files = await Promise.all(
|
|
278
|
+
subdirs.map(async (subdir) => {
|
|
279
|
+
const res = path.join(dir, subdir);
|
|
280
|
+
return (await fs.stat(res)).isDirectory() ? recursiveReadDir(res) : [res];
|
|
281
|
+
})
|
|
282
|
+
);
|
|
262
283
|
return files.reduce((a, f) => a.concat(f), []);
|
|
263
284
|
}
|
|
264
285
|
|
|
@@ -293,7 +314,9 @@ async function fetchContents({
|
|
|
293
314
|
base: baseUrl
|
|
294
315
|
});
|
|
295
316
|
} else {
|
|
296
|
-
throw new errors.InputError(
|
|
317
|
+
throw new errors.InputError(
|
|
318
|
+
`Failed to fetch, template location could not be determined and the fetch URL is relative, ${fetchUrl}`
|
|
319
|
+
);
|
|
297
320
|
}
|
|
298
321
|
const res = await reader.readTree(readUrl);
|
|
299
322
|
await fs__default["default"].ensureDir(outputPath);
|
|
@@ -422,13 +445,21 @@ class SecureTemplater {
|
|
|
422
445
|
sandbox.parseRepoUrl = (url) => JSON.stringify(parseRepoUrl(url));
|
|
423
446
|
}
|
|
424
447
|
if (additionalTemplateFilters) {
|
|
425
|
-
sandbox.additionalTemplateFilters = Object.fromEntries(
|
|
426
|
-
filterName,
|
|
427
|
-
|
|
428
|
-
|
|
448
|
+
sandbox.additionalTemplateFilters = Object.fromEntries(
|
|
449
|
+
Object.entries(additionalTemplateFilters).filter(([_, filterFunction]) => !!filterFunction).map(([filterName, filterFunction]) => [
|
|
450
|
+
filterName,
|
|
451
|
+
(...args) => JSON.stringify(filterFunction(...args))
|
|
452
|
+
])
|
|
453
|
+
);
|
|
429
454
|
}
|
|
430
455
|
const vm = new vm2.VM({ sandbox });
|
|
431
|
-
const nunjucksSource = await fs__default["default"].readFile(
|
|
456
|
+
const nunjucksSource = await fs__default["default"].readFile(
|
|
457
|
+
backendCommon.resolvePackagePath(
|
|
458
|
+
"@backstage/plugin-scaffolder-backend",
|
|
459
|
+
"assets/nunjucks.js.txt"
|
|
460
|
+
),
|
|
461
|
+
"utf-8"
|
|
462
|
+
);
|
|
432
463
|
vm.run(mkScript(nunjucksSource));
|
|
433
464
|
const render = (template, values) => {
|
|
434
465
|
if (!vm) {
|
|
@@ -471,13 +502,21 @@ function createFetchTemplateAction(options) {
|
|
|
471
502
|
type: "object"
|
|
472
503
|
},
|
|
473
504
|
copyWithoutRender: {
|
|
474
|
-
title: "Copy Without Render",
|
|
505
|
+
title: "[Deprecated] Copy Without Render",
|
|
475
506
|
description: "An array of glob patterns. Any files or directories which match are copied without being processed as templates.",
|
|
476
507
|
type: "array",
|
|
477
508
|
items: {
|
|
478
509
|
type: "string"
|
|
479
510
|
}
|
|
480
511
|
},
|
|
512
|
+
copyWithoutTemplating: {
|
|
513
|
+
title: "Copy Without Templating",
|
|
514
|
+
description: "An array of glob patterns. Contents of matched files or directories are copied without being processed, but paths are subject to rendering.",
|
|
515
|
+
type: "array",
|
|
516
|
+
items: {
|
|
517
|
+
type: "string"
|
|
518
|
+
}
|
|
519
|
+
},
|
|
481
520
|
cookiecutterCompat: {
|
|
482
521
|
title: "Cookiecutter compatibility mode",
|
|
483
522
|
description: "Enable features to maximise compatibility with templates built for fetch:cookiecutter",
|
|
@@ -499,11 +538,32 @@ function createFetchTemplateAction(options) {
|
|
|
499
538
|
const templateDir = backendCommon.resolveSafeChildPath(workDir, "template");
|
|
500
539
|
const targetPath = (_a = ctx.input.targetPath) != null ? _a : "./";
|
|
501
540
|
const outputDir = backendCommon.resolveSafeChildPath(ctx.workspacePath, targetPath);
|
|
502
|
-
if (ctx.input.copyWithoutRender &&
|
|
503
|
-
throw new errors.InputError(
|
|
541
|
+
if (ctx.input.copyWithoutRender && ctx.input.copyWithoutTemplating) {
|
|
542
|
+
throw new errors.InputError(
|
|
543
|
+
"Fetch action input copyWithoutRender and copyWithoutTemplating can not be used at the same time"
|
|
544
|
+
);
|
|
545
|
+
}
|
|
546
|
+
let copyOnlyPatterns;
|
|
547
|
+
let renderFilename;
|
|
548
|
+
if (ctx.input.copyWithoutRender) {
|
|
549
|
+
ctx.logger.warn(
|
|
550
|
+
"[Deprecated] Please use copyWithoutTemplating instead."
|
|
551
|
+
);
|
|
552
|
+
copyOnlyPatterns = ctx.input.copyWithoutRender;
|
|
553
|
+
renderFilename = false;
|
|
554
|
+
} else {
|
|
555
|
+
copyOnlyPatterns = ctx.input.copyWithoutTemplating;
|
|
556
|
+
renderFilename = true;
|
|
557
|
+
}
|
|
558
|
+
if (copyOnlyPatterns && !Array.isArray(copyOnlyPatterns)) {
|
|
559
|
+
throw new errors.InputError(
|
|
560
|
+
"Fetch action input copyWithoutRender/copyWithoutTemplating must be an Array"
|
|
561
|
+
);
|
|
504
562
|
}
|
|
505
|
-
if (ctx.input.templateFileExtension && (
|
|
506
|
-
throw new errors.InputError(
|
|
563
|
+
if (ctx.input.templateFileExtension && (copyOnlyPatterns || ctx.input.cookiecutterCompat)) {
|
|
564
|
+
throw new errors.InputError(
|
|
565
|
+
"Fetch action input extension incompatible with copyWithoutRender/copyWithoutTemplating and cookiecutterCompat"
|
|
566
|
+
);
|
|
507
567
|
}
|
|
508
568
|
let extension = false;
|
|
509
569
|
if (ctx.input.templateFileExtension) {
|
|
@@ -527,58 +587,84 @@ function createFetchTemplateAction(options) {
|
|
|
527
587
|
markDirectories: true,
|
|
528
588
|
followSymbolicLinks: false
|
|
529
589
|
});
|
|
530
|
-
const nonTemplatedEntries = new Set(
|
|
531
|
-
|
|
532
|
-
|
|
533
|
-
|
|
534
|
-
|
|
535
|
-
|
|
536
|
-
|
|
590
|
+
const nonTemplatedEntries = new Set(
|
|
591
|
+
(await Promise.all(
|
|
592
|
+
(copyOnlyPatterns || []).map(
|
|
593
|
+
(pattern) => globby__default["default"](pattern, {
|
|
594
|
+
cwd: templateDir,
|
|
595
|
+
dot: true,
|
|
596
|
+
onlyFiles: false,
|
|
597
|
+
markDirectories: true,
|
|
598
|
+
followSymbolicLinks: false
|
|
599
|
+
})
|
|
600
|
+
)
|
|
601
|
+
)).flat()
|
|
602
|
+
);
|
|
537
603
|
const { cookiecutterCompat, values } = ctx.input;
|
|
538
604
|
const context = {
|
|
539
605
|
[cookiecutterCompat ? "cookiecutter" : "values"]: values
|
|
540
606
|
};
|
|
541
|
-
ctx.logger.info(
|
|
607
|
+
ctx.logger.info(
|
|
608
|
+
`Processing ${allEntriesInTemplate.length} template files/directories with input values`,
|
|
609
|
+
ctx.input.values
|
|
610
|
+
);
|
|
542
611
|
const renderTemplate = await SecureTemplater.loadRenderer({
|
|
543
612
|
cookiecutterCompat: ctx.input.cookiecutterCompat,
|
|
544
613
|
additionalTemplateFilters
|
|
545
614
|
});
|
|
546
615
|
for (const location of allEntriesInTemplate) {
|
|
547
|
-
let renderFilename;
|
|
548
616
|
let renderContents;
|
|
549
617
|
let localOutputPath = location;
|
|
550
618
|
if (extension) {
|
|
551
|
-
renderFilename = true;
|
|
552
619
|
renderContents = path.extname(localOutputPath) === extension;
|
|
553
620
|
if (renderContents) {
|
|
554
621
|
localOutputPath = localOutputPath.slice(0, -extension.length);
|
|
555
622
|
}
|
|
623
|
+
localOutputPath = renderTemplate(localOutputPath, context);
|
|
556
624
|
} else {
|
|
557
|
-
|
|
625
|
+
renderContents = !nonTemplatedEntries.has(location);
|
|
626
|
+
if (renderFilename) {
|
|
627
|
+
localOutputPath = renderTemplate(localOutputPath, context);
|
|
628
|
+
} else {
|
|
629
|
+
localOutputPath = renderContents ? renderTemplate(localOutputPath, context) : localOutputPath;
|
|
630
|
+
}
|
|
558
631
|
}
|
|
559
|
-
if (
|
|
560
|
-
|
|
632
|
+
if (containsSkippedContent(localOutputPath)) {
|
|
633
|
+
continue;
|
|
561
634
|
}
|
|
562
635
|
const outputPath = backendCommon.resolveSafeChildPath(outputDir, localOutputPath);
|
|
563
|
-
if (
|
|
636
|
+
if (fs__default["default"].existsSync(outputPath)) {
|
|
564
637
|
continue;
|
|
565
638
|
}
|
|
566
639
|
if (!renderContents && !extension) {
|
|
567
|
-
ctx.logger.info(
|
|
640
|
+
ctx.logger.info(
|
|
641
|
+
`Copying file/directory ${location} without processing.`
|
|
642
|
+
);
|
|
568
643
|
}
|
|
569
644
|
if (location.endsWith("/")) {
|
|
570
|
-
ctx.logger.info(
|
|
645
|
+
ctx.logger.info(
|
|
646
|
+
`Writing directory ${location} to template output path.`
|
|
647
|
+
);
|
|
571
648
|
await fs__default["default"].ensureDir(outputPath);
|
|
572
649
|
} else {
|
|
573
650
|
const inputFilePath = backendCommon.resolveSafeChildPath(templateDir, location);
|
|
574
|
-
|
|
575
|
-
|
|
651
|
+
const stats = await fs__default["default"].promises.lstat(inputFilePath);
|
|
652
|
+
if (stats.isSymbolicLink() || await isbinaryfile.isBinaryFile(inputFilePath)) {
|
|
653
|
+
ctx.logger.info(
|
|
654
|
+
`Copying file binary or symbolic link at ${location}, to template output path.`
|
|
655
|
+
);
|
|
576
656
|
await fs__default["default"].copy(inputFilePath, outputPath);
|
|
577
657
|
} else {
|
|
578
658
|
const statsObj = await fs__default["default"].stat(inputFilePath);
|
|
579
|
-
ctx.logger.info(
|
|
659
|
+
ctx.logger.info(
|
|
660
|
+
`Writing file ${location} to template output path with mode ${statsObj.mode}.`
|
|
661
|
+
);
|
|
580
662
|
const inputFileContents = await fs__default["default"].readFile(inputFilePath, "utf-8");
|
|
581
|
-
await fs__default["default"].outputFile(
|
|
663
|
+
await fs__default["default"].outputFile(
|
|
664
|
+
outputPath,
|
|
665
|
+
renderContents ? renderTemplate(inputFileContents, context) : inputFileContents,
|
|
666
|
+
{ mode: statsObj.mode }
|
|
667
|
+
);
|
|
582
668
|
}
|
|
583
669
|
}
|
|
584
670
|
}
|
|
@@ -586,6 +672,9 @@ function createFetchTemplateAction(options) {
|
|
|
586
672
|
}
|
|
587
673
|
});
|
|
588
674
|
}
|
|
675
|
+
function containsSkippedContent(localOutputPath) {
|
|
676
|
+
return localOutputPath === "" || path__default["default"].isAbsolute(localOutputPath) || localOutputPath.includes(`${path__default["default"].sep}${path__default["default"].sep}`);
|
|
677
|
+
}
|
|
589
678
|
|
|
590
679
|
const createFilesystemDeleteAction = () => {
|
|
591
680
|
return createTemplateAction({
|
|
@@ -672,15 +761,23 @@ const createFilesystemRenameAction = () => {
|
|
|
672
761
|
if (!file.from || !file.to) {
|
|
673
762
|
throw new errors.InputError("each file must have a from and to property");
|
|
674
763
|
}
|
|
675
|
-
const sourceFilepath = backendCommon.resolveSafeChildPath(
|
|
764
|
+
const sourceFilepath = backendCommon.resolveSafeChildPath(
|
|
765
|
+
ctx.workspacePath,
|
|
766
|
+
file.from
|
|
767
|
+
);
|
|
676
768
|
const destFilepath = backendCommon.resolveSafeChildPath(ctx.workspacePath, file.to);
|
|
677
769
|
try {
|
|
678
770
|
await fs__default["default"].move(sourceFilepath, destFilepath, {
|
|
679
771
|
overwrite: (_b = file.overwrite) != null ? _b : false
|
|
680
772
|
});
|
|
681
|
-
ctx.logger.info(
|
|
773
|
+
ctx.logger.info(
|
|
774
|
+
`File ${sourceFilepath} renamed to ${destFilepath} successfully`
|
|
775
|
+
);
|
|
682
776
|
} catch (err) {
|
|
683
|
-
ctx.logger.error(
|
|
777
|
+
ctx.logger.error(
|
|
778
|
+
`Failed to rename file ${sourceFilepath} to ${destFilepath}:`,
|
|
779
|
+
err
|
|
780
|
+
);
|
|
684
781
|
throw err;
|
|
685
782
|
}
|
|
686
783
|
}
|
|
@@ -690,7 +787,10 @@ const createFilesystemRenameAction = () => {
|
|
|
690
787
|
|
|
691
788
|
const getRepoSourceDirectory = (workspacePath, sourcePath) => {
|
|
692
789
|
if (sourcePath) {
|
|
693
|
-
const safeSuffix = path.normalize(sourcePath).replace(
|
|
790
|
+
const safeSuffix = path.normalize(sourcePath).replace(
|
|
791
|
+
/^(\.\.(\/|\\|$))+/,
|
|
792
|
+
""
|
|
793
|
+
);
|
|
694
794
|
const path$1 = path.join(workspacePath, safeSuffix);
|
|
695
795
|
if (!backendCommon.isChildPath(workspacePath, path$1)) {
|
|
696
796
|
throw new Error("Invalid source path");
|
|
@@ -705,7 +805,9 @@ const parseRepoUrl = (repoUrl, integrations) => {
|
|
|
705
805
|
try {
|
|
706
806
|
parsed = new URL(`https://${repoUrl}`);
|
|
707
807
|
} catch (error) {
|
|
708
|
-
throw new errors.InputError(
|
|
808
|
+
throw new errors.InputError(
|
|
809
|
+
`Invalid repo URL passed to publisher, got ${repoUrl}, ${error}`
|
|
810
|
+
);
|
|
709
811
|
}
|
|
710
812
|
const host = parsed.host;
|
|
711
813
|
const owner = (_a = parsed.searchParams.get("owner")) != null ? _a : void 0;
|
|
@@ -714,25 +816,35 @@ const parseRepoUrl = (repoUrl, integrations) => {
|
|
|
714
816
|
const project = (_d = parsed.searchParams.get("project")) != null ? _d : void 0;
|
|
715
817
|
const type = (_e = integrations.byHost(host)) == null ? void 0 : _e.type;
|
|
716
818
|
if (!type) {
|
|
717
|
-
throw new errors.InputError(
|
|
819
|
+
throw new errors.InputError(
|
|
820
|
+
`No matching integration configuration for host ${host}, please check your integrations config`
|
|
821
|
+
);
|
|
718
822
|
}
|
|
719
823
|
if (type === "bitbucket") {
|
|
720
824
|
if (host === "bitbucket.org") {
|
|
721
825
|
if (!workspace) {
|
|
722
|
-
throw new errors.InputError(
|
|
826
|
+
throw new errors.InputError(
|
|
827
|
+
`Invalid repo URL passed to publisher: ${repoUrl}, missing workspace`
|
|
828
|
+
);
|
|
723
829
|
}
|
|
724
830
|
}
|
|
725
831
|
if (!project) {
|
|
726
|
-
throw new errors.InputError(
|
|
832
|
+
throw new errors.InputError(
|
|
833
|
+
`Invalid repo URL passed to publisher: ${repoUrl}, missing project`
|
|
834
|
+
);
|
|
727
835
|
}
|
|
728
836
|
} else {
|
|
729
|
-
if (!owner) {
|
|
730
|
-
throw new errors.InputError(
|
|
837
|
+
if (!owner && type !== "gerrit") {
|
|
838
|
+
throw new errors.InputError(
|
|
839
|
+
`Invalid repo URL passed to publisher: ${repoUrl}, missing owner`
|
|
840
|
+
);
|
|
731
841
|
}
|
|
732
842
|
}
|
|
733
843
|
const repo = parsed.searchParams.get("repo");
|
|
734
844
|
if (!repo) {
|
|
735
|
-
throw new errors.InputError(
|
|
845
|
+
throw new errors.InputError(
|
|
846
|
+
`Invalid repo URL passed to publisher: ${repoUrl}, missing repo`
|
|
847
|
+
);
|
|
736
848
|
}
|
|
737
849
|
return { host, owner, repo, organization, workspace, project };
|
|
738
850
|
};
|
|
@@ -757,7 +869,9 @@ const executeShellCommand = async (options) => {
|
|
|
757
869
|
});
|
|
758
870
|
process.on("close", (code) => {
|
|
759
871
|
if (code !== 0) {
|
|
760
|
-
return reject(
|
|
872
|
+
return reject(
|
|
873
|
+
new Error(`Command ${command} failed, exit code: ${code}`)
|
|
874
|
+
);
|
|
761
875
|
}
|
|
762
876
|
return resolve();
|
|
763
877
|
});
|
|
@@ -774,8 +888,7 @@ async function initRepoAndPush({
|
|
|
774
888
|
}) {
|
|
775
889
|
var _a, _b;
|
|
776
890
|
const git = backendCommon.Git.fromAuth({
|
|
777
|
-
|
|
778
|
-
password: auth.password,
|
|
891
|
+
...auth,
|
|
779
892
|
logger
|
|
780
893
|
});
|
|
781
894
|
await git.init({
|
|
@@ -803,6 +916,39 @@ async function initRepoAndPush({
|
|
|
803
916
|
remote: "origin"
|
|
804
917
|
});
|
|
805
918
|
}
|
|
919
|
+
async function commitAndPushRepo({
|
|
920
|
+
dir,
|
|
921
|
+
auth,
|
|
922
|
+
logger,
|
|
923
|
+
commitMessage,
|
|
924
|
+
gitAuthorInfo,
|
|
925
|
+
branch = "master",
|
|
926
|
+
remoteRef
|
|
927
|
+
}) {
|
|
928
|
+
var _a, _b;
|
|
929
|
+
const git = backendCommon.Git.fromAuth({
|
|
930
|
+
...auth,
|
|
931
|
+
logger
|
|
932
|
+
});
|
|
933
|
+
await git.fetch({ dir });
|
|
934
|
+
await git.checkout({ dir, ref: branch });
|
|
935
|
+
await git.add({ dir, filepath: "." });
|
|
936
|
+
const authorInfo = {
|
|
937
|
+
name: (_a = gitAuthorInfo == null ? void 0 : gitAuthorInfo.name) != null ? _a : "Scaffolder",
|
|
938
|
+
email: (_b = gitAuthorInfo == null ? void 0 : gitAuthorInfo.email) != null ? _b : "scaffolder@backstage.io"
|
|
939
|
+
};
|
|
940
|
+
await git.commit({
|
|
941
|
+
dir,
|
|
942
|
+
message: commitMessage,
|
|
943
|
+
author: authorInfo,
|
|
944
|
+
committer: authorInfo
|
|
945
|
+
});
|
|
946
|
+
await git.push({
|
|
947
|
+
dir,
|
|
948
|
+
remote: "origin",
|
|
949
|
+
remoteRef: remoteRef != null ? remoteRef : `refs/heads/${branch}`
|
|
950
|
+
});
|
|
951
|
+
}
|
|
806
952
|
const enableBranchProtectionOnDefaultRepoBranch = async ({
|
|
807
953
|
repoName,
|
|
808
954
|
client,
|
|
@@ -810,7 +956,8 @@ const enableBranchProtectionOnDefaultRepoBranch = async ({
|
|
|
810
956
|
logger,
|
|
811
957
|
requireCodeOwnerReviews,
|
|
812
958
|
requiredStatusCheckContexts = [],
|
|
813
|
-
defaultBranch = "master"
|
|
959
|
+
defaultBranch = "master",
|
|
960
|
+
enforceAdmins = true
|
|
814
961
|
}) => {
|
|
815
962
|
const tryOnce = async () => {
|
|
816
963
|
try {
|
|
@@ -826,7 +973,7 @@ const enableBranchProtectionOnDefaultRepoBranch = async ({
|
|
|
826
973
|
contexts: requiredStatusCheckContexts
|
|
827
974
|
},
|
|
828
975
|
restrictions: null,
|
|
829
|
-
enforce_admins:
|
|
976
|
+
enforce_admins: enforceAdmins,
|
|
830
977
|
required_pull_request_reviews: {
|
|
831
978
|
required_approving_review_count: 1,
|
|
832
979
|
require_code_owner_reviews: requireCodeOwnerReviews
|
|
@@ -834,8 +981,12 @@ const enableBranchProtectionOnDefaultRepoBranch = async ({
|
|
|
834
981
|
});
|
|
835
982
|
} catch (e) {
|
|
836
983
|
errors.assertError(e);
|
|
837
|
-
if (e.message.includes(
|
|
838
|
-
|
|
984
|
+
if (e.message.includes(
|
|
985
|
+
"Upgrade to GitHub Pro or make this repository public to enable this feature"
|
|
986
|
+
)) {
|
|
987
|
+
logger.warn(
|
|
988
|
+
"Branch protection was not enabled as it requires GitHub Pro for private repositories"
|
|
989
|
+
);
|
|
839
990
|
} else {
|
|
840
991
|
throw e;
|
|
841
992
|
}
|
|
@@ -877,10 +1028,14 @@ async function getOctokitOptions(options) {
|
|
|
877
1028
|
}
|
|
878
1029
|
const githubCredentialsProvider = credentialsProvider != null ? credentialsProvider : integration.DefaultGithubCredentialsProvider.fromIntegrations(integrations);
|
|
879
1030
|
const { token: credentialProviderToken } = await githubCredentialsProvider.getCredentials({
|
|
880
|
-
url: `https://${host}/${encodeURIComponent(owner)}/${encodeURIComponent(
|
|
1031
|
+
url: `https://${host}/${encodeURIComponent(owner)}/${encodeURIComponent(
|
|
1032
|
+
repo
|
|
1033
|
+
)}`
|
|
881
1034
|
});
|
|
882
1035
|
if (!credentialProviderToken) {
|
|
883
|
-
throw new errors.InputError(
|
|
1036
|
+
throw new errors.InputError(
|
|
1037
|
+
`No token available for host: ${host}, with owner ${owner}, and repo ${repo}`
|
|
1038
|
+
);
|
|
884
1039
|
}
|
|
885
1040
|
return {
|
|
886
1041
|
auth: credentialProviderToken,
|
|
@@ -917,9 +1072,13 @@ async function createGithubRepoWithCollaboratorsAndTopics(client, repo, owner, r
|
|
|
917
1072
|
} catch (e) {
|
|
918
1073
|
errors.assertError(e);
|
|
919
1074
|
if (e.message === "Resource not accessible by integration") {
|
|
920
|
-
logger.warn(
|
|
1075
|
+
logger.warn(
|
|
1076
|
+
`The GitHub app or token provided may not have the required permissions to create the ${user.data.type} repository ${owner}/${repo}.`
|
|
1077
|
+
);
|
|
921
1078
|
}
|
|
922
|
-
throw new Error(
|
|
1079
|
+
throw new Error(
|
|
1080
|
+
`Failed to create the ${user.data.type} repository ${owner}/${repo}, ${e.message}`
|
|
1081
|
+
);
|
|
923
1082
|
}
|
|
924
1083
|
if (access == null ? void 0 : access.startsWith(`${owner}/`)) {
|
|
925
1084
|
const [, team] = access.split("/");
|
|
@@ -960,7 +1119,9 @@ async function createGithubRepoWithCollaboratorsAndTopics(client, repo, owner, r
|
|
|
960
1119
|
} catch (e) {
|
|
961
1120
|
errors.assertError(e);
|
|
962
1121
|
const name = extractCollaboratorName(collaborator);
|
|
963
|
-
logger.warn(
|
|
1122
|
+
logger.warn(
|
|
1123
|
+
`Skipping ${collaborator.access} access for ${name}, ${e.message}`
|
|
1124
|
+
);
|
|
964
1125
|
}
|
|
965
1126
|
}
|
|
966
1127
|
}
|
|
@@ -978,7 +1139,7 @@ async function createGithubRepoWithCollaboratorsAndTopics(client, repo, owner, r
|
|
|
978
1139
|
}
|
|
979
1140
|
return newRepo;
|
|
980
1141
|
}
|
|
981
|
-
async function initRepoPushAndProtect(remoteUrl, password, workspacePath, sourcePath, defaultBranch, protectDefaultBranch, owner, client, repo, requireCodeOwnerReviews, requiredStatusCheckContexts, config, logger, gitCommitMessage, gitAuthorName, gitAuthorEmail) {
|
|
1142
|
+
async function initRepoPushAndProtect(remoteUrl, password, workspacePath, sourcePath, defaultBranch, protectDefaultBranch, protectEnforceAdmins, owner, client, repo, requireCodeOwnerReviews, requiredStatusCheckContexts, config, logger, gitCommitMessage, gitAuthorName, gitAuthorEmail) {
|
|
982
1143
|
const gitAuthorInfo = {
|
|
983
1144
|
name: gitAuthorName ? gitAuthorName : config.getOptionalString("scaffolder.defaultAuthor.name"),
|
|
984
1145
|
email: gitAuthorEmail ? gitAuthorEmail : config.getOptionalString("scaffolder.defaultAuthor.email")
|
|
@@ -1005,11 +1166,14 @@ async function initRepoPushAndProtect(remoteUrl, password, workspacePath, source
|
|
|
1005
1166
|
logger,
|
|
1006
1167
|
defaultBranch,
|
|
1007
1168
|
requireCodeOwnerReviews,
|
|
1008
|
-
requiredStatusCheckContexts
|
|
1169
|
+
requiredStatusCheckContexts,
|
|
1170
|
+
enforceAdmins: protectEnforceAdmins
|
|
1009
1171
|
});
|
|
1010
1172
|
} catch (e) {
|
|
1011
1173
|
errors.assertError(e);
|
|
1012
|
-
logger.warn(
|
|
1174
|
+
logger.warn(
|
|
1175
|
+
`Skipping: default branch protection on '${repo}', ${e.message}`
|
|
1176
|
+
);
|
|
1013
1177
|
}
|
|
1014
1178
|
}
|
|
1015
1179
|
}
|
|
@@ -1067,17 +1231,21 @@ function createGithubActionsDispatchAction(options) {
|
|
|
1067
1231
|
workflowInputs,
|
|
1068
1232
|
token: providedToken
|
|
1069
1233
|
} = ctx.input;
|
|
1070
|
-
ctx.logger.info(
|
|
1234
|
+
ctx.logger.info(
|
|
1235
|
+
`Dispatching workflow ${workflowId} for repo ${repoUrl} on ${branchOrTagName}`
|
|
1236
|
+
);
|
|
1071
1237
|
const { owner, repo } = parseRepoUrl(repoUrl, integrations);
|
|
1072
1238
|
if (!owner) {
|
|
1073
1239
|
throw new errors.InputError("Invalid repository owner provided in repoUrl");
|
|
1074
1240
|
}
|
|
1075
|
-
const client = new octokit.Octokit(
|
|
1076
|
-
|
|
1077
|
-
|
|
1078
|
-
|
|
1079
|
-
|
|
1080
|
-
|
|
1241
|
+
const client = new octokit.Octokit(
|
|
1242
|
+
await getOctokitOptions({
|
|
1243
|
+
integrations,
|
|
1244
|
+
repoUrl,
|
|
1245
|
+
credentialsProvider: githubCredentialsProvider,
|
|
1246
|
+
token: providedToken
|
|
1247
|
+
})
|
|
1248
|
+
);
|
|
1081
1249
|
await client.rest.actions.createWorkflowDispatch({
|
|
1082
1250
|
owner,
|
|
1083
1251
|
repo,
|
|
@@ -1133,12 +1301,14 @@ function createGithubIssuesLabelAction(options) {
|
|
|
1133
1301
|
if (!owner) {
|
|
1134
1302
|
throw new errors.InputError("Invalid repository owner provided in repoUrl");
|
|
1135
1303
|
}
|
|
1136
|
-
const client = new octokit.Octokit(
|
|
1137
|
-
|
|
1138
|
-
|
|
1139
|
-
|
|
1140
|
-
|
|
1141
|
-
|
|
1304
|
+
const client = new octokit.Octokit(
|
|
1305
|
+
await getOctokitOptions({
|
|
1306
|
+
integrations,
|
|
1307
|
+
credentialsProvider: githubCredentialsProvider,
|
|
1308
|
+
repoUrl,
|
|
1309
|
+
token: providedToken
|
|
1310
|
+
})
|
|
1311
|
+
);
|
|
1142
1312
|
try {
|
|
1143
1313
|
await client.rest.issues.addLabels({
|
|
1144
1314
|
owner,
|
|
@@ -1148,7 +1318,9 @@ function createGithubIssuesLabelAction(options) {
|
|
|
1148
1318
|
});
|
|
1149
1319
|
} catch (e) {
|
|
1150
1320
|
errors.assertError(e);
|
|
1151
|
-
ctx.logger.warn(
|
|
1321
|
+
ctx.logger.warn(
|
|
1322
|
+
`Failed: adding labels to issue: '${number}' on repo: '${repo}', ${e.message}`
|
|
1323
|
+
);
|
|
1152
1324
|
}
|
|
1153
1325
|
}
|
|
1154
1326
|
});
|
|
@@ -1264,6 +1436,11 @@ const protectDefaultBranch = {
|
|
|
1264
1436
|
type: "boolean",
|
|
1265
1437
|
description: `Protect the default branch after creating the repository. The default value is 'true'`
|
|
1266
1438
|
};
|
|
1439
|
+
const protectEnforceAdmins = {
|
|
1440
|
+
title: "Enforce Admins On Protected Branches",
|
|
1441
|
+
type: "boolean",
|
|
1442
|
+
description: `Enforce admins to adhere to default branch protection. The default value is 'true'`
|
|
1443
|
+
};
|
|
1267
1444
|
const gitCommitMessage = {
|
|
1268
1445
|
title: "Git Commit Message",
|
|
1269
1446
|
type: "string",
|
|
@@ -1342,7 +1519,21 @@ function createGithubRepoCreateAction(options) {
|
|
|
1342
1519
|
if (!owner) {
|
|
1343
1520
|
throw new errors.InputError("Invalid repository owner provided in repoUrl");
|
|
1344
1521
|
}
|
|
1345
|
-
const newRepo = await createGithubRepoWithCollaboratorsAndTopics(
|
|
1522
|
+
const newRepo = await createGithubRepoWithCollaboratorsAndTopics(
|
|
1523
|
+
client,
|
|
1524
|
+
repo,
|
|
1525
|
+
owner,
|
|
1526
|
+
repoVisibility,
|
|
1527
|
+
description,
|
|
1528
|
+
deleteBranchOnMerge,
|
|
1529
|
+
allowMergeCommit,
|
|
1530
|
+
allowSquashMerge,
|
|
1531
|
+
allowRebaseMerge,
|
|
1532
|
+
access,
|
|
1533
|
+
collaborators,
|
|
1534
|
+
topics,
|
|
1535
|
+
ctx.logger
|
|
1536
|
+
);
|
|
1346
1537
|
ctx.output("remoteUrl", newRepo.clone_url);
|
|
1347
1538
|
}
|
|
1348
1539
|
});
|
|
@@ -1363,6 +1554,7 @@ function createGithubRepoPushAction(options) {
|
|
|
1363
1554
|
requiredStatusCheckContexts: requiredStatusCheckContexts,
|
|
1364
1555
|
defaultBranch: defaultBranch,
|
|
1365
1556
|
protectDefaultBranch: protectDefaultBranch,
|
|
1557
|
+
protectEnforceAdmins: protectEnforceAdmins,
|
|
1366
1558
|
gitCommitMessage: gitCommitMessage,
|
|
1367
1559
|
gitAuthorName: gitAuthorName,
|
|
1368
1560
|
gitAuthorEmail: gitAuthorEmail,
|
|
@@ -1383,6 +1575,7 @@ function createGithubRepoPushAction(options) {
|
|
|
1383
1575
|
repoUrl,
|
|
1384
1576
|
defaultBranch = "master",
|
|
1385
1577
|
protectDefaultBranch = true,
|
|
1578
|
+
protectEnforceAdmins = true,
|
|
1386
1579
|
gitCommitMessage = "initial commit",
|
|
1387
1580
|
gitAuthorName,
|
|
1388
1581
|
gitAuthorEmail,
|
|
@@ -1404,7 +1597,25 @@ function createGithubRepoPushAction(options) {
|
|
|
1404
1597
|
const targetRepo = await client.rest.repos.get({ owner, repo });
|
|
1405
1598
|
const remoteUrl = targetRepo.data.clone_url;
|
|
1406
1599
|
const repoContentsUrl = `${targetRepo.data.html_url}/blob/${defaultBranch}`;
|
|
1407
|
-
await initRepoPushAndProtect(
|
|
1600
|
+
await initRepoPushAndProtect(
|
|
1601
|
+
remoteUrl,
|
|
1602
|
+
octokitOptions.auth,
|
|
1603
|
+
ctx.workspacePath,
|
|
1604
|
+
ctx.input.sourcePath,
|
|
1605
|
+
defaultBranch,
|
|
1606
|
+
protectDefaultBranch,
|
|
1607
|
+
protectEnforceAdmins,
|
|
1608
|
+
owner,
|
|
1609
|
+
client,
|
|
1610
|
+
repo,
|
|
1611
|
+
requireCodeOwnerReviews,
|
|
1612
|
+
requiredStatusCheckContexts,
|
|
1613
|
+
config,
|
|
1614
|
+
ctx.logger,
|
|
1615
|
+
gitCommitMessage,
|
|
1616
|
+
gitAuthorName,
|
|
1617
|
+
gitAuthorEmail
|
|
1618
|
+
);
|
|
1408
1619
|
ctx.output("remoteUrl", remoteUrl);
|
|
1409
1620
|
ctx.output("repoContentsUrl", repoContentsUrl);
|
|
1410
1621
|
}
|
|
@@ -1496,12 +1707,14 @@ function createGithubWebhookAction(options) {
|
|
|
1496
1707
|
if (!owner) {
|
|
1497
1708
|
throw new errors.InputError("Invalid repository owner provided in repoUrl");
|
|
1498
1709
|
}
|
|
1499
|
-
const client = new octokit.Octokit(
|
|
1500
|
-
|
|
1501
|
-
|
|
1502
|
-
|
|
1503
|
-
|
|
1504
|
-
|
|
1710
|
+
const client = new octokit.Octokit(
|
|
1711
|
+
await getOctokitOptions({
|
|
1712
|
+
integrations,
|
|
1713
|
+
credentialsProvider: githubCredentialsProvider,
|
|
1714
|
+
repoUrl,
|
|
1715
|
+
token: providedToken
|
|
1716
|
+
})
|
|
1717
|
+
);
|
|
1505
1718
|
try {
|
|
1506
1719
|
const insecure_ssl = insecureSsl ? "1" : "0";
|
|
1507
1720
|
await client.rest.repos.createWebhook({
|
|
@@ -1519,7 +1732,9 @@ function createGithubWebhookAction(options) {
|
|
|
1519
1732
|
ctx.logger.info(`Webhook '${webhookUrl}' created successfully`);
|
|
1520
1733
|
} catch (e) {
|
|
1521
1734
|
errors.assertError(e);
|
|
1522
|
-
ctx.logger.warn(
|
|
1735
|
+
ctx.logger.warn(
|
|
1736
|
+
`Failed: create webhook '${webhookUrl}' on repo: '${repo}', ${e.message}`
|
|
1737
|
+
);
|
|
1523
1738
|
}
|
|
1524
1739
|
}
|
|
1525
1740
|
});
|
|
@@ -1598,13 +1813,20 @@ function createPublishAzureAction(options) {
|
|
|
1598
1813
|
gitAuthorName,
|
|
1599
1814
|
gitAuthorEmail
|
|
1600
1815
|
} = ctx.input;
|
|
1601
|
-
const { owner, repo, host, organization } = parseRepoUrl(
|
|
1816
|
+
const { owner, repo, host, organization } = parseRepoUrl(
|
|
1817
|
+
repoUrl,
|
|
1818
|
+
integrations
|
|
1819
|
+
);
|
|
1602
1820
|
if (!organization) {
|
|
1603
|
-
throw new errors.InputError(
|
|
1821
|
+
throw new errors.InputError(
|
|
1822
|
+
`Invalid URL provider was included in the repo URL to create ${ctx.input.repoUrl}, missing organization`
|
|
1823
|
+
);
|
|
1604
1824
|
}
|
|
1605
1825
|
const integrationConfig = integrations.azure.byHost(host);
|
|
1606
1826
|
if (!integrationConfig) {
|
|
1607
|
-
throw new errors.InputError(
|
|
1827
|
+
throw new errors.InputError(
|
|
1828
|
+
`No matching integration configuration for host ${host}, please check your integrations config`
|
|
1829
|
+
);
|
|
1608
1830
|
}
|
|
1609
1831
|
if (!integrationConfig.config.token && !ctx.input.token) {
|
|
1610
1832
|
throw new errors.InputError(`No token provided for Azure Integration ${host}`);
|
|
@@ -1616,12 +1838,16 @@ function createPublishAzureAction(options) {
|
|
|
1616
1838
|
const createOptions = { name: repo };
|
|
1617
1839
|
const returnedRepo = await client.createRepository(createOptions, owner);
|
|
1618
1840
|
if (!returnedRepo) {
|
|
1619
|
-
throw new errors.InputError(
|
|
1620
|
-
|
|
1841
|
+
throw new errors.InputError(
|
|
1842
|
+
`Unable to create the repository with Organization ${organization}, Project ${owner} and Repo ${repo}.
|
|
1843
|
+
Please make sure that both the Org and Project are typed corrected and exist.`
|
|
1844
|
+
);
|
|
1621
1845
|
}
|
|
1622
1846
|
const remoteUrl = returnedRepo.remoteUrl;
|
|
1623
1847
|
if (!remoteUrl) {
|
|
1624
|
-
throw new errors.InputError(
|
|
1848
|
+
throw new errors.InputError(
|
|
1849
|
+
"No remote URL returned from create repository for Azure"
|
|
1850
|
+
);
|
|
1625
1851
|
}
|
|
1626
1852
|
const repoContentsUrl = remoteUrl;
|
|
1627
1853
|
const gitAuthorInfo = {
|
|
@@ -1672,12 +1898,17 @@ const createBitbucketCloudRepository = async (opts) => {
|
|
|
1672
1898
|
};
|
|
1673
1899
|
let response;
|
|
1674
1900
|
try {
|
|
1675
|
-
response = await fetch__default["default"](
|
|
1901
|
+
response = await fetch__default["default"](
|
|
1902
|
+
`${apiBaseUrl}/repositories/${workspace}/${repo}`,
|
|
1903
|
+
options
|
|
1904
|
+
);
|
|
1676
1905
|
} catch (e) {
|
|
1677
1906
|
throw new Error(`Unable to create repository, ${e}`);
|
|
1678
1907
|
}
|
|
1679
1908
|
if (response.status !== 200) {
|
|
1680
|
-
throw new Error(
|
|
1909
|
+
throw new Error(
|
|
1910
|
+
`Unable to create repository, ${response.status} ${response.statusText}, ${await response.text()}`
|
|
1911
|
+
);
|
|
1681
1912
|
}
|
|
1682
1913
|
const r = await response.json();
|
|
1683
1914
|
let remoteUrl = "";
|
|
@@ -1717,7 +1948,9 @@ const createBitbucketServerRepository = async (opts) => {
|
|
|
1717
1948
|
throw new Error(`Unable to create repository, ${e}`);
|
|
1718
1949
|
}
|
|
1719
1950
|
if (response.status !== 201) {
|
|
1720
|
-
throw new Error(
|
|
1951
|
+
throw new Error(
|
|
1952
|
+
`Unable to create repository, ${response.status} ${response.statusText}, ${await response.text()}`
|
|
1953
|
+
);
|
|
1721
1954
|
}
|
|
1722
1955
|
const r = await response.json();
|
|
1723
1956
|
let remoteUrl = "";
|
|
@@ -1729,15 +1962,20 @@ const createBitbucketServerRepository = async (opts) => {
|
|
|
1729
1962
|
const repoContentsUrl = `${r.links.self[0].href}`;
|
|
1730
1963
|
return { remoteUrl, repoContentsUrl };
|
|
1731
1964
|
};
|
|
1732
|
-
const getAuthorizationHeader$
|
|
1965
|
+
const getAuthorizationHeader$1 = (config) => {
|
|
1733
1966
|
if (config.username && config.appPassword) {
|
|
1734
|
-
const buffer = Buffer.from(
|
|
1967
|
+
const buffer = Buffer.from(
|
|
1968
|
+
`${config.username}:${config.appPassword}`,
|
|
1969
|
+
"utf8"
|
|
1970
|
+
);
|
|
1735
1971
|
return `Basic ${buffer.toString("base64")}`;
|
|
1736
1972
|
}
|
|
1737
1973
|
if (config.token) {
|
|
1738
1974
|
return `Bearer ${config.token}`;
|
|
1739
1975
|
}
|
|
1740
|
-
throw new Error(
|
|
1976
|
+
throw new Error(
|
|
1977
|
+
`Authorization has not been provided for Bitbucket. Please add either username + appPassword or token to the Integrations config`
|
|
1978
|
+
);
|
|
1741
1979
|
};
|
|
1742
1980
|
const performEnableLFS$1 = async (opts) => {
|
|
1743
1981
|
const { authorization, host, project, repo } = opts;
|
|
@@ -1747,9 +1985,14 @@ const performEnableLFS$1 = async (opts) => {
|
|
|
1747
1985
|
Authorization: authorization
|
|
1748
1986
|
}
|
|
1749
1987
|
};
|
|
1750
|
-
const { ok, status, statusText } = await fetch__default["default"](
|
|
1988
|
+
const { ok, status, statusText } = await fetch__default["default"](
|
|
1989
|
+
`https://${host}/rest/git-lfs/admin/projects/${project}/repos/${repo}/enabled`,
|
|
1990
|
+
options
|
|
1991
|
+
);
|
|
1751
1992
|
if (!ok)
|
|
1752
|
-
throw new Error(
|
|
1993
|
+
throw new Error(
|
|
1994
|
+
`Failed to enable LFS in the repository, ${status}: ${statusText}`
|
|
1995
|
+
);
|
|
1753
1996
|
};
|
|
1754
1997
|
function createPublishBitbucketAction(options) {
|
|
1755
1998
|
const { integrations, config } = options;
|
|
@@ -1827,7 +2070,9 @@ function createPublishBitbucketAction(options) {
|
|
|
1827
2070
|
},
|
|
1828
2071
|
async handler(ctx) {
|
|
1829
2072
|
var _a;
|
|
1830
|
-
ctx.logger.warn(
|
|
2073
|
+
ctx.logger.warn(
|
|
2074
|
+
`[Deprecated] Please migrate the use of action "publish:bitbucket" to "publish:bitbucketCloud" or "publish:bitbucketServer".`
|
|
2075
|
+
);
|
|
1831
2076
|
const {
|
|
1832
2077
|
repoUrl,
|
|
1833
2078
|
description,
|
|
@@ -1838,24 +2083,35 @@ function createPublishBitbucketAction(options) {
|
|
|
1838
2083
|
gitAuthorName,
|
|
1839
2084
|
gitAuthorEmail
|
|
1840
2085
|
} = ctx.input;
|
|
1841
|
-
const { workspace, project, repo, host } = parseRepoUrl(
|
|
2086
|
+
const { workspace, project, repo, host } = parseRepoUrl(
|
|
2087
|
+
repoUrl,
|
|
2088
|
+
integrations
|
|
2089
|
+
);
|
|
1842
2090
|
if (host === "bitbucket.org") {
|
|
1843
2091
|
if (!workspace) {
|
|
1844
|
-
throw new errors.InputError(
|
|
2092
|
+
throw new errors.InputError(
|
|
2093
|
+
`Invalid URL provider was included in the repo URL to create ${ctx.input.repoUrl}, missing workspace`
|
|
2094
|
+
);
|
|
1845
2095
|
}
|
|
1846
2096
|
}
|
|
1847
2097
|
if (!project) {
|
|
1848
|
-
throw new errors.InputError(
|
|
2098
|
+
throw new errors.InputError(
|
|
2099
|
+
`Invalid URL provider was included in the repo URL to create ${ctx.input.repoUrl}, missing project`
|
|
2100
|
+
);
|
|
1849
2101
|
}
|
|
1850
2102
|
const integrationConfig = integrations.bitbucket.byHost(host);
|
|
1851
2103
|
if (!integrationConfig) {
|
|
1852
|
-
throw new errors.InputError(
|
|
2104
|
+
throw new errors.InputError(
|
|
2105
|
+
`No matching integration configuration for host ${host}, please check your integrations config`
|
|
2106
|
+
);
|
|
1853
2107
|
}
|
|
1854
|
-
const authorization = getAuthorizationHeader$
|
|
1855
|
-
|
|
1856
|
-
|
|
1857
|
-
|
|
1858
|
-
|
|
2108
|
+
const authorization = getAuthorizationHeader$1(
|
|
2109
|
+
ctx.input.token ? {
|
|
2110
|
+
host: integrationConfig.config.host,
|
|
2111
|
+
apiBaseUrl: integrationConfig.config.apiBaseUrl,
|
|
2112
|
+
token: ctx.input.token
|
|
2113
|
+
} : integrationConfig.config
|
|
2114
|
+
);
|
|
1859
2115
|
const apiBaseUrl = integrationConfig.config.apiBaseUrl;
|
|
1860
2116
|
const createMethod = host === "bitbucket.org" ? createBitbucketCloudRepository : createBitbucketServerRepository;
|
|
1861
2117
|
const { remoteUrl, repoContentsUrl } = await createMethod({
|
|
@@ -1928,12 +2184,17 @@ const createRepository$1 = async (opts) => {
|
|
|
1928
2184
|
};
|
|
1929
2185
|
let response;
|
|
1930
2186
|
try {
|
|
1931
|
-
response = await fetch__default["default"](
|
|
2187
|
+
response = await fetch__default["default"](
|
|
2188
|
+
`${apiBaseUrl}/repositories/${workspace}/${repo}`,
|
|
2189
|
+
options
|
|
2190
|
+
);
|
|
1932
2191
|
} catch (e) {
|
|
1933
2192
|
throw new Error(`Unable to create repository, ${e}`);
|
|
1934
2193
|
}
|
|
1935
2194
|
if (response.status !== 200) {
|
|
1936
|
-
throw new Error(
|
|
2195
|
+
throw new Error(
|
|
2196
|
+
`Unable to create repository, ${response.status} ${response.statusText}, ${await response.text()}`
|
|
2197
|
+
);
|
|
1937
2198
|
}
|
|
1938
2199
|
const r = await response.json();
|
|
1939
2200
|
let remoteUrl = "";
|
|
@@ -1945,15 +2206,20 @@ const createRepository$1 = async (opts) => {
|
|
|
1945
2206
|
const repoContentsUrl = `${r.links.html.href}/src/${mainBranch}`;
|
|
1946
2207
|
return { remoteUrl, repoContentsUrl };
|
|
1947
2208
|
};
|
|
1948
|
-
const getAuthorizationHeader
|
|
2209
|
+
const getAuthorizationHeader = (config) => {
|
|
1949
2210
|
if (config.username && config.appPassword) {
|
|
1950
|
-
const buffer = Buffer.from(
|
|
2211
|
+
const buffer = Buffer.from(
|
|
2212
|
+
`${config.username}:${config.appPassword}`,
|
|
2213
|
+
"utf8"
|
|
2214
|
+
);
|
|
1951
2215
|
return `Basic ${buffer.toString("base64")}`;
|
|
1952
2216
|
}
|
|
1953
2217
|
if (config.token) {
|
|
1954
2218
|
return `Bearer ${config.token}`;
|
|
1955
2219
|
}
|
|
1956
|
-
throw new Error(
|
|
2220
|
+
throw new Error(
|
|
2221
|
+
`Authorization has not been provided for Bitbucket Cloud. Please add either username + appPassword to the Integrations config or a user login auth token`
|
|
2222
|
+
);
|
|
1957
2223
|
};
|
|
1958
2224
|
function createPublishBitbucketCloudAction(options) {
|
|
1959
2225
|
const { integrations, config } = options;
|
|
@@ -2016,18 +2282,29 @@ function createPublishBitbucketCloudAction(options) {
|
|
|
2016
2282
|
defaultBranch = "master",
|
|
2017
2283
|
repoVisibility = "private"
|
|
2018
2284
|
} = ctx.input;
|
|
2019
|
-
const { workspace, project, repo, host } = parseRepoUrl(
|
|
2285
|
+
const { workspace, project, repo, host } = parseRepoUrl(
|
|
2286
|
+
repoUrl,
|
|
2287
|
+
integrations
|
|
2288
|
+
);
|
|
2020
2289
|
if (!workspace) {
|
|
2021
|
-
throw new errors.InputError(
|
|
2290
|
+
throw new errors.InputError(
|
|
2291
|
+
`Invalid URL provider was included in the repo URL to create ${ctx.input.repoUrl}, missing workspace`
|
|
2292
|
+
);
|
|
2022
2293
|
}
|
|
2023
2294
|
if (!project) {
|
|
2024
|
-
throw new errors.InputError(
|
|
2295
|
+
throw new errors.InputError(
|
|
2296
|
+
`Invalid URL provider was included in the repo URL to create ${ctx.input.repoUrl}, missing project`
|
|
2297
|
+
);
|
|
2025
2298
|
}
|
|
2026
2299
|
const integrationConfig = integrations.bitbucketCloud.byHost(host);
|
|
2027
2300
|
if (!integrationConfig) {
|
|
2028
|
-
throw new errors.InputError(
|
|
2301
|
+
throw new errors.InputError(
|
|
2302
|
+
`No matching integration configuration for host ${host}, please check your integrations config`
|
|
2303
|
+
);
|
|
2029
2304
|
}
|
|
2030
|
-
const authorization = getAuthorizationHeader
|
|
2305
|
+
const authorization = getAuthorizationHeader(
|
|
2306
|
+
ctx.input.token ? { token: ctx.input.token } : integrationConfig.config
|
|
2307
|
+
);
|
|
2031
2308
|
const apiBaseUrl = integrationConfig.config.apiBaseUrl;
|
|
2032
2309
|
const { remoteUrl, repoContentsUrl } = await createRepository$1({
|
|
2033
2310
|
authorization,
|
|
@@ -2051,7 +2328,9 @@ function createPublishBitbucketCloudAction(options) {
|
|
|
2051
2328
|
};
|
|
2052
2329
|
} else {
|
|
2053
2330
|
if (!integrationConfig.config.username || !integrationConfig.config.appPassword) {
|
|
2054
|
-
throw new Error(
|
|
2331
|
+
throw new Error(
|
|
2332
|
+
"Credentials for Bitbucket Cloud integration required for this action."
|
|
2333
|
+
);
|
|
2055
2334
|
}
|
|
2056
2335
|
auth = {
|
|
2057
2336
|
username: integrationConfig.config.username,
|
|
@@ -2064,7 +2343,9 @@ function createPublishBitbucketCloudAction(options) {
|
|
|
2064
2343
|
auth,
|
|
2065
2344
|
defaultBranch,
|
|
2066
2345
|
logger: ctx.logger,
|
|
2067
|
-
commitMessage: config.getOptionalString(
|
|
2346
|
+
commitMessage: config.getOptionalString(
|
|
2347
|
+
"scaffolder.defaultCommitMessage"
|
|
2348
|
+
),
|
|
2068
2349
|
gitAuthorInfo
|
|
2069
2350
|
});
|
|
2070
2351
|
ctx.output("remoteUrl", remoteUrl);
|
|
@@ -2101,7 +2382,9 @@ const createRepository = async (opts) => {
|
|
|
2101
2382
|
throw new Error(`Unable to create repository, ${e}`);
|
|
2102
2383
|
}
|
|
2103
2384
|
if (response.status !== 201) {
|
|
2104
|
-
throw new Error(
|
|
2385
|
+
throw new Error(
|
|
2386
|
+
`Unable to create repository, ${response.status} ${response.statusText}, ${await response.text()}`
|
|
2387
|
+
);
|
|
2105
2388
|
}
|
|
2106
2389
|
const r = await response.json();
|
|
2107
2390
|
let remoteUrl = "";
|
|
@@ -2113,9 +2396,6 @@ const createRepository = async (opts) => {
|
|
|
2113
2396
|
const repoContentsUrl = `${r.links.self[0].href}`;
|
|
2114
2397
|
return { remoteUrl, repoContentsUrl };
|
|
2115
2398
|
};
|
|
2116
|
-
const getAuthorizationHeader = (config) => {
|
|
2117
|
-
return `Bearer ${config.token}`;
|
|
2118
|
-
};
|
|
2119
2399
|
const performEnableLFS = async (opts) => {
|
|
2120
2400
|
const { authorization, host, project, repo } = opts;
|
|
2121
2401
|
const options = {
|
|
@@ -2124,9 +2404,14 @@ const performEnableLFS = async (opts) => {
|
|
|
2124
2404
|
Authorization: authorization
|
|
2125
2405
|
}
|
|
2126
2406
|
};
|
|
2127
|
-
const { ok, status, statusText } = await fetch__default["default"](
|
|
2407
|
+
const { ok, status, statusText } = await fetch__default["default"](
|
|
2408
|
+
`https://${host}/rest/git-lfs/admin/projects/${project}/repos/${repo}/enabled`,
|
|
2409
|
+
options
|
|
2410
|
+
);
|
|
2128
2411
|
if (!ok)
|
|
2129
|
-
throw new Error(
|
|
2412
|
+
throw new Error(
|
|
2413
|
+
`Failed to enable LFS in the repository, ${status}: ${statusText}`
|
|
2414
|
+
);
|
|
2130
2415
|
};
|
|
2131
2416
|
function createPublishBitbucketServerAction(options) {
|
|
2132
2417
|
const { integrations, config } = options;
|
|
@@ -2198,17 +2483,28 @@ function createPublishBitbucketServerAction(options) {
|
|
|
2198
2483
|
} = ctx.input;
|
|
2199
2484
|
const { project, repo, host } = parseRepoUrl(repoUrl, integrations);
|
|
2200
2485
|
if (!project) {
|
|
2201
|
-
throw new errors.InputError(
|
|
2486
|
+
throw new errors.InputError(
|
|
2487
|
+
`Invalid URL provider was included in the repo URL to create ${ctx.input.repoUrl}, missing project`
|
|
2488
|
+
);
|
|
2202
2489
|
}
|
|
2203
2490
|
const integrationConfig = integrations.bitbucketServer.byHost(host);
|
|
2204
2491
|
if (!integrationConfig) {
|
|
2205
|
-
throw new errors.InputError(
|
|
2492
|
+
throw new errors.InputError(
|
|
2493
|
+
`No matching integration configuration for host ${host}, please check your integrations config`
|
|
2494
|
+
);
|
|
2206
2495
|
}
|
|
2207
2496
|
const token = (_a = ctx.input.token) != null ? _a : integrationConfig.config.token;
|
|
2208
|
-
|
|
2209
|
-
|
|
2497
|
+
const authConfig = {
|
|
2498
|
+
...integrationConfig.config,
|
|
2499
|
+
...{ token }
|
|
2500
|
+
};
|
|
2501
|
+
const reqOpts = integration.getBitbucketServerRequestOptions(authConfig);
|
|
2502
|
+
const authorization = reqOpts.headers.Authorization;
|
|
2503
|
+
if (!authorization) {
|
|
2504
|
+
throw new Error(
|
|
2505
|
+
`Authorization has not been provided for ${integrationConfig.config.host}. Please add either (a) a user login auth token, or (b) a token or (c) username + password to the integration config.`
|
|
2506
|
+
);
|
|
2210
2507
|
}
|
|
2211
|
-
const authorization = getAuthorizationHeader({ token });
|
|
2212
2508
|
const apiBaseUrl = integrationConfig.config.apiBaseUrl;
|
|
2213
2509
|
const { remoteUrl, repoContentsUrl } = await createRepository({
|
|
2214
2510
|
authorization,
|
|
@@ -2222,9 +2518,11 @@ function createPublishBitbucketServerAction(options) {
|
|
|
2222
2518
|
name: config.getOptionalString("scaffolder.defaultAuthor.name"),
|
|
2223
2519
|
email: config.getOptionalString("scaffolder.defaultAuthor.email")
|
|
2224
2520
|
};
|
|
2225
|
-
const auth = {
|
|
2226
|
-
|
|
2227
|
-
|
|
2521
|
+
const auth = authConfig.token ? {
|
|
2522
|
+
token
|
|
2523
|
+
} : {
|
|
2524
|
+
username: authConfig.username,
|
|
2525
|
+
password: authConfig.password
|
|
2228
2526
|
};
|
|
2229
2527
|
await initRepoAndPush({
|
|
2230
2528
|
dir: getRepoSourceDirectory(ctx.workspacePath, ctx.input.sourcePath),
|
|
@@ -2232,7 +2530,9 @@ function createPublishBitbucketServerAction(options) {
|
|
|
2232
2530
|
auth,
|
|
2233
2531
|
defaultBranch,
|
|
2234
2532
|
logger: ctx.logger,
|
|
2235
|
-
commitMessage: config.getOptionalString(
|
|
2533
|
+
commitMessage: config.getOptionalString(
|
|
2534
|
+
"scaffolder.defaultCommitMessage"
|
|
2535
|
+
),
|
|
2236
2536
|
gitAuthorInfo
|
|
2237
2537
|
});
|
|
2238
2538
|
if (enableLFS) {
|
|
@@ -2279,7 +2579,7 @@ const createGerritProject = async (config, options) => {
|
|
|
2279
2579
|
body: JSON.stringify({
|
|
2280
2580
|
parent,
|
|
2281
2581
|
description,
|
|
2282
|
-
owners: [owner],
|
|
2582
|
+
owners: owner ? [owner] : [],
|
|
2283
2583
|
create_empty_commit: false
|
|
2284
2584
|
}),
|
|
2285
2585
|
headers: {
|
|
@@ -2287,9 +2587,14 @@ const createGerritProject = async (config, options) => {
|
|
|
2287
2587
|
"Content-Type": "application/json"
|
|
2288
2588
|
}
|
|
2289
2589
|
};
|
|
2290
|
-
const response = await fetch__default["default"](
|
|
2590
|
+
const response = await fetch__default["default"](
|
|
2591
|
+
`${config.baseUrl}/a/projects/${encodeURIComponent(projectName)}`,
|
|
2592
|
+
fetchOptions
|
|
2593
|
+
);
|
|
2291
2594
|
if (response.status !== 201) {
|
|
2292
|
-
throw new Error(
|
|
2595
|
+
throw new Error(
|
|
2596
|
+
`Unable to create repository, ${response.status} ${response.statusText}, ${await response.text()}`
|
|
2597
|
+
);
|
|
2293
2598
|
}
|
|
2294
2599
|
};
|
|
2295
2600
|
const generateCommitMessage = (config, commitSubject) => {
|
|
@@ -2336,6 +2641,11 @@ function createPublishGerritAction(options) {
|
|
|
2336
2641
|
title: "Default Author Email",
|
|
2337
2642
|
type: "string",
|
|
2338
2643
|
description: `Sets the default author email for the commit.`
|
|
2644
|
+
},
|
|
2645
|
+
sourcePath: {
|
|
2646
|
+
title: "Source Path",
|
|
2647
|
+
type: "string",
|
|
2648
|
+
description: `Path within the workspace that will be used as the repository root. If omitted, the entire workspace will be published as the repository.`
|
|
2339
2649
|
}
|
|
2340
2650
|
}
|
|
2341
2651
|
},
|
|
@@ -2360,18 +2670,23 @@ function createPublishGerritAction(options) {
|
|
|
2360
2670
|
defaultBranch = "master",
|
|
2361
2671
|
gitAuthorName,
|
|
2362
2672
|
gitAuthorEmail,
|
|
2363
|
-
gitCommitMessage = "initial commit"
|
|
2673
|
+
gitCommitMessage = "initial commit",
|
|
2674
|
+
sourcePath
|
|
2364
2675
|
} = ctx.input;
|
|
2365
|
-
const { repo, host, owner, workspace } = parseRepoUrl(
|
|
2676
|
+
const { repo, host, owner, workspace } = parseRepoUrl(
|
|
2677
|
+
repoUrl,
|
|
2678
|
+
integrations
|
|
2679
|
+
);
|
|
2366
2680
|
const integrationConfig = integrations.gerrit.byHost(host);
|
|
2367
2681
|
if (!integrationConfig) {
|
|
2368
|
-
throw new errors.InputError(
|
|
2369
|
-
|
|
2370
|
-
|
|
2371
|
-
throw new errors.InputError(`Invalid URL provider was included in the repo URL to create ${ctx.input.repoUrl}, missing owner`);
|
|
2682
|
+
throw new errors.InputError(
|
|
2683
|
+
`No matching integration configuration for host ${host}, please check your integrations config`
|
|
2684
|
+
);
|
|
2372
2685
|
}
|
|
2373
2686
|
if (!workspace) {
|
|
2374
|
-
throw new errors.InputError(
|
|
2687
|
+
throw new errors.InputError(
|
|
2688
|
+
`Invalid URL provider was included in the repo URL to create ${ctx.input.repoUrl}, missing workspace`
|
|
2689
|
+
);
|
|
2375
2690
|
}
|
|
2376
2691
|
await createGerritProject(integrationConfig.config, {
|
|
2377
2692
|
description,
|
|
@@ -2389,7 +2704,7 @@ function createPublishGerritAction(options) {
|
|
|
2389
2704
|
};
|
|
2390
2705
|
const remoteUrl = `${integrationConfig.config.cloneUrl}/a/${repo}`;
|
|
2391
2706
|
await initRepoAndPush({
|
|
2392
|
-
dir: getRepoSourceDirectory(ctx.workspacePath,
|
|
2707
|
+
dir: getRepoSourceDirectory(ctx.workspacePath, sourcePath),
|
|
2393
2708
|
remoteUrl,
|
|
2394
2709
|
auth,
|
|
2395
2710
|
defaultBranch,
|
|
@@ -2404,6 +2719,115 @@ function createPublishGerritAction(options) {
|
|
|
2404
2719
|
});
|
|
2405
2720
|
}
|
|
2406
2721
|
|
|
2722
|
+
const generateGerritChangeId = () => {
|
|
2723
|
+
const changeId = crypto__default["default"].randomBytes(20).toString("hex");
|
|
2724
|
+
return `I${changeId}`;
|
|
2725
|
+
};
|
|
2726
|
+
function createPublishGerritReviewAction(options) {
|
|
2727
|
+
const { integrations, config } = options;
|
|
2728
|
+
return createTemplateAction({
|
|
2729
|
+
id: "publish:gerrit:review",
|
|
2730
|
+
description: "Creates a new Gerrit review.",
|
|
2731
|
+
schema: {
|
|
2732
|
+
input: {
|
|
2733
|
+
type: "object",
|
|
2734
|
+
required: ["repoUrl", "gitCommitMessage"],
|
|
2735
|
+
properties: {
|
|
2736
|
+
repoUrl: {
|
|
2737
|
+
title: "Repository Location",
|
|
2738
|
+
type: "string"
|
|
2739
|
+
},
|
|
2740
|
+
branch: {
|
|
2741
|
+
title: "Repository branch",
|
|
2742
|
+
type: "string",
|
|
2743
|
+
description: "Branch of the repository the review will be created on"
|
|
2744
|
+
},
|
|
2745
|
+
sourcePath: {
|
|
2746
|
+
type: "string",
|
|
2747
|
+
title: "Working Subdirectory",
|
|
2748
|
+
description: "Subdirectory of working directory containing the repository"
|
|
2749
|
+
},
|
|
2750
|
+
gitCommitMessage: {
|
|
2751
|
+
title: "Git Commit Message",
|
|
2752
|
+
type: "string",
|
|
2753
|
+
description: `Sets the commit message on the repository.`
|
|
2754
|
+
},
|
|
2755
|
+
gitAuthorName: {
|
|
2756
|
+
title: "Default Author Name",
|
|
2757
|
+
type: "string",
|
|
2758
|
+
description: `Sets the default author name for the commit. The default value is 'Scaffolder'`
|
|
2759
|
+
},
|
|
2760
|
+
gitAuthorEmail: {
|
|
2761
|
+
title: "Default Author Email",
|
|
2762
|
+
type: "string",
|
|
2763
|
+
description: `Sets the default author email for the commit.`
|
|
2764
|
+
}
|
|
2765
|
+
}
|
|
2766
|
+
},
|
|
2767
|
+
output: {
|
|
2768
|
+
type: "object",
|
|
2769
|
+
properties: {
|
|
2770
|
+
reviewUrl: {
|
|
2771
|
+
title: "A URL to the review",
|
|
2772
|
+
type: "string"
|
|
2773
|
+
},
|
|
2774
|
+
repoContentsUrl: {
|
|
2775
|
+
title: "A URL to the root of the repository",
|
|
2776
|
+
type: "string"
|
|
2777
|
+
}
|
|
2778
|
+
}
|
|
2779
|
+
}
|
|
2780
|
+
},
|
|
2781
|
+
async handler(ctx) {
|
|
2782
|
+
var _a;
|
|
2783
|
+
const {
|
|
2784
|
+
repoUrl,
|
|
2785
|
+
branch = "master",
|
|
2786
|
+
sourcePath,
|
|
2787
|
+
gitAuthorName,
|
|
2788
|
+
gitAuthorEmail,
|
|
2789
|
+
gitCommitMessage
|
|
2790
|
+
} = ctx.input;
|
|
2791
|
+
const { host, repo } = parseRepoUrl(repoUrl, integrations);
|
|
2792
|
+
if (!gitCommitMessage) {
|
|
2793
|
+
throw new errors.InputError(`Missing gitCommitMessage input`);
|
|
2794
|
+
}
|
|
2795
|
+
const integrationConfig = integrations.gerrit.byHost(host);
|
|
2796
|
+
if (!integrationConfig) {
|
|
2797
|
+
throw new errors.InputError(
|
|
2798
|
+
`No matching integration configuration for host ${host}, please check your integrations config`
|
|
2799
|
+
);
|
|
2800
|
+
}
|
|
2801
|
+
const auth = {
|
|
2802
|
+
username: integrationConfig.config.username,
|
|
2803
|
+
password: integrationConfig.config.password
|
|
2804
|
+
};
|
|
2805
|
+
const gitAuthorInfo = {
|
|
2806
|
+
name: gitAuthorName ? gitAuthorName : config.getOptionalString("scaffolder.defaultAuthor.name"),
|
|
2807
|
+
email: gitAuthorEmail ? gitAuthorEmail : config.getOptionalString("scaffolder.defaultAuthor.email")
|
|
2808
|
+
};
|
|
2809
|
+
const changeId = generateGerritChangeId();
|
|
2810
|
+
const commitMessage = `${gitCommitMessage}
|
|
2811
|
+
|
|
2812
|
+
Change-Id: ${changeId}`;
|
|
2813
|
+
await commitAndPushRepo({
|
|
2814
|
+
dir: getRepoSourceDirectory(ctx.workspacePath, sourcePath),
|
|
2815
|
+
auth,
|
|
2816
|
+
logger: ctx.logger,
|
|
2817
|
+
commitMessage,
|
|
2818
|
+
gitAuthorInfo,
|
|
2819
|
+
branch,
|
|
2820
|
+
remoteRef: `refs/for/${branch}`
|
|
2821
|
+
});
|
|
2822
|
+
const repoContentsUrl = `${integrationConfig.config.gitilesBaseUrl}/${repo}/+/refs/heads/${branch}`;
|
|
2823
|
+
const reviewUrl = `${integrationConfig.config.baseUrl}/#/q/${changeId}`;
|
|
2824
|
+
(_a = ctx.logger) == null ? void 0 : _a.info(`Review available on ${reviewUrl}`);
|
|
2825
|
+
ctx.output("repoContentsUrl", repoContentsUrl);
|
|
2826
|
+
ctx.output("reviewUrl", reviewUrl);
|
|
2827
|
+
}
|
|
2828
|
+
});
|
|
2829
|
+
}
|
|
2830
|
+
|
|
2407
2831
|
function createPublishGithubAction(options) {
|
|
2408
2832
|
const { integrations, config, githubCredentialsProvider } = options;
|
|
2409
2833
|
return createTemplateAction({
|
|
@@ -2422,6 +2846,7 @@ function createPublishGithubAction(options) {
|
|
|
2422
2846
|
repoVisibility: repoVisibility,
|
|
2423
2847
|
defaultBranch: defaultBranch,
|
|
2424
2848
|
protectDefaultBranch: protectDefaultBranch,
|
|
2849
|
+
protectEnforceAdmins: protectEnforceAdmins,
|
|
2425
2850
|
deleteBranchOnMerge: deleteBranchOnMerge,
|
|
2426
2851
|
gitCommitMessage: gitCommitMessage,
|
|
2427
2852
|
gitAuthorName: gitAuthorName,
|
|
@@ -2453,6 +2878,7 @@ function createPublishGithubAction(options) {
|
|
|
2453
2878
|
repoVisibility = "private",
|
|
2454
2879
|
defaultBranch = "master",
|
|
2455
2880
|
protectDefaultBranch = true,
|
|
2881
|
+
protectEnforceAdmins = true,
|
|
2456
2882
|
deleteBranchOnMerge = false,
|
|
2457
2883
|
gitCommitMessage = "initial commit",
|
|
2458
2884
|
gitAuthorName,
|
|
@@ -2475,10 +2901,42 @@ function createPublishGithubAction(options) {
|
|
|
2475
2901
|
if (!owner) {
|
|
2476
2902
|
throw new errors.InputError("Invalid repository owner provided in repoUrl");
|
|
2477
2903
|
}
|
|
2478
|
-
const newRepo = await createGithubRepoWithCollaboratorsAndTopics(
|
|
2904
|
+
const newRepo = await createGithubRepoWithCollaboratorsAndTopics(
|
|
2905
|
+
client,
|
|
2906
|
+
repo,
|
|
2907
|
+
owner,
|
|
2908
|
+
repoVisibility,
|
|
2909
|
+
description,
|
|
2910
|
+
deleteBranchOnMerge,
|
|
2911
|
+
allowMergeCommit,
|
|
2912
|
+
allowSquashMerge,
|
|
2913
|
+
allowRebaseMerge,
|
|
2914
|
+
access,
|
|
2915
|
+
collaborators,
|
|
2916
|
+
topics,
|
|
2917
|
+
ctx.logger
|
|
2918
|
+
);
|
|
2479
2919
|
const remoteUrl = newRepo.clone_url;
|
|
2480
2920
|
const repoContentsUrl = `${newRepo.html_url}/blob/${defaultBranch}`;
|
|
2481
|
-
await initRepoPushAndProtect(
|
|
2921
|
+
await initRepoPushAndProtect(
|
|
2922
|
+
remoteUrl,
|
|
2923
|
+
octokitOptions.auth,
|
|
2924
|
+
ctx.workspacePath,
|
|
2925
|
+
ctx.input.sourcePath,
|
|
2926
|
+
defaultBranch,
|
|
2927
|
+
protectDefaultBranch,
|
|
2928
|
+
protectEnforceAdmins,
|
|
2929
|
+
owner,
|
|
2930
|
+
client,
|
|
2931
|
+
repo,
|
|
2932
|
+
requireCodeOwnerReviews,
|
|
2933
|
+
requiredStatusCheckContexts,
|
|
2934
|
+
config,
|
|
2935
|
+
ctx.logger,
|
|
2936
|
+
gitCommitMessage,
|
|
2937
|
+
gitAuthorName,
|
|
2938
|
+
gitAuthorEmail
|
|
2939
|
+
);
|
|
2482
2940
|
ctx.output("remoteUrl", remoteUrl);
|
|
2483
2941
|
ctx.output("repoContentsUrl", repoContentsUrl);
|
|
2484
2942
|
}
|
|
@@ -2505,11 +2963,15 @@ async function serializeDirectoryContents(sourcePath, options) {
|
|
|
2505
2963
|
stats: true
|
|
2506
2964
|
});
|
|
2507
2965
|
const limiter = limiterFactory__default["default"](10);
|
|
2508
|
-
return Promise.all(
|
|
2509
|
-
path: path$1,
|
|
2510
|
-
|
|
2511
|
-
|
|
2512
|
-
|
|
2966
|
+
return Promise.all(
|
|
2967
|
+
paths.map(async ({ path: path$1, stats }) => ({
|
|
2968
|
+
path: path$1,
|
|
2969
|
+
content: await limiter(
|
|
2970
|
+
async () => fs__default["default"].readFile(path.join(sourcePath, path$1))
|
|
2971
|
+
),
|
|
2972
|
+
executable: isExecutable(stats == null ? void 0 : stats.mode)
|
|
2973
|
+
}))
|
|
2974
|
+
);
|
|
2513
2975
|
}
|
|
2514
2976
|
|
|
2515
2977
|
async function deserializeDirectoryContents(targetPath, files) {
|
|
@@ -2530,7 +2992,9 @@ const defaultClientFactory = async ({
|
|
|
2530
2992
|
host = "github.com",
|
|
2531
2993
|
token: providedToken
|
|
2532
2994
|
}) => {
|
|
2533
|
-
const [encodedHost, encodedOwner, encodedRepo] = [host, owner, repo].map(
|
|
2995
|
+
const [encodedHost, encodedOwner, encodedRepo] = [host, owner, repo].map(
|
|
2996
|
+
encodeURIComponent
|
|
2997
|
+
);
|
|
2534
2998
|
const octokitOptions = await getOctokitOptions({
|
|
2535
2999
|
integrations,
|
|
2536
3000
|
credentialsProvider: githubCredentialsProvider,
|
|
@@ -2624,7 +3088,9 @@ const createPublishGithubPullRequestAction = ({
|
|
|
2624
3088
|
} = ctx.input;
|
|
2625
3089
|
const { owner, repo, host } = parseRepoUrl(repoUrl, integrations);
|
|
2626
3090
|
if (!owner) {
|
|
2627
|
-
throw new errors.InputError(
|
|
3091
|
+
throw new errors.InputError(
|
|
3092
|
+
`No owner provided for host: ${host}, and repo ${repo}`
|
|
3093
|
+
);
|
|
2628
3094
|
}
|
|
2629
3095
|
const client = await clientFactory({
|
|
2630
3096
|
integrations,
|
|
@@ -2638,14 +3104,16 @@ const createPublishGithubPullRequestAction = ({
|
|
|
2638
3104
|
const directoryContents = await serializeDirectoryContents(fileRoot, {
|
|
2639
3105
|
gitignore: true
|
|
2640
3106
|
});
|
|
2641
|
-
const files = Object.fromEntries(
|
|
2642
|
-
|
|
2643
|
-
|
|
2644
|
-
|
|
2645
|
-
|
|
2646
|
-
|
|
2647
|
-
|
|
2648
|
-
|
|
3107
|
+
const files = Object.fromEntries(
|
|
3108
|
+
directoryContents.map((file) => [
|
|
3109
|
+
targetPath ? path__default["default"].posix.join(targetPath, file.path) : file.path,
|
|
3110
|
+
{
|
|
3111
|
+
mode: file.executable ? "100755" : "100644",
|
|
3112
|
+
encoding: "base64",
|
|
3113
|
+
content: file.content.toString("base64")
|
|
3114
|
+
}
|
|
3115
|
+
])
|
|
3116
|
+
);
|
|
2649
3117
|
try {
|
|
2650
3118
|
const response = await client.createPullRequest({
|
|
2651
3119
|
owner,
|
|
@@ -2721,6 +3189,11 @@ function createPublishGitlabAction(options) {
|
|
|
2721
3189
|
title: "Authentication Token",
|
|
2722
3190
|
type: "string",
|
|
2723
3191
|
description: "The token to use for authorization to GitLab"
|
|
3192
|
+
},
|
|
3193
|
+
setUserAsOwner: {
|
|
3194
|
+
title: "Set User As Owner",
|
|
3195
|
+
type: "boolean",
|
|
3196
|
+
description: "Set the token user as owner of the newly created repository. Requires a token authorized to do the edit in the integration configuration for the matching host"
|
|
2724
3197
|
}
|
|
2725
3198
|
}
|
|
2726
3199
|
},
|
|
@@ -2745,15 +3218,20 @@ function createPublishGitlabAction(options) {
|
|
|
2745
3218
|
defaultBranch = "master",
|
|
2746
3219
|
gitCommitMessage = "initial commit",
|
|
2747
3220
|
gitAuthorName,
|
|
2748
|
-
gitAuthorEmail
|
|
3221
|
+
gitAuthorEmail,
|
|
3222
|
+
setUserAsOwner = false
|
|
2749
3223
|
} = ctx.input;
|
|
2750
3224
|
const { owner, repo, host } = parseRepoUrl(repoUrl, integrations);
|
|
2751
3225
|
if (!owner) {
|
|
2752
|
-
throw new errors.InputError(
|
|
3226
|
+
throw new errors.InputError(
|
|
3227
|
+
`No owner provided for host: ${host}, and repo ${repo}`
|
|
3228
|
+
);
|
|
2753
3229
|
}
|
|
2754
3230
|
const integrationConfig = integrations.gitlab.byHost(host);
|
|
2755
3231
|
if (!integrationConfig) {
|
|
2756
|
-
throw new errors.InputError(
|
|
3232
|
+
throw new errors.InputError(
|
|
3233
|
+
`No matching integration configuration for host ${host}, please check your integrations config`
|
|
3234
|
+
);
|
|
2757
3235
|
}
|
|
2758
3236
|
if (!integrationConfig.config.token && !ctx.input.token) {
|
|
2759
3237
|
throw new errors.InputError(`No token available for host ${host}`);
|
|
@@ -2765,15 +3243,22 @@ function createPublishGitlabAction(options) {
|
|
|
2765
3243
|
[tokenType]: token
|
|
2766
3244
|
});
|
|
2767
3245
|
let { id: targetNamespace } = await client.Namespaces.show(owner);
|
|
3246
|
+
const { id: userId } = await client.Users.current();
|
|
2768
3247
|
if (!targetNamespace) {
|
|
2769
|
-
|
|
2770
|
-
targetNamespace = id;
|
|
3248
|
+
targetNamespace = userId;
|
|
2771
3249
|
}
|
|
2772
|
-
const { http_url_to_repo } = await client.Projects.create({
|
|
3250
|
+
const { id: projectId, http_url_to_repo } = await client.Projects.create({
|
|
2773
3251
|
namespace_id: targetNamespace,
|
|
2774
3252
|
name: repo,
|
|
2775
3253
|
visibility: repoVisibility
|
|
2776
3254
|
});
|
|
3255
|
+
if (setUserAsOwner && integrationConfig.config.token) {
|
|
3256
|
+
const adminClient = new node.Gitlab({
|
|
3257
|
+
host: integrationConfig.config.baseUrl,
|
|
3258
|
+
token: integrationConfig.config.token
|
|
3259
|
+
});
|
|
3260
|
+
await adminClient.ProjectMembers.add(projectId, userId, 50);
|
|
3261
|
+
}
|
|
2777
3262
|
const remoteUrl = http_url_to_repo.replace(/\.git$/, "");
|
|
2778
3263
|
const repoContentsUrl = `${remoteUrl}/-/blob/${defaultBranch}`;
|
|
2779
3264
|
const gitAuthorInfo = {
|
|
@@ -2842,10 +3327,21 @@ const createPublishGitlabMergeRequestAction = (options) => {
|
|
|
2842
3327
|
type: "string",
|
|
2843
3328
|
description: "The token to use for authorization to GitLab"
|
|
2844
3329
|
},
|
|
3330
|
+
commitAction: {
|
|
3331
|
+
title: "Commit action",
|
|
3332
|
+
type: "string",
|
|
3333
|
+
enum: ["create", "update", "delete"],
|
|
3334
|
+
description: "The action to be used for git commit. Defaults to create."
|
|
3335
|
+
},
|
|
2845
3336
|
removeSourceBranch: {
|
|
2846
3337
|
title: "Delete source branch",
|
|
2847
3338
|
type: "boolean",
|
|
2848
3339
|
description: "Option to delete source branch once the MR has been merged. Default: false"
|
|
3340
|
+
},
|
|
3341
|
+
assignee: {
|
|
3342
|
+
title: "Merge Request Assignee",
|
|
3343
|
+
type: "string",
|
|
3344
|
+
description: "User this merge request will be assigned to"
|
|
2849
3345
|
}
|
|
2850
3346
|
}
|
|
2851
3347
|
},
|
|
@@ -2881,7 +3377,9 @@ const createPublishGitlabMergeRequestAction = (options) => {
|
|
|
2881
3377
|
const integrationConfig = integrations.gitlab.byHost(host);
|
|
2882
3378
|
const destinationBranch = ctx.input.branchName;
|
|
2883
3379
|
if (!integrationConfig) {
|
|
2884
|
-
throw new errors.InputError(
|
|
3380
|
+
throw new errors.InputError(
|
|
3381
|
+
`No matching integration configuration for host ${host}, please check your integrations config`
|
|
3382
|
+
);
|
|
2885
3383
|
}
|
|
2886
3384
|
if (!integrationConfig.config.token && !ctx.input.token) {
|
|
2887
3385
|
throw new errors.InputError(`No token available for host ${host}`);
|
|
@@ -2892,34 +3390,70 @@ const createPublishGitlabMergeRequestAction = (options) => {
|
|
|
2892
3390
|
host: integrationConfig.config.baseUrl,
|
|
2893
3391
|
[tokenType]: token
|
|
2894
3392
|
});
|
|
2895
|
-
const
|
|
3393
|
+
const assignee = ctx.input.assignee;
|
|
3394
|
+
let assigneeId = void 0;
|
|
3395
|
+
if (assignee !== void 0) {
|
|
3396
|
+
try {
|
|
3397
|
+
const assigneeUser = await api.Users.username(assignee);
|
|
3398
|
+
assigneeId = assigneeUser[0].id;
|
|
3399
|
+
} catch (e) {
|
|
3400
|
+
ctx.logger.warn(
|
|
3401
|
+
`Failed to find gitlab user id for ${assignee}: ${e}. Proceeding with MR creation without an assignee.`
|
|
3402
|
+
);
|
|
3403
|
+
}
|
|
3404
|
+
}
|
|
3405
|
+
const targetPath = backendCommon.resolveSafeChildPath(
|
|
3406
|
+
ctx.workspacePath,
|
|
3407
|
+
ctx.input.targetPath
|
|
3408
|
+
);
|
|
2896
3409
|
const fileContents = await serializeDirectoryContents(targetPath, {
|
|
2897
3410
|
gitignore: true
|
|
2898
3411
|
});
|
|
2899
|
-
const actions = fileContents.map((file) =>
|
|
2900
|
-
|
|
2901
|
-
|
|
2902
|
-
|
|
2903
|
-
|
|
2904
|
-
|
|
2905
|
-
|
|
3412
|
+
const actions = fileContents.map((file) => {
|
|
3413
|
+
var _a2;
|
|
3414
|
+
return {
|
|
3415
|
+
action: (_a2 = ctx.input.commitAction) != null ? _a2 : "create",
|
|
3416
|
+
filePath: path__default["default"].posix.join(ctx.input.targetPath, file.path),
|
|
3417
|
+
encoding: "base64",
|
|
3418
|
+
content: file.content.toString("base64"),
|
|
3419
|
+
execute_filemode: file.executable
|
|
3420
|
+
};
|
|
3421
|
+
});
|
|
2906
3422
|
const projects = await api.Projects.show(projectPath);
|
|
2907
3423
|
const { default_branch: defaultBranch } = projects;
|
|
2908
3424
|
try {
|
|
2909
|
-
await api.Branches.create(
|
|
3425
|
+
await api.Branches.create(
|
|
3426
|
+
projectPath,
|
|
3427
|
+
destinationBranch,
|
|
3428
|
+
String(defaultBranch)
|
|
3429
|
+
);
|
|
2910
3430
|
} catch (e) {
|
|
2911
3431
|
throw new errors.InputError(`The branch creation failed ${e}`);
|
|
2912
3432
|
}
|
|
2913
3433
|
try {
|
|
2914
|
-
await api.Commits.create(
|
|
3434
|
+
await api.Commits.create(
|
|
3435
|
+
projectPath,
|
|
3436
|
+
destinationBranch,
|
|
3437
|
+
ctx.input.title,
|
|
3438
|
+
actions
|
|
3439
|
+
);
|
|
2915
3440
|
} catch (e) {
|
|
2916
|
-
throw new errors.InputError(
|
|
3441
|
+
throw new errors.InputError(
|
|
3442
|
+
`Committing the changes to ${destinationBranch} failed ${e}`
|
|
3443
|
+
);
|
|
2917
3444
|
}
|
|
2918
3445
|
try {
|
|
2919
|
-
const mergeRequestUrl = await api.MergeRequests.create(
|
|
2920
|
-
|
|
2921
|
-
|
|
2922
|
-
|
|
3446
|
+
const mergeRequestUrl = await api.MergeRequests.create(
|
|
3447
|
+
projectPath,
|
|
3448
|
+
destinationBranch,
|
|
3449
|
+
String(defaultBranch),
|
|
3450
|
+
ctx.input.title,
|
|
3451
|
+
{
|
|
3452
|
+
description: ctx.input.description,
|
|
3453
|
+
removeSourceBranch: ctx.input.removeSourceBranch ? ctx.input.removeSourceBranch : false,
|
|
3454
|
+
assigneeId
|
|
3455
|
+
}
|
|
3456
|
+
).then((mergeRequest) => {
|
|
2923
3457
|
return mergeRequest.web_url;
|
|
2924
3458
|
});
|
|
2925
3459
|
ctx.output("projectid", projectPath);
|
|
@@ -2955,6 +3489,10 @@ const createBuiltinActions = (options) => {
|
|
|
2955
3489
|
integrations,
|
|
2956
3490
|
config
|
|
2957
3491
|
}),
|
|
3492
|
+
createPublishGerritReviewAction({
|
|
3493
|
+
integrations,
|
|
3494
|
+
config
|
|
3495
|
+
}),
|
|
2958
3496
|
createPublishGithubAction({
|
|
2959
3497
|
integrations,
|
|
2960
3498
|
config,
|
|
@@ -3023,14 +3561,18 @@ class TemplateActionRegistry {
|
|
|
3023
3561
|
}
|
|
3024
3562
|
register(action) {
|
|
3025
3563
|
if (this.actions.has(action.id)) {
|
|
3026
|
-
throw new errors.ConflictError(
|
|
3564
|
+
throw new errors.ConflictError(
|
|
3565
|
+
`Template action with ID '${action.id}' has already been registered`
|
|
3566
|
+
);
|
|
3027
3567
|
}
|
|
3028
3568
|
this.actions.set(action.id, action);
|
|
3029
3569
|
}
|
|
3030
3570
|
get(actionId) {
|
|
3031
3571
|
const action = this.actions.get(actionId);
|
|
3032
3572
|
if (!action) {
|
|
3033
|
-
throw new errors.NotFoundError(
|
|
3573
|
+
throw new errors.NotFoundError(
|
|
3574
|
+
`Template action with ID '${actionId}' is not registered.`
|
|
3575
|
+
);
|
|
3034
3576
|
}
|
|
3035
3577
|
return action;
|
|
3036
3578
|
}
|
|
@@ -3039,7 +3581,10 @@ class TemplateActionRegistry {
|
|
|
3039
3581
|
}
|
|
3040
3582
|
}
|
|
3041
3583
|
|
|
3042
|
-
const migrationsDir = backendCommon.resolvePackagePath(
|
|
3584
|
+
const migrationsDir = backendCommon.resolvePackagePath(
|
|
3585
|
+
"@backstage/plugin-scaffolder-backend",
|
|
3586
|
+
"migrations"
|
|
3587
|
+
);
|
|
3043
3588
|
const parseSqlDateToIsoString = (input) => {
|
|
3044
3589
|
if (typeof input === "string") {
|
|
3045
3590
|
return luxon.DateTime.fromSQL(input, { zone: "UTC" }).toISO();
|
|
@@ -3154,10 +3699,14 @@ class DatabaseTaskStore {
|
|
|
3154
3699
|
}
|
|
3155
3700
|
}
|
|
3156
3701
|
async listStaleTasks({ timeoutS }) {
|
|
3157
|
-
const rawRows = await this.db("tasks").where("status", "processing").andWhere(
|
|
3158
|
-
|
|
3159
|
-
|
|
3160
|
-
|
|
3702
|
+
const rawRows = await this.db("tasks").where("status", "processing").andWhere(
|
|
3703
|
+
"last_heartbeat_at",
|
|
3704
|
+
"<=",
|
|
3705
|
+
this.db.client.config.client.includes("sqlite3") ? this.db.raw(`datetime('now', ?)`, [`-${timeoutS} seconds`]) : this.db.raw(`dateadd('second', ?, ?)`, [
|
|
3706
|
+
`-${timeoutS}`,
|
|
3707
|
+
this.db.fn.now()
|
|
3708
|
+
])
|
|
3709
|
+
);
|
|
3161
3710
|
const tasks = rawRows.map((row) => ({
|
|
3162
3711
|
taskId: row.id
|
|
3163
3712
|
}));
|
|
@@ -3172,7 +3721,9 @@ class DatabaseTaskStore {
|
|
|
3172
3721
|
if (status === "failed" || status === "completed") {
|
|
3173
3722
|
oldStatus = "processing";
|
|
3174
3723
|
} else {
|
|
3175
|
-
throw new Error(
|
|
3724
|
+
throw new Error(
|
|
3725
|
+
`Invalid status update of run '${taskId}' to status '${status}'`
|
|
3726
|
+
);
|
|
3176
3727
|
}
|
|
3177
3728
|
await this.db.transaction(async (tx) => {
|
|
3178
3729
|
const [task] = await tx("tasks").where({
|
|
@@ -3182,7 +3733,9 @@ class DatabaseTaskStore {
|
|
|
3182
3733
|
throw new Error(`No task with taskId ${taskId} found`);
|
|
3183
3734
|
}
|
|
3184
3735
|
if (task.status !== oldStatus) {
|
|
3185
|
-
throw new errors.ConflictError(
|
|
3736
|
+
throw new errors.ConflictError(
|
|
3737
|
+
`Refusing to update status of run '${taskId}' to status '${status}' as it is currently '${task.status}', expected '${oldStatus}'`
|
|
3738
|
+
);
|
|
3186
3739
|
}
|
|
3187
3740
|
const updateCount = await tx("tasks").where({
|
|
3188
3741
|
id: taskId,
|
|
@@ -3191,7 +3744,9 @@ class DatabaseTaskStore {
|
|
|
3191
3744
|
status
|
|
3192
3745
|
});
|
|
3193
3746
|
if (updateCount !== 1) {
|
|
3194
|
-
throw new errors.ConflictError(
|
|
3747
|
+
throw new errors.ConflictError(
|
|
3748
|
+
`Failed to update status to '${status}' for taskId ${taskId}`
|
|
3749
|
+
);
|
|
3195
3750
|
}
|
|
3196
3751
|
await tx("task_events").insert({
|
|
3197
3752
|
task_id: taskId,
|
|
@@ -3231,7 +3786,9 @@ class DatabaseTaskStore {
|
|
|
3231
3786
|
createdAt: parseSqlDateToIsoString(event.created_at)
|
|
3232
3787
|
};
|
|
3233
3788
|
} catch (error) {
|
|
3234
|
-
throw new Error(
|
|
3789
|
+
throw new Error(
|
|
3790
|
+
`Failed to parse event body from event taskId=${taskId} id=${event.id}, ${error}`
|
|
3791
|
+
);
|
|
3235
3792
|
}
|
|
3236
3793
|
});
|
|
3237
3794
|
return { events };
|
|
@@ -3292,7 +3849,10 @@ class TaskManager {
|
|
|
3292
3849
|
this.startTimeout();
|
|
3293
3850
|
} catch (error) {
|
|
3294
3851
|
this.isDone = true;
|
|
3295
|
-
this.logger.error(
|
|
3852
|
+
this.logger.error(
|
|
3853
|
+
`Heartbeat for task ${this.task.taskId} failed`,
|
|
3854
|
+
error
|
|
3855
|
+
);
|
|
3296
3856
|
}
|
|
3297
3857
|
}, 1e3);
|
|
3298
3858
|
}
|
|
@@ -3313,7 +3873,9 @@ class StorageTaskBroker {
|
|
|
3313
3873
|
}
|
|
3314
3874
|
async list(options) {
|
|
3315
3875
|
if (!this.storage.list) {
|
|
3316
|
-
throw new Error(
|
|
3876
|
+
throw new Error(
|
|
3877
|
+
"TaskStore does not implement the list method. Please implement the list method to be able to list tasks"
|
|
3878
|
+
);
|
|
3317
3879
|
}
|
|
3318
3880
|
return await this.storage.list({ createdBy: options == null ? void 0 : options.createdBy });
|
|
3319
3881
|
}
|
|
@@ -3321,12 +3883,16 @@ class StorageTaskBroker {
|
|
|
3321
3883
|
for (; ; ) {
|
|
3322
3884
|
const pendingTask = await this.storage.claimTask();
|
|
3323
3885
|
if (pendingTask) {
|
|
3324
|
-
return TaskManager.create(
|
|
3325
|
-
|
|
3326
|
-
|
|
3327
|
-
|
|
3328
|
-
|
|
3329
|
-
|
|
3886
|
+
return TaskManager.create(
|
|
3887
|
+
{
|
|
3888
|
+
taskId: pendingTask.id,
|
|
3889
|
+
spec: pendingTask.spec,
|
|
3890
|
+
secrets: pendingTask.secrets,
|
|
3891
|
+
createdBy: pendingTask.createdBy
|
|
3892
|
+
},
|
|
3893
|
+
this.storage,
|
|
3894
|
+
this.logger
|
|
3895
|
+
);
|
|
3330
3896
|
}
|
|
3331
3897
|
await this.waitForDispatch();
|
|
3332
3898
|
}
|
|
@@ -3364,19 +3930,21 @@ class StorageTaskBroker {
|
|
|
3364
3930
|
}
|
|
3365
3931
|
async vacuumTasks(options) {
|
|
3366
3932
|
const { tasks } = await this.storage.listStaleTasks(options);
|
|
3367
|
-
await Promise.all(
|
|
3368
|
-
|
|
3369
|
-
|
|
3370
|
-
|
|
3371
|
-
|
|
3372
|
-
|
|
3373
|
-
|
|
3374
|
-
|
|
3375
|
-
|
|
3376
|
-
|
|
3377
|
-
|
|
3378
|
-
|
|
3379
|
-
|
|
3933
|
+
await Promise.all(
|
|
3934
|
+
tasks.map(async (task) => {
|
|
3935
|
+
try {
|
|
3936
|
+
await this.storage.completeTask({
|
|
3937
|
+
taskId: task.taskId,
|
|
3938
|
+
status: "failed",
|
|
3939
|
+
eventBody: {
|
|
3940
|
+
message: "The task was cancelled because the task worker lost connection to the task broker"
|
|
3941
|
+
}
|
|
3942
|
+
});
|
|
3943
|
+
} catch (error) {
|
|
3944
|
+
this.logger.warn(`Failed to cancel task '${task.taskId}', ${error}`);
|
|
3945
|
+
}
|
|
3946
|
+
})
|
|
3947
|
+
);
|
|
3380
3948
|
}
|
|
3381
3949
|
waitForDispatch() {
|
|
3382
3950
|
return this.deferredDispatch.promise;
|
|
@@ -3397,10 +3965,12 @@ function generateExampleOutput(schema) {
|
|
|
3397
3965
|
return examples[0];
|
|
3398
3966
|
}
|
|
3399
3967
|
if (schema.type === "object") {
|
|
3400
|
-
return Object.fromEntries(
|
|
3401
|
-
key,
|
|
3402
|
-
|
|
3403
|
-
|
|
3968
|
+
return Object.fromEntries(
|
|
3969
|
+
Object.entries((_a = schema.properties) != null ? _a : {}).map(([key, value]) => [
|
|
3970
|
+
key,
|
|
3971
|
+
generateExampleOutput(value)
|
|
3972
|
+
])
|
|
3973
|
+
);
|
|
3404
3974
|
} else if (schema.type === "array") {
|
|
3405
3975
|
const [firstSchema] = (_b = [schema.items]) == null ? void 0 : _b.flat();
|
|
3406
3976
|
if (firstSchema) {
|
|
@@ -3427,7 +3997,11 @@ const createStepLogger = ({
|
|
|
3427
3997
|
const metadata = { stepId: step.id };
|
|
3428
3998
|
const taskLogger = winston__namespace.createLogger({
|
|
3429
3999
|
level: process.env.LOG_LEVEL || "info",
|
|
3430
|
-
format: winston__namespace.format.combine(
|
|
4000
|
+
format: winston__namespace.format.combine(
|
|
4001
|
+
winston__namespace.format.colorize(),
|
|
4002
|
+
winston__namespace.format.timestamp(),
|
|
4003
|
+
winston__namespace.format.simple()
|
|
4004
|
+
),
|
|
3431
4005
|
defaultMeta: {}
|
|
3432
4006
|
});
|
|
3433
4007
|
const streamLogger = new stream.PassThrough();
|
|
@@ -3447,13 +4021,17 @@ class NunjucksWorkflowRunner {
|
|
|
3447
4021
|
isSingleTemplateString(input) {
|
|
3448
4022
|
var _a, _b;
|
|
3449
4023
|
const { parser, nodes } = nunjucks__default["default"];
|
|
3450
|
-
const parsed = parser.parse(
|
|
3451
|
-
|
|
3452
|
-
|
|
3453
|
-
|
|
3454
|
-
|
|
4024
|
+
const parsed = parser.parse(
|
|
4025
|
+
input,
|
|
4026
|
+
{},
|
|
4027
|
+
{
|
|
4028
|
+
autoescape: false,
|
|
4029
|
+
tags: {
|
|
4030
|
+
variableStart: "${{",
|
|
4031
|
+
variableEnd: "}}"
|
|
4032
|
+
}
|
|
3455
4033
|
}
|
|
3456
|
-
|
|
4034
|
+
);
|
|
3457
4035
|
return parsed.children.length === 1 && !(((_b = (_a = parsed.children[0]) == null ? void 0 : _a.children) == null ? void 0 : _b[0]) instanceof nodes.TemplateData);
|
|
3458
4036
|
}
|
|
3459
4037
|
render(input, context, renderTemplate) {
|
|
@@ -3462,7 +4040,10 @@ class NunjucksWorkflowRunner {
|
|
|
3462
4040
|
if (typeof value === "string") {
|
|
3463
4041
|
try {
|
|
3464
4042
|
if (this.isSingleTemplateString(value)) {
|
|
3465
|
-
const wrappedDumped = value.replace(
|
|
4043
|
+
const wrappedDumped = value.replace(
|
|
4044
|
+
/\${{(.+)}}/g,
|
|
4045
|
+
"${{ ( $1 ) | dump }}"
|
|
4046
|
+
);
|
|
3466
4047
|
const templated2 = renderTemplate(wrappedDumped, context);
|
|
3467
4048
|
if (templated2 === "") {
|
|
3468
4049
|
return void 0;
|
|
@@ -3470,7 +4051,9 @@ class NunjucksWorkflowRunner {
|
|
|
3470
4051
|
return JSON.parse(templated2);
|
|
3471
4052
|
}
|
|
3472
4053
|
} catch (ex) {
|
|
3473
|
-
this.options.logger.error(
|
|
4054
|
+
this.options.logger.error(
|
|
4055
|
+
`Failed to parse template string: ${value} with error ${ex.message}`
|
|
4056
|
+
);
|
|
3474
4057
|
}
|
|
3475
4058
|
const templated = renderTemplate(value, context);
|
|
3476
4059
|
if (templated === "") {
|
|
@@ -3487,9 +4070,14 @@ class NunjucksWorkflowRunner {
|
|
|
3487
4070
|
async execute(task) {
|
|
3488
4071
|
var _a, _b, _c, _d, _e;
|
|
3489
4072
|
if (!isValidTaskSpec(task.spec)) {
|
|
3490
|
-
throw new errors.InputError(
|
|
4073
|
+
throw new errors.InputError(
|
|
4074
|
+
"Wrong template version executed with the workflow engine"
|
|
4075
|
+
);
|
|
3491
4076
|
}
|
|
3492
|
-
const workspacePath = path__default["default"].join(
|
|
4077
|
+
const workspacePath = path__default["default"].join(
|
|
4078
|
+
this.options.workingDirectory,
|
|
4079
|
+
await task.getWorkspaceName()
|
|
4080
|
+
);
|
|
3493
4081
|
const { integrations } = this.options;
|
|
3494
4082
|
const renderTemplate = await SecureTemplater.loadRenderer({
|
|
3495
4083
|
parseRepoUrl(url) {
|
|
@@ -3499,7 +4087,9 @@ class NunjucksWorkflowRunner {
|
|
|
3499
4087
|
});
|
|
3500
4088
|
try {
|
|
3501
4089
|
await fs__default["default"].ensureDir(workspacePath);
|
|
3502
|
-
await task.emitLog(
|
|
4090
|
+
await task.emitLog(
|
|
4091
|
+
`Starting up task with ${task.spec.steps.length} steps`
|
|
4092
|
+
);
|
|
3503
4093
|
const context = {
|
|
3504
4094
|
parameters: task.spec.parameters,
|
|
3505
4095
|
steps: {},
|
|
@@ -3508,9 +4098,16 @@ class NunjucksWorkflowRunner {
|
|
|
3508
4098
|
for (const step of task.spec.steps) {
|
|
3509
4099
|
try {
|
|
3510
4100
|
if (step.if) {
|
|
3511
|
-
const ifResult = await this.render(
|
|
4101
|
+
const ifResult = await this.render(
|
|
4102
|
+
step.if,
|
|
4103
|
+
context,
|
|
4104
|
+
renderTemplate
|
|
4105
|
+
);
|
|
3512
4106
|
if (!isTruthy(ifResult)) {
|
|
3513
|
-
await task.emitLog(
|
|
4107
|
+
await task.emitLog(
|
|
4108
|
+
`Skipping step ${step.id} because it's if condition was false`,
|
|
4109
|
+
{ stepId: step.id, status: "skipped" }
|
|
4110
|
+
);
|
|
3514
4111
|
continue;
|
|
3515
4112
|
}
|
|
3516
4113
|
}
|
|
@@ -3521,10 +4118,13 @@ class NunjucksWorkflowRunner {
|
|
|
3521
4118
|
const action = this.options.actionRegistry.get(step.action);
|
|
3522
4119
|
const { taskLogger, streamLogger } = createStepLogger({ task, step });
|
|
3523
4120
|
if (task.isDryRun && !action.supportsDryRun) {
|
|
3524
|
-
task.emitLog(
|
|
3525
|
-
|
|
3526
|
-
|
|
3527
|
-
|
|
4121
|
+
task.emitLog(
|
|
4122
|
+
`Skipping because ${action.id} does not support dry-run`,
|
|
4123
|
+
{
|
|
4124
|
+
stepId: step.id,
|
|
4125
|
+
status: "skipped"
|
|
4126
|
+
}
|
|
4127
|
+
);
|
|
3528
4128
|
const outputSchema = (_a = action.schema) == null ? void 0 : _a.output;
|
|
3529
4129
|
if (outputSchema) {
|
|
3530
4130
|
context.steps[step.id] = {
|
|
@@ -3535,12 +4135,21 @@ class NunjucksWorkflowRunner {
|
|
|
3535
4135
|
}
|
|
3536
4136
|
continue;
|
|
3537
4137
|
}
|
|
3538
|
-
const input = (_c = step.input && this.render(
|
|
4138
|
+
const input = (_c = step.input && this.render(
|
|
4139
|
+
step.input,
|
|
4140
|
+
{ ...context, secrets: (_b = task.secrets) != null ? _b : {} },
|
|
4141
|
+
renderTemplate
|
|
4142
|
+
)) != null ? _c : {};
|
|
3539
4143
|
if ((_d = action.schema) == null ? void 0 : _d.input) {
|
|
3540
|
-
const validateResult = jsonschema.validate(
|
|
4144
|
+
const validateResult = jsonschema.validate(
|
|
4145
|
+
input,
|
|
4146
|
+
action.schema.input
|
|
4147
|
+
);
|
|
3541
4148
|
if (!validateResult.valid) {
|
|
3542
4149
|
const errors$1 = validateResult.errors.join(", ");
|
|
3543
|
-
throw new errors.InputError(
|
|
4150
|
+
throw new errors.InputError(
|
|
4151
|
+
`Invalid input passed to action ${action.id}, ${errors$1}`
|
|
4152
|
+
);
|
|
3544
4153
|
}
|
|
3545
4154
|
}
|
|
3546
4155
|
const tmpDirs = new Array();
|
|
@@ -3552,7 +4161,9 @@ class NunjucksWorkflowRunner {
|
|
|
3552
4161
|
logStream: streamLogger,
|
|
3553
4162
|
workspacePath,
|
|
3554
4163
|
createTemporaryDirectory: async () => {
|
|
3555
|
-
const tmpDir = await fs__default["default"].mkdtemp(
|
|
4164
|
+
const tmpDir = await fs__default["default"].mkdtemp(
|
|
4165
|
+
`${workspacePath}_step-${step.id}-`
|
|
4166
|
+
);
|
|
3556
4167
|
tmpDirs.push(tmpDir);
|
|
3557
4168
|
return tmpDir;
|
|
3558
4169
|
},
|
|
@@ -3623,9 +4234,13 @@ class TaskWorker {
|
|
|
3623
4234
|
async runOneTask(task) {
|
|
3624
4235
|
try {
|
|
3625
4236
|
if (task.spec.apiVersion !== "scaffolder.backstage.io/v1beta3") {
|
|
3626
|
-
throw new Error(
|
|
4237
|
+
throw new Error(
|
|
4238
|
+
`Unsupported Template apiVersion ${task.spec.apiVersion}`
|
|
4239
|
+
);
|
|
3627
4240
|
}
|
|
3628
|
-
const { output } = await this.options.runners.workflowRunner.execute(
|
|
4241
|
+
const { output } = await this.options.runners.workflowRunner.execute(
|
|
4242
|
+
task
|
|
4243
|
+
);
|
|
3629
4244
|
await task.complete("completed", { output });
|
|
3630
4245
|
} catch (error) {
|
|
3631
4246
|
errors.assertError(error);
|
|
@@ -3672,7 +4287,10 @@ function createDryRunner(options) {
|
|
|
3672
4287
|
});
|
|
3673
4288
|
const dryRunId = uuid.v4();
|
|
3674
4289
|
const log = new Array();
|
|
3675
|
-
const contentsPath = backendCommon.resolveSafeChildPath(
|
|
4290
|
+
const contentsPath = backendCommon.resolveSafeChildPath(
|
|
4291
|
+
options.workingDirectory,
|
|
4292
|
+
`dry-run-content-${dryRunId}`
|
|
4293
|
+
);
|
|
3676
4294
|
try {
|
|
3677
4295
|
await deserializeDirectoryContents(contentsPath, input.directoryContents);
|
|
3678
4296
|
const result = await workflowRunner.execute({
|
|
@@ -3688,7 +4306,9 @@ function createDryRunner(options) {
|
|
|
3688
4306
|
],
|
|
3689
4307
|
templateInfo: {
|
|
3690
4308
|
entityRef: "template:default/dry-run",
|
|
3691
|
-
baseUrl: url.pathToFileURL(
|
|
4309
|
+
baseUrl: url.pathToFileURL(
|
|
4310
|
+
backendCommon.resolveSafeChildPath(contentsPath, "template.yaml")
|
|
4311
|
+
).toString()
|
|
3692
4312
|
}
|
|
3693
4313
|
},
|
|
3694
4314
|
secrets: input.secrets,
|
|
@@ -3735,7 +4355,9 @@ async function getWorkingDirectory(config, logger) {
|
|
|
3735
4355
|
logger.info(`using working directory: ${workingDirectory}`);
|
|
3736
4356
|
} catch (err) {
|
|
3737
4357
|
errors.assertError(err);
|
|
3738
|
-
logger.error(
|
|
4358
|
+
logger.error(
|
|
4359
|
+
`working directory ${workingDirectory} ${err.code === "ENOENT" ? "does not exist" : "is not writable"}`
|
|
4360
|
+
);
|
|
3739
4361
|
throw err;
|
|
3740
4362
|
}
|
|
3741
4363
|
return workingDirectory;
|
|
@@ -3764,7 +4386,9 @@ async function findTemplate(options) {
|
|
|
3764
4386
|
}
|
|
3765
4387
|
const template = await catalogApi.getEntityByRef(entityRef, { token });
|
|
3766
4388
|
if (!template) {
|
|
3767
|
-
throw new errors.NotFoundError(
|
|
4389
|
+
throw new errors.NotFoundError(
|
|
4390
|
+
`Template ${catalogModel.stringifyEntityRef(entityRef)} not found`
|
|
4391
|
+
);
|
|
3768
4392
|
}
|
|
3769
4393
|
return template;
|
|
3770
4394
|
}
|
|
@@ -3826,31 +4450,36 @@ async function createRouter(options) {
|
|
|
3826
4450
|
workingDirectory,
|
|
3827
4451
|
additionalTemplateFilters
|
|
3828
4452
|
});
|
|
3829
|
-
router.get(
|
|
3830
|
-
|
|
3831
|
-
|
|
3832
|
-
|
|
3833
|
-
|
|
3834
|
-
|
|
3835
|
-
|
|
3836
|
-
|
|
3837
|
-
|
|
3838
|
-
|
|
3839
|
-
const parameters = [(_a = template.spec.parameters) != null ? _a : []].flat();
|
|
3840
|
-
res.json({
|
|
3841
|
-
title: (_b = template.metadata.title) != null ? _b : template.metadata.name,
|
|
3842
|
-
steps: parameters.map((schema) => {
|
|
3843
|
-
var _a2;
|
|
3844
|
-
return {
|
|
3845
|
-
title: (_a2 = schema.title) != null ? _a2 : "Fill in template parameters",
|
|
3846
|
-
schema
|
|
3847
|
-
};
|
|
3848
|
-
})
|
|
4453
|
+
router.get(
|
|
4454
|
+
"/v2/templates/:namespace/:kind/:name/parameter-schema",
|
|
4455
|
+
async (req, res) => {
|
|
4456
|
+
var _a, _b;
|
|
4457
|
+
const { namespace, kind, name } = req.params;
|
|
4458
|
+
const { token } = parseBearerToken(req.headers.authorization);
|
|
4459
|
+
const template = await findTemplate({
|
|
4460
|
+
catalogApi: catalogClient,
|
|
4461
|
+
entityRef: { kind, namespace, name },
|
|
4462
|
+
token
|
|
3849
4463
|
});
|
|
3850
|
-
|
|
3851
|
-
|
|
4464
|
+
if (isSupportedTemplate(template)) {
|
|
4465
|
+
const parameters = [(_a = template.spec.parameters) != null ? _a : []].flat();
|
|
4466
|
+
res.json({
|
|
4467
|
+
title: (_b = template.metadata.title) != null ? _b : template.metadata.name,
|
|
4468
|
+
steps: parameters.map((schema) => {
|
|
4469
|
+
var _a2;
|
|
4470
|
+
return {
|
|
4471
|
+
title: (_a2 = schema.title) != null ? _a2 : "Fill in template parameters",
|
|
4472
|
+
schema
|
|
4473
|
+
};
|
|
4474
|
+
})
|
|
4475
|
+
});
|
|
4476
|
+
} else {
|
|
4477
|
+
throw new errors.InputError(
|
|
4478
|
+
`Unsupported apiVersion field in schema entity, ${template.apiVersion}`
|
|
4479
|
+
);
|
|
4480
|
+
}
|
|
3852
4481
|
}
|
|
3853
|
-
|
|
4482
|
+
).get("/v2/actions", async (_req, res) => {
|
|
3854
4483
|
const actionsList = actionRegistry.list().map((action) => {
|
|
3855
4484
|
return {
|
|
3856
4485
|
id: action.id,
|
|
@@ -3865,8 +4494,15 @@ async function createRouter(options) {
|
|
|
3865
4494
|
const { kind, namespace, name } = catalogModel.parseEntityRef(templateRef, {
|
|
3866
4495
|
defaultKind: "template"
|
|
3867
4496
|
});
|
|
3868
|
-
const { token, entityRef: userEntityRef } = parseBearerToken(
|
|
4497
|
+
const { token, entityRef: userEntityRef } = parseBearerToken(
|
|
4498
|
+
req.headers.authorization
|
|
4499
|
+
);
|
|
3869
4500
|
const userEntity = userEntityRef ? await catalogClient.getEntityByRef(userEntityRef, { token }) : void 0;
|
|
4501
|
+
let auditLog = `Scaffolding task for ${templateRef}`;
|
|
4502
|
+
if (userEntityRef) {
|
|
4503
|
+
auditLog += ` created by ${userEntityRef}`;
|
|
4504
|
+
}
|
|
4505
|
+
logger.info(auditLog);
|
|
3870
4506
|
const values = req.body.values;
|
|
3871
4507
|
const template = await findTemplate({
|
|
3872
4508
|
catalogApi: catalogClient,
|
|
@@ -3874,7 +4510,9 @@ async function createRouter(options) {
|
|
|
3874
4510
|
token
|
|
3875
4511
|
});
|
|
3876
4512
|
if (!isSupportedTemplate(template)) {
|
|
3877
|
-
throw new errors.InputError(
|
|
4513
|
+
throw new errors.InputError(
|
|
4514
|
+
`Unsupported apiVersion field in schema entity, ${template.apiVersion}`
|
|
4515
|
+
);
|
|
3878
4516
|
}
|
|
3879
4517
|
for (const parameters of [(_a = template.spec.parameters) != null ? _a : []].flat()) {
|
|
3880
4518
|
const result2 = jsonschema.validate(values, parameters);
|
|
@@ -3924,7 +4562,9 @@ async function createRouter(options) {
|
|
|
3924
4562
|
throw new errors.InputError("createdBy query parameter must be a string");
|
|
3925
4563
|
}
|
|
3926
4564
|
if (!taskBroker.list) {
|
|
3927
|
-
throw new Error(
|
|
4565
|
+
throw new Error(
|
|
4566
|
+
"TaskBroker does not support listing tasks, please implement the list method on the TaskBroker."
|
|
4567
|
+
);
|
|
3928
4568
|
}
|
|
3929
4569
|
const tasks = await taskBroker.list({
|
|
3930
4570
|
createdBy: userEntityRef
|
|
@@ -3949,23 +4589,30 @@ async function createRouter(options) {
|
|
|
3949
4589
|
});
|
|
3950
4590
|
const subscription = taskBroker.event$({ taskId, after }).subscribe({
|
|
3951
4591
|
error: (error) => {
|
|
3952
|
-
logger.error(
|
|
4592
|
+
logger.error(
|
|
4593
|
+
`Received error from event stream when observing taskId '${taskId}', ${error}`
|
|
4594
|
+
);
|
|
4595
|
+
res.end();
|
|
3953
4596
|
},
|
|
3954
4597
|
next: ({ events }) => {
|
|
3955
4598
|
var _a;
|
|
3956
4599
|
let shouldUnsubscribe = false;
|
|
3957
4600
|
for (const event of events) {
|
|
3958
|
-
res.write(
|
|
4601
|
+
res.write(
|
|
4602
|
+
`event: ${event.type}
|
|
3959
4603
|
data: ${JSON.stringify(event)}
|
|
3960
4604
|
|
|
3961
|
-
`
|
|
4605
|
+
`
|
|
4606
|
+
);
|
|
3962
4607
|
if (event.type === "completion") {
|
|
3963
4608
|
shouldUnsubscribe = true;
|
|
3964
4609
|
}
|
|
3965
4610
|
}
|
|
3966
4611
|
(_a = res.flush) == null ? void 0 : _a.call(res);
|
|
3967
|
-
if (shouldUnsubscribe)
|
|
4612
|
+
if (shouldUnsubscribe) {
|
|
3968
4613
|
subscription.unsubscribe();
|
|
4614
|
+
res.end();
|
|
4615
|
+
}
|
|
3969
4616
|
}
|
|
3970
4617
|
});
|
|
3971
4618
|
req.on("close", () => {
|
|
@@ -3980,7 +4627,9 @@ data: ${JSON.stringify(event)}
|
|
|
3980
4627
|
}, 3e4);
|
|
3981
4628
|
const subscription = taskBroker.event$({ taskId, after }).subscribe({
|
|
3982
4629
|
error: (error) => {
|
|
3983
|
-
logger.error(
|
|
4630
|
+
logger.error(
|
|
4631
|
+
`Received error from event stream when observing taskId '${taskId}', ${error}`
|
|
4632
|
+
);
|
|
3984
4633
|
},
|
|
3985
4634
|
next: ({ events }) => {
|
|
3986
4635
|
clearTimeout(timeout);
|
|
@@ -3998,7 +4647,9 @@ data: ${JSON.stringify(event)}
|
|
|
3998
4647
|
template: zod.z.unknown(),
|
|
3999
4648
|
values: zod.z.record(zod.z.unknown()),
|
|
4000
4649
|
secrets: zod.z.record(zod.z.string()).optional(),
|
|
4001
|
-
directoryContents: zod.z.array(
|
|
4650
|
+
directoryContents: zod.z.array(
|
|
4651
|
+
zod.z.object({ path: zod.z.string(), base64Content: zod.z.string() })
|
|
4652
|
+
)
|
|
4002
4653
|
});
|
|
4003
4654
|
const body = await bodySchema.parseAsync(req.body).catch((e) => {
|
|
4004
4655
|
throw new errors.InputError(`Malformed request: ${e}`);
|
|
@@ -4065,7 +4716,9 @@ function parseBearerToken(header) {
|
|
|
4065
4716
|
throw new TypeError("Expected Bearer with JWT");
|
|
4066
4717
|
}
|
|
4067
4718
|
const [_header, rawPayload, _signature] = token.split(".");
|
|
4068
|
-
const payload = JSON.parse(
|
|
4719
|
+
const payload = JSON.parse(
|
|
4720
|
+
Buffer.from(rawPayload, "base64").toString()
|
|
4721
|
+
);
|
|
4069
4722
|
if (typeof payload !== "object" || payload === null || Array.isArray(payload)) {
|
|
4070
4723
|
throw new TypeError("Malformed JWT payload");
|
|
4071
4724
|
}
|
|
@@ -4104,30 +4757,51 @@ class ScaffolderEntitiesProcessor {
|
|
|
4104
4757
|
defaultKind: "Group",
|
|
4105
4758
|
defaultNamespace: selfRef.namespace
|
|
4106
4759
|
});
|
|
4107
|
-
emit(
|
|
4108
|
-
|
|
4109
|
-
|
|
4110
|
-
|
|
4111
|
-
|
|
4112
|
-
|
|
4113
|
-
|
|
4114
|
-
|
|
4115
|
-
|
|
4116
|
-
|
|
4117
|
-
|
|
4118
|
-
|
|
4119
|
-
|
|
4120
|
-
|
|
4121
|
-
|
|
4122
|
-
|
|
4123
|
-
|
|
4124
|
-
|
|
4760
|
+
emit(
|
|
4761
|
+
pluginCatalogBackend.processingResult.relation({
|
|
4762
|
+
source: selfRef,
|
|
4763
|
+
type: catalogModel.RELATION_OWNED_BY,
|
|
4764
|
+
target: {
|
|
4765
|
+
kind: targetRef.kind,
|
|
4766
|
+
namespace: targetRef.namespace,
|
|
4767
|
+
name: targetRef.name
|
|
4768
|
+
}
|
|
4769
|
+
})
|
|
4770
|
+
);
|
|
4771
|
+
emit(
|
|
4772
|
+
pluginCatalogBackend.processingResult.relation({
|
|
4773
|
+
source: {
|
|
4774
|
+
kind: targetRef.kind,
|
|
4775
|
+
namespace: targetRef.namespace,
|
|
4776
|
+
name: targetRef.name
|
|
4777
|
+
},
|
|
4778
|
+
type: catalogModel.RELATION_OWNER_OF,
|
|
4779
|
+
target: selfRef
|
|
4780
|
+
})
|
|
4781
|
+
);
|
|
4125
4782
|
}
|
|
4126
4783
|
}
|
|
4127
4784
|
return entity;
|
|
4128
4785
|
}
|
|
4129
4786
|
}
|
|
4130
4787
|
|
|
4788
|
+
const scaffolderCatalogModule = backendPluginApi.createBackendModule({
|
|
4789
|
+
moduleId: "scaffolder.module",
|
|
4790
|
+
pluginId: "catalog",
|
|
4791
|
+
register(env) {
|
|
4792
|
+
env.registerInit({
|
|
4793
|
+
deps: {
|
|
4794
|
+
catalogProcessingExtensionPoint: pluginCatalogNode.catalogProcessingExtentionPoint
|
|
4795
|
+
},
|
|
4796
|
+
async init({ catalogProcessingExtensionPoint }) {
|
|
4797
|
+
catalogProcessingExtensionPoint.addProcessor(
|
|
4798
|
+
new ScaffolderEntitiesProcessor()
|
|
4799
|
+
);
|
|
4800
|
+
}
|
|
4801
|
+
});
|
|
4802
|
+
}
|
|
4803
|
+
});
|
|
4804
|
+
|
|
4131
4805
|
exports.DatabaseTaskStore = DatabaseTaskStore;
|
|
4132
4806
|
exports.ScaffolderEntitiesProcessor = ScaffolderEntitiesProcessor;
|
|
4133
4807
|
exports.TaskManager = TaskManager;
|
|
@@ -4152,6 +4826,7 @@ exports.createPublishBitbucketCloudAction = createPublishBitbucketCloudAction;
|
|
|
4152
4826
|
exports.createPublishBitbucketServerAction = createPublishBitbucketServerAction;
|
|
4153
4827
|
exports.createPublishFileAction = createPublishFileAction;
|
|
4154
4828
|
exports.createPublishGerritAction = createPublishGerritAction;
|
|
4829
|
+
exports.createPublishGerritReviewAction = createPublishGerritReviewAction;
|
|
4155
4830
|
exports.createPublishGithubAction = createPublishGithubAction;
|
|
4156
4831
|
exports.createPublishGithubPullRequestAction = createPublishGithubPullRequestAction;
|
|
4157
4832
|
exports.createPublishGitlabAction = createPublishGitlabAction;
|
|
@@ -4160,4 +4835,5 @@ exports.createRouter = createRouter;
|
|
|
4160
4835
|
exports.createTemplateAction = createTemplateAction;
|
|
4161
4836
|
exports.executeShellCommand = executeShellCommand;
|
|
4162
4837
|
exports.fetchContents = fetchContents;
|
|
4838
|
+
exports.scaffolderCatalogModule = scaffolderCatalogModule;
|
|
4163
4839
|
//# sourceMappingURL=index.cjs.js.map
|