@backstage/plugin-scaffolder-backend 1.4.0-next.3 → 1.5.0-next.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +86 -0
- package/alpha/package.json +1 -1
- package/dist/index.alpha.d.ts +25 -0
- package/dist/index.beta.d.ts +25 -0
- package/dist/index.cjs.js +956 -310
- package/dist/index.cjs.js.map +1 -1
- package/dist/index.d.ts +25 -0
- package/package.json +14 -14
package/dist/index.cjs.js
CHANGED
|
@@ -147,7 +147,9 @@ function createCatalogRegisterAction(options) {
|
|
|
147
147
|
const { repoContentsUrl, catalogInfoPath = "/catalog-info.yaml" } = input;
|
|
148
148
|
const integration = integrations.byUrl(repoContentsUrl);
|
|
149
149
|
if (!integration) {
|
|
150
|
-
throw new errors.InputError(
|
|
150
|
+
throw new errors.InputError(
|
|
151
|
+
`No integration found for host ${repoContentsUrl}`
|
|
152
|
+
);
|
|
151
153
|
}
|
|
152
154
|
catalogInfoUrl = integration.resolveUrl({
|
|
153
155
|
base: repoContentsUrl,
|
|
@@ -155,22 +157,32 @@ function createCatalogRegisterAction(options) {
|
|
|
155
157
|
});
|
|
156
158
|
}
|
|
157
159
|
ctx.logger.info(`Registering ${catalogInfoUrl} in the catalog`);
|
|
158
|
-
await catalogClient.addLocation(
|
|
159
|
-
|
|
160
|
-
target: catalogInfoUrl
|
|
161
|
-
}, ((_a = ctx.secrets) == null ? void 0 : _a.backstageToken) ? { token: ctx.secrets.backstageToken } : {});
|
|
162
|
-
try {
|
|
163
|
-
const result = await catalogClient.addLocation({
|
|
164
|
-
dryRun: true,
|
|
160
|
+
await catalogClient.addLocation(
|
|
161
|
+
{
|
|
165
162
|
type: "url",
|
|
166
163
|
target: catalogInfoUrl
|
|
167
|
-
},
|
|
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
|
+
);
|
|
168
176
|
if (result.entities.length > 0) {
|
|
169
177
|
const { entities } = result;
|
|
170
178
|
let entity;
|
|
171
|
-
entity = entities.find(
|
|
179
|
+
entity = entities.find(
|
|
180
|
+
(e) => !e.metadata.name.startsWith("generated-") && e.kind === "Component"
|
|
181
|
+
);
|
|
172
182
|
if (!entity) {
|
|
173
|
-
entity = entities.find(
|
|
183
|
+
entity = entities.find(
|
|
184
|
+
(e) => !e.metadata.name.startsWith("generated-")
|
|
185
|
+
);
|
|
174
186
|
}
|
|
175
187
|
if (!entity) {
|
|
176
188
|
entity = entities[0];
|
|
@@ -213,7 +225,10 @@ function createCatalogWriteAction() {
|
|
|
213
225
|
ctx.logStream.write(`Writing catalog-info.yaml`);
|
|
214
226
|
const { filePath, entity } = ctx.input;
|
|
215
227
|
const path = filePath != null ? filePath : "catalog-info.yaml";
|
|
216
|
-
await fs__default["default"].writeFile(
|
|
228
|
+
await fs__default["default"].writeFile(
|
|
229
|
+
backendCommon.resolveSafeChildPath(ctx.workspacePath, path),
|
|
230
|
+
yaml__namespace.stringify(entity)
|
|
231
|
+
);
|
|
217
232
|
}
|
|
218
233
|
});
|
|
219
234
|
}
|
|
@@ -249,18 +264,22 @@ function createDebugLogAction() {
|
|
|
249
264
|
}
|
|
250
265
|
if ((_b = ctx.input) == null ? void 0 : _b.listWorkspace) {
|
|
251
266
|
const files = await recursiveReadDir(ctx.workspacePath);
|
|
252
|
-
ctx.logStream.write(
|
|
253
|
-
|
|
267
|
+
ctx.logStream.write(
|
|
268
|
+
`Workspace:
|
|
269
|
+
${files.map((f) => ` - ${path.relative(ctx.workspacePath, f)}`).join("\n")}`
|
|
270
|
+
);
|
|
254
271
|
}
|
|
255
272
|
}
|
|
256
273
|
});
|
|
257
274
|
}
|
|
258
275
|
async function recursiveReadDir(dir) {
|
|
259
276
|
const subdirs = await fs.readdir(dir);
|
|
260
|
-
const files = await Promise.all(
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
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
|
+
);
|
|
264
283
|
return files.reduce((a, f) => a.concat(f), []);
|
|
265
284
|
}
|
|
266
285
|
|
|
@@ -295,7 +314,9 @@ async function fetchContents({
|
|
|
295
314
|
base: baseUrl
|
|
296
315
|
});
|
|
297
316
|
} else {
|
|
298
|
-
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
|
+
);
|
|
299
320
|
}
|
|
300
321
|
const res = await reader.readTree(readUrl);
|
|
301
322
|
await fs__default["default"].ensureDir(outputPath);
|
|
@@ -424,13 +445,21 @@ class SecureTemplater {
|
|
|
424
445
|
sandbox.parseRepoUrl = (url) => JSON.stringify(parseRepoUrl(url));
|
|
425
446
|
}
|
|
426
447
|
if (additionalTemplateFilters) {
|
|
427
|
-
sandbox.additionalTemplateFilters = Object.fromEntries(
|
|
428
|
-
filterName,
|
|
429
|
-
|
|
430
|
-
|
|
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
|
+
);
|
|
431
454
|
}
|
|
432
455
|
const vm = new vm2.VM({ sandbox });
|
|
433
|
-
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
|
+
);
|
|
434
463
|
vm.run(mkScript(nunjucksSource));
|
|
435
464
|
const render = (template, values) => {
|
|
436
465
|
if (!vm) {
|
|
@@ -473,13 +502,21 @@ function createFetchTemplateAction(options) {
|
|
|
473
502
|
type: "object"
|
|
474
503
|
},
|
|
475
504
|
copyWithoutRender: {
|
|
476
|
-
title: "Copy Without Render",
|
|
505
|
+
title: "[Deprecated] Copy Without Render",
|
|
477
506
|
description: "An array of glob patterns. Any files or directories which match are copied without being processed as templates.",
|
|
478
507
|
type: "array",
|
|
479
508
|
items: {
|
|
480
509
|
type: "string"
|
|
481
510
|
}
|
|
482
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
|
+
},
|
|
483
520
|
cookiecutterCompat: {
|
|
484
521
|
title: "Cookiecutter compatibility mode",
|
|
485
522
|
description: "Enable features to maximise compatibility with templates built for fetch:cookiecutter",
|
|
@@ -501,11 +538,32 @@ function createFetchTemplateAction(options) {
|
|
|
501
538
|
const templateDir = backendCommon.resolveSafeChildPath(workDir, "template");
|
|
502
539
|
const targetPath = (_a = ctx.input.targetPath) != null ? _a : "./";
|
|
503
540
|
const outputDir = backendCommon.resolveSafeChildPath(ctx.workspacePath, targetPath);
|
|
504
|
-
if (ctx.input.copyWithoutRender &&
|
|
505
|
-
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
|
+
);
|
|
506
545
|
}
|
|
507
|
-
|
|
508
|
-
|
|
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
|
+
);
|
|
562
|
+
}
|
|
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
|
+
);
|
|
509
567
|
}
|
|
510
568
|
let extension = false;
|
|
511
569
|
if (ctx.input.templateFileExtension) {
|
|
@@ -529,59 +587,84 @@ function createFetchTemplateAction(options) {
|
|
|
529
587
|
markDirectories: true,
|
|
530
588
|
followSymbolicLinks: false
|
|
531
589
|
});
|
|
532
|
-
const nonTemplatedEntries = new Set(
|
|
533
|
-
|
|
534
|
-
|
|
535
|
-
|
|
536
|
-
|
|
537
|
-
|
|
538
|
-
|
|
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
|
+
);
|
|
539
603
|
const { cookiecutterCompat, values } = ctx.input;
|
|
540
604
|
const context = {
|
|
541
605
|
[cookiecutterCompat ? "cookiecutter" : "values"]: values
|
|
542
606
|
};
|
|
543
|
-
ctx.logger.info(
|
|
607
|
+
ctx.logger.info(
|
|
608
|
+
`Processing ${allEntriesInTemplate.length} template files/directories with input values`,
|
|
609
|
+
ctx.input.values
|
|
610
|
+
);
|
|
544
611
|
const renderTemplate = await SecureTemplater.loadRenderer({
|
|
545
612
|
cookiecutterCompat: ctx.input.cookiecutterCompat,
|
|
546
613
|
additionalTemplateFilters
|
|
547
614
|
});
|
|
548
615
|
for (const location of allEntriesInTemplate) {
|
|
549
|
-
let renderFilename;
|
|
550
616
|
let renderContents;
|
|
551
617
|
let localOutputPath = location;
|
|
552
618
|
if (extension) {
|
|
553
|
-
renderFilename = true;
|
|
554
619
|
renderContents = path.extname(localOutputPath) === extension;
|
|
555
620
|
if (renderContents) {
|
|
556
621
|
localOutputPath = localOutputPath.slice(0, -extension.length);
|
|
557
622
|
}
|
|
623
|
+
localOutputPath = renderTemplate(localOutputPath, context);
|
|
558
624
|
} else {
|
|
559
|
-
|
|
625
|
+
renderContents = !nonTemplatedEntries.has(location);
|
|
626
|
+
if (renderFilename) {
|
|
627
|
+
localOutputPath = renderTemplate(localOutputPath, context);
|
|
628
|
+
} else {
|
|
629
|
+
localOutputPath = renderContents ? renderTemplate(localOutputPath, context) : localOutputPath;
|
|
630
|
+
}
|
|
560
631
|
}
|
|
561
|
-
if (
|
|
562
|
-
|
|
632
|
+
if (containsSkippedContent(localOutputPath)) {
|
|
633
|
+
continue;
|
|
563
634
|
}
|
|
564
635
|
const outputPath = backendCommon.resolveSafeChildPath(outputDir, localOutputPath);
|
|
565
|
-
if (
|
|
636
|
+
if (fs__default["default"].existsSync(outputPath)) {
|
|
566
637
|
continue;
|
|
567
638
|
}
|
|
568
639
|
if (!renderContents && !extension) {
|
|
569
|
-
ctx.logger.info(
|
|
640
|
+
ctx.logger.info(
|
|
641
|
+
`Copying file/directory ${location} without processing.`
|
|
642
|
+
);
|
|
570
643
|
}
|
|
571
644
|
if (location.endsWith("/")) {
|
|
572
|
-
ctx.logger.info(
|
|
645
|
+
ctx.logger.info(
|
|
646
|
+
`Writing directory ${location} to template output path.`
|
|
647
|
+
);
|
|
573
648
|
await fs__default["default"].ensureDir(outputPath);
|
|
574
649
|
} else {
|
|
575
650
|
const inputFilePath = backendCommon.resolveSafeChildPath(templateDir, location);
|
|
576
651
|
const stats = await fs__default["default"].promises.lstat(inputFilePath);
|
|
577
652
|
if (stats.isSymbolicLink() || await isbinaryfile.isBinaryFile(inputFilePath)) {
|
|
578
|
-
ctx.logger.info(
|
|
653
|
+
ctx.logger.info(
|
|
654
|
+
`Copying file binary or symbolic link at ${location}, to template output path.`
|
|
655
|
+
);
|
|
579
656
|
await fs__default["default"].copy(inputFilePath, outputPath);
|
|
580
657
|
} else {
|
|
581
658
|
const statsObj = await fs__default["default"].stat(inputFilePath);
|
|
582
|
-
ctx.logger.info(
|
|
659
|
+
ctx.logger.info(
|
|
660
|
+
`Writing file ${location} to template output path with mode ${statsObj.mode}.`
|
|
661
|
+
);
|
|
583
662
|
const inputFileContents = await fs__default["default"].readFile(inputFilePath, "utf-8");
|
|
584
|
-
await fs__default["default"].outputFile(
|
|
663
|
+
await fs__default["default"].outputFile(
|
|
664
|
+
outputPath,
|
|
665
|
+
renderContents ? renderTemplate(inputFileContents, context) : inputFileContents,
|
|
666
|
+
{ mode: statsObj.mode }
|
|
667
|
+
);
|
|
585
668
|
}
|
|
586
669
|
}
|
|
587
670
|
}
|
|
@@ -589,6 +672,9 @@ function createFetchTemplateAction(options) {
|
|
|
589
672
|
}
|
|
590
673
|
});
|
|
591
674
|
}
|
|
675
|
+
function containsSkippedContent(localOutputPath) {
|
|
676
|
+
return localOutputPath === "" || path__default["default"].isAbsolute(localOutputPath) || localOutputPath.includes(`${path__default["default"].sep}${path__default["default"].sep}`);
|
|
677
|
+
}
|
|
592
678
|
|
|
593
679
|
const createFilesystemDeleteAction = () => {
|
|
594
680
|
return createTemplateAction({
|
|
@@ -675,15 +761,23 @@ const createFilesystemRenameAction = () => {
|
|
|
675
761
|
if (!file.from || !file.to) {
|
|
676
762
|
throw new errors.InputError("each file must have a from and to property");
|
|
677
763
|
}
|
|
678
|
-
const sourceFilepath = backendCommon.resolveSafeChildPath(
|
|
764
|
+
const sourceFilepath = backendCommon.resolveSafeChildPath(
|
|
765
|
+
ctx.workspacePath,
|
|
766
|
+
file.from
|
|
767
|
+
);
|
|
679
768
|
const destFilepath = backendCommon.resolveSafeChildPath(ctx.workspacePath, file.to);
|
|
680
769
|
try {
|
|
681
770
|
await fs__default["default"].move(sourceFilepath, destFilepath, {
|
|
682
771
|
overwrite: (_b = file.overwrite) != null ? _b : false
|
|
683
772
|
});
|
|
684
|
-
ctx.logger.info(
|
|
773
|
+
ctx.logger.info(
|
|
774
|
+
`File ${sourceFilepath} renamed to ${destFilepath} successfully`
|
|
775
|
+
);
|
|
685
776
|
} catch (err) {
|
|
686
|
-
ctx.logger.error(
|
|
777
|
+
ctx.logger.error(
|
|
778
|
+
`Failed to rename file ${sourceFilepath} to ${destFilepath}:`,
|
|
779
|
+
err
|
|
780
|
+
);
|
|
687
781
|
throw err;
|
|
688
782
|
}
|
|
689
783
|
}
|
|
@@ -693,7 +787,10 @@ const createFilesystemRenameAction = () => {
|
|
|
693
787
|
|
|
694
788
|
const getRepoSourceDirectory = (workspacePath, sourcePath) => {
|
|
695
789
|
if (sourcePath) {
|
|
696
|
-
const safeSuffix = path.normalize(sourcePath).replace(
|
|
790
|
+
const safeSuffix = path.normalize(sourcePath).replace(
|
|
791
|
+
/^(\.\.(\/|\\|$))+/,
|
|
792
|
+
""
|
|
793
|
+
);
|
|
697
794
|
const path$1 = path.join(workspacePath, safeSuffix);
|
|
698
795
|
if (!backendCommon.isChildPath(workspacePath, path$1)) {
|
|
699
796
|
throw new Error("Invalid source path");
|
|
@@ -708,7 +805,9 @@ const parseRepoUrl = (repoUrl, integrations) => {
|
|
|
708
805
|
try {
|
|
709
806
|
parsed = new URL(`https://${repoUrl}`);
|
|
710
807
|
} catch (error) {
|
|
711
|
-
throw new errors.InputError(
|
|
808
|
+
throw new errors.InputError(
|
|
809
|
+
`Invalid repo URL passed to publisher, got ${repoUrl}, ${error}`
|
|
810
|
+
);
|
|
712
811
|
}
|
|
713
812
|
const host = parsed.host;
|
|
714
813
|
const owner = (_a = parsed.searchParams.get("owner")) != null ? _a : void 0;
|
|
@@ -717,25 +816,35 @@ const parseRepoUrl = (repoUrl, integrations) => {
|
|
|
717
816
|
const project = (_d = parsed.searchParams.get("project")) != null ? _d : void 0;
|
|
718
817
|
const type = (_e = integrations.byHost(host)) == null ? void 0 : _e.type;
|
|
719
818
|
if (!type) {
|
|
720
|
-
throw new errors.InputError(
|
|
819
|
+
throw new errors.InputError(
|
|
820
|
+
`No matching integration configuration for host ${host}, please check your integrations config`
|
|
821
|
+
);
|
|
721
822
|
}
|
|
722
823
|
if (type === "bitbucket") {
|
|
723
824
|
if (host === "bitbucket.org") {
|
|
724
825
|
if (!workspace) {
|
|
725
|
-
throw new errors.InputError(
|
|
826
|
+
throw new errors.InputError(
|
|
827
|
+
`Invalid repo URL passed to publisher: ${repoUrl}, missing workspace`
|
|
828
|
+
);
|
|
726
829
|
}
|
|
727
830
|
}
|
|
728
831
|
if (!project) {
|
|
729
|
-
throw new errors.InputError(
|
|
832
|
+
throw new errors.InputError(
|
|
833
|
+
`Invalid repo URL passed to publisher: ${repoUrl}, missing project`
|
|
834
|
+
);
|
|
730
835
|
}
|
|
731
836
|
} else {
|
|
732
|
-
if (!owner) {
|
|
733
|
-
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
|
+
);
|
|
734
841
|
}
|
|
735
842
|
}
|
|
736
843
|
const repo = parsed.searchParams.get("repo");
|
|
737
844
|
if (!repo) {
|
|
738
|
-
throw new errors.InputError(
|
|
845
|
+
throw new errors.InputError(
|
|
846
|
+
`Invalid repo URL passed to publisher: ${repoUrl}, missing repo`
|
|
847
|
+
);
|
|
739
848
|
}
|
|
740
849
|
return { host, owner, repo, organization, workspace, project };
|
|
741
850
|
};
|
|
@@ -760,7 +869,9 @@ const executeShellCommand = async (options) => {
|
|
|
760
869
|
});
|
|
761
870
|
process.on("close", (code) => {
|
|
762
871
|
if (code !== 0) {
|
|
763
|
-
return reject(
|
|
872
|
+
return reject(
|
|
873
|
+
new Error(`Command ${command} failed, exit code: ${code}`)
|
|
874
|
+
);
|
|
764
875
|
}
|
|
765
876
|
return resolve();
|
|
766
877
|
});
|
|
@@ -777,8 +888,7 @@ async function initRepoAndPush({
|
|
|
777
888
|
}) {
|
|
778
889
|
var _a, _b;
|
|
779
890
|
const git = backendCommon.Git.fromAuth({
|
|
780
|
-
|
|
781
|
-
password: auth.password,
|
|
891
|
+
...auth,
|
|
782
892
|
logger
|
|
783
893
|
});
|
|
784
894
|
await git.init({
|
|
@@ -806,6 +916,39 @@ async function initRepoAndPush({
|
|
|
806
916
|
remote: "origin"
|
|
807
917
|
});
|
|
808
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
|
+
}
|
|
809
952
|
const enableBranchProtectionOnDefaultRepoBranch = async ({
|
|
810
953
|
repoName,
|
|
811
954
|
client,
|
|
@@ -813,7 +956,8 @@ const enableBranchProtectionOnDefaultRepoBranch = async ({
|
|
|
813
956
|
logger,
|
|
814
957
|
requireCodeOwnerReviews,
|
|
815
958
|
requiredStatusCheckContexts = [],
|
|
816
|
-
defaultBranch = "master"
|
|
959
|
+
defaultBranch = "master",
|
|
960
|
+
enforceAdmins = true
|
|
817
961
|
}) => {
|
|
818
962
|
const tryOnce = async () => {
|
|
819
963
|
try {
|
|
@@ -829,7 +973,7 @@ const enableBranchProtectionOnDefaultRepoBranch = async ({
|
|
|
829
973
|
contexts: requiredStatusCheckContexts
|
|
830
974
|
},
|
|
831
975
|
restrictions: null,
|
|
832
|
-
enforce_admins:
|
|
976
|
+
enforce_admins: enforceAdmins,
|
|
833
977
|
required_pull_request_reviews: {
|
|
834
978
|
required_approving_review_count: 1,
|
|
835
979
|
require_code_owner_reviews: requireCodeOwnerReviews
|
|
@@ -837,8 +981,12 @@ const enableBranchProtectionOnDefaultRepoBranch = async ({
|
|
|
837
981
|
});
|
|
838
982
|
} catch (e) {
|
|
839
983
|
errors.assertError(e);
|
|
840
|
-
if (e.message.includes(
|
|
841
|
-
|
|
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
|
+
);
|
|
842
990
|
} else {
|
|
843
991
|
throw e;
|
|
844
992
|
}
|
|
@@ -880,10 +1028,14 @@ async function getOctokitOptions(options) {
|
|
|
880
1028
|
}
|
|
881
1029
|
const githubCredentialsProvider = credentialsProvider != null ? credentialsProvider : integration.DefaultGithubCredentialsProvider.fromIntegrations(integrations);
|
|
882
1030
|
const { token: credentialProviderToken } = await githubCredentialsProvider.getCredentials({
|
|
883
|
-
url: `https://${host}/${encodeURIComponent(owner)}/${encodeURIComponent(
|
|
1031
|
+
url: `https://${host}/${encodeURIComponent(owner)}/${encodeURIComponent(
|
|
1032
|
+
repo
|
|
1033
|
+
)}`
|
|
884
1034
|
});
|
|
885
1035
|
if (!credentialProviderToken) {
|
|
886
|
-
throw new errors.InputError(
|
|
1036
|
+
throw new errors.InputError(
|
|
1037
|
+
`No token available for host: ${host}, with owner ${owner}, and repo ${repo}`
|
|
1038
|
+
);
|
|
887
1039
|
}
|
|
888
1040
|
return {
|
|
889
1041
|
auth: credentialProviderToken,
|
|
@@ -920,9 +1072,13 @@ async function createGithubRepoWithCollaboratorsAndTopics(client, repo, owner, r
|
|
|
920
1072
|
} catch (e) {
|
|
921
1073
|
errors.assertError(e);
|
|
922
1074
|
if (e.message === "Resource not accessible by integration") {
|
|
923
|
-
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
|
+
);
|
|
924
1078
|
}
|
|
925
|
-
throw new Error(
|
|
1079
|
+
throw new Error(
|
|
1080
|
+
`Failed to create the ${user.data.type} repository ${owner}/${repo}, ${e.message}`
|
|
1081
|
+
);
|
|
926
1082
|
}
|
|
927
1083
|
if (access == null ? void 0 : access.startsWith(`${owner}/`)) {
|
|
928
1084
|
const [, team] = access.split("/");
|
|
@@ -963,7 +1119,9 @@ async function createGithubRepoWithCollaboratorsAndTopics(client, repo, owner, r
|
|
|
963
1119
|
} catch (e) {
|
|
964
1120
|
errors.assertError(e);
|
|
965
1121
|
const name = extractCollaboratorName(collaborator);
|
|
966
|
-
logger.warn(
|
|
1122
|
+
logger.warn(
|
|
1123
|
+
`Skipping ${collaborator.access} access for ${name}, ${e.message}`
|
|
1124
|
+
);
|
|
967
1125
|
}
|
|
968
1126
|
}
|
|
969
1127
|
}
|
|
@@ -981,7 +1139,7 @@ async function createGithubRepoWithCollaboratorsAndTopics(client, repo, owner, r
|
|
|
981
1139
|
}
|
|
982
1140
|
return newRepo;
|
|
983
1141
|
}
|
|
984
|
-
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) {
|
|
985
1143
|
const gitAuthorInfo = {
|
|
986
1144
|
name: gitAuthorName ? gitAuthorName : config.getOptionalString("scaffolder.defaultAuthor.name"),
|
|
987
1145
|
email: gitAuthorEmail ? gitAuthorEmail : config.getOptionalString("scaffolder.defaultAuthor.email")
|
|
@@ -1008,11 +1166,14 @@ async function initRepoPushAndProtect(remoteUrl, password, workspacePath, source
|
|
|
1008
1166
|
logger,
|
|
1009
1167
|
defaultBranch,
|
|
1010
1168
|
requireCodeOwnerReviews,
|
|
1011
|
-
requiredStatusCheckContexts
|
|
1169
|
+
requiredStatusCheckContexts,
|
|
1170
|
+
enforceAdmins: protectEnforceAdmins
|
|
1012
1171
|
});
|
|
1013
1172
|
} catch (e) {
|
|
1014
1173
|
errors.assertError(e);
|
|
1015
|
-
logger.warn(
|
|
1174
|
+
logger.warn(
|
|
1175
|
+
`Skipping: default branch protection on '${repo}', ${e.message}`
|
|
1176
|
+
);
|
|
1016
1177
|
}
|
|
1017
1178
|
}
|
|
1018
1179
|
}
|
|
@@ -1070,17 +1231,21 @@ function createGithubActionsDispatchAction(options) {
|
|
|
1070
1231
|
workflowInputs,
|
|
1071
1232
|
token: providedToken
|
|
1072
1233
|
} = ctx.input;
|
|
1073
|
-
ctx.logger.info(
|
|
1234
|
+
ctx.logger.info(
|
|
1235
|
+
`Dispatching workflow ${workflowId} for repo ${repoUrl} on ${branchOrTagName}`
|
|
1236
|
+
);
|
|
1074
1237
|
const { owner, repo } = parseRepoUrl(repoUrl, integrations);
|
|
1075
1238
|
if (!owner) {
|
|
1076
1239
|
throw new errors.InputError("Invalid repository owner provided in repoUrl");
|
|
1077
1240
|
}
|
|
1078
|
-
const client = new octokit.Octokit(
|
|
1079
|
-
|
|
1080
|
-
|
|
1081
|
-
|
|
1082
|
-
|
|
1083
|
-
|
|
1241
|
+
const client = new octokit.Octokit(
|
|
1242
|
+
await getOctokitOptions({
|
|
1243
|
+
integrations,
|
|
1244
|
+
repoUrl,
|
|
1245
|
+
credentialsProvider: githubCredentialsProvider,
|
|
1246
|
+
token: providedToken
|
|
1247
|
+
})
|
|
1248
|
+
);
|
|
1084
1249
|
await client.rest.actions.createWorkflowDispatch({
|
|
1085
1250
|
owner,
|
|
1086
1251
|
repo,
|
|
@@ -1136,12 +1301,14 @@ function createGithubIssuesLabelAction(options) {
|
|
|
1136
1301
|
if (!owner) {
|
|
1137
1302
|
throw new errors.InputError("Invalid repository owner provided in repoUrl");
|
|
1138
1303
|
}
|
|
1139
|
-
const client = new octokit.Octokit(
|
|
1140
|
-
|
|
1141
|
-
|
|
1142
|
-
|
|
1143
|
-
|
|
1144
|
-
|
|
1304
|
+
const client = new octokit.Octokit(
|
|
1305
|
+
await getOctokitOptions({
|
|
1306
|
+
integrations,
|
|
1307
|
+
credentialsProvider: githubCredentialsProvider,
|
|
1308
|
+
repoUrl,
|
|
1309
|
+
token: providedToken
|
|
1310
|
+
})
|
|
1311
|
+
);
|
|
1145
1312
|
try {
|
|
1146
1313
|
await client.rest.issues.addLabels({
|
|
1147
1314
|
owner,
|
|
@@ -1151,7 +1318,9 @@ function createGithubIssuesLabelAction(options) {
|
|
|
1151
1318
|
});
|
|
1152
1319
|
} catch (e) {
|
|
1153
1320
|
errors.assertError(e);
|
|
1154
|
-
ctx.logger.warn(
|
|
1321
|
+
ctx.logger.warn(
|
|
1322
|
+
`Failed: adding labels to issue: '${number}' on repo: '${repo}', ${e.message}`
|
|
1323
|
+
);
|
|
1155
1324
|
}
|
|
1156
1325
|
}
|
|
1157
1326
|
});
|
|
@@ -1267,6 +1436,11 @@ const protectDefaultBranch = {
|
|
|
1267
1436
|
type: "boolean",
|
|
1268
1437
|
description: `Protect the default branch after creating the repository. The default value is 'true'`
|
|
1269
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
|
+
};
|
|
1270
1444
|
const gitCommitMessage = {
|
|
1271
1445
|
title: "Git Commit Message",
|
|
1272
1446
|
type: "string",
|
|
@@ -1345,7 +1519,21 @@ function createGithubRepoCreateAction(options) {
|
|
|
1345
1519
|
if (!owner) {
|
|
1346
1520
|
throw new errors.InputError("Invalid repository owner provided in repoUrl");
|
|
1347
1521
|
}
|
|
1348
|
-
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
|
+
);
|
|
1349
1537
|
ctx.output("remoteUrl", newRepo.clone_url);
|
|
1350
1538
|
}
|
|
1351
1539
|
});
|
|
@@ -1366,6 +1554,7 @@ function createGithubRepoPushAction(options) {
|
|
|
1366
1554
|
requiredStatusCheckContexts: requiredStatusCheckContexts,
|
|
1367
1555
|
defaultBranch: defaultBranch,
|
|
1368
1556
|
protectDefaultBranch: protectDefaultBranch,
|
|
1557
|
+
protectEnforceAdmins: protectEnforceAdmins,
|
|
1369
1558
|
gitCommitMessage: gitCommitMessage,
|
|
1370
1559
|
gitAuthorName: gitAuthorName,
|
|
1371
1560
|
gitAuthorEmail: gitAuthorEmail,
|
|
@@ -1386,6 +1575,7 @@ function createGithubRepoPushAction(options) {
|
|
|
1386
1575
|
repoUrl,
|
|
1387
1576
|
defaultBranch = "master",
|
|
1388
1577
|
protectDefaultBranch = true,
|
|
1578
|
+
protectEnforceAdmins = true,
|
|
1389
1579
|
gitCommitMessage = "initial commit",
|
|
1390
1580
|
gitAuthorName,
|
|
1391
1581
|
gitAuthorEmail,
|
|
@@ -1407,7 +1597,25 @@ function createGithubRepoPushAction(options) {
|
|
|
1407
1597
|
const targetRepo = await client.rest.repos.get({ owner, repo });
|
|
1408
1598
|
const remoteUrl = targetRepo.data.clone_url;
|
|
1409
1599
|
const repoContentsUrl = `${targetRepo.data.html_url}/blob/${defaultBranch}`;
|
|
1410
|
-
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
|
+
);
|
|
1411
1619
|
ctx.output("remoteUrl", remoteUrl);
|
|
1412
1620
|
ctx.output("repoContentsUrl", repoContentsUrl);
|
|
1413
1621
|
}
|
|
@@ -1499,12 +1707,14 @@ function createGithubWebhookAction(options) {
|
|
|
1499
1707
|
if (!owner) {
|
|
1500
1708
|
throw new errors.InputError("Invalid repository owner provided in repoUrl");
|
|
1501
1709
|
}
|
|
1502
|
-
const client = new octokit.Octokit(
|
|
1503
|
-
|
|
1504
|
-
|
|
1505
|
-
|
|
1506
|
-
|
|
1507
|
-
|
|
1710
|
+
const client = new octokit.Octokit(
|
|
1711
|
+
await getOctokitOptions({
|
|
1712
|
+
integrations,
|
|
1713
|
+
credentialsProvider: githubCredentialsProvider,
|
|
1714
|
+
repoUrl,
|
|
1715
|
+
token: providedToken
|
|
1716
|
+
})
|
|
1717
|
+
);
|
|
1508
1718
|
try {
|
|
1509
1719
|
const insecure_ssl = insecureSsl ? "1" : "0";
|
|
1510
1720
|
await client.rest.repos.createWebhook({
|
|
@@ -1522,7 +1732,9 @@ function createGithubWebhookAction(options) {
|
|
|
1522
1732
|
ctx.logger.info(`Webhook '${webhookUrl}' created successfully`);
|
|
1523
1733
|
} catch (e) {
|
|
1524
1734
|
errors.assertError(e);
|
|
1525
|
-
ctx.logger.warn(
|
|
1735
|
+
ctx.logger.warn(
|
|
1736
|
+
`Failed: create webhook '${webhookUrl}' on repo: '${repo}', ${e.message}`
|
|
1737
|
+
);
|
|
1526
1738
|
}
|
|
1527
1739
|
}
|
|
1528
1740
|
});
|
|
@@ -1601,13 +1813,20 @@ function createPublishAzureAction(options) {
|
|
|
1601
1813
|
gitAuthorName,
|
|
1602
1814
|
gitAuthorEmail
|
|
1603
1815
|
} = ctx.input;
|
|
1604
|
-
const { owner, repo, host, organization } = parseRepoUrl(
|
|
1816
|
+
const { owner, repo, host, organization } = parseRepoUrl(
|
|
1817
|
+
repoUrl,
|
|
1818
|
+
integrations
|
|
1819
|
+
);
|
|
1605
1820
|
if (!organization) {
|
|
1606
|
-
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
|
+
);
|
|
1607
1824
|
}
|
|
1608
1825
|
const integrationConfig = integrations.azure.byHost(host);
|
|
1609
1826
|
if (!integrationConfig) {
|
|
1610
|
-
throw new errors.InputError(
|
|
1827
|
+
throw new errors.InputError(
|
|
1828
|
+
`No matching integration configuration for host ${host}, please check your integrations config`
|
|
1829
|
+
);
|
|
1611
1830
|
}
|
|
1612
1831
|
if (!integrationConfig.config.token && !ctx.input.token) {
|
|
1613
1832
|
throw new errors.InputError(`No token provided for Azure Integration ${host}`);
|
|
@@ -1619,12 +1838,16 @@ function createPublishAzureAction(options) {
|
|
|
1619
1838
|
const createOptions = { name: repo };
|
|
1620
1839
|
const returnedRepo = await client.createRepository(createOptions, owner);
|
|
1621
1840
|
if (!returnedRepo) {
|
|
1622
|
-
throw new errors.InputError(
|
|
1623
|
-
|
|
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
|
+
);
|
|
1624
1845
|
}
|
|
1625
1846
|
const remoteUrl = returnedRepo.remoteUrl;
|
|
1626
1847
|
if (!remoteUrl) {
|
|
1627
|
-
throw new errors.InputError(
|
|
1848
|
+
throw new errors.InputError(
|
|
1849
|
+
"No remote URL returned from create repository for Azure"
|
|
1850
|
+
);
|
|
1628
1851
|
}
|
|
1629
1852
|
const repoContentsUrl = remoteUrl;
|
|
1630
1853
|
const gitAuthorInfo = {
|
|
@@ -1675,12 +1898,17 @@ const createBitbucketCloudRepository = async (opts) => {
|
|
|
1675
1898
|
};
|
|
1676
1899
|
let response;
|
|
1677
1900
|
try {
|
|
1678
|
-
response = await fetch__default["default"](
|
|
1901
|
+
response = await fetch__default["default"](
|
|
1902
|
+
`${apiBaseUrl}/repositories/${workspace}/${repo}`,
|
|
1903
|
+
options
|
|
1904
|
+
);
|
|
1679
1905
|
} catch (e) {
|
|
1680
1906
|
throw new Error(`Unable to create repository, ${e}`);
|
|
1681
1907
|
}
|
|
1682
1908
|
if (response.status !== 200) {
|
|
1683
|
-
throw new Error(
|
|
1909
|
+
throw new Error(
|
|
1910
|
+
`Unable to create repository, ${response.status} ${response.statusText}, ${await response.text()}`
|
|
1911
|
+
);
|
|
1684
1912
|
}
|
|
1685
1913
|
const r = await response.json();
|
|
1686
1914
|
let remoteUrl = "";
|
|
@@ -1720,7 +1948,9 @@ const createBitbucketServerRepository = async (opts) => {
|
|
|
1720
1948
|
throw new Error(`Unable to create repository, ${e}`);
|
|
1721
1949
|
}
|
|
1722
1950
|
if (response.status !== 201) {
|
|
1723
|
-
throw new Error(
|
|
1951
|
+
throw new Error(
|
|
1952
|
+
`Unable to create repository, ${response.status} ${response.statusText}, ${await response.text()}`
|
|
1953
|
+
);
|
|
1724
1954
|
}
|
|
1725
1955
|
const r = await response.json();
|
|
1726
1956
|
let remoteUrl = "";
|
|
@@ -1732,15 +1962,20 @@ const createBitbucketServerRepository = async (opts) => {
|
|
|
1732
1962
|
const repoContentsUrl = `${r.links.self[0].href}`;
|
|
1733
1963
|
return { remoteUrl, repoContentsUrl };
|
|
1734
1964
|
};
|
|
1735
|
-
const getAuthorizationHeader$
|
|
1965
|
+
const getAuthorizationHeader$1 = (config) => {
|
|
1736
1966
|
if (config.username && config.appPassword) {
|
|
1737
|
-
const buffer = Buffer.from(
|
|
1967
|
+
const buffer = Buffer.from(
|
|
1968
|
+
`${config.username}:${config.appPassword}`,
|
|
1969
|
+
"utf8"
|
|
1970
|
+
);
|
|
1738
1971
|
return `Basic ${buffer.toString("base64")}`;
|
|
1739
1972
|
}
|
|
1740
1973
|
if (config.token) {
|
|
1741
1974
|
return `Bearer ${config.token}`;
|
|
1742
1975
|
}
|
|
1743
|
-
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
|
+
);
|
|
1744
1979
|
};
|
|
1745
1980
|
const performEnableLFS$1 = async (opts) => {
|
|
1746
1981
|
const { authorization, host, project, repo } = opts;
|
|
@@ -1750,9 +1985,14 @@ const performEnableLFS$1 = async (opts) => {
|
|
|
1750
1985
|
Authorization: authorization
|
|
1751
1986
|
}
|
|
1752
1987
|
};
|
|
1753
|
-
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
|
+
);
|
|
1754
1992
|
if (!ok)
|
|
1755
|
-
throw new Error(
|
|
1993
|
+
throw new Error(
|
|
1994
|
+
`Failed to enable LFS in the repository, ${status}: ${statusText}`
|
|
1995
|
+
);
|
|
1756
1996
|
};
|
|
1757
1997
|
function createPublishBitbucketAction(options) {
|
|
1758
1998
|
const { integrations, config } = options;
|
|
@@ -1830,7 +2070,9 @@ function createPublishBitbucketAction(options) {
|
|
|
1830
2070
|
},
|
|
1831
2071
|
async handler(ctx) {
|
|
1832
2072
|
var _a;
|
|
1833
|
-
ctx.logger.warn(
|
|
2073
|
+
ctx.logger.warn(
|
|
2074
|
+
`[Deprecated] Please migrate the use of action "publish:bitbucket" to "publish:bitbucketCloud" or "publish:bitbucketServer".`
|
|
2075
|
+
);
|
|
1834
2076
|
const {
|
|
1835
2077
|
repoUrl,
|
|
1836
2078
|
description,
|
|
@@ -1841,24 +2083,35 @@ function createPublishBitbucketAction(options) {
|
|
|
1841
2083
|
gitAuthorName,
|
|
1842
2084
|
gitAuthorEmail
|
|
1843
2085
|
} = ctx.input;
|
|
1844
|
-
const { workspace, project, repo, host } = parseRepoUrl(
|
|
2086
|
+
const { workspace, project, repo, host } = parseRepoUrl(
|
|
2087
|
+
repoUrl,
|
|
2088
|
+
integrations
|
|
2089
|
+
);
|
|
1845
2090
|
if (host === "bitbucket.org") {
|
|
1846
2091
|
if (!workspace) {
|
|
1847
|
-
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
|
+
);
|
|
1848
2095
|
}
|
|
1849
2096
|
}
|
|
1850
2097
|
if (!project) {
|
|
1851
|
-
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
|
+
);
|
|
1852
2101
|
}
|
|
1853
2102
|
const integrationConfig = integrations.bitbucket.byHost(host);
|
|
1854
2103
|
if (!integrationConfig) {
|
|
1855
|
-
throw new errors.InputError(
|
|
2104
|
+
throw new errors.InputError(
|
|
2105
|
+
`No matching integration configuration for host ${host}, please check your integrations config`
|
|
2106
|
+
);
|
|
1856
2107
|
}
|
|
1857
|
-
const authorization = getAuthorizationHeader$
|
|
1858
|
-
|
|
1859
|
-
|
|
1860
|
-
|
|
1861
|
-
|
|
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
|
+
);
|
|
1862
2115
|
const apiBaseUrl = integrationConfig.config.apiBaseUrl;
|
|
1863
2116
|
const createMethod = host === "bitbucket.org" ? createBitbucketCloudRepository : createBitbucketServerRepository;
|
|
1864
2117
|
const { remoteUrl, repoContentsUrl } = await createMethod({
|
|
@@ -1931,12 +2184,17 @@ const createRepository$1 = async (opts) => {
|
|
|
1931
2184
|
};
|
|
1932
2185
|
let response;
|
|
1933
2186
|
try {
|
|
1934
|
-
response = await fetch__default["default"](
|
|
2187
|
+
response = await fetch__default["default"](
|
|
2188
|
+
`${apiBaseUrl}/repositories/${workspace}/${repo}`,
|
|
2189
|
+
options
|
|
2190
|
+
);
|
|
1935
2191
|
} catch (e) {
|
|
1936
2192
|
throw new Error(`Unable to create repository, ${e}`);
|
|
1937
2193
|
}
|
|
1938
2194
|
if (response.status !== 200) {
|
|
1939
|
-
throw new Error(
|
|
2195
|
+
throw new Error(
|
|
2196
|
+
`Unable to create repository, ${response.status} ${response.statusText}, ${await response.text()}`
|
|
2197
|
+
);
|
|
1940
2198
|
}
|
|
1941
2199
|
const r = await response.json();
|
|
1942
2200
|
let remoteUrl = "";
|
|
@@ -1948,15 +2206,20 @@ const createRepository$1 = async (opts) => {
|
|
|
1948
2206
|
const repoContentsUrl = `${r.links.html.href}/src/${mainBranch}`;
|
|
1949
2207
|
return { remoteUrl, repoContentsUrl };
|
|
1950
2208
|
};
|
|
1951
|
-
const getAuthorizationHeader
|
|
2209
|
+
const getAuthorizationHeader = (config) => {
|
|
1952
2210
|
if (config.username && config.appPassword) {
|
|
1953
|
-
const buffer = Buffer.from(
|
|
2211
|
+
const buffer = Buffer.from(
|
|
2212
|
+
`${config.username}:${config.appPassword}`,
|
|
2213
|
+
"utf8"
|
|
2214
|
+
);
|
|
1954
2215
|
return `Basic ${buffer.toString("base64")}`;
|
|
1955
2216
|
}
|
|
1956
2217
|
if (config.token) {
|
|
1957
2218
|
return `Bearer ${config.token}`;
|
|
1958
2219
|
}
|
|
1959
|
-
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
|
+
);
|
|
1960
2223
|
};
|
|
1961
2224
|
function createPublishBitbucketCloudAction(options) {
|
|
1962
2225
|
const { integrations, config } = options;
|
|
@@ -2019,18 +2282,29 @@ function createPublishBitbucketCloudAction(options) {
|
|
|
2019
2282
|
defaultBranch = "master",
|
|
2020
2283
|
repoVisibility = "private"
|
|
2021
2284
|
} = ctx.input;
|
|
2022
|
-
const { workspace, project, repo, host } = parseRepoUrl(
|
|
2285
|
+
const { workspace, project, repo, host } = parseRepoUrl(
|
|
2286
|
+
repoUrl,
|
|
2287
|
+
integrations
|
|
2288
|
+
);
|
|
2023
2289
|
if (!workspace) {
|
|
2024
|
-
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
|
+
);
|
|
2025
2293
|
}
|
|
2026
2294
|
if (!project) {
|
|
2027
|
-
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
|
+
);
|
|
2028
2298
|
}
|
|
2029
2299
|
const integrationConfig = integrations.bitbucketCloud.byHost(host);
|
|
2030
2300
|
if (!integrationConfig) {
|
|
2031
|
-
throw new errors.InputError(
|
|
2301
|
+
throw new errors.InputError(
|
|
2302
|
+
`No matching integration configuration for host ${host}, please check your integrations config`
|
|
2303
|
+
);
|
|
2032
2304
|
}
|
|
2033
|
-
const authorization = getAuthorizationHeader
|
|
2305
|
+
const authorization = getAuthorizationHeader(
|
|
2306
|
+
ctx.input.token ? { token: ctx.input.token } : integrationConfig.config
|
|
2307
|
+
);
|
|
2034
2308
|
const apiBaseUrl = integrationConfig.config.apiBaseUrl;
|
|
2035
2309
|
const { remoteUrl, repoContentsUrl } = await createRepository$1({
|
|
2036
2310
|
authorization,
|
|
@@ -2054,7 +2328,9 @@ function createPublishBitbucketCloudAction(options) {
|
|
|
2054
2328
|
};
|
|
2055
2329
|
} else {
|
|
2056
2330
|
if (!integrationConfig.config.username || !integrationConfig.config.appPassword) {
|
|
2057
|
-
throw new Error(
|
|
2331
|
+
throw new Error(
|
|
2332
|
+
"Credentials for Bitbucket Cloud integration required for this action."
|
|
2333
|
+
);
|
|
2058
2334
|
}
|
|
2059
2335
|
auth = {
|
|
2060
2336
|
username: integrationConfig.config.username,
|
|
@@ -2067,7 +2343,9 @@ function createPublishBitbucketCloudAction(options) {
|
|
|
2067
2343
|
auth,
|
|
2068
2344
|
defaultBranch,
|
|
2069
2345
|
logger: ctx.logger,
|
|
2070
|
-
commitMessage: config.getOptionalString(
|
|
2346
|
+
commitMessage: config.getOptionalString(
|
|
2347
|
+
"scaffolder.defaultCommitMessage"
|
|
2348
|
+
),
|
|
2071
2349
|
gitAuthorInfo
|
|
2072
2350
|
});
|
|
2073
2351
|
ctx.output("remoteUrl", remoteUrl);
|
|
@@ -2104,7 +2382,9 @@ const createRepository = async (opts) => {
|
|
|
2104
2382
|
throw new Error(`Unable to create repository, ${e}`);
|
|
2105
2383
|
}
|
|
2106
2384
|
if (response.status !== 201) {
|
|
2107
|
-
throw new Error(
|
|
2385
|
+
throw new Error(
|
|
2386
|
+
`Unable to create repository, ${response.status} ${response.statusText}, ${await response.text()}`
|
|
2387
|
+
);
|
|
2108
2388
|
}
|
|
2109
2389
|
const r = await response.json();
|
|
2110
2390
|
let remoteUrl = "";
|
|
@@ -2116,9 +2396,6 @@ const createRepository = async (opts) => {
|
|
|
2116
2396
|
const repoContentsUrl = `${r.links.self[0].href}`;
|
|
2117
2397
|
return { remoteUrl, repoContentsUrl };
|
|
2118
2398
|
};
|
|
2119
|
-
const getAuthorizationHeader = (config) => {
|
|
2120
|
-
return `Bearer ${config.token}`;
|
|
2121
|
-
};
|
|
2122
2399
|
const performEnableLFS = async (opts) => {
|
|
2123
2400
|
const { authorization, host, project, repo } = opts;
|
|
2124
2401
|
const options = {
|
|
@@ -2127,9 +2404,14 @@ const performEnableLFS = async (opts) => {
|
|
|
2127
2404
|
Authorization: authorization
|
|
2128
2405
|
}
|
|
2129
2406
|
};
|
|
2130
|
-
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
|
+
);
|
|
2131
2411
|
if (!ok)
|
|
2132
|
-
throw new Error(
|
|
2412
|
+
throw new Error(
|
|
2413
|
+
`Failed to enable LFS in the repository, ${status}: ${statusText}`
|
|
2414
|
+
);
|
|
2133
2415
|
};
|
|
2134
2416
|
function createPublishBitbucketServerAction(options) {
|
|
2135
2417
|
const { integrations, config } = options;
|
|
@@ -2201,17 +2483,28 @@ function createPublishBitbucketServerAction(options) {
|
|
|
2201
2483
|
} = ctx.input;
|
|
2202
2484
|
const { project, repo, host } = parseRepoUrl(repoUrl, integrations);
|
|
2203
2485
|
if (!project) {
|
|
2204
|
-
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
|
+
);
|
|
2205
2489
|
}
|
|
2206
2490
|
const integrationConfig = integrations.bitbucketServer.byHost(host);
|
|
2207
2491
|
if (!integrationConfig) {
|
|
2208
|
-
throw new errors.InputError(
|
|
2492
|
+
throw new errors.InputError(
|
|
2493
|
+
`No matching integration configuration for host ${host}, please check your integrations config`
|
|
2494
|
+
);
|
|
2209
2495
|
}
|
|
2210
2496
|
const token = (_a = ctx.input.token) != null ? _a : integrationConfig.config.token;
|
|
2211
|
-
|
|
2212
|
-
|
|
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
|
+
);
|
|
2213
2507
|
}
|
|
2214
|
-
const authorization = getAuthorizationHeader({ token });
|
|
2215
2508
|
const apiBaseUrl = integrationConfig.config.apiBaseUrl;
|
|
2216
2509
|
const { remoteUrl, repoContentsUrl } = await createRepository({
|
|
2217
2510
|
authorization,
|
|
@@ -2225,9 +2518,11 @@ function createPublishBitbucketServerAction(options) {
|
|
|
2225
2518
|
name: config.getOptionalString("scaffolder.defaultAuthor.name"),
|
|
2226
2519
|
email: config.getOptionalString("scaffolder.defaultAuthor.email")
|
|
2227
2520
|
};
|
|
2228
|
-
const auth = {
|
|
2229
|
-
|
|
2230
|
-
|
|
2521
|
+
const auth = authConfig.token ? {
|
|
2522
|
+
token
|
|
2523
|
+
} : {
|
|
2524
|
+
username: authConfig.username,
|
|
2525
|
+
password: authConfig.password
|
|
2231
2526
|
};
|
|
2232
2527
|
await initRepoAndPush({
|
|
2233
2528
|
dir: getRepoSourceDirectory(ctx.workspacePath, ctx.input.sourcePath),
|
|
@@ -2235,7 +2530,9 @@ function createPublishBitbucketServerAction(options) {
|
|
|
2235
2530
|
auth,
|
|
2236
2531
|
defaultBranch,
|
|
2237
2532
|
logger: ctx.logger,
|
|
2238
|
-
commitMessage: config.getOptionalString(
|
|
2533
|
+
commitMessage: config.getOptionalString(
|
|
2534
|
+
"scaffolder.defaultCommitMessage"
|
|
2535
|
+
),
|
|
2239
2536
|
gitAuthorInfo
|
|
2240
2537
|
});
|
|
2241
2538
|
if (enableLFS) {
|
|
@@ -2282,7 +2579,7 @@ const createGerritProject = async (config, options) => {
|
|
|
2282
2579
|
body: JSON.stringify({
|
|
2283
2580
|
parent,
|
|
2284
2581
|
description,
|
|
2285
|
-
owners: [owner],
|
|
2582
|
+
owners: owner ? [owner] : [],
|
|
2286
2583
|
create_empty_commit: false
|
|
2287
2584
|
}),
|
|
2288
2585
|
headers: {
|
|
@@ -2290,9 +2587,14 @@ const createGerritProject = async (config, options) => {
|
|
|
2290
2587
|
"Content-Type": "application/json"
|
|
2291
2588
|
}
|
|
2292
2589
|
};
|
|
2293
|
-
const response = await fetch__default["default"](
|
|
2590
|
+
const response = await fetch__default["default"](
|
|
2591
|
+
`${config.baseUrl}/a/projects/${encodeURIComponent(projectName)}`,
|
|
2592
|
+
fetchOptions
|
|
2593
|
+
);
|
|
2294
2594
|
if (response.status !== 201) {
|
|
2295
|
-
throw new Error(
|
|
2595
|
+
throw new Error(
|
|
2596
|
+
`Unable to create repository, ${response.status} ${response.statusText}, ${await response.text()}`
|
|
2597
|
+
);
|
|
2296
2598
|
}
|
|
2297
2599
|
};
|
|
2298
2600
|
const generateCommitMessage = (config, commitSubject) => {
|
|
@@ -2339,6 +2641,11 @@ function createPublishGerritAction(options) {
|
|
|
2339
2641
|
title: "Default Author Email",
|
|
2340
2642
|
type: "string",
|
|
2341
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.`
|
|
2342
2649
|
}
|
|
2343
2650
|
}
|
|
2344
2651
|
},
|
|
@@ -2363,18 +2670,23 @@ function createPublishGerritAction(options) {
|
|
|
2363
2670
|
defaultBranch = "master",
|
|
2364
2671
|
gitAuthorName,
|
|
2365
2672
|
gitAuthorEmail,
|
|
2366
|
-
gitCommitMessage = "initial commit"
|
|
2673
|
+
gitCommitMessage = "initial commit",
|
|
2674
|
+
sourcePath
|
|
2367
2675
|
} = ctx.input;
|
|
2368
|
-
const { repo, host, owner, workspace } = parseRepoUrl(
|
|
2676
|
+
const { repo, host, owner, workspace } = parseRepoUrl(
|
|
2677
|
+
repoUrl,
|
|
2678
|
+
integrations
|
|
2679
|
+
);
|
|
2369
2680
|
const integrationConfig = integrations.gerrit.byHost(host);
|
|
2370
2681
|
if (!integrationConfig) {
|
|
2371
|
-
throw new errors.InputError(
|
|
2372
|
-
|
|
2373
|
-
|
|
2374
|
-
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
|
+
);
|
|
2375
2685
|
}
|
|
2376
2686
|
if (!workspace) {
|
|
2377
|
-
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
|
+
);
|
|
2378
2690
|
}
|
|
2379
2691
|
await createGerritProject(integrationConfig.config, {
|
|
2380
2692
|
description,
|
|
@@ -2392,7 +2704,7 @@ function createPublishGerritAction(options) {
|
|
|
2392
2704
|
};
|
|
2393
2705
|
const remoteUrl = `${integrationConfig.config.cloneUrl}/a/${repo}`;
|
|
2394
2706
|
await initRepoAndPush({
|
|
2395
|
-
dir: getRepoSourceDirectory(ctx.workspacePath,
|
|
2707
|
+
dir: getRepoSourceDirectory(ctx.workspacePath, sourcePath),
|
|
2396
2708
|
remoteUrl,
|
|
2397
2709
|
auth,
|
|
2398
2710
|
defaultBranch,
|
|
@@ -2407,6 +2719,115 @@ function createPublishGerritAction(options) {
|
|
|
2407
2719
|
});
|
|
2408
2720
|
}
|
|
2409
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
|
+
|
|
2410
2831
|
function createPublishGithubAction(options) {
|
|
2411
2832
|
const { integrations, config, githubCredentialsProvider } = options;
|
|
2412
2833
|
return createTemplateAction({
|
|
@@ -2425,6 +2846,7 @@ function createPublishGithubAction(options) {
|
|
|
2425
2846
|
repoVisibility: repoVisibility,
|
|
2426
2847
|
defaultBranch: defaultBranch,
|
|
2427
2848
|
protectDefaultBranch: protectDefaultBranch,
|
|
2849
|
+
protectEnforceAdmins: protectEnforceAdmins,
|
|
2428
2850
|
deleteBranchOnMerge: deleteBranchOnMerge,
|
|
2429
2851
|
gitCommitMessage: gitCommitMessage,
|
|
2430
2852
|
gitAuthorName: gitAuthorName,
|
|
@@ -2456,6 +2878,7 @@ function createPublishGithubAction(options) {
|
|
|
2456
2878
|
repoVisibility = "private",
|
|
2457
2879
|
defaultBranch = "master",
|
|
2458
2880
|
protectDefaultBranch = true,
|
|
2881
|
+
protectEnforceAdmins = true,
|
|
2459
2882
|
deleteBranchOnMerge = false,
|
|
2460
2883
|
gitCommitMessage = "initial commit",
|
|
2461
2884
|
gitAuthorName,
|
|
@@ -2478,10 +2901,42 @@ function createPublishGithubAction(options) {
|
|
|
2478
2901
|
if (!owner) {
|
|
2479
2902
|
throw new errors.InputError("Invalid repository owner provided in repoUrl");
|
|
2480
2903
|
}
|
|
2481
|
-
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
|
+
);
|
|
2482
2919
|
const remoteUrl = newRepo.clone_url;
|
|
2483
2920
|
const repoContentsUrl = `${newRepo.html_url}/blob/${defaultBranch}`;
|
|
2484
|
-
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
|
+
);
|
|
2485
2940
|
ctx.output("remoteUrl", remoteUrl);
|
|
2486
2941
|
ctx.output("repoContentsUrl", repoContentsUrl);
|
|
2487
2942
|
}
|
|
@@ -2508,11 +2963,15 @@ async function serializeDirectoryContents(sourcePath, options) {
|
|
|
2508
2963
|
stats: true
|
|
2509
2964
|
});
|
|
2510
2965
|
const limiter = limiterFactory__default["default"](10);
|
|
2511
|
-
return Promise.all(
|
|
2512
|
-
path: path$1,
|
|
2513
|
-
|
|
2514
|
-
|
|
2515
|
-
|
|
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
|
+
);
|
|
2516
2975
|
}
|
|
2517
2976
|
|
|
2518
2977
|
async function deserializeDirectoryContents(targetPath, files) {
|
|
@@ -2533,7 +2992,9 @@ const defaultClientFactory = async ({
|
|
|
2533
2992
|
host = "github.com",
|
|
2534
2993
|
token: providedToken
|
|
2535
2994
|
}) => {
|
|
2536
|
-
const [encodedHost, encodedOwner, encodedRepo] = [host, owner, repo].map(
|
|
2995
|
+
const [encodedHost, encodedOwner, encodedRepo] = [host, owner, repo].map(
|
|
2996
|
+
encodeURIComponent
|
|
2997
|
+
);
|
|
2537
2998
|
const octokitOptions = await getOctokitOptions({
|
|
2538
2999
|
integrations,
|
|
2539
3000
|
credentialsProvider: githubCredentialsProvider,
|
|
@@ -2627,7 +3088,9 @@ const createPublishGithubPullRequestAction = ({
|
|
|
2627
3088
|
} = ctx.input;
|
|
2628
3089
|
const { owner, repo, host } = parseRepoUrl(repoUrl, integrations);
|
|
2629
3090
|
if (!owner) {
|
|
2630
|
-
throw new errors.InputError(
|
|
3091
|
+
throw new errors.InputError(
|
|
3092
|
+
`No owner provided for host: ${host}, and repo ${repo}`
|
|
3093
|
+
);
|
|
2631
3094
|
}
|
|
2632
3095
|
const client = await clientFactory({
|
|
2633
3096
|
integrations,
|
|
@@ -2641,14 +3104,16 @@ const createPublishGithubPullRequestAction = ({
|
|
|
2641
3104
|
const directoryContents = await serializeDirectoryContents(fileRoot, {
|
|
2642
3105
|
gitignore: true
|
|
2643
3106
|
});
|
|
2644
|
-
const files = Object.fromEntries(
|
|
2645
|
-
|
|
2646
|
-
|
|
2647
|
-
|
|
2648
|
-
|
|
2649
|
-
|
|
2650
|
-
|
|
2651
|
-
|
|
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
|
+
);
|
|
2652
3117
|
try {
|
|
2653
3118
|
const response = await client.createPullRequest({
|
|
2654
3119
|
owner,
|
|
@@ -2758,11 +3223,15 @@ function createPublishGitlabAction(options) {
|
|
|
2758
3223
|
} = ctx.input;
|
|
2759
3224
|
const { owner, repo, host } = parseRepoUrl(repoUrl, integrations);
|
|
2760
3225
|
if (!owner) {
|
|
2761
|
-
throw new errors.InputError(
|
|
3226
|
+
throw new errors.InputError(
|
|
3227
|
+
`No owner provided for host: ${host}, and repo ${repo}`
|
|
3228
|
+
);
|
|
2762
3229
|
}
|
|
2763
3230
|
const integrationConfig = integrations.gitlab.byHost(host);
|
|
2764
3231
|
if (!integrationConfig) {
|
|
2765
|
-
throw new errors.InputError(
|
|
3232
|
+
throw new errors.InputError(
|
|
3233
|
+
`No matching integration configuration for host ${host}, please check your integrations config`
|
|
3234
|
+
);
|
|
2766
3235
|
}
|
|
2767
3236
|
if (!integrationConfig.config.token && !ctx.input.token) {
|
|
2768
3237
|
throw new errors.InputError(`No token available for host ${host}`);
|
|
@@ -2858,10 +3327,21 @@ const createPublishGitlabMergeRequestAction = (options) => {
|
|
|
2858
3327
|
type: "string",
|
|
2859
3328
|
description: "The token to use for authorization to GitLab"
|
|
2860
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
|
+
},
|
|
2861
3336
|
removeSourceBranch: {
|
|
2862
3337
|
title: "Delete source branch",
|
|
2863
3338
|
type: "boolean",
|
|
2864
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"
|
|
2865
3345
|
}
|
|
2866
3346
|
}
|
|
2867
3347
|
},
|
|
@@ -2897,7 +3377,9 @@ const createPublishGitlabMergeRequestAction = (options) => {
|
|
|
2897
3377
|
const integrationConfig = integrations.gitlab.byHost(host);
|
|
2898
3378
|
const destinationBranch = ctx.input.branchName;
|
|
2899
3379
|
if (!integrationConfig) {
|
|
2900
|
-
throw new errors.InputError(
|
|
3380
|
+
throw new errors.InputError(
|
|
3381
|
+
`No matching integration configuration for host ${host}, please check your integrations config`
|
|
3382
|
+
);
|
|
2901
3383
|
}
|
|
2902
3384
|
if (!integrationConfig.config.token && !ctx.input.token) {
|
|
2903
3385
|
throw new errors.InputError(`No token available for host ${host}`);
|
|
@@ -2908,34 +3390,70 @@ const createPublishGitlabMergeRequestAction = (options) => {
|
|
|
2908
3390
|
host: integrationConfig.config.baseUrl,
|
|
2909
3391
|
[tokenType]: token
|
|
2910
3392
|
});
|
|
2911
|
-
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
|
+
);
|
|
2912
3409
|
const fileContents = await serializeDirectoryContents(targetPath, {
|
|
2913
3410
|
gitignore: true
|
|
2914
3411
|
});
|
|
2915
|
-
const actions = fileContents.map((file) =>
|
|
2916
|
-
|
|
2917
|
-
|
|
2918
|
-
|
|
2919
|
-
|
|
2920
|
-
|
|
2921
|
-
|
|
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
|
+
});
|
|
2922
3422
|
const projects = await api.Projects.show(projectPath);
|
|
2923
3423
|
const { default_branch: defaultBranch } = projects;
|
|
2924
3424
|
try {
|
|
2925
|
-
await api.Branches.create(
|
|
3425
|
+
await api.Branches.create(
|
|
3426
|
+
projectPath,
|
|
3427
|
+
destinationBranch,
|
|
3428
|
+
String(defaultBranch)
|
|
3429
|
+
);
|
|
2926
3430
|
} catch (e) {
|
|
2927
3431
|
throw new errors.InputError(`The branch creation failed ${e}`);
|
|
2928
3432
|
}
|
|
2929
3433
|
try {
|
|
2930
|
-
await api.Commits.create(
|
|
3434
|
+
await api.Commits.create(
|
|
3435
|
+
projectPath,
|
|
3436
|
+
destinationBranch,
|
|
3437
|
+
ctx.input.title,
|
|
3438
|
+
actions
|
|
3439
|
+
);
|
|
2931
3440
|
} catch (e) {
|
|
2932
|
-
throw new errors.InputError(
|
|
3441
|
+
throw new errors.InputError(
|
|
3442
|
+
`Committing the changes to ${destinationBranch} failed ${e}`
|
|
3443
|
+
);
|
|
2933
3444
|
}
|
|
2934
3445
|
try {
|
|
2935
|
-
const mergeRequestUrl = await api.MergeRequests.create(
|
|
2936
|
-
|
|
2937
|
-
|
|
2938
|
-
|
|
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) => {
|
|
2939
3457
|
return mergeRequest.web_url;
|
|
2940
3458
|
});
|
|
2941
3459
|
ctx.output("projectid", projectPath);
|
|
@@ -2971,6 +3489,10 @@ const createBuiltinActions = (options) => {
|
|
|
2971
3489
|
integrations,
|
|
2972
3490
|
config
|
|
2973
3491
|
}),
|
|
3492
|
+
createPublishGerritReviewAction({
|
|
3493
|
+
integrations,
|
|
3494
|
+
config
|
|
3495
|
+
}),
|
|
2974
3496
|
createPublishGithubAction({
|
|
2975
3497
|
integrations,
|
|
2976
3498
|
config,
|
|
@@ -3039,14 +3561,18 @@ class TemplateActionRegistry {
|
|
|
3039
3561
|
}
|
|
3040
3562
|
register(action) {
|
|
3041
3563
|
if (this.actions.has(action.id)) {
|
|
3042
|
-
throw new errors.ConflictError(
|
|
3564
|
+
throw new errors.ConflictError(
|
|
3565
|
+
`Template action with ID '${action.id}' has already been registered`
|
|
3566
|
+
);
|
|
3043
3567
|
}
|
|
3044
3568
|
this.actions.set(action.id, action);
|
|
3045
3569
|
}
|
|
3046
3570
|
get(actionId) {
|
|
3047
3571
|
const action = this.actions.get(actionId);
|
|
3048
3572
|
if (!action) {
|
|
3049
|
-
throw new errors.NotFoundError(
|
|
3573
|
+
throw new errors.NotFoundError(
|
|
3574
|
+
`Template action with ID '${actionId}' is not registered.`
|
|
3575
|
+
);
|
|
3050
3576
|
}
|
|
3051
3577
|
return action;
|
|
3052
3578
|
}
|
|
@@ -3055,7 +3581,10 @@ class TemplateActionRegistry {
|
|
|
3055
3581
|
}
|
|
3056
3582
|
}
|
|
3057
3583
|
|
|
3058
|
-
const migrationsDir = backendCommon.resolvePackagePath(
|
|
3584
|
+
const migrationsDir = backendCommon.resolvePackagePath(
|
|
3585
|
+
"@backstage/plugin-scaffolder-backend",
|
|
3586
|
+
"migrations"
|
|
3587
|
+
);
|
|
3059
3588
|
const parseSqlDateToIsoString = (input) => {
|
|
3060
3589
|
if (typeof input === "string") {
|
|
3061
3590
|
return luxon.DateTime.fromSQL(input, { zone: "UTC" }).toISO();
|
|
@@ -3170,10 +3699,14 @@ class DatabaseTaskStore {
|
|
|
3170
3699
|
}
|
|
3171
3700
|
}
|
|
3172
3701
|
async listStaleTasks({ timeoutS }) {
|
|
3173
|
-
const rawRows = await this.db("tasks").where("status", "processing").andWhere(
|
|
3174
|
-
|
|
3175
|
-
|
|
3176
|
-
|
|
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
|
+
);
|
|
3177
3710
|
const tasks = rawRows.map((row) => ({
|
|
3178
3711
|
taskId: row.id
|
|
3179
3712
|
}));
|
|
@@ -3188,7 +3721,9 @@ class DatabaseTaskStore {
|
|
|
3188
3721
|
if (status === "failed" || status === "completed") {
|
|
3189
3722
|
oldStatus = "processing";
|
|
3190
3723
|
} else {
|
|
3191
|
-
throw new Error(
|
|
3724
|
+
throw new Error(
|
|
3725
|
+
`Invalid status update of run '${taskId}' to status '${status}'`
|
|
3726
|
+
);
|
|
3192
3727
|
}
|
|
3193
3728
|
await this.db.transaction(async (tx) => {
|
|
3194
3729
|
const [task] = await tx("tasks").where({
|
|
@@ -3198,7 +3733,9 @@ class DatabaseTaskStore {
|
|
|
3198
3733
|
throw new Error(`No task with taskId ${taskId} found`);
|
|
3199
3734
|
}
|
|
3200
3735
|
if (task.status !== oldStatus) {
|
|
3201
|
-
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
|
+
);
|
|
3202
3739
|
}
|
|
3203
3740
|
const updateCount = await tx("tasks").where({
|
|
3204
3741
|
id: taskId,
|
|
@@ -3207,7 +3744,9 @@ class DatabaseTaskStore {
|
|
|
3207
3744
|
status
|
|
3208
3745
|
});
|
|
3209
3746
|
if (updateCount !== 1) {
|
|
3210
|
-
throw new errors.ConflictError(
|
|
3747
|
+
throw new errors.ConflictError(
|
|
3748
|
+
`Failed to update status to '${status}' for taskId ${taskId}`
|
|
3749
|
+
);
|
|
3211
3750
|
}
|
|
3212
3751
|
await tx("task_events").insert({
|
|
3213
3752
|
task_id: taskId,
|
|
@@ -3247,7 +3786,9 @@ class DatabaseTaskStore {
|
|
|
3247
3786
|
createdAt: parseSqlDateToIsoString(event.created_at)
|
|
3248
3787
|
};
|
|
3249
3788
|
} catch (error) {
|
|
3250
|
-
throw new Error(
|
|
3789
|
+
throw new Error(
|
|
3790
|
+
`Failed to parse event body from event taskId=${taskId} id=${event.id}, ${error}`
|
|
3791
|
+
);
|
|
3251
3792
|
}
|
|
3252
3793
|
});
|
|
3253
3794
|
return { events };
|
|
@@ -3308,7 +3849,10 @@ class TaskManager {
|
|
|
3308
3849
|
this.startTimeout();
|
|
3309
3850
|
} catch (error) {
|
|
3310
3851
|
this.isDone = true;
|
|
3311
|
-
this.logger.error(
|
|
3852
|
+
this.logger.error(
|
|
3853
|
+
`Heartbeat for task ${this.task.taskId} failed`,
|
|
3854
|
+
error
|
|
3855
|
+
);
|
|
3312
3856
|
}
|
|
3313
3857
|
}, 1e3);
|
|
3314
3858
|
}
|
|
@@ -3329,7 +3873,9 @@ class StorageTaskBroker {
|
|
|
3329
3873
|
}
|
|
3330
3874
|
async list(options) {
|
|
3331
3875
|
if (!this.storage.list) {
|
|
3332
|
-
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
|
+
);
|
|
3333
3879
|
}
|
|
3334
3880
|
return await this.storage.list({ createdBy: options == null ? void 0 : options.createdBy });
|
|
3335
3881
|
}
|
|
@@ -3337,12 +3883,16 @@ class StorageTaskBroker {
|
|
|
3337
3883
|
for (; ; ) {
|
|
3338
3884
|
const pendingTask = await this.storage.claimTask();
|
|
3339
3885
|
if (pendingTask) {
|
|
3340
|
-
return TaskManager.create(
|
|
3341
|
-
|
|
3342
|
-
|
|
3343
|
-
|
|
3344
|
-
|
|
3345
|
-
|
|
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
|
+
);
|
|
3346
3896
|
}
|
|
3347
3897
|
await this.waitForDispatch();
|
|
3348
3898
|
}
|
|
@@ -3380,19 +3930,21 @@ class StorageTaskBroker {
|
|
|
3380
3930
|
}
|
|
3381
3931
|
async vacuumTasks(options) {
|
|
3382
3932
|
const { tasks } = await this.storage.listStaleTasks(options);
|
|
3383
|
-
await Promise.all(
|
|
3384
|
-
|
|
3385
|
-
|
|
3386
|
-
|
|
3387
|
-
|
|
3388
|
-
|
|
3389
|
-
|
|
3390
|
-
|
|
3391
|
-
|
|
3392
|
-
|
|
3393
|
-
|
|
3394
|
-
|
|
3395
|
-
|
|
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
|
+
);
|
|
3396
3948
|
}
|
|
3397
3949
|
waitForDispatch() {
|
|
3398
3950
|
return this.deferredDispatch.promise;
|
|
@@ -3413,10 +3965,12 @@ function generateExampleOutput(schema) {
|
|
|
3413
3965
|
return examples[0];
|
|
3414
3966
|
}
|
|
3415
3967
|
if (schema.type === "object") {
|
|
3416
|
-
return Object.fromEntries(
|
|
3417
|
-
key,
|
|
3418
|
-
|
|
3419
|
-
|
|
3968
|
+
return Object.fromEntries(
|
|
3969
|
+
Object.entries((_a = schema.properties) != null ? _a : {}).map(([key, value]) => [
|
|
3970
|
+
key,
|
|
3971
|
+
generateExampleOutput(value)
|
|
3972
|
+
])
|
|
3973
|
+
);
|
|
3420
3974
|
} else if (schema.type === "array") {
|
|
3421
3975
|
const [firstSchema] = (_b = [schema.items]) == null ? void 0 : _b.flat();
|
|
3422
3976
|
if (firstSchema) {
|
|
@@ -3443,7 +3997,11 @@ const createStepLogger = ({
|
|
|
3443
3997
|
const metadata = { stepId: step.id };
|
|
3444
3998
|
const taskLogger = winston__namespace.createLogger({
|
|
3445
3999
|
level: process.env.LOG_LEVEL || "info",
|
|
3446
|
-
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
|
+
),
|
|
3447
4005
|
defaultMeta: {}
|
|
3448
4006
|
});
|
|
3449
4007
|
const streamLogger = new stream.PassThrough();
|
|
@@ -3463,13 +4021,17 @@ class NunjucksWorkflowRunner {
|
|
|
3463
4021
|
isSingleTemplateString(input) {
|
|
3464
4022
|
var _a, _b;
|
|
3465
4023
|
const { parser, nodes } = nunjucks__default["default"];
|
|
3466
|
-
const parsed = parser.parse(
|
|
3467
|
-
|
|
3468
|
-
|
|
3469
|
-
|
|
3470
|
-
|
|
4024
|
+
const parsed = parser.parse(
|
|
4025
|
+
input,
|
|
4026
|
+
{},
|
|
4027
|
+
{
|
|
4028
|
+
autoescape: false,
|
|
4029
|
+
tags: {
|
|
4030
|
+
variableStart: "${{",
|
|
4031
|
+
variableEnd: "}}"
|
|
4032
|
+
}
|
|
3471
4033
|
}
|
|
3472
|
-
|
|
4034
|
+
);
|
|
3473
4035
|
return parsed.children.length === 1 && !(((_b = (_a = parsed.children[0]) == null ? void 0 : _a.children) == null ? void 0 : _b[0]) instanceof nodes.TemplateData);
|
|
3474
4036
|
}
|
|
3475
4037
|
render(input, context, renderTemplate) {
|
|
@@ -3478,7 +4040,10 @@ class NunjucksWorkflowRunner {
|
|
|
3478
4040
|
if (typeof value === "string") {
|
|
3479
4041
|
try {
|
|
3480
4042
|
if (this.isSingleTemplateString(value)) {
|
|
3481
|
-
const wrappedDumped = value.replace(
|
|
4043
|
+
const wrappedDumped = value.replace(
|
|
4044
|
+
/\${{(.+)}}/g,
|
|
4045
|
+
"${{ ( $1 ) | dump }}"
|
|
4046
|
+
);
|
|
3482
4047
|
const templated2 = renderTemplate(wrappedDumped, context);
|
|
3483
4048
|
if (templated2 === "") {
|
|
3484
4049
|
return void 0;
|
|
@@ -3486,7 +4051,9 @@ class NunjucksWorkflowRunner {
|
|
|
3486
4051
|
return JSON.parse(templated2);
|
|
3487
4052
|
}
|
|
3488
4053
|
} catch (ex) {
|
|
3489
|
-
this.options.logger.error(
|
|
4054
|
+
this.options.logger.error(
|
|
4055
|
+
`Failed to parse template string: ${value} with error ${ex.message}`
|
|
4056
|
+
);
|
|
3490
4057
|
}
|
|
3491
4058
|
const templated = renderTemplate(value, context);
|
|
3492
4059
|
if (templated === "") {
|
|
@@ -3503,9 +4070,14 @@ class NunjucksWorkflowRunner {
|
|
|
3503
4070
|
async execute(task) {
|
|
3504
4071
|
var _a, _b, _c, _d, _e;
|
|
3505
4072
|
if (!isValidTaskSpec(task.spec)) {
|
|
3506
|
-
throw new errors.InputError(
|
|
4073
|
+
throw new errors.InputError(
|
|
4074
|
+
"Wrong template version executed with the workflow engine"
|
|
4075
|
+
);
|
|
3507
4076
|
}
|
|
3508
|
-
const workspacePath = path__default["default"].join(
|
|
4077
|
+
const workspacePath = path__default["default"].join(
|
|
4078
|
+
this.options.workingDirectory,
|
|
4079
|
+
await task.getWorkspaceName()
|
|
4080
|
+
);
|
|
3509
4081
|
const { integrations } = this.options;
|
|
3510
4082
|
const renderTemplate = await SecureTemplater.loadRenderer({
|
|
3511
4083
|
parseRepoUrl(url) {
|
|
@@ -3515,7 +4087,9 @@ class NunjucksWorkflowRunner {
|
|
|
3515
4087
|
});
|
|
3516
4088
|
try {
|
|
3517
4089
|
await fs__default["default"].ensureDir(workspacePath);
|
|
3518
|
-
await task.emitLog(
|
|
4090
|
+
await task.emitLog(
|
|
4091
|
+
`Starting up task with ${task.spec.steps.length} steps`
|
|
4092
|
+
);
|
|
3519
4093
|
const context = {
|
|
3520
4094
|
parameters: task.spec.parameters,
|
|
3521
4095
|
steps: {},
|
|
@@ -3524,9 +4098,16 @@ class NunjucksWorkflowRunner {
|
|
|
3524
4098
|
for (const step of task.spec.steps) {
|
|
3525
4099
|
try {
|
|
3526
4100
|
if (step.if) {
|
|
3527
|
-
const ifResult = await this.render(
|
|
4101
|
+
const ifResult = await this.render(
|
|
4102
|
+
step.if,
|
|
4103
|
+
context,
|
|
4104
|
+
renderTemplate
|
|
4105
|
+
);
|
|
3528
4106
|
if (!isTruthy(ifResult)) {
|
|
3529
|
-
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
|
+
);
|
|
3530
4111
|
continue;
|
|
3531
4112
|
}
|
|
3532
4113
|
}
|
|
@@ -3537,10 +4118,13 @@ class NunjucksWorkflowRunner {
|
|
|
3537
4118
|
const action = this.options.actionRegistry.get(step.action);
|
|
3538
4119
|
const { taskLogger, streamLogger } = createStepLogger({ task, step });
|
|
3539
4120
|
if (task.isDryRun && !action.supportsDryRun) {
|
|
3540
|
-
task.emitLog(
|
|
3541
|
-
|
|
3542
|
-
|
|
3543
|
-
|
|
4121
|
+
task.emitLog(
|
|
4122
|
+
`Skipping because ${action.id} does not support dry-run`,
|
|
4123
|
+
{
|
|
4124
|
+
stepId: step.id,
|
|
4125
|
+
status: "skipped"
|
|
4126
|
+
}
|
|
4127
|
+
);
|
|
3544
4128
|
const outputSchema = (_a = action.schema) == null ? void 0 : _a.output;
|
|
3545
4129
|
if (outputSchema) {
|
|
3546
4130
|
context.steps[step.id] = {
|
|
@@ -3551,12 +4135,21 @@ class NunjucksWorkflowRunner {
|
|
|
3551
4135
|
}
|
|
3552
4136
|
continue;
|
|
3553
4137
|
}
|
|
3554
|
-
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 : {};
|
|
3555
4143
|
if ((_d = action.schema) == null ? void 0 : _d.input) {
|
|
3556
|
-
const validateResult = jsonschema.validate(
|
|
4144
|
+
const validateResult = jsonschema.validate(
|
|
4145
|
+
input,
|
|
4146
|
+
action.schema.input
|
|
4147
|
+
);
|
|
3557
4148
|
if (!validateResult.valid) {
|
|
3558
4149
|
const errors$1 = validateResult.errors.join(", ");
|
|
3559
|
-
throw new errors.InputError(
|
|
4150
|
+
throw new errors.InputError(
|
|
4151
|
+
`Invalid input passed to action ${action.id}, ${errors$1}`
|
|
4152
|
+
);
|
|
3560
4153
|
}
|
|
3561
4154
|
}
|
|
3562
4155
|
const tmpDirs = new Array();
|
|
@@ -3568,7 +4161,9 @@ class NunjucksWorkflowRunner {
|
|
|
3568
4161
|
logStream: streamLogger,
|
|
3569
4162
|
workspacePath,
|
|
3570
4163
|
createTemporaryDirectory: async () => {
|
|
3571
|
-
const tmpDir = await fs__default["default"].mkdtemp(
|
|
4164
|
+
const tmpDir = await fs__default["default"].mkdtemp(
|
|
4165
|
+
`${workspacePath}_step-${step.id}-`
|
|
4166
|
+
);
|
|
3572
4167
|
tmpDirs.push(tmpDir);
|
|
3573
4168
|
return tmpDir;
|
|
3574
4169
|
},
|
|
@@ -3639,9 +4234,13 @@ class TaskWorker {
|
|
|
3639
4234
|
async runOneTask(task) {
|
|
3640
4235
|
try {
|
|
3641
4236
|
if (task.spec.apiVersion !== "scaffolder.backstage.io/v1beta3") {
|
|
3642
|
-
throw new Error(
|
|
4237
|
+
throw new Error(
|
|
4238
|
+
`Unsupported Template apiVersion ${task.spec.apiVersion}`
|
|
4239
|
+
);
|
|
3643
4240
|
}
|
|
3644
|
-
const { output } = await this.options.runners.workflowRunner.execute(
|
|
4241
|
+
const { output } = await this.options.runners.workflowRunner.execute(
|
|
4242
|
+
task
|
|
4243
|
+
);
|
|
3645
4244
|
await task.complete("completed", { output });
|
|
3646
4245
|
} catch (error) {
|
|
3647
4246
|
errors.assertError(error);
|
|
@@ -3688,7 +4287,10 @@ function createDryRunner(options) {
|
|
|
3688
4287
|
});
|
|
3689
4288
|
const dryRunId = uuid.v4();
|
|
3690
4289
|
const log = new Array();
|
|
3691
|
-
const contentsPath = backendCommon.resolveSafeChildPath(
|
|
4290
|
+
const contentsPath = backendCommon.resolveSafeChildPath(
|
|
4291
|
+
options.workingDirectory,
|
|
4292
|
+
`dry-run-content-${dryRunId}`
|
|
4293
|
+
);
|
|
3692
4294
|
try {
|
|
3693
4295
|
await deserializeDirectoryContents(contentsPath, input.directoryContents);
|
|
3694
4296
|
const result = await workflowRunner.execute({
|
|
@@ -3704,7 +4306,9 @@ function createDryRunner(options) {
|
|
|
3704
4306
|
],
|
|
3705
4307
|
templateInfo: {
|
|
3706
4308
|
entityRef: "template:default/dry-run",
|
|
3707
|
-
baseUrl: url.pathToFileURL(
|
|
4309
|
+
baseUrl: url.pathToFileURL(
|
|
4310
|
+
backendCommon.resolveSafeChildPath(contentsPath, "template.yaml")
|
|
4311
|
+
).toString()
|
|
3708
4312
|
}
|
|
3709
4313
|
},
|
|
3710
4314
|
secrets: input.secrets,
|
|
@@ -3751,7 +4355,9 @@ async function getWorkingDirectory(config, logger) {
|
|
|
3751
4355
|
logger.info(`using working directory: ${workingDirectory}`);
|
|
3752
4356
|
} catch (err) {
|
|
3753
4357
|
errors.assertError(err);
|
|
3754
|
-
logger.error(
|
|
4358
|
+
logger.error(
|
|
4359
|
+
`working directory ${workingDirectory} ${err.code === "ENOENT" ? "does not exist" : "is not writable"}`
|
|
4360
|
+
);
|
|
3755
4361
|
throw err;
|
|
3756
4362
|
}
|
|
3757
4363
|
return workingDirectory;
|
|
@@ -3780,7 +4386,9 @@ async function findTemplate(options) {
|
|
|
3780
4386
|
}
|
|
3781
4387
|
const template = await catalogApi.getEntityByRef(entityRef, { token });
|
|
3782
4388
|
if (!template) {
|
|
3783
|
-
throw new errors.NotFoundError(
|
|
4389
|
+
throw new errors.NotFoundError(
|
|
4390
|
+
`Template ${catalogModel.stringifyEntityRef(entityRef)} not found`
|
|
4391
|
+
);
|
|
3784
4392
|
}
|
|
3785
4393
|
return template;
|
|
3786
4394
|
}
|
|
@@ -3842,31 +4450,38 @@ async function createRouter(options) {
|
|
|
3842
4450
|
workingDirectory,
|
|
3843
4451
|
additionalTemplateFilters
|
|
3844
4452
|
});
|
|
3845
|
-
router.get(
|
|
3846
|
-
|
|
3847
|
-
|
|
3848
|
-
|
|
3849
|
-
|
|
3850
|
-
|
|
3851
|
-
|
|
3852
|
-
|
|
3853
|
-
|
|
3854
|
-
|
|
3855
|
-
const parameters = [(_a = template.spec.parameters) != null ? _a : []].flat();
|
|
3856
|
-
res.json({
|
|
3857
|
-
title: (_b = template.metadata.title) != null ? _b : template.metadata.name,
|
|
3858
|
-
steps: parameters.map((schema) => {
|
|
3859
|
-
var _a2;
|
|
3860
|
-
return {
|
|
3861
|
-
title: (_a2 = schema.title) != null ? _a2 : "Fill in template parameters",
|
|
3862
|
-
schema
|
|
3863
|
-
};
|
|
3864
|
-
})
|
|
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
|
|
3865
4463
|
});
|
|
3866
|
-
|
|
3867
|
-
|
|
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
|
+
description: template.metadata.description,
|
|
4469
|
+
steps: parameters.map((schema) => {
|
|
4470
|
+
var _a2;
|
|
4471
|
+
return {
|
|
4472
|
+
title: (_a2 = schema.title) != null ? _a2 : "Please enter the following information",
|
|
4473
|
+
description: schema.description,
|
|
4474
|
+
schema
|
|
4475
|
+
};
|
|
4476
|
+
})
|
|
4477
|
+
});
|
|
4478
|
+
} else {
|
|
4479
|
+
throw new errors.InputError(
|
|
4480
|
+
`Unsupported apiVersion field in schema entity, ${template.apiVersion}`
|
|
4481
|
+
);
|
|
4482
|
+
}
|
|
3868
4483
|
}
|
|
3869
|
-
|
|
4484
|
+
).get("/v2/actions", async (_req, res) => {
|
|
3870
4485
|
const actionsList = actionRegistry.list().map((action) => {
|
|
3871
4486
|
return {
|
|
3872
4487
|
id: action.id,
|
|
@@ -3881,8 +4496,15 @@ async function createRouter(options) {
|
|
|
3881
4496
|
const { kind, namespace, name } = catalogModel.parseEntityRef(templateRef, {
|
|
3882
4497
|
defaultKind: "template"
|
|
3883
4498
|
});
|
|
3884
|
-
const { token, entityRef: userEntityRef } = parseBearerToken(
|
|
4499
|
+
const { token, entityRef: userEntityRef } = parseBearerToken(
|
|
4500
|
+
req.headers.authorization
|
|
4501
|
+
);
|
|
3885
4502
|
const userEntity = userEntityRef ? await catalogClient.getEntityByRef(userEntityRef, { token }) : void 0;
|
|
4503
|
+
let auditLog = `Scaffolding task for ${templateRef}`;
|
|
4504
|
+
if (userEntityRef) {
|
|
4505
|
+
auditLog += ` created by ${userEntityRef}`;
|
|
4506
|
+
}
|
|
4507
|
+
logger.info(auditLog);
|
|
3886
4508
|
const values = req.body.values;
|
|
3887
4509
|
const template = await findTemplate({
|
|
3888
4510
|
catalogApi: catalogClient,
|
|
@@ -3890,7 +4512,9 @@ async function createRouter(options) {
|
|
|
3890
4512
|
token
|
|
3891
4513
|
});
|
|
3892
4514
|
if (!isSupportedTemplate(template)) {
|
|
3893
|
-
throw new errors.InputError(
|
|
4515
|
+
throw new errors.InputError(
|
|
4516
|
+
`Unsupported apiVersion field in schema entity, ${template.apiVersion}`
|
|
4517
|
+
);
|
|
3894
4518
|
}
|
|
3895
4519
|
for (const parameters of [(_a = template.spec.parameters) != null ? _a : []].flat()) {
|
|
3896
4520
|
const result2 = jsonschema.validate(values, parameters);
|
|
@@ -3940,7 +4564,9 @@ async function createRouter(options) {
|
|
|
3940
4564
|
throw new errors.InputError("createdBy query parameter must be a string");
|
|
3941
4565
|
}
|
|
3942
4566
|
if (!taskBroker.list) {
|
|
3943
|
-
throw new Error(
|
|
4567
|
+
throw new Error(
|
|
4568
|
+
"TaskBroker does not support listing tasks, please implement the list method on the TaskBroker."
|
|
4569
|
+
);
|
|
3944
4570
|
}
|
|
3945
4571
|
const tasks = await taskBroker.list({
|
|
3946
4572
|
createdBy: userEntityRef
|
|
@@ -3965,23 +4591,30 @@ async function createRouter(options) {
|
|
|
3965
4591
|
});
|
|
3966
4592
|
const subscription = taskBroker.event$({ taskId, after }).subscribe({
|
|
3967
4593
|
error: (error) => {
|
|
3968
|
-
logger.error(
|
|
4594
|
+
logger.error(
|
|
4595
|
+
`Received error from event stream when observing taskId '${taskId}', ${error}`
|
|
4596
|
+
);
|
|
4597
|
+
res.end();
|
|
3969
4598
|
},
|
|
3970
4599
|
next: ({ events }) => {
|
|
3971
4600
|
var _a;
|
|
3972
4601
|
let shouldUnsubscribe = false;
|
|
3973
4602
|
for (const event of events) {
|
|
3974
|
-
res.write(
|
|
4603
|
+
res.write(
|
|
4604
|
+
`event: ${event.type}
|
|
3975
4605
|
data: ${JSON.stringify(event)}
|
|
3976
4606
|
|
|
3977
|
-
`
|
|
4607
|
+
`
|
|
4608
|
+
);
|
|
3978
4609
|
if (event.type === "completion") {
|
|
3979
4610
|
shouldUnsubscribe = true;
|
|
3980
4611
|
}
|
|
3981
4612
|
}
|
|
3982
4613
|
(_a = res.flush) == null ? void 0 : _a.call(res);
|
|
3983
|
-
if (shouldUnsubscribe)
|
|
4614
|
+
if (shouldUnsubscribe) {
|
|
3984
4615
|
subscription.unsubscribe();
|
|
4616
|
+
res.end();
|
|
4617
|
+
}
|
|
3985
4618
|
}
|
|
3986
4619
|
});
|
|
3987
4620
|
req.on("close", () => {
|
|
@@ -3996,7 +4629,9 @@ data: ${JSON.stringify(event)}
|
|
|
3996
4629
|
}, 3e4);
|
|
3997
4630
|
const subscription = taskBroker.event$({ taskId, after }).subscribe({
|
|
3998
4631
|
error: (error) => {
|
|
3999
|
-
logger.error(
|
|
4632
|
+
logger.error(
|
|
4633
|
+
`Received error from event stream when observing taskId '${taskId}', ${error}`
|
|
4634
|
+
);
|
|
4000
4635
|
},
|
|
4001
4636
|
next: ({ events }) => {
|
|
4002
4637
|
clearTimeout(timeout);
|
|
@@ -4014,7 +4649,9 @@ data: ${JSON.stringify(event)}
|
|
|
4014
4649
|
template: zod.z.unknown(),
|
|
4015
4650
|
values: zod.z.record(zod.z.unknown()),
|
|
4016
4651
|
secrets: zod.z.record(zod.z.string()).optional(),
|
|
4017
|
-
directoryContents: zod.z.array(
|
|
4652
|
+
directoryContents: zod.z.array(
|
|
4653
|
+
zod.z.object({ path: zod.z.string(), base64Content: zod.z.string() })
|
|
4654
|
+
)
|
|
4018
4655
|
});
|
|
4019
4656
|
const body = await bodySchema.parseAsync(req.body).catch((e) => {
|
|
4020
4657
|
throw new errors.InputError(`Malformed request: ${e}`);
|
|
@@ -4081,7 +4718,9 @@ function parseBearerToken(header) {
|
|
|
4081
4718
|
throw new TypeError("Expected Bearer with JWT");
|
|
4082
4719
|
}
|
|
4083
4720
|
const [_header, rawPayload, _signature] = token.split(".");
|
|
4084
|
-
const payload = JSON.parse(
|
|
4721
|
+
const payload = JSON.parse(
|
|
4722
|
+
Buffer.from(rawPayload, "base64").toString()
|
|
4723
|
+
);
|
|
4085
4724
|
if (typeof payload !== "object" || payload === null || Array.isArray(payload)) {
|
|
4086
4725
|
throw new TypeError("Malformed JWT payload");
|
|
4087
4726
|
}
|
|
@@ -4120,24 +4759,28 @@ class ScaffolderEntitiesProcessor {
|
|
|
4120
4759
|
defaultKind: "Group",
|
|
4121
4760
|
defaultNamespace: selfRef.namespace
|
|
4122
4761
|
});
|
|
4123
|
-
emit(
|
|
4124
|
-
|
|
4125
|
-
|
|
4126
|
-
|
|
4127
|
-
|
|
4128
|
-
|
|
4129
|
-
|
|
4130
|
-
|
|
4131
|
-
|
|
4132
|
-
|
|
4133
|
-
|
|
4134
|
-
|
|
4135
|
-
|
|
4136
|
-
|
|
4137
|
-
|
|
4138
|
-
|
|
4139
|
-
|
|
4140
|
-
|
|
4762
|
+
emit(
|
|
4763
|
+
pluginCatalogBackend.processingResult.relation({
|
|
4764
|
+
source: selfRef,
|
|
4765
|
+
type: catalogModel.RELATION_OWNED_BY,
|
|
4766
|
+
target: {
|
|
4767
|
+
kind: targetRef.kind,
|
|
4768
|
+
namespace: targetRef.namespace,
|
|
4769
|
+
name: targetRef.name
|
|
4770
|
+
}
|
|
4771
|
+
})
|
|
4772
|
+
);
|
|
4773
|
+
emit(
|
|
4774
|
+
pluginCatalogBackend.processingResult.relation({
|
|
4775
|
+
source: {
|
|
4776
|
+
kind: targetRef.kind,
|
|
4777
|
+
namespace: targetRef.namespace,
|
|
4778
|
+
name: targetRef.name
|
|
4779
|
+
},
|
|
4780
|
+
type: catalogModel.RELATION_OWNER_OF,
|
|
4781
|
+
target: selfRef
|
|
4782
|
+
})
|
|
4783
|
+
);
|
|
4141
4784
|
}
|
|
4142
4785
|
}
|
|
4143
4786
|
return entity;
|
|
@@ -4153,7 +4796,9 @@ const scaffolderCatalogModule = backendPluginApi.createBackendModule({
|
|
|
4153
4796
|
catalogProcessingExtensionPoint: pluginCatalogNode.catalogProcessingExtentionPoint
|
|
4154
4797
|
},
|
|
4155
4798
|
async init({ catalogProcessingExtensionPoint }) {
|
|
4156
|
-
catalogProcessingExtensionPoint.addProcessor(
|
|
4799
|
+
catalogProcessingExtensionPoint.addProcessor(
|
|
4800
|
+
new ScaffolderEntitiesProcessor()
|
|
4801
|
+
);
|
|
4157
4802
|
}
|
|
4158
4803
|
});
|
|
4159
4804
|
}
|
|
@@ -4183,6 +4828,7 @@ exports.createPublishBitbucketCloudAction = createPublishBitbucketCloudAction;
|
|
|
4183
4828
|
exports.createPublishBitbucketServerAction = createPublishBitbucketServerAction;
|
|
4184
4829
|
exports.createPublishFileAction = createPublishFileAction;
|
|
4185
4830
|
exports.createPublishGerritAction = createPublishGerritAction;
|
|
4831
|
+
exports.createPublishGerritReviewAction = createPublishGerritReviewAction;
|
|
4186
4832
|
exports.createPublishGithubAction = createPublishGithubAction;
|
|
4187
4833
|
exports.createPublishGithubPullRequestAction = createPublishGithubPullRequestAction;
|
|
4188
4834
|
exports.createPublishGitlabAction = createPublishGitlabAction;
|