@backstage/plugin-scaffolder-backend 1.4.0-next.1 → 1.4.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +106 -4
- package/alpha/package.json +6 -0
- package/dist/index.alpha.d.ts +967 -0
- package/dist/index.beta.d.ts +963 -0
- package/dist/index.cjs.js +1674 -1419
- package/dist/index.cjs.js.map +1 -1
- package/dist/index.d.ts +665 -553
- package/package.json +25 -21
package/dist/index.cjs.js
CHANGED
|
@@ -12,16 +12,16 @@ var path = require('path');
|
|
|
12
12
|
var globby = require('globby');
|
|
13
13
|
var isbinaryfile = require('isbinaryfile');
|
|
14
14
|
var vm2 = require('vm2');
|
|
15
|
+
var octokit = require('octokit');
|
|
15
16
|
var child_process = require('child_process');
|
|
16
17
|
var stream = require('stream');
|
|
18
|
+
var webhooks = require('@octokit/webhooks');
|
|
17
19
|
var azureDevopsNodeApi = require('azure-devops-node-api');
|
|
18
20
|
var fetch = require('node-fetch');
|
|
19
21
|
var crypto = require('crypto');
|
|
20
|
-
var octokit = require('octokit');
|
|
21
22
|
var octokitPluginCreatePullRequest = require('octokit-plugin-create-pull-request');
|
|
22
23
|
var limiterFactory = require('p-limit');
|
|
23
24
|
var node = require('@gitbeaker/node');
|
|
24
|
-
var webhooks = require('@octokit/webhooks');
|
|
25
25
|
var uuid = require('uuid');
|
|
26
26
|
var luxon = require('luxon');
|
|
27
27
|
var ObservableImpl = require('zen-observable');
|
|
@@ -36,6 +36,8 @@ var zod = require('zod');
|
|
|
36
36
|
var url = require('url');
|
|
37
37
|
var os = require('os');
|
|
38
38
|
var pluginCatalogBackend = require('@backstage/plugin-catalog-backend');
|
|
39
|
+
var backendPluginApi = require('@backstage/backend-plugin-api');
|
|
40
|
+
var pluginCatalogNode = require('@backstage/plugin-catalog-node');
|
|
39
41
|
|
|
40
42
|
function _interopDefaultLegacy (e) { return e && typeof e === 'object' && 'default' in e ? e : { 'default': e }; }
|
|
41
43
|
|
|
@@ -471,13 +473,21 @@ function createFetchTemplateAction(options) {
|
|
|
471
473
|
type: "object"
|
|
472
474
|
},
|
|
473
475
|
copyWithoutRender: {
|
|
474
|
-
title: "Copy Without Render",
|
|
476
|
+
title: "[Deprecated] Copy Without Render",
|
|
475
477
|
description: "An array of glob patterns. Any files or directories which match are copied without being processed as templates.",
|
|
476
478
|
type: "array",
|
|
477
479
|
items: {
|
|
478
480
|
type: "string"
|
|
479
481
|
}
|
|
480
482
|
},
|
|
483
|
+
copyWithoutTemplating: {
|
|
484
|
+
title: "Copy Without Templating",
|
|
485
|
+
description: "An array of glob patterns. Contents of matched files or directories are copied without being processed, but paths are subject to rendering.",
|
|
486
|
+
type: "array",
|
|
487
|
+
items: {
|
|
488
|
+
type: "string"
|
|
489
|
+
}
|
|
490
|
+
},
|
|
481
491
|
cookiecutterCompat: {
|
|
482
492
|
title: "Cookiecutter compatibility mode",
|
|
483
493
|
description: "Enable features to maximise compatibility with templates built for fetch:cookiecutter",
|
|
@@ -499,11 +509,24 @@ function createFetchTemplateAction(options) {
|
|
|
499
509
|
const templateDir = backendCommon.resolveSafeChildPath(workDir, "template");
|
|
500
510
|
const targetPath = (_a = ctx.input.targetPath) != null ? _a : "./";
|
|
501
511
|
const outputDir = backendCommon.resolveSafeChildPath(ctx.workspacePath, targetPath);
|
|
502
|
-
if (ctx.input.copyWithoutRender &&
|
|
503
|
-
throw new errors.InputError("Fetch action input copyWithoutRender
|
|
512
|
+
if (ctx.input.copyWithoutRender && ctx.input.copyWithoutTemplating) {
|
|
513
|
+
throw new errors.InputError("Fetch action input copyWithoutRender and copyWithoutTemplating can not be used at the same time");
|
|
514
|
+
}
|
|
515
|
+
let copyOnlyPatterns;
|
|
516
|
+
let renderFilename;
|
|
517
|
+
if (ctx.input.copyWithoutRender) {
|
|
518
|
+
ctx.logger.warn("[Deprecated] Please use copyWithoutTemplating instead.");
|
|
519
|
+
copyOnlyPatterns = ctx.input.copyWithoutRender;
|
|
520
|
+
renderFilename = false;
|
|
521
|
+
} else {
|
|
522
|
+
copyOnlyPatterns = ctx.input.copyWithoutTemplating;
|
|
523
|
+
renderFilename = true;
|
|
524
|
+
}
|
|
525
|
+
if (copyOnlyPatterns && !Array.isArray(copyOnlyPatterns)) {
|
|
526
|
+
throw new errors.InputError("Fetch action input copyWithoutRender/copyWithoutTemplating must be an Array");
|
|
504
527
|
}
|
|
505
|
-
if (ctx.input.templateFileExtension && (
|
|
506
|
-
throw new errors.InputError("Fetch action input extension incompatible with copyWithoutRender and cookiecutterCompat");
|
|
528
|
+
if (ctx.input.templateFileExtension && (copyOnlyPatterns || ctx.input.cookiecutterCompat)) {
|
|
529
|
+
throw new errors.InputError("Fetch action input extension incompatible with copyWithoutRender/copyWithoutTemplating and cookiecutterCompat");
|
|
507
530
|
}
|
|
508
531
|
let extension = false;
|
|
509
532
|
if (ctx.input.templateFileExtension) {
|
|
@@ -527,7 +550,7 @@ function createFetchTemplateAction(options) {
|
|
|
527
550
|
markDirectories: true,
|
|
528
551
|
followSymbolicLinks: false
|
|
529
552
|
});
|
|
530
|
-
const nonTemplatedEntries = new Set((await Promise.all((
|
|
553
|
+
const nonTemplatedEntries = new Set((await Promise.all((copyOnlyPatterns || []).map((pattern) => globby__default["default"](pattern, {
|
|
531
554
|
cwd: templateDir,
|
|
532
555
|
dot: true,
|
|
533
556
|
onlyFiles: false,
|
|
@@ -544,23 +567,27 @@ function createFetchTemplateAction(options) {
|
|
|
544
567
|
additionalTemplateFilters
|
|
545
568
|
});
|
|
546
569
|
for (const location of allEntriesInTemplate) {
|
|
547
|
-
let renderFilename;
|
|
548
570
|
let renderContents;
|
|
549
571
|
let localOutputPath = location;
|
|
550
572
|
if (extension) {
|
|
551
|
-
renderFilename = true;
|
|
552
573
|
renderContents = path.extname(localOutputPath) === extension;
|
|
553
574
|
if (renderContents) {
|
|
554
575
|
localOutputPath = localOutputPath.slice(0, -extension.length);
|
|
555
576
|
}
|
|
577
|
+
localOutputPath = renderTemplate(localOutputPath, context);
|
|
556
578
|
} else {
|
|
557
|
-
|
|
579
|
+
renderContents = !nonTemplatedEntries.has(location);
|
|
580
|
+
if (renderFilename) {
|
|
581
|
+
localOutputPath = renderTemplate(localOutputPath, context);
|
|
582
|
+
} else {
|
|
583
|
+
localOutputPath = renderContents ? renderTemplate(localOutputPath, context) : localOutputPath;
|
|
584
|
+
}
|
|
558
585
|
}
|
|
559
|
-
if (
|
|
560
|
-
|
|
586
|
+
if (containsSkippedContent(localOutputPath)) {
|
|
587
|
+
continue;
|
|
561
588
|
}
|
|
562
589
|
const outputPath = backendCommon.resolveSafeChildPath(outputDir, localOutputPath);
|
|
563
|
-
if (
|
|
590
|
+
if (fs__default["default"].existsSync(outputPath)) {
|
|
564
591
|
continue;
|
|
565
592
|
}
|
|
566
593
|
if (!renderContents && !extension) {
|
|
@@ -571,8 +598,9 @@ function createFetchTemplateAction(options) {
|
|
|
571
598
|
await fs__default["default"].ensureDir(outputPath);
|
|
572
599
|
} else {
|
|
573
600
|
const inputFilePath = backendCommon.resolveSafeChildPath(templateDir, location);
|
|
574
|
-
|
|
575
|
-
|
|
601
|
+
const stats = await fs__default["default"].promises.lstat(inputFilePath);
|
|
602
|
+
if (stats.isSymbolicLink() || await isbinaryfile.isBinaryFile(inputFilePath)) {
|
|
603
|
+
ctx.logger.info(`Copying file binary or symbolic link at ${location}, to template output path.`);
|
|
576
604
|
await fs__default["default"].copy(inputFilePath, outputPath);
|
|
577
605
|
} else {
|
|
578
606
|
const statsObj = await fs__default["default"].stat(inputFilePath);
|
|
@@ -586,6 +614,9 @@ function createFetchTemplateAction(options) {
|
|
|
586
614
|
}
|
|
587
615
|
});
|
|
588
616
|
}
|
|
617
|
+
function containsSkippedContent(localOutputPath) {
|
|
618
|
+
return localOutputPath === "" || path__default["default"].isAbsolute(localOutputPath) || localOutputPath.includes(`${path__default["default"].sep}${path__default["default"].sep}`);
|
|
619
|
+
}
|
|
589
620
|
|
|
590
621
|
const createFilesystemDeleteAction = () => {
|
|
591
622
|
return createTemplateAction({
|
|
@@ -688,6 +719,55 @@ const createFilesystemRenameAction = () => {
|
|
|
688
719
|
});
|
|
689
720
|
};
|
|
690
721
|
|
|
722
|
+
const getRepoSourceDirectory = (workspacePath, sourcePath) => {
|
|
723
|
+
if (sourcePath) {
|
|
724
|
+
const safeSuffix = path.normalize(sourcePath).replace(/^(\.\.(\/|\\|$))+/, "");
|
|
725
|
+
const path$1 = path.join(workspacePath, safeSuffix);
|
|
726
|
+
if (!backendCommon.isChildPath(workspacePath, path$1)) {
|
|
727
|
+
throw new Error("Invalid source path");
|
|
728
|
+
}
|
|
729
|
+
return path$1;
|
|
730
|
+
}
|
|
731
|
+
return workspacePath;
|
|
732
|
+
};
|
|
733
|
+
const parseRepoUrl = (repoUrl, integrations) => {
|
|
734
|
+
var _a, _b, _c, _d, _e;
|
|
735
|
+
let parsed;
|
|
736
|
+
try {
|
|
737
|
+
parsed = new URL(`https://${repoUrl}`);
|
|
738
|
+
} catch (error) {
|
|
739
|
+
throw new errors.InputError(`Invalid repo URL passed to publisher, got ${repoUrl}, ${error}`);
|
|
740
|
+
}
|
|
741
|
+
const host = parsed.host;
|
|
742
|
+
const owner = (_a = parsed.searchParams.get("owner")) != null ? _a : void 0;
|
|
743
|
+
const organization = (_b = parsed.searchParams.get("organization")) != null ? _b : void 0;
|
|
744
|
+
const workspace = (_c = parsed.searchParams.get("workspace")) != null ? _c : void 0;
|
|
745
|
+
const project = (_d = parsed.searchParams.get("project")) != null ? _d : void 0;
|
|
746
|
+
const type = (_e = integrations.byHost(host)) == null ? void 0 : _e.type;
|
|
747
|
+
if (!type) {
|
|
748
|
+
throw new errors.InputError(`No matching integration configuration for host ${host}, please check your integrations config`);
|
|
749
|
+
}
|
|
750
|
+
if (type === "bitbucket") {
|
|
751
|
+
if (host === "bitbucket.org") {
|
|
752
|
+
if (!workspace) {
|
|
753
|
+
throw new errors.InputError(`Invalid repo URL passed to publisher: ${repoUrl}, missing workspace`);
|
|
754
|
+
}
|
|
755
|
+
}
|
|
756
|
+
if (!project) {
|
|
757
|
+
throw new errors.InputError(`Invalid repo URL passed to publisher: ${repoUrl}, missing project`);
|
|
758
|
+
}
|
|
759
|
+
} else {
|
|
760
|
+
if (!owner && type !== "gerrit") {
|
|
761
|
+
throw new errors.InputError(`Invalid repo URL passed to publisher: ${repoUrl}, missing owner`);
|
|
762
|
+
}
|
|
763
|
+
}
|
|
764
|
+
const repo = parsed.searchParams.get("repo");
|
|
765
|
+
if (!repo) {
|
|
766
|
+
throw new errors.InputError(`Invalid repo URL passed to publisher: ${repoUrl}, missing repo`);
|
|
767
|
+
}
|
|
768
|
+
return { host, owner, repo, organization, workspace, project };
|
|
769
|
+
};
|
|
770
|
+
|
|
691
771
|
const executeShellCommand = async (options) => {
|
|
692
772
|
const {
|
|
693
773
|
command,
|
|
@@ -761,7 +841,8 @@ const enableBranchProtectionOnDefaultRepoBranch = async ({
|
|
|
761
841
|
logger,
|
|
762
842
|
requireCodeOwnerReviews,
|
|
763
843
|
requiredStatusCheckContexts = [],
|
|
764
|
-
defaultBranch = "master"
|
|
844
|
+
defaultBranch = "master",
|
|
845
|
+
enforceAdmins = true
|
|
765
846
|
}) => {
|
|
766
847
|
const tryOnce = async () => {
|
|
767
848
|
try {
|
|
@@ -777,7 +858,7 @@ const enableBranchProtectionOnDefaultRepoBranch = async ({
|
|
|
777
858
|
contexts: requiredStatusCheckContexts
|
|
778
859
|
},
|
|
779
860
|
restrictions: null,
|
|
780
|
-
enforce_admins:
|
|
861
|
+
enforce_admins: enforceAdmins,
|
|
781
862
|
required_pull_request_reviews: {
|
|
782
863
|
required_approving_review_count: 1,
|
|
783
864
|
require_code_owner_reviews: requireCodeOwnerReviews
|
|
@@ -803,535 +884,638 @@ const enableBranchProtectionOnDefaultRepoBranch = async ({
|
|
|
803
884
|
}
|
|
804
885
|
};
|
|
805
886
|
|
|
806
|
-
const
|
|
807
|
-
|
|
808
|
-
|
|
809
|
-
|
|
810
|
-
|
|
811
|
-
|
|
812
|
-
|
|
813
|
-
|
|
887
|
+
const DEFAULT_TIMEOUT_MS = 6e4;
|
|
888
|
+
async function getOctokitOptions(options) {
|
|
889
|
+
var _a;
|
|
890
|
+
const { integrations, credentialsProvider, repoUrl, token } = options;
|
|
891
|
+
const { owner, repo, host } = parseRepoUrl(repoUrl, integrations);
|
|
892
|
+
const requestOptions = {
|
|
893
|
+
timeout: DEFAULT_TIMEOUT_MS
|
|
894
|
+
};
|
|
895
|
+
if (!owner) {
|
|
896
|
+
throw new errors.InputError(`No owner provided for repo ${repoUrl}`);
|
|
814
897
|
}
|
|
815
|
-
|
|
816
|
-
|
|
817
|
-
|
|
818
|
-
|
|
819
|
-
|
|
898
|
+
const integrationConfig = (_a = integrations.github.byHost(host)) == null ? void 0 : _a.config;
|
|
899
|
+
if (!integrationConfig) {
|
|
900
|
+
throw new errors.InputError(`No integration for host ${host}`);
|
|
901
|
+
}
|
|
902
|
+
if (token) {
|
|
903
|
+
return {
|
|
904
|
+
auth: token,
|
|
905
|
+
baseUrl: integrationConfig.apiBaseUrl,
|
|
906
|
+
previews: ["nebula-preview"],
|
|
907
|
+
request: requestOptions
|
|
908
|
+
};
|
|
909
|
+
}
|
|
910
|
+
const githubCredentialsProvider = credentialsProvider != null ? credentialsProvider : integration.DefaultGithubCredentialsProvider.fromIntegrations(integrations);
|
|
911
|
+
const { token: credentialProviderToken } = await githubCredentialsProvider.getCredentials({
|
|
912
|
+
url: `https://${host}/${encodeURIComponent(owner)}/${encodeURIComponent(repo)}`
|
|
913
|
+
});
|
|
914
|
+
if (!credentialProviderToken) {
|
|
915
|
+
throw new errors.InputError(`No token available for host: ${host}, with owner ${owner}, and repo ${repo}`);
|
|
916
|
+
}
|
|
917
|
+
return {
|
|
918
|
+
auth: credentialProviderToken,
|
|
919
|
+
baseUrl: integrationConfig.apiBaseUrl,
|
|
920
|
+
previews: ["nebula-preview"]
|
|
921
|
+
};
|
|
922
|
+
}
|
|
923
|
+
async function createGithubRepoWithCollaboratorsAndTopics(client, repo, owner, repoVisibility, description, deleteBranchOnMerge, allowMergeCommit, allowSquashMerge, allowRebaseMerge, access, collaborators, topics, logger) {
|
|
924
|
+
const user = await client.rest.users.getByUsername({
|
|
925
|
+
username: owner
|
|
926
|
+
});
|
|
927
|
+
const repoCreationPromise = user.data.type === "Organization" ? client.rest.repos.createInOrg({
|
|
928
|
+
name: repo,
|
|
929
|
+
org: owner,
|
|
930
|
+
private: repoVisibility === "private",
|
|
931
|
+
visibility: repoVisibility,
|
|
932
|
+
description,
|
|
933
|
+
delete_branch_on_merge: deleteBranchOnMerge,
|
|
934
|
+
allow_merge_commit: allowMergeCommit,
|
|
935
|
+
allow_squash_merge: allowSquashMerge,
|
|
936
|
+
allow_rebase_merge: allowRebaseMerge
|
|
937
|
+
}) : client.rest.repos.createForAuthenticatedUser({
|
|
938
|
+
name: repo,
|
|
939
|
+
private: repoVisibility === "private",
|
|
940
|
+
description,
|
|
941
|
+
delete_branch_on_merge: deleteBranchOnMerge,
|
|
942
|
+
allow_merge_commit: allowMergeCommit,
|
|
943
|
+
allow_squash_merge: allowSquashMerge,
|
|
944
|
+
allow_rebase_merge: allowRebaseMerge
|
|
945
|
+
});
|
|
946
|
+
let newRepo;
|
|
820
947
|
try {
|
|
821
|
-
|
|
822
|
-
} catch (
|
|
823
|
-
|
|
948
|
+
newRepo = (await repoCreationPromise).data;
|
|
949
|
+
} catch (e) {
|
|
950
|
+
errors.assertError(e);
|
|
951
|
+
if (e.message === "Resource not accessible by integration") {
|
|
952
|
+
logger.warn(`The GitHub app or token provided may not have the required permissions to create the ${user.data.type} repository ${owner}/${repo}.`);
|
|
953
|
+
}
|
|
954
|
+
throw new Error(`Failed to create the ${user.data.type} repository ${owner}/${repo}, ${e.message}`);
|
|
824
955
|
}
|
|
825
|
-
|
|
826
|
-
|
|
827
|
-
|
|
828
|
-
|
|
829
|
-
|
|
830
|
-
|
|
831
|
-
|
|
832
|
-
|
|
956
|
+
if (access == null ? void 0 : access.startsWith(`${owner}/`)) {
|
|
957
|
+
const [, team] = access.split("/");
|
|
958
|
+
await client.rest.teams.addOrUpdateRepoPermissionsInOrg({
|
|
959
|
+
org: owner,
|
|
960
|
+
team_slug: team,
|
|
961
|
+
owner,
|
|
962
|
+
repo,
|
|
963
|
+
permission: "admin"
|
|
964
|
+
});
|
|
965
|
+
} else if (access && access !== owner) {
|
|
966
|
+
await client.rest.repos.addCollaborator({
|
|
967
|
+
owner,
|
|
968
|
+
repo,
|
|
969
|
+
username: access,
|
|
970
|
+
permission: "admin"
|
|
971
|
+
});
|
|
833
972
|
}
|
|
834
|
-
if (
|
|
835
|
-
|
|
836
|
-
|
|
837
|
-
|
|
973
|
+
if (collaborators) {
|
|
974
|
+
for (const collaborator of collaborators) {
|
|
975
|
+
try {
|
|
976
|
+
if ("user" in collaborator) {
|
|
977
|
+
await client.rest.repos.addCollaborator({
|
|
978
|
+
owner,
|
|
979
|
+
repo,
|
|
980
|
+
username: collaborator.user,
|
|
981
|
+
permission: collaborator.access
|
|
982
|
+
});
|
|
983
|
+
} else if ("team" in collaborator) {
|
|
984
|
+
await client.rest.teams.addOrUpdateRepoPermissionsInOrg({
|
|
985
|
+
org: owner,
|
|
986
|
+
team_slug: collaborator.team,
|
|
987
|
+
owner,
|
|
988
|
+
repo,
|
|
989
|
+
permission: collaborator.access
|
|
990
|
+
});
|
|
991
|
+
}
|
|
992
|
+
} catch (e) {
|
|
993
|
+
errors.assertError(e);
|
|
994
|
+
const name = extractCollaboratorName(collaborator);
|
|
995
|
+
logger.warn(`Skipping ${collaborator.access} access for ${name}, ${e.message}`);
|
|
838
996
|
}
|
|
839
997
|
}
|
|
840
|
-
|
|
841
|
-
|
|
842
|
-
|
|
843
|
-
|
|
844
|
-
|
|
845
|
-
|
|
998
|
+
}
|
|
999
|
+
if (topics) {
|
|
1000
|
+
try {
|
|
1001
|
+
await client.rest.repos.replaceAllTopics({
|
|
1002
|
+
owner,
|
|
1003
|
+
repo,
|
|
1004
|
+
names: topics.map((t) => t.toLowerCase())
|
|
1005
|
+
});
|
|
1006
|
+
} catch (e) {
|
|
1007
|
+
errors.assertError(e);
|
|
1008
|
+
logger.warn(`Skipping topics ${topics.join(" ")}, ${e.message}`);
|
|
846
1009
|
}
|
|
847
1010
|
}
|
|
848
|
-
|
|
849
|
-
|
|
850
|
-
|
|
1011
|
+
return newRepo;
|
|
1012
|
+
}
|
|
1013
|
+
async function initRepoPushAndProtect(remoteUrl, password, workspacePath, sourcePath, defaultBranch, protectDefaultBranch, protectEnforceAdmins, owner, client, repo, requireCodeOwnerReviews, requiredStatusCheckContexts, config, logger, gitCommitMessage, gitAuthorName, gitAuthorEmail) {
|
|
1014
|
+
const gitAuthorInfo = {
|
|
1015
|
+
name: gitAuthorName ? gitAuthorName : config.getOptionalString("scaffolder.defaultAuthor.name"),
|
|
1016
|
+
email: gitAuthorEmail ? gitAuthorEmail : config.getOptionalString("scaffolder.defaultAuthor.email")
|
|
1017
|
+
};
|
|
1018
|
+
const commitMessage = gitCommitMessage ? gitCommitMessage : config.getOptionalString("scaffolder.defaultCommitMessage");
|
|
1019
|
+
await initRepoAndPush({
|
|
1020
|
+
dir: getRepoSourceDirectory(workspacePath, sourcePath),
|
|
1021
|
+
remoteUrl,
|
|
1022
|
+
defaultBranch,
|
|
1023
|
+
auth: {
|
|
1024
|
+
username: "x-access-token",
|
|
1025
|
+
password
|
|
1026
|
+
},
|
|
1027
|
+
logger,
|
|
1028
|
+
commitMessage,
|
|
1029
|
+
gitAuthorInfo
|
|
1030
|
+
});
|
|
1031
|
+
if (protectDefaultBranch) {
|
|
1032
|
+
try {
|
|
1033
|
+
await enableBranchProtectionOnDefaultRepoBranch({
|
|
1034
|
+
owner,
|
|
1035
|
+
client,
|
|
1036
|
+
repoName: repo,
|
|
1037
|
+
logger,
|
|
1038
|
+
defaultBranch,
|
|
1039
|
+
requireCodeOwnerReviews,
|
|
1040
|
+
requiredStatusCheckContexts,
|
|
1041
|
+
enforceAdmins: protectEnforceAdmins
|
|
1042
|
+
});
|
|
1043
|
+
} catch (e) {
|
|
1044
|
+
errors.assertError(e);
|
|
1045
|
+
logger.warn(`Skipping: default branch protection on '${repo}', ${e.message}`);
|
|
1046
|
+
}
|
|
851
1047
|
}
|
|
852
|
-
|
|
853
|
-
|
|
1048
|
+
}
|
|
1049
|
+
function extractCollaboratorName(collaborator) {
|
|
1050
|
+
if ("username" in collaborator)
|
|
1051
|
+
return collaborator.username;
|
|
1052
|
+
if ("user" in collaborator)
|
|
1053
|
+
return collaborator.user;
|
|
1054
|
+
return collaborator.team;
|
|
1055
|
+
}
|
|
854
1056
|
|
|
855
|
-
function
|
|
856
|
-
const { integrations,
|
|
1057
|
+
function createGithubActionsDispatchAction(options) {
|
|
1058
|
+
const { integrations, githubCredentialsProvider } = options;
|
|
857
1059
|
return createTemplateAction({
|
|
858
|
-
id: "
|
|
859
|
-
description: "
|
|
1060
|
+
id: "github:actions:dispatch",
|
|
1061
|
+
description: "Dispatches a GitHub Action workflow for a given branch or tag",
|
|
860
1062
|
schema: {
|
|
861
1063
|
input: {
|
|
862
1064
|
type: "object",
|
|
863
|
-
required: ["repoUrl"],
|
|
1065
|
+
required: ["repoUrl", "workflowId", "branchOrTagName"],
|
|
864
1066
|
properties: {
|
|
865
1067
|
repoUrl: {
|
|
866
1068
|
title: "Repository Location",
|
|
1069
|
+
description: `Accepts the format 'github.com?repo=reponame&owner=owner' where 'reponame' is the new repository name and 'owner' is an organization or username`,
|
|
867
1070
|
type: "string"
|
|
868
1071
|
},
|
|
869
|
-
|
|
870
|
-
title: "
|
|
1072
|
+
workflowId: {
|
|
1073
|
+
title: "Workflow ID",
|
|
1074
|
+
description: "The GitHub Action Workflow filename",
|
|
871
1075
|
type: "string"
|
|
872
1076
|
},
|
|
873
|
-
|
|
874
|
-
title: "
|
|
875
|
-
|
|
876
|
-
description: `Sets the default branch on the repository. The default value is 'master'`
|
|
877
|
-
},
|
|
878
|
-
gitCommitMessage: {
|
|
879
|
-
title: "Git Commit Message",
|
|
880
|
-
type: "string",
|
|
881
|
-
description: `Sets the commit message on the repository. The default value is 'initial commit'`
|
|
882
|
-
},
|
|
883
|
-
gitAuthorName: {
|
|
884
|
-
title: "Default Author Name",
|
|
885
|
-
type: "string",
|
|
886
|
-
description: `Sets the default author name for the commit. The default value is 'Scaffolder'`
|
|
887
|
-
},
|
|
888
|
-
gitAuthorEmail: {
|
|
889
|
-
title: "Default Author Email",
|
|
890
|
-
type: "string",
|
|
891
|
-
description: `Sets the default author email for the commit.`
|
|
892
|
-
},
|
|
893
|
-
sourcePath: {
|
|
894
|
-
title: "Source Path",
|
|
895
|
-
description: "Path within the workspace that will be used as the repository root. If omitted, the entire workspace will be published as the repository.",
|
|
1077
|
+
branchOrTagName: {
|
|
1078
|
+
title: "Branch or Tag name",
|
|
1079
|
+
description: "The git branch or tag name used to dispatch the workflow",
|
|
896
1080
|
type: "string"
|
|
897
1081
|
},
|
|
1082
|
+
workflowInputs: {
|
|
1083
|
+
title: "Workflow Inputs",
|
|
1084
|
+
description: "Inputs keys and values to send to GitHub Action configured on the workflow file. The maximum number of properties is 10. ",
|
|
1085
|
+
type: "object"
|
|
1086
|
+
},
|
|
898
1087
|
token: {
|
|
899
1088
|
title: "Authentication Token",
|
|
900
1089
|
type: "string",
|
|
901
|
-
description: "The
|
|
902
|
-
}
|
|
903
|
-
}
|
|
904
|
-
},
|
|
905
|
-
output: {
|
|
906
|
-
type: "object",
|
|
907
|
-
properties: {
|
|
908
|
-
remoteUrl: {
|
|
909
|
-
title: "A URL to the repository with the provider",
|
|
910
|
-
type: "string"
|
|
911
|
-
},
|
|
912
|
-
repoContentsUrl: {
|
|
913
|
-
title: "A URL to the root of the repository",
|
|
914
|
-
type: "string"
|
|
1090
|
+
description: "The GITHUB_TOKEN to use for authorization to GitHub"
|
|
915
1091
|
}
|
|
916
1092
|
}
|
|
917
1093
|
}
|
|
918
1094
|
},
|
|
919
1095
|
async handler(ctx) {
|
|
920
|
-
var _a;
|
|
921
1096
|
const {
|
|
922
1097
|
repoUrl,
|
|
923
|
-
|
|
924
|
-
|
|
925
|
-
|
|
926
|
-
|
|
1098
|
+
workflowId,
|
|
1099
|
+
branchOrTagName,
|
|
1100
|
+
workflowInputs,
|
|
1101
|
+
token: providedToken
|
|
927
1102
|
} = ctx.input;
|
|
928
|
-
|
|
929
|
-
|
|
930
|
-
|
|
931
|
-
|
|
932
|
-
const integrationConfig = integrations.azure.byHost(host);
|
|
933
|
-
if (!integrationConfig) {
|
|
934
|
-
throw new errors.InputError(`No matching integration configuration for host ${host}, please check your integrations config`);
|
|
935
|
-
}
|
|
936
|
-
if (!integrationConfig.config.token && !ctx.input.token) {
|
|
937
|
-
throw new errors.InputError(`No token provided for Azure Integration ${host}`);
|
|
938
|
-
}
|
|
939
|
-
const token = (_a = ctx.input.token) != null ? _a : integrationConfig.config.token;
|
|
940
|
-
const authHandler = azureDevopsNodeApi.getPersonalAccessTokenHandler(token);
|
|
941
|
-
const webApi = new azureDevopsNodeApi.WebApi(`https://${host}/${organization}`, authHandler);
|
|
942
|
-
const client = await webApi.getGitApi();
|
|
943
|
-
const createOptions = { name: repo };
|
|
944
|
-
const returnedRepo = await client.createRepository(createOptions, owner);
|
|
945
|
-
if (!returnedRepo) {
|
|
946
|
-
throw new errors.InputError(`Unable to create the repository with Organization ${organization}, Project ${owner} and Repo ${repo}.
|
|
947
|
-
Please make sure that both the Org and Project are typed corrected and exist.`);
|
|
948
|
-
}
|
|
949
|
-
const remoteUrl = returnedRepo.remoteUrl;
|
|
950
|
-
if (!remoteUrl) {
|
|
951
|
-
throw new errors.InputError("No remote URL returned from create repository for Azure");
|
|
1103
|
+
ctx.logger.info(`Dispatching workflow ${workflowId} for repo ${repoUrl} on ${branchOrTagName}`);
|
|
1104
|
+
const { owner, repo } = parseRepoUrl(repoUrl, integrations);
|
|
1105
|
+
if (!owner) {
|
|
1106
|
+
throw new errors.InputError("Invalid repository owner provided in repoUrl");
|
|
952
1107
|
}
|
|
953
|
-
const
|
|
954
|
-
|
|
955
|
-
|
|
956
|
-
|
|
957
|
-
|
|
958
|
-
|
|
959
|
-
|
|
960
|
-
|
|
961
|
-
|
|
962
|
-
|
|
963
|
-
|
|
964
|
-
|
|
965
|
-
},
|
|
966
|
-
logger: ctx.logger,
|
|
967
|
-
commitMessage: gitCommitMessage ? gitCommitMessage : config.getOptionalString("scaffolder.defaultCommitMessage"),
|
|
968
|
-
gitAuthorInfo
|
|
1108
|
+
const client = new octokit.Octokit(await getOctokitOptions({
|
|
1109
|
+
integrations,
|
|
1110
|
+
repoUrl,
|
|
1111
|
+
credentialsProvider: githubCredentialsProvider,
|
|
1112
|
+
token: providedToken
|
|
1113
|
+
}));
|
|
1114
|
+
await client.rest.actions.createWorkflowDispatch({
|
|
1115
|
+
owner,
|
|
1116
|
+
repo,
|
|
1117
|
+
workflow_id: workflowId,
|
|
1118
|
+
ref: branchOrTagName,
|
|
1119
|
+
inputs: workflowInputs
|
|
969
1120
|
});
|
|
970
|
-
ctx.
|
|
971
|
-
ctx.output("repoContentsUrl", repoContentsUrl);
|
|
1121
|
+
ctx.logger.info(`Workflow ${workflowId} dispatched successfully`);
|
|
972
1122
|
}
|
|
973
1123
|
});
|
|
974
1124
|
}
|
|
975
1125
|
|
|
976
|
-
|
|
977
|
-
const {
|
|
978
|
-
workspace,
|
|
979
|
-
project,
|
|
980
|
-
repo,
|
|
981
|
-
description,
|
|
982
|
-
repoVisibility,
|
|
983
|
-
mainBranch,
|
|
984
|
-
authorization,
|
|
985
|
-
apiBaseUrl
|
|
986
|
-
} = opts;
|
|
987
|
-
const options = {
|
|
988
|
-
method: "POST",
|
|
989
|
-
body: JSON.stringify({
|
|
990
|
-
scm: "git",
|
|
991
|
-
description,
|
|
992
|
-
is_private: repoVisibility === "private",
|
|
993
|
-
project: { key: project }
|
|
994
|
-
}),
|
|
995
|
-
headers: {
|
|
996
|
-
Authorization: authorization,
|
|
997
|
-
"Content-Type": "application/json"
|
|
998
|
-
}
|
|
999
|
-
};
|
|
1000
|
-
let response;
|
|
1001
|
-
try {
|
|
1002
|
-
response = await fetch__default["default"](`${apiBaseUrl}/repositories/${workspace}/${repo}`, options);
|
|
1003
|
-
} catch (e) {
|
|
1004
|
-
throw new Error(`Unable to create repository, ${e}`);
|
|
1005
|
-
}
|
|
1006
|
-
if (response.status !== 200) {
|
|
1007
|
-
throw new Error(`Unable to create repository, ${response.status} ${response.statusText}, ${await response.text()}`);
|
|
1008
|
-
}
|
|
1009
|
-
const r = await response.json();
|
|
1010
|
-
let remoteUrl = "";
|
|
1011
|
-
for (const link of r.links.clone) {
|
|
1012
|
-
if (link.name === "https") {
|
|
1013
|
-
remoteUrl = link.href;
|
|
1014
|
-
}
|
|
1015
|
-
}
|
|
1016
|
-
const repoContentsUrl = `${r.links.html.href}/src/${mainBranch}`;
|
|
1017
|
-
return { remoteUrl, repoContentsUrl };
|
|
1018
|
-
};
|
|
1019
|
-
const createBitbucketServerRepository = async (opts) => {
|
|
1020
|
-
const {
|
|
1021
|
-
project,
|
|
1022
|
-
repo,
|
|
1023
|
-
description,
|
|
1024
|
-
authorization,
|
|
1025
|
-
repoVisibility,
|
|
1026
|
-
apiBaseUrl
|
|
1027
|
-
} = opts;
|
|
1028
|
-
let response;
|
|
1029
|
-
const options = {
|
|
1030
|
-
method: "POST",
|
|
1031
|
-
body: JSON.stringify({
|
|
1032
|
-
name: repo,
|
|
1033
|
-
description,
|
|
1034
|
-
public: repoVisibility === "public"
|
|
1035
|
-
}),
|
|
1036
|
-
headers: {
|
|
1037
|
-
Authorization: authorization,
|
|
1038
|
-
"Content-Type": "application/json"
|
|
1039
|
-
}
|
|
1040
|
-
};
|
|
1041
|
-
try {
|
|
1042
|
-
response = await fetch__default["default"](`${apiBaseUrl}/projects/${project}/repos`, options);
|
|
1043
|
-
} catch (e) {
|
|
1044
|
-
throw new Error(`Unable to create repository, ${e}`);
|
|
1045
|
-
}
|
|
1046
|
-
if (response.status !== 201) {
|
|
1047
|
-
throw new Error(`Unable to create repository, ${response.status} ${response.statusText}, ${await response.text()}`);
|
|
1048
|
-
}
|
|
1049
|
-
const r = await response.json();
|
|
1050
|
-
let remoteUrl = "";
|
|
1051
|
-
for (const link of r.links.clone) {
|
|
1052
|
-
if (link.name === "http") {
|
|
1053
|
-
remoteUrl = link.href;
|
|
1054
|
-
}
|
|
1055
|
-
}
|
|
1056
|
-
const repoContentsUrl = `${r.links.self[0].href}`;
|
|
1057
|
-
return { remoteUrl, repoContentsUrl };
|
|
1058
|
-
};
|
|
1059
|
-
const getAuthorizationHeader$2 = (config) => {
|
|
1060
|
-
if (config.username && config.appPassword) {
|
|
1061
|
-
const buffer = Buffer.from(`${config.username}:${config.appPassword}`, "utf8");
|
|
1062
|
-
return `Basic ${buffer.toString("base64")}`;
|
|
1063
|
-
}
|
|
1064
|
-
if (config.token) {
|
|
1065
|
-
return `Bearer ${config.token}`;
|
|
1066
|
-
}
|
|
1067
|
-
throw new Error(`Authorization has not been provided for Bitbucket. Please add either username + appPassword or token to the Integrations config`);
|
|
1068
|
-
};
|
|
1069
|
-
const performEnableLFS$1 = async (opts) => {
|
|
1070
|
-
const { authorization, host, project, repo } = opts;
|
|
1071
|
-
const options = {
|
|
1072
|
-
method: "PUT",
|
|
1073
|
-
headers: {
|
|
1074
|
-
Authorization: authorization
|
|
1075
|
-
}
|
|
1076
|
-
};
|
|
1077
|
-
const { ok, status, statusText } = await fetch__default["default"](`https://${host}/rest/git-lfs/admin/projects/${project}/repos/${repo}/enabled`, options);
|
|
1078
|
-
if (!ok)
|
|
1079
|
-
throw new Error(`Failed to enable LFS in the repository, ${status}: ${statusText}`);
|
|
1080
|
-
};
|
|
1081
|
-
function createPublishBitbucketAction(options) {
|
|
1082
|
-
const { integrations, config } = options;
|
|
1126
|
+
function createGithubIssuesLabelAction(options) {
|
|
1127
|
+
const { integrations, githubCredentialsProvider } = options;
|
|
1083
1128
|
return createTemplateAction({
|
|
1084
|
-
id: "
|
|
1085
|
-
description: "
|
|
1129
|
+
id: "github:issues:label",
|
|
1130
|
+
description: "Adds labels to a pull request or issue on GitHub.",
|
|
1086
1131
|
schema: {
|
|
1087
1132
|
input: {
|
|
1088
1133
|
type: "object",
|
|
1089
|
-
required: ["repoUrl"],
|
|
1134
|
+
required: ["repoUrl", "number", "labels"],
|
|
1090
1135
|
properties: {
|
|
1091
1136
|
repoUrl: {
|
|
1092
1137
|
title: "Repository Location",
|
|
1138
|
+
description: `Accepts the format 'github.com?repo=reponame&owner=owner' where 'reponame' is the repository name and 'owner' is an organization or username`,
|
|
1093
1139
|
type: "string"
|
|
1094
1140
|
},
|
|
1095
|
-
|
|
1096
|
-
title: "
|
|
1097
|
-
|
|
1098
|
-
|
|
1099
|
-
repoVisibility: {
|
|
1100
|
-
title: "Repository Visibility",
|
|
1101
|
-
type: "string",
|
|
1102
|
-
enum: ["private", "public"]
|
|
1103
|
-
},
|
|
1104
|
-
defaultBranch: {
|
|
1105
|
-
title: "Default Branch",
|
|
1106
|
-
type: "string",
|
|
1107
|
-
description: `Sets the default branch on the repository. The default value is 'master'`
|
|
1108
|
-
},
|
|
1109
|
-
sourcePath: {
|
|
1110
|
-
title: "Source Path",
|
|
1111
|
-
description: "Path within the workspace that will be used as the repository root. If omitted, the entire workspace will be published as the repository.",
|
|
1112
|
-
type: "string"
|
|
1141
|
+
number: {
|
|
1142
|
+
title: "Pull Request or issue number",
|
|
1143
|
+
description: "The pull request or issue number to add labels to",
|
|
1144
|
+
type: "number"
|
|
1113
1145
|
},
|
|
1114
|
-
|
|
1115
|
-
title: "
|
|
1116
|
-
description: "
|
|
1117
|
-
type: "
|
|
1146
|
+
labels: {
|
|
1147
|
+
title: "Labels",
|
|
1148
|
+
description: "The labels to add to the pull request or issue",
|
|
1149
|
+
type: "array",
|
|
1150
|
+
items: {
|
|
1151
|
+
type: "string"
|
|
1152
|
+
}
|
|
1118
1153
|
},
|
|
1119
1154
|
token: {
|
|
1120
1155
|
title: "Authentication Token",
|
|
1121
1156
|
type: "string",
|
|
1122
|
-
description: "The
|
|
1123
|
-
},
|
|
1124
|
-
gitCommitMessage: {
|
|
1125
|
-
title: "Git Commit Message",
|
|
1126
|
-
type: "string",
|
|
1127
|
-
description: `Sets the commit message on the repository. The default value is 'initial commit'`
|
|
1128
|
-
},
|
|
1129
|
-
gitAuthorName: {
|
|
1130
|
-
title: "Default Author Name",
|
|
1131
|
-
type: "string",
|
|
1132
|
-
description: `Sets the default author name for the commit. The default value is 'Scaffolder'`
|
|
1133
|
-
},
|
|
1134
|
-
gitAuthorEmail: {
|
|
1135
|
-
title: "Default Author Email",
|
|
1136
|
-
type: "string",
|
|
1137
|
-
description: `Sets the default author email for the commit.`
|
|
1157
|
+
description: "The GITHUB_TOKEN to use for authorization to GitHub"
|
|
1138
1158
|
}
|
|
1139
1159
|
}
|
|
1160
|
+
}
|
|
1161
|
+
},
|
|
1162
|
+
async handler(ctx) {
|
|
1163
|
+
const { repoUrl, number, labels, token: providedToken } = ctx.input;
|
|
1164
|
+
const { owner, repo } = parseRepoUrl(repoUrl, integrations);
|
|
1165
|
+
ctx.logger.info(`Adding labels to ${number} issue on repo ${repo}`);
|
|
1166
|
+
if (!owner) {
|
|
1167
|
+
throw new errors.InputError("Invalid repository owner provided in repoUrl");
|
|
1168
|
+
}
|
|
1169
|
+
const client = new octokit.Octokit(await getOctokitOptions({
|
|
1170
|
+
integrations,
|
|
1171
|
+
credentialsProvider: githubCredentialsProvider,
|
|
1172
|
+
repoUrl,
|
|
1173
|
+
token: providedToken
|
|
1174
|
+
}));
|
|
1175
|
+
try {
|
|
1176
|
+
await client.rest.issues.addLabels({
|
|
1177
|
+
owner,
|
|
1178
|
+
repo,
|
|
1179
|
+
issue_number: number,
|
|
1180
|
+
labels
|
|
1181
|
+
});
|
|
1182
|
+
} catch (e) {
|
|
1183
|
+
errors.assertError(e);
|
|
1184
|
+
ctx.logger.warn(`Failed: adding labels to issue: '${number}' on repo: '${repo}', ${e.message}`);
|
|
1185
|
+
}
|
|
1186
|
+
}
|
|
1187
|
+
});
|
|
1188
|
+
}
|
|
1189
|
+
|
|
1190
|
+
const repoUrl = {
|
|
1191
|
+
title: "Repository Location",
|
|
1192
|
+
description: `Accepts the format 'github.com?repo=reponame&owner=owner' where 'reponame' is the new repository name and 'owner' is an organization or username`,
|
|
1193
|
+
type: "string"
|
|
1194
|
+
};
|
|
1195
|
+
const description = {
|
|
1196
|
+
title: "Repository Description",
|
|
1197
|
+
type: "string"
|
|
1198
|
+
};
|
|
1199
|
+
const access = {
|
|
1200
|
+
title: "Repository Access",
|
|
1201
|
+
description: `Sets an admin collaborator on the repository. Can either be a user reference different from 'owner' in 'repoUrl' or team reference, eg. 'org/team-name'`,
|
|
1202
|
+
type: "string"
|
|
1203
|
+
};
|
|
1204
|
+
const requireCodeOwnerReviews = {
|
|
1205
|
+
title: "Require CODEOWNER Reviews?",
|
|
1206
|
+
description: "Require an approved review in PR including files with a designated Code Owner",
|
|
1207
|
+
type: "boolean"
|
|
1208
|
+
};
|
|
1209
|
+
const requiredStatusCheckContexts = {
|
|
1210
|
+
title: "Required Status Check Contexts",
|
|
1211
|
+
description: "The list of status checks to require in order to merge into this branch",
|
|
1212
|
+
type: "array",
|
|
1213
|
+
items: {
|
|
1214
|
+
type: "string"
|
|
1215
|
+
}
|
|
1216
|
+
};
|
|
1217
|
+
const repoVisibility = {
|
|
1218
|
+
title: "Repository Visibility",
|
|
1219
|
+
type: "string",
|
|
1220
|
+
enum: ["private", "public", "internal"]
|
|
1221
|
+
};
|
|
1222
|
+
const deleteBranchOnMerge = {
|
|
1223
|
+
title: "Delete Branch On Merge",
|
|
1224
|
+
type: "boolean",
|
|
1225
|
+
description: `Delete the branch after merging the PR. The default value is 'false'`
|
|
1226
|
+
};
|
|
1227
|
+
const gitAuthorName = {
|
|
1228
|
+
title: "Default Author Name",
|
|
1229
|
+
type: "string",
|
|
1230
|
+
description: `Sets the default author name for the commit. The default value is 'Scaffolder'`
|
|
1231
|
+
};
|
|
1232
|
+
const gitAuthorEmail = {
|
|
1233
|
+
title: "Default Author Email",
|
|
1234
|
+
type: "string",
|
|
1235
|
+
description: `Sets the default author email for the commit.`
|
|
1236
|
+
};
|
|
1237
|
+
const allowMergeCommit = {
|
|
1238
|
+
title: "Allow Merge Commits",
|
|
1239
|
+
type: "boolean",
|
|
1240
|
+
description: `Allow merge commits. The default value is 'true'`
|
|
1241
|
+
};
|
|
1242
|
+
const allowSquashMerge = {
|
|
1243
|
+
title: "Allow Squash Merges",
|
|
1244
|
+
type: "boolean",
|
|
1245
|
+
description: `Allow squash merges. The default value is 'true'`
|
|
1246
|
+
};
|
|
1247
|
+
const allowRebaseMerge = {
|
|
1248
|
+
title: "Allow Rebase Merges",
|
|
1249
|
+
type: "boolean",
|
|
1250
|
+
description: `Allow rebase merges. The default value is 'true'`
|
|
1251
|
+
};
|
|
1252
|
+
const collaborators = {
|
|
1253
|
+
title: "Collaborators",
|
|
1254
|
+
description: "Provide additional users or teams with permissions",
|
|
1255
|
+
type: "array",
|
|
1256
|
+
items: {
|
|
1257
|
+
type: "object",
|
|
1258
|
+
additionalProperties: false,
|
|
1259
|
+
required: ["access"],
|
|
1260
|
+
properties: {
|
|
1261
|
+
access: {
|
|
1262
|
+
type: "string",
|
|
1263
|
+
description: "The type of access for the user",
|
|
1264
|
+
enum: ["push", "pull", "admin", "maintain", "triage"]
|
|
1265
|
+
},
|
|
1266
|
+
user: {
|
|
1267
|
+
type: "string",
|
|
1268
|
+
description: "The name of the user that will be added as a collaborator"
|
|
1269
|
+
},
|
|
1270
|
+
team: {
|
|
1271
|
+
type: "string",
|
|
1272
|
+
description: "The name of the team that will be added as a collaborator"
|
|
1273
|
+
}
|
|
1274
|
+
},
|
|
1275
|
+
oneOf: [{ required: ["user"] }, { required: ["team"] }]
|
|
1276
|
+
}
|
|
1277
|
+
};
|
|
1278
|
+
const token = {
|
|
1279
|
+
title: "Authentication Token",
|
|
1280
|
+
type: "string",
|
|
1281
|
+
description: "The token to use for authorization to GitHub"
|
|
1282
|
+
};
|
|
1283
|
+
const topics = {
|
|
1284
|
+
title: "Topics",
|
|
1285
|
+
type: "array",
|
|
1286
|
+
items: {
|
|
1287
|
+
type: "string"
|
|
1288
|
+
}
|
|
1289
|
+
};
|
|
1290
|
+
const defaultBranch = {
|
|
1291
|
+
title: "Default Branch",
|
|
1292
|
+
type: "string",
|
|
1293
|
+
description: `Sets the default branch on the repository. The default value is 'master'`
|
|
1294
|
+
};
|
|
1295
|
+
const protectDefaultBranch = {
|
|
1296
|
+
title: "Protect Default Branch",
|
|
1297
|
+
type: "boolean",
|
|
1298
|
+
description: `Protect the default branch after creating the repository. The default value is 'true'`
|
|
1299
|
+
};
|
|
1300
|
+
const protectEnforceAdmins = {
|
|
1301
|
+
title: "Enforce Admins On Protected Branches",
|
|
1302
|
+
type: "boolean",
|
|
1303
|
+
description: `Enforce admins to adhere to default branch protection. The default value is 'true'`
|
|
1304
|
+
};
|
|
1305
|
+
const gitCommitMessage = {
|
|
1306
|
+
title: "Git Commit Message",
|
|
1307
|
+
type: "string",
|
|
1308
|
+
description: `Sets the commit message on the repository. The default value is 'initial commit'`
|
|
1309
|
+
};
|
|
1310
|
+
const sourcePath = {
|
|
1311
|
+
title: "Source Path",
|
|
1312
|
+
description: "Path within the workspace that will be used as the repository root. If omitted, the entire workspace will be published as the repository.",
|
|
1313
|
+
type: "string"
|
|
1314
|
+
};
|
|
1315
|
+
|
|
1316
|
+
const remoteUrl = {
|
|
1317
|
+
title: "A URL to the repository with the provider",
|
|
1318
|
+
type: "string"
|
|
1319
|
+
};
|
|
1320
|
+
const repoContentsUrl = {
|
|
1321
|
+
title: "A URL to the root of the repository",
|
|
1322
|
+
type: "string"
|
|
1323
|
+
};
|
|
1324
|
+
|
|
1325
|
+
function createGithubRepoCreateAction(options) {
|
|
1326
|
+
const { integrations, githubCredentialsProvider } = options;
|
|
1327
|
+
return createTemplateAction({
|
|
1328
|
+
id: "github:repo:create",
|
|
1329
|
+
description: "Creates a GitHub repository.",
|
|
1330
|
+
schema: {
|
|
1331
|
+
input: {
|
|
1332
|
+
type: "object",
|
|
1333
|
+
required: ["repoUrl"],
|
|
1334
|
+
properties: {
|
|
1335
|
+
repoUrl: repoUrl,
|
|
1336
|
+
description: description,
|
|
1337
|
+
access: access,
|
|
1338
|
+
requireCodeOwnerReviews: requireCodeOwnerReviews,
|
|
1339
|
+
requiredStatusCheckContexts: requiredStatusCheckContexts,
|
|
1340
|
+
repoVisibility: repoVisibility,
|
|
1341
|
+
deleteBranchOnMerge: deleteBranchOnMerge,
|
|
1342
|
+
allowMergeCommit: allowMergeCommit,
|
|
1343
|
+
allowSquashMerge: allowSquashMerge,
|
|
1344
|
+
allowRebaseMerge: allowRebaseMerge,
|
|
1345
|
+
collaborators: collaborators,
|
|
1346
|
+
token: token,
|
|
1347
|
+
topics: topics
|
|
1348
|
+
}
|
|
1349
|
+
},
|
|
1350
|
+
output: {
|
|
1351
|
+
type: "object",
|
|
1352
|
+
properties: {
|
|
1353
|
+
remoteUrl: remoteUrl,
|
|
1354
|
+
repoContentsUrl: repoContentsUrl
|
|
1355
|
+
}
|
|
1356
|
+
}
|
|
1357
|
+
},
|
|
1358
|
+
async handler(ctx) {
|
|
1359
|
+
const {
|
|
1360
|
+
repoUrl,
|
|
1361
|
+
description,
|
|
1362
|
+
access,
|
|
1363
|
+
repoVisibility = "private",
|
|
1364
|
+
deleteBranchOnMerge = false,
|
|
1365
|
+
allowMergeCommit = true,
|
|
1366
|
+
allowSquashMerge = true,
|
|
1367
|
+
allowRebaseMerge = true,
|
|
1368
|
+
collaborators,
|
|
1369
|
+
topics,
|
|
1370
|
+
token: providedToken
|
|
1371
|
+
} = ctx.input;
|
|
1372
|
+
const octokitOptions = await getOctokitOptions({
|
|
1373
|
+
integrations,
|
|
1374
|
+
credentialsProvider: githubCredentialsProvider,
|
|
1375
|
+
token: providedToken,
|
|
1376
|
+
repoUrl
|
|
1377
|
+
});
|
|
1378
|
+
const client = new octokit.Octokit(octokitOptions);
|
|
1379
|
+
const { owner, repo } = parseRepoUrl(repoUrl, integrations);
|
|
1380
|
+
if (!owner) {
|
|
1381
|
+
throw new errors.InputError("Invalid repository owner provided in repoUrl");
|
|
1382
|
+
}
|
|
1383
|
+
const newRepo = await createGithubRepoWithCollaboratorsAndTopics(client, repo, owner, repoVisibility, description, deleteBranchOnMerge, allowMergeCommit, allowSquashMerge, allowRebaseMerge, access, collaborators, topics, ctx.logger);
|
|
1384
|
+
ctx.output("remoteUrl", newRepo.clone_url);
|
|
1385
|
+
}
|
|
1386
|
+
});
|
|
1387
|
+
}
|
|
1388
|
+
|
|
1389
|
+
function createGithubRepoPushAction(options) {
|
|
1390
|
+
const { integrations, config, githubCredentialsProvider } = options;
|
|
1391
|
+
return createTemplateAction({
|
|
1392
|
+
id: "github:repo:push",
|
|
1393
|
+
description: "Initializes a git repository of contents in workspace and publishes it to GitHub.",
|
|
1394
|
+
schema: {
|
|
1395
|
+
input: {
|
|
1396
|
+
type: "object",
|
|
1397
|
+
required: ["repoUrl"],
|
|
1398
|
+
properties: {
|
|
1399
|
+
repoUrl: repoUrl,
|
|
1400
|
+
requireCodeOwnerReviews: requireCodeOwnerReviews,
|
|
1401
|
+
requiredStatusCheckContexts: requiredStatusCheckContexts,
|
|
1402
|
+
defaultBranch: defaultBranch,
|
|
1403
|
+
protectDefaultBranch: protectDefaultBranch,
|
|
1404
|
+
protectEnforceAdmins: protectEnforceAdmins,
|
|
1405
|
+
gitCommitMessage: gitCommitMessage,
|
|
1406
|
+
gitAuthorName: gitAuthorName,
|
|
1407
|
+
gitAuthorEmail: gitAuthorEmail,
|
|
1408
|
+
sourcePath: sourcePath,
|
|
1409
|
+
token: token
|
|
1410
|
+
}
|
|
1140
1411
|
},
|
|
1141
1412
|
output: {
|
|
1142
1413
|
type: "object",
|
|
1143
1414
|
properties: {
|
|
1144
|
-
remoteUrl:
|
|
1145
|
-
|
|
1146
|
-
type: "string"
|
|
1147
|
-
},
|
|
1148
|
-
repoContentsUrl: {
|
|
1149
|
-
title: "A URL to the root of the repository",
|
|
1150
|
-
type: "string"
|
|
1151
|
-
}
|
|
1415
|
+
remoteUrl: remoteUrl,
|
|
1416
|
+
repoContentsUrl: repoContentsUrl
|
|
1152
1417
|
}
|
|
1153
1418
|
}
|
|
1154
1419
|
},
|
|
1155
1420
|
async handler(ctx) {
|
|
1156
|
-
var _a;
|
|
1157
|
-
ctx.logger.warn(`[Deprecated] Please migrate the use of action "publish:bitbucket" to "publish:bitbucketCloud" or "publish:bitbucketServer".`);
|
|
1158
1421
|
const {
|
|
1159
1422
|
repoUrl,
|
|
1160
|
-
description,
|
|
1161
1423
|
defaultBranch = "master",
|
|
1162
|
-
|
|
1163
|
-
|
|
1424
|
+
protectDefaultBranch = true,
|
|
1425
|
+
protectEnforceAdmins = true,
|
|
1164
1426
|
gitCommitMessage = "initial commit",
|
|
1165
1427
|
gitAuthorName,
|
|
1166
|
-
gitAuthorEmail
|
|
1428
|
+
gitAuthorEmail,
|
|
1429
|
+
requireCodeOwnerReviews = false,
|
|
1430
|
+
requiredStatusCheckContexts = [],
|
|
1431
|
+
token: providedToken
|
|
1167
1432
|
} = ctx.input;
|
|
1168
|
-
const {
|
|
1169
|
-
if (
|
|
1170
|
-
|
|
1171
|
-
throw new errors.InputError(`Invalid URL provider was included in the repo URL to create ${ctx.input.repoUrl}, missing workspace`);
|
|
1172
|
-
}
|
|
1173
|
-
}
|
|
1174
|
-
if (!project) {
|
|
1175
|
-
throw new errors.InputError(`Invalid URL provider was included in the repo URL to create ${ctx.input.repoUrl}, missing project`);
|
|
1176
|
-
}
|
|
1177
|
-
const integrationConfig = integrations.bitbucket.byHost(host);
|
|
1178
|
-
if (!integrationConfig) {
|
|
1179
|
-
throw new errors.InputError(`No matching integration configuration for host ${host}, please check your integrations config`);
|
|
1180
|
-
}
|
|
1181
|
-
const authorization = getAuthorizationHeader$2(ctx.input.token ? {
|
|
1182
|
-
host: integrationConfig.config.host,
|
|
1183
|
-
apiBaseUrl: integrationConfig.config.apiBaseUrl,
|
|
1184
|
-
token: ctx.input.token
|
|
1185
|
-
} : integrationConfig.config);
|
|
1186
|
-
const apiBaseUrl = integrationConfig.config.apiBaseUrl;
|
|
1187
|
-
const createMethod = host === "bitbucket.org" ? createBitbucketCloudRepository : createBitbucketServerRepository;
|
|
1188
|
-
const { remoteUrl, repoContentsUrl } = await createMethod({
|
|
1189
|
-
authorization,
|
|
1190
|
-
workspace: workspace || "",
|
|
1191
|
-
project,
|
|
1192
|
-
repo,
|
|
1193
|
-
repoVisibility,
|
|
1194
|
-
mainBranch: defaultBranch,
|
|
1195
|
-
description,
|
|
1196
|
-
apiBaseUrl
|
|
1197
|
-
});
|
|
1198
|
-
const gitAuthorInfo = {
|
|
1199
|
-
name: gitAuthorName ? gitAuthorName : config.getOptionalString("scaffolder.defaultAuthor.name"),
|
|
1200
|
-
email: gitAuthorEmail ? gitAuthorEmail : config.getOptionalString("scaffolder.defaultAuthor.email")
|
|
1201
|
-
};
|
|
1202
|
-
let auth;
|
|
1203
|
-
if (ctx.input.token) {
|
|
1204
|
-
auth = {
|
|
1205
|
-
username: "x-token-auth",
|
|
1206
|
-
password: ctx.input.token
|
|
1207
|
-
};
|
|
1208
|
-
} else {
|
|
1209
|
-
auth = {
|
|
1210
|
-
username: integrationConfig.config.username ? integrationConfig.config.username : "x-token-auth",
|
|
1211
|
-
password: integrationConfig.config.appPassword ? integrationConfig.config.appPassword : (_a = integrationConfig.config.token) != null ? _a : ""
|
|
1212
|
-
};
|
|
1433
|
+
const { owner, repo } = parseRepoUrl(repoUrl, integrations);
|
|
1434
|
+
if (!owner) {
|
|
1435
|
+
throw new errors.InputError("Invalid repository owner provided in repoUrl");
|
|
1213
1436
|
}
|
|
1214
|
-
await
|
|
1215
|
-
|
|
1216
|
-
|
|
1217
|
-
|
|
1218
|
-
|
|
1219
|
-
logger: ctx.logger,
|
|
1220
|
-
commitMessage: gitCommitMessage ? gitCommitMessage : config.getOptionalString("scaffolder.defaultCommitMessage"),
|
|
1221
|
-
gitAuthorInfo
|
|
1437
|
+
const octokitOptions = await getOctokitOptions({
|
|
1438
|
+
integrations,
|
|
1439
|
+
credentialsProvider: githubCredentialsProvider,
|
|
1440
|
+
token: providedToken,
|
|
1441
|
+
repoUrl
|
|
1222
1442
|
});
|
|
1223
|
-
|
|
1224
|
-
|
|
1225
|
-
|
|
1443
|
+
const client = new octokit.Octokit(octokitOptions);
|
|
1444
|
+
const targetRepo = await client.rest.repos.get({ owner, repo });
|
|
1445
|
+
const remoteUrl = targetRepo.data.clone_url;
|
|
1446
|
+
const repoContentsUrl = `${targetRepo.data.html_url}/blob/${defaultBranch}`;
|
|
1447
|
+
await initRepoPushAndProtect(remoteUrl, octokitOptions.auth, ctx.workspacePath, ctx.input.sourcePath, defaultBranch, protectDefaultBranch, protectEnforceAdmins, owner, client, repo, requireCodeOwnerReviews, requiredStatusCheckContexts, config, ctx.logger, gitCommitMessage, gitAuthorName, gitAuthorEmail);
|
|
1226
1448
|
ctx.output("remoteUrl", remoteUrl);
|
|
1227
1449
|
ctx.output("repoContentsUrl", repoContentsUrl);
|
|
1228
1450
|
}
|
|
1229
1451
|
});
|
|
1230
1452
|
}
|
|
1231
1453
|
|
|
1232
|
-
|
|
1233
|
-
const {
|
|
1234
|
-
|
|
1235
|
-
project,
|
|
1236
|
-
repo,
|
|
1237
|
-
description,
|
|
1238
|
-
repoVisibility,
|
|
1239
|
-
mainBranch,
|
|
1240
|
-
authorization,
|
|
1241
|
-
apiBaseUrl
|
|
1242
|
-
} = opts;
|
|
1243
|
-
const options = {
|
|
1244
|
-
method: "POST",
|
|
1245
|
-
body: JSON.stringify({
|
|
1246
|
-
scm: "git",
|
|
1247
|
-
description,
|
|
1248
|
-
is_private: repoVisibility === "private",
|
|
1249
|
-
project: { key: project }
|
|
1250
|
-
}),
|
|
1251
|
-
headers: {
|
|
1252
|
-
Authorization: authorization,
|
|
1253
|
-
"Content-Type": "application/json"
|
|
1254
|
-
}
|
|
1255
|
-
};
|
|
1256
|
-
let response;
|
|
1257
|
-
try {
|
|
1258
|
-
response = await fetch__default["default"](`${apiBaseUrl}/repositories/${workspace}/${repo}`, options);
|
|
1259
|
-
} catch (e) {
|
|
1260
|
-
throw new Error(`Unable to create repository, ${e}`);
|
|
1261
|
-
}
|
|
1262
|
-
if (response.status !== 200) {
|
|
1263
|
-
throw new Error(`Unable to create repository, ${response.status} ${response.statusText}, ${await response.text()}`);
|
|
1264
|
-
}
|
|
1265
|
-
const r = await response.json();
|
|
1266
|
-
let remoteUrl = "";
|
|
1267
|
-
for (const link of r.links.clone) {
|
|
1268
|
-
if (link.name === "https") {
|
|
1269
|
-
remoteUrl = link.href;
|
|
1270
|
-
}
|
|
1271
|
-
}
|
|
1272
|
-
const repoContentsUrl = `${r.links.html.href}/src/${mainBranch}`;
|
|
1273
|
-
return { remoteUrl, repoContentsUrl };
|
|
1274
|
-
};
|
|
1275
|
-
const getAuthorizationHeader$1 = (config) => {
|
|
1276
|
-
if (config.username && config.appPassword) {
|
|
1277
|
-
const buffer = Buffer.from(`${config.username}:${config.appPassword}`, "utf8");
|
|
1278
|
-
return `Basic ${buffer.toString("base64")}`;
|
|
1279
|
-
}
|
|
1280
|
-
if (config.token) {
|
|
1281
|
-
return `Bearer ${config.token}`;
|
|
1282
|
-
}
|
|
1283
|
-
throw new Error(`Authorization has not been provided for Bitbucket Cloud. Please add either username + appPassword to the Integrations config or a user login auth token`);
|
|
1284
|
-
};
|
|
1285
|
-
function createPublishBitbucketCloudAction(options) {
|
|
1286
|
-
const { integrations, config } = options;
|
|
1454
|
+
function createGithubWebhookAction(options) {
|
|
1455
|
+
const { integrations, defaultWebhookSecret, githubCredentialsProvider } = options;
|
|
1456
|
+
const eventNames = webhooks.emitterEventNames.filter((event) => !event.includes("."));
|
|
1287
1457
|
return createTemplateAction({
|
|
1288
|
-
id: "
|
|
1289
|
-
description: "
|
|
1458
|
+
id: "github:webhook",
|
|
1459
|
+
description: "Creates webhook for a repository on GitHub.",
|
|
1290
1460
|
schema: {
|
|
1291
1461
|
input: {
|
|
1292
1462
|
type: "object",
|
|
1293
|
-
required: ["repoUrl"],
|
|
1463
|
+
required: ["repoUrl", "webhookUrl"],
|
|
1294
1464
|
properties: {
|
|
1295
1465
|
repoUrl: {
|
|
1296
1466
|
title: "Repository Location",
|
|
1467
|
+
description: `Accepts the format 'github.com?repo=reponame&owner=owner' where 'reponame' is the new repository name and 'owner' is an organization or username`,
|
|
1297
1468
|
type: "string"
|
|
1298
1469
|
},
|
|
1299
|
-
|
|
1300
|
-
title: "
|
|
1470
|
+
webhookUrl: {
|
|
1471
|
+
title: "Webhook URL",
|
|
1472
|
+
description: "The URL to which the payloads will be delivered",
|
|
1301
1473
|
type: "string"
|
|
1302
1474
|
},
|
|
1303
|
-
|
|
1304
|
-
title: "
|
|
1305
|
-
|
|
1306
|
-
|
|
1475
|
+
webhookSecret: {
|
|
1476
|
+
title: "Webhook Secret",
|
|
1477
|
+
description: "Webhook secret value. The default can be provided internally in action creation",
|
|
1478
|
+
type: "string"
|
|
1307
1479
|
},
|
|
1308
|
-
|
|
1309
|
-
title: "
|
|
1480
|
+
events: {
|
|
1481
|
+
title: "Triggering Events",
|
|
1482
|
+
description: "Determines what events the hook is triggered for. Default: push",
|
|
1483
|
+
type: "array",
|
|
1484
|
+
oneOf: [
|
|
1485
|
+
{
|
|
1486
|
+
items: {
|
|
1487
|
+
type: "string",
|
|
1488
|
+
enum: eventNames
|
|
1489
|
+
}
|
|
1490
|
+
},
|
|
1491
|
+
{
|
|
1492
|
+
items: {
|
|
1493
|
+
type: "string",
|
|
1494
|
+
const: "*"
|
|
1495
|
+
}
|
|
1496
|
+
}
|
|
1497
|
+
]
|
|
1498
|
+
},
|
|
1499
|
+
active: {
|
|
1500
|
+
title: "Active",
|
|
1501
|
+
type: "boolean",
|
|
1502
|
+
description: `Determines if notifications are sent when the webhook is triggered. Default: true`
|
|
1503
|
+
},
|
|
1504
|
+
contentType: {
|
|
1505
|
+
title: "Content Type",
|
|
1310
1506
|
type: "string",
|
|
1311
|
-
|
|
1507
|
+
enum: ["form", "json"],
|
|
1508
|
+
description: `The media type used to serialize the payloads. The default is 'form'`
|
|
1312
1509
|
},
|
|
1313
|
-
|
|
1314
|
-
title: "
|
|
1315
|
-
|
|
1316
|
-
|
|
1510
|
+
insecureSsl: {
|
|
1511
|
+
title: "Insecure SSL",
|
|
1512
|
+
type: "boolean",
|
|
1513
|
+
description: `Determines whether the SSL certificate of the host for url will be verified when delivering payloads. Default 'false'`
|
|
1317
1514
|
},
|
|
1318
1515
|
token: {
|
|
1319
1516
|
title: "Authentication Token",
|
|
1320
1517
|
type: "string",
|
|
1321
|
-
description: "The
|
|
1322
|
-
}
|
|
1323
|
-
}
|
|
1324
|
-
},
|
|
1325
|
-
output: {
|
|
1326
|
-
type: "object",
|
|
1327
|
-
properties: {
|
|
1328
|
-
remoteUrl: {
|
|
1329
|
-
title: "A URL to the repository with the provider",
|
|
1330
|
-
type: "string"
|
|
1331
|
-
},
|
|
1332
|
-
repoContentsUrl: {
|
|
1333
|
-
title: "A URL to the root of the repository",
|
|
1334
|
-
type: "string"
|
|
1518
|
+
description: "The GITHUB_TOKEN to use for authorization to GitHub"
|
|
1335
1519
|
}
|
|
1336
1520
|
}
|
|
1337
1521
|
}
|
|
@@ -1339,127 +1523,53 @@ function createPublishBitbucketCloudAction(options) {
|
|
|
1339
1523
|
async handler(ctx) {
|
|
1340
1524
|
const {
|
|
1341
1525
|
repoUrl,
|
|
1342
|
-
|
|
1343
|
-
|
|
1344
|
-
|
|
1526
|
+
webhookUrl,
|
|
1527
|
+
webhookSecret = defaultWebhookSecret,
|
|
1528
|
+
events = ["push"],
|
|
1529
|
+
active = true,
|
|
1530
|
+
contentType = "form",
|
|
1531
|
+
insecureSsl = false,
|
|
1532
|
+
token: providedToken
|
|
1345
1533
|
} = ctx.input;
|
|
1346
|
-
|
|
1347
|
-
|
|
1348
|
-
|
|
1349
|
-
|
|
1350
|
-
if (!project) {
|
|
1351
|
-
throw new errors.InputError(`Invalid URL provider was included in the repo URL to create ${ctx.input.repoUrl}, missing project`);
|
|
1352
|
-
}
|
|
1353
|
-
const integrationConfig = integrations.bitbucketCloud.byHost(host);
|
|
1354
|
-
if (!integrationConfig) {
|
|
1355
|
-
throw new errors.InputError(`No matching integration configuration for host ${host}, please check your integrations config`);
|
|
1534
|
+
ctx.logger.info(`Creating webhook ${webhookUrl} for repo ${repoUrl}`);
|
|
1535
|
+
const { owner, repo } = parseRepoUrl(repoUrl, integrations);
|
|
1536
|
+
if (!owner) {
|
|
1537
|
+
throw new errors.InputError("Invalid repository owner provided in repoUrl");
|
|
1356
1538
|
}
|
|
1357
|
-
const
|
|
1358
|
-
|
|
1359
|
-
|
|
1360
|
-
|
|
1361
|
-
|
|
1362
|
-
|
|
1363
|
-
|
|
1364
|
-
|
|
1365
|
-
|
|
1366
|
-
|
|
1367
|
-
|
|
1368
|
-
|
|
1369
|
-
|
|
1370
|
-
|
|
1371
|
-
|
|
1372
|
-
|
|
1373
|
-
|
|
1374
|
-
|
|
1375
|
-
|
|
1376
|
-
|
|
1377
|
-
|
|
1378
|
-
|
|
1379
|
-
|
|
1380
|
-
|
|
1381
|
-
throw new Error("Credentials for Bitbucket Cloud integration required for this action.");
|
|
1382
|
-
}
|
|
1383
|
-
auth = {
|
|
1384
|
-
username: integrationConfig.config.username,
|
|
1385
|
-
password: integrationConfig.config.appPassword
|
|
1386
|
-
};
|
|
1539
|
+
const client = new octokit.Octokit(await getOctokitOptions({
|
|
1540
|
+
integrations,
|
|
1541
|
+
credentialsProvider: githubCredentialsProvider,
|
|
1542
|
+
repoUrl,
|
|
1543
|
+
token: providedToken
|
|
1544
|
+
}));
|
|
1545
|
+
try {
|
|
1546
|
+
const insecure_ssl = insecureSsl ? "1" : "0";
|
|
1547
|
+
await client.rest.repos.createWebhook({
|
|
1548
|
+
owner,
|
|
1549
|
+
repo,
|
|
1550
|
+
config: {
|
|
1551
|
+
url: webhookUrl,
|
|
1552
|
+
content_type: contentType,
|
|
1553
|
+
secret: webhookSecret,
|
|
1554
|
+
insecure_ssl
|
|
1555
|
+
},
|
|
1556
|
+
events,
|
|
1557
|
+
active
|
|
1558
|
+
});
|
|
1559
|
+
ctx.logger.info(`Webhook '${webhookUrl}' created successfully`);
|
|
1560
|
+
} catch (e) {
|
|
1561
|
+
errors.assertError(e);
|
|
1562
|
+
ctx.logger.warn(`Failed: create webhook '${webhookUrl}' on repo: '${repo}', ${e.message}`);
|
|
1387
1563
|
}
|
|
1388
|
-
await initRepoAndPush({
|
|
1389
|
-
dir: getRepoSourceDirectory(ctx.workspacePath, ctx.input.sourcePath),
|
|
1390
|
-
remoteUrl,
|
|
1391
|
-
auth,
|
|
1392
|
-
defaultBranch,
|
|
1393
|
-
logger: ctx.logger,
|
|
1394
|
-
commitMessage: config.getOptionalString("scaffolder.defaultCommitMessage"),
|
|
1395
|
-
gitAuthorInfo
|
|
1396
|
-
});
|
|
1397
|
-
ctx.output("remoteUrl", remoteUrl);
|
|
1398
|
-
ctx.output("repoContentsUrl", repoContentsUrl);
|
|
1399
1564
|
}
|
|
1400
1565
|
});
|
|
1401
1566
|
}
|
|
1402
1567
|
|
|
1403
|
-
|
|
1404
|
-
const {
|
|
1405
|
-
project,
|
|
1406
|
-
repo,
|
|
1407
|
-
description,
|
|
1408
|
-
authorization,
|
|
1409
|
-
repoVisibility,
|
|
1410
|
-
apiBaseUrl
|
|
1411
|
-
} = opts;
|
|
1412
|
-
let response;
|
|
1413
|
-
const options = {
|
|
1414
|
-
method: "POST",
|
|
1415
|
-
body: JSON.stringify({
|
|
1416
|
-
name: repo,
|
|
1417
|
-
description,
|
|
1418
|
-
public: repoVisibility === "public"
|
|
1419
|
-
}),
|
|
1420
|
-
headers: {
|
|
1421
|
-
Authorization: authorization,
|
|
1422
|
-
"Content-Type": "application/json"
|
|
1423
|
-
}
|
|
1424
|
-
};
|
|
1425
|
-
try {
|
|
1426
|
-
response = await fetch__default["default"](`${apiBaseUrl}/projects/${project}/repos`, options);
|
|
1427
|
-
} catch (e) {
|
|
1428
|
-
throw new Error(`Unable to create repository, ${e}`);
|
|
1429
|
-
}
|
|
1430
|
-
if (response.status !== 201) {
|
|
1431
|
-
throw new Error(`Unable to create repository, ${response.status} ${response.statusText}, ${await response.text()}`);
|
|
1432
|
-
}
|
|
1433
|
-
const r = await response.json();
|
|
1434
|
-
let remoteUrl = "";
|
|
1435
|
-
for (const link of r.links.clone) {
|
|
1436
|
-
if (link.name === "http") {
|
|
1437
|
-
remoteUrl = link.href;
|
|
1438
|
-
}
|
|
1439
|
-
}
|
|
1440
|
-
const repoContentsUrl = `${r.links.self[0].href}`;
|
|
1441
|
-
return { remoteUrl, repoContentsUrl };
|
|
1442
|
-
};
|
|
1443
|
-
const getAuthorizationHeader = (config) => {
|
|
1444
|
-
return `Bearer ${config.token}`;
|
|
1445
|
-
};
|
|
1446
|
-
const performEnableLFS = async (opts) => {
|
|
1447
|
-
const { authorization, host, project, repo } = opts;
|
|
1448
|
-
const options = {
|
|
1449
|
-
method: "PUT",
|
|
1450
|
-
headers: {
|
|
1451
|
-
Authorization: authorization
|
|
1452
|
-
}
|
|
1453
|
-
};
|
|
1454
|
-
const { ok, status, statusText } = await fetch__default["default"](`https://${host}/rest/git-lfs/admin/projects/${project}/repos/${repo}/enabled`, options);
|
|
1455
|
-
if (!ok)
|
|
1456
|
-
throw new Error(`Failed to enable LFS in the repository, ${status}: ${statusText}`);
|
|
1457
|
-
};
|
|
1458
|
-
function createPublishBitbucketServerAction(options) {
|
|
1568
|
+
function createPublishAzureAction(options) {
|
|
1459
1569
|
const { integrations, config } = options;
|
|
1460
1570
|
return createTemplateAction({
|
|
1461
|
-
id: "publish:
|
|
1462
|
-
description: "Initializes a git repository of the content in the workspace, and publishes it to
|
|
1571
|
+
id: "publish:azure",
|
|
1572
|
+
description: "Initializes a git repository of the content in the workspace, and publishes it to Azure.",
|
|
1463
1573
|
schema: {
|
|
1464
1574
|
input: {
|
|
1465
1575
|
type: "object",
|
|
@@ -1473,30 +1583,35 @@ function createPublishBitbucketServerAction(options) {
|
|
|
1473
1583
|
title: "Repository Description",
|
|
1474
1584
|
type: "string"
|
|
1475
1585
|
},
|
|
1476
|
-
repoVisibility: {
|
|
1477
|
-
title: "Repository Visibility",
|
|
1478
|
-
type: "string",
|
|
1479
|
-
enum: ["private", "public"]
|
|
1480
|
-
},
|
|
1481
1586
|
defaultBranch: {
|
|
1482
1587
|
title: "Default Branch",
|
|
1483
1588
|
type: "string",
|
|
1484
1589
|
description: `Sets the default branch on the repository. The default value is 'master'`
|
|
1485
1590
|
},
|
|
1591
|
+
gitCommitMessage: {
|
|
1592
|
+
title: "Git Commit Message",
|
|
1593
|
+
type: "string",
|
|
1594
|
+
description: `Sets the commit message on the repository. The default value is 'initial commit'`
|
|
1595
|
+
},
|
|
1596
|
+
gitAuthorName: {
|
|
1597
|
+
title: "Default Author Name",
|
|
1598
|
+
type: "string",
|
|
1599
|
+
description: `Sets the default author name for the commit. The default value is 'Scaffolder'`
|
|
1600
|
+
},
|
|
1601
|
+
gitAuthorEmail: {
|
|
1602
|
+
title: "Default Author Email",
|
|
1603
|
+
type: "string",
|
|
1604
|
+
description: `Sets the default author email for the commit.`
|
|
1605
|
+
},
|
|
1486
1606
|
sourcePath: {
|
|
1487
1607
|
title: "Source Path",
|
|
1488
1608
|
description: "Path within the workspace that will be used as the repository root. If omitted, the entire workspace will be published as the repository.",
|
|
1489
1609
|
type: "string"
|
|
1490
1610
|
},
|
|
1491
|
-
enableLFS: {
|
|
1492
|
-
title: "Enable LFS?",
|
|
1493
|
-
description: "Enable LFS for the repository.",
|
|
1494
|
-
type: "boolean"
|
|
1495
|
-
},
|
|
1496
1611
|
token: {
|
|
1497
1612
|
title: "Authentication Token",
|
|
1498
1613
|
type: "string",
|
|
1499
|
-
description: "The token to use for authorization to
|
|
1614
|
+
description: "The token to use for authorization to Azure"
|
|
1500
1615
|
}
|
|
1501
1616
|
}
|
|
1502
1617
|
},
|
|
@@ -1518,119 +1633,169 @@ function createPublishBitbucketServerAction(options) {
|
|
|
1518
1633
|
var _a;
|
|
1519
1634
|
const {
|
|
1520
1635
|
repoUrl,
|
|
1521
|
-
description,
|
|
1522
1636
|
defaultBranch = "master",
|
|
1523
|
-
|
|
1524
|
-
|
|
1637
|
+
gitCommitMessage = "initial commit",
|
|
1638
|
+
gitAuthorName,
|
|
1639
|
+
gitAuthorEmail
|
|
1525
1640
|
} = ctx.input;
|
|
1526
|
-
const {
|
|
1527
|
-
if (!
|
|
1528
|
-
throw new errors.InputError(`Invalid URL provider was included in the repo URL to create ${ctx.input.repoUrl}, missing
|
|
1641
|
+
const { owner, repo, host, organization } = parseRepoUrl(repoUrl, integrations);
|
|
1642
|
+
if (!organization) {
|
|
1643
|
+
throw new errors.InputError(`Invalid URL provider was included in the repo URL to create ${ctx.input.repoUrl}, missing organization`);
|
|
1529
1644
|
}
|
|
1530
|
-
const integrationConfig = integrations.
|
|
1645
|
+
const integrationConfig = integrations.azure.byHost(host);
|
|
1531
1646
|
if (!integrationConfig) {
|
|
1532
1647
|
throw new errors.InputError(`No matching integration configuration for host ${host}, please check your integrations config`);
|
|
1533
1648
|
}
|
|
1534
|
-
|
|
1535
|
-
|
|
1536
|
-
throw new Error(`Authorization has not been provided for ${integrationConfig.config.host}. Please add either token to the Integrations config or a user login auth token`);
|
|
1537
|
-
}
|
|
1538
|
-
const authorization = getAuthorizationHeader({ token });
|
|
1539
|
-
const apiBaseUrl = integrationConfig.config.apiBaseUrl;
|
|
1540
|
-
const { remoteUrl, repoContentsUrl } = await createRepository({
|
|
1541
|
-
authorization,
|
|
1542
|
-
project,
|
|
1543
|
-
repo,
|
|
1544
|
-
repoVisibility,
|
|
1545
|
-
description,
|
|
1546
|
-
apiBaseUrl
|
|
1547
|
-
});
|
|
1548
|
-
const gitAuthorInfo = {
|
|
1549
|
-
name: config.getOptionalString("scaffolder.defaultAuthor.name"),
|
|
1550
|
-
email: config.getOptionalString("scaffolder.defaultAuthor.email")
|
|
1551
|
-
};
|
|
1552
|
-
const auth = {
|
|
1553
|
-
username: "x-token-auth",
|
|
1554
|
-
password: token
|
|
1555
|
-
};
|
|
1556
|
-
await initRepoAndPush({
|
|
1557
|
-
dir: getRepoSourceDirectory(ctx.workspacePath, ctx.input.sourcePath),
|
|
1558
|
-
remoteUrl,
|
|
1559
|
-
auth,
|
|
1560
|
-
defaultBranch,
|
|
1561
|
-
logger: ctx.logger,
|
|
1562
|
-
commitMessage: config.getOptionalString("scaffolder.defaultCommitMessage"),
|
|
1563
|
-
gitAuthorInfo
|
|
1564
|
-
});
|
|
1565
|
-
if (enableLFS) {
|
|
1566
|
-
await performEnableLFS({ authorization, host, project, repo });
|
|
1649
|
+
if (!integrationConfig.config.token && !ctx.input.token) {
|
|
1650
|
+
throw new errors.InputError(`No token provided for Azure Integration ${host}`);
|
|
1567
1651
|
}
|
|
1568
|
-
ctx.
|
|
1569
|
-
|
|
1570
|
-
|
|
1571
|
-
|
|
1572
|
-
}
|
|
1573
|
-
|
|
1574
|
-
|
|
1575
|
-
|
|
1576
|
-
|
|
1577
|
-
description: "Writes contents of the workspace to a local directory",
|
|
1578
|
-
schema: {
|
|
1579
|
-
input: {
|
|
1580
|
-
type: "object",
|
|
1581
|
-
required: ["path"],
|
|
1582
|
-
properties: {
|
|
1583
|
-
path: {
|
|
1584
|
-
title: "Path to a directory where the output will be written",
|
|
1585
|
-
type: "string"
|
|
1586
|
-
}
|
|
1587
|
-
}
|
|
1652
|
+
const token = (_a = ctx.input.token) != null ? _a : integrationConfig.config.token;
|
|
1653
|
+
const authHandler = azureDevopsNodeApi.getPersonalAccessTokenHandler(token);
|
|
1654
|
+
const webApi = new azureDevopsNodeApi.WebApi(`https://${host}/${organization}`, authHandler);
|
|
1655
|
+
const client = await webApi.getGitApi();
|
|
1656
|
+
const createOptions = { name: repo };
|
|
1657
|
+
const returnedRepo = await client.createRepository(createOptions, owner);
|
|
1658
|
+
if (!returnedRepo) {
|
|
1659
|
+
throw new errors.InputError(`Unable to create the repository with Organization ${organization}, Project ${owner} and Repo ${repo}.
|
|
1660
|
+
Please make sure that both the Org and Project are typed corrected and exist.`);
|
|
1588
1661
|
}
|
|
1589
|
-
|
|
1590
|
-
|
|
1591
|
-
|
|
1592
|
-
const exists = await fs__default["default"].pathExists(path$1);
|
|
1593
|
-
if (exists) {
|
|
1594
|
-
throw new errors.InputError("Output path already exists");
|
|
1662
|
+
const remoteUrl = returnedRepo.remoteUrl;
|
|
1663
|
+
if (!remoteUrl) {
|
|
1664
|
+
throw new errors.InputError("No remote URL returned from create repository for Azure");
|
|
1595
1665
|
}
|
|
1596
|
-
|
|
1597
|
-
|
|
1666
|
+
const repoContentsUrl = remoteUrl;
|
|
1667
|
+
const gitAuthorInfo = {
|
|
1668
|
+
name: gitAuthorName ? gitAuthorName : config.getOptionalString("scaffolder.defaultAuthor.name"),
|
|
1669
|
+
email: gitAuthorEmail ? gitAuthorEmail : config.getOptionalString("scaffolder.defaultAuthor.email")
|
|
1670
|
+
};
|
|
1671
|
+
await initRepoAndPush({
|
|
1672
|
+
dir: getRepoSourceDirectory(ctx.workspacePath, ctx.input.sourcePath),
|
|
1673
|
+
remoteUrl,
|
|
1674
|
+
defaultBranch,
|
|
1675
|
+
auth: {
|
|
1676
|
+
username: "notempty",
|
|
1677
|
+
password: token
|
|
1678
|
+
},
|
|
1679
|
+
logger: ctx.logger,
|
|
1680
|
+
commitMessage: gitCommitMessage ? gitCommitMessage : config.getOptionalString("scaffolder.defaultCommitMessage"),
|
|
1681
|
+
gitAuthorInfo
|
|
1682
|
+
});
|
|
1683
|
+
ctx.output("remoteUrl", remoteUrl);
|
|
1684
|
+
ctx.output("repoContentsUrl", repoContentsUrl);
|
|
1598
1685
|
}
|
|
1599
1686
|
});
|
|
1600
1687
|
}
|
|
1601
1688
|
|
|
1602
|
-
const
|
|
1603
|
-
const {
|
|
1604
|
-
|
|
1605
|
-
|
|
1689
|
+
const createBitbucketCloudRepository = async (opts) => {
|
|
1690
|
+
const {
|
|
1691
|
+
workspace,
|
|
1692
|
+
project,
|
|
1693
|
+
repo,
|
|
1694
|
+
description,
|
|
1695
|
+
repoVisibility,
|
|
1696
|
+
mainBranch,
|
|
1697
|
+
authorization,
|
|
1698
|
+
apiBaseUrl
|
|
1699
|
+
} = opts;
|
|
1700
|
+
const options = {
|
|
1701
|
+
method: "POST",
|
|
1606
1702
|
body: JSON.stringify({
|
|
1607
|
-
|
|
1703
|
+
scm: "git",
|
|
1608
1704
|
description,
|
|
1609
|
-
|
|
1610
|
-
|
|
1705
|
+
is_private: repoVisibility === "private",
|
|
1706
|
+
project: { key: project }
|
|
1611
1707
|
}),
|
|
1612
1708
|
headers: {
|
|
1613
|
-
|
|
1709
|
+
Authorization: authorization,
|
|
1614
1710
|
"Content-Type": "application/json"
|
|
1615
1711
|
}
|
|
1616
1712
|
};
|
|
1617
|
-
|
|
1713
|
+
let response;
|
|
1714
|
+
try {
|
|
1715
|
+
response = await fetch__default["default"](`${apiBaseUrl}/repositories/${workspace}/${repo}`, options);
|
|
1716
|
+
} catch (e) {
|
|
1717
|
+
throw new Error(`Unable to create repository, ${e}`);
|
|
1718
|
+
}
|
|
1719
|
+
if (response.status !== 200) {
|
|
1720
|
+
throw new Error(`Unable to create repository, ${response.status} ${response.statusText}, ${await response.text()}`);
|
|
1721
|
+
}
|
|
1722
|
+
const r = await response.json();
|
|
1723
|
+
let remoteUrl = "";
|
|
1724
|
+
for (const link of r.links.clone) {
|
|
1725
|
+
if (link.name === "https") {
|
|
1726
|
+
remoteUrl = link.href;
|
|
1727
|
+
}
|
|
1728
|
+
}
|
|
1729
|
+
const repoContentsUrl = `${r.links.html.href}/src/${mainBranch}`;
|
|
1730
|
+
return { remoteUrl, repoContentsUrl };
|
|
1731
|
+
};
|
|
1732
|
+
const createBitbucketServerRepository = async (opts) => {
|
|
1733
|
+
const {
|
|
1734
|
+
project,
|
|
1735
|
+
repo,
|
|
1736
|
+
description,
|
|
1737
|
+
authorization,
|
|
1738
|
+
repoVisibility,
|
|
1739
|
+
apiBaseUrl
|
|
1740
|
+
} = opts;
|
|
1741
|
+
let response;
|
|
1742
|
+
const options = {
|
|
1743
|
+
method: "POST",
|
|
1744
|
+
body: JSON.stringify({
|
|
1745
|
+
name: repo,
|
|
1746
|
+
description,
|
|
1747
|
+
public: repoVisibility === "public"
|
|
1748
|
+
}),
|
|
1749
|
+
headers: {
|
|
1750
|
+
Authorization: authorization,
|
|
1751
|
+
"Content-Type": "application/json"
|
|
1752
|
+
}
|
|
1753
|
+
};
|
|
1754
|
+
try {
|
|
1755
|
+
response = await fetch__default["default"](`${apiBaseUrl}/projects/${project}/repos`, options);
|
|
1756
|
+
} catch (e) {
|
|
1757
|
+
throw new Error(`Unable to create repository, ${e}`);
|
|
1758
|
+
}
|
|
1618
1759
|
if (response.status !== 201) {
|
|
1619
1760
|
throw new Error(`Unable to create repository, ${response.status} ${response.statusText}, ${await response.text()}`);
|
|
1620
1761
|
}
|
|
1762
|
+
const r = await response.json();
|
|
1763
|
+
let remoteUrl = "";
|
|
1764
|
+
for (const link of r.links.clone) {
|
|
1765
|
+
if (link.name === "http") {
|
|
1766
|
+
remoteUrl = link.href;
|
|
1767
|
+
}
|
|
1768
|
+
}
|
|
1769
|
+
const repoContentsUrl = `${r.links.self[0].href}`;
|
|
1770
|
+
return { remoteUrl, repoContentsUrl };
|
|
1621
1771
|
};
|
|
1622
|
-
const
|
|
1623
|
-
|
|
1624
|
-
|
|
1625
|
-
|
|
1626
|
-
|
|
1627
|
-
|
|
1772
|
+
const getAuthorizationHeader$2 = (config) => {
|
|
1773
|
+
if (config.username && config.appPassword) {
|
|
1774
|
+
const buffer = Buffer.from(`${config.username}:${config.appPassword}`, "utf8");
|
|
1775
|
+
return `Basic ${buffer.toString("base64")}`;
|
|
1776
|
+
}
|
|
1777
|
+
if (config.token) {
|
|
1778
|
+
return `Bearer ${config.token}`;
|
|
1779
|
+
}
|
|
1780
|
+
throw new Error(`Authorization has not been provided for Bitbucket. Please add either username + appPassword or token to the Integrations config`);
|
|
1628
1781
|
};
|
|
1629
|
-
|
|
1782
|
+
const performEnableLFS$1 = async (opts) => {
|
|
1783
|
+
const { authorization, host, project, repo } = opts;
|
|
1784
|
+
const options = {
|
|
1785
|
+
method: "PUT",
|
|
1786
|
+
headers: {
|
|
1787
|
+
Authorization: authorization
|
|
1788
|
+
}
|
|
1789
|
+
};
|
|
1790
|
+
const { ok, status, statusText } = await fetch__default["default"](`https://${host}/rest/git-lfs/admin/projects/${project}/repos/${repo}/enabled`, options);
|
|
1791
|
+
if (!ok)
|
|
1792
|
+
throw new Error(`Failed to enable LFS in the repository, ${status}: ${statusText}`);
|
|
1793
|
+
};
|
|
1794
|
+
function createPublishBitbucketAction(options) {
|
|
1630
1795
|
const { integrations, config } = options;
|
|
1631
1796
|
return createTemplateAction({
|
|
1632
|
-
id: "publish:
|
|
1633
|
-
description: "Initializes a git repository of the content in the workspace, and publishes it to
|
|
1797
|
+
id: "publish:bitbucket",
|
|
1798
|
+
description: "Initializes a git repository of the content in the workspace, and publishes it to Bitbucket.",
|
|
1634
1799
|
schema: {
|
|
1635
1800
|
input: {
|
|
1636
1801
|
type: "object",
|
|
@@ -1644,11 +1809,31 @@ function createPublishGerritAction(options) {
|
|
|
1644
1809
|
title: "Repository Description",
|
|
1645
1810
|
type: "string"
|
|
1646
1811
|
},
|
|
1812
|
+
repoVisibility: {
|
|
1813
|
+
title: "Repository Visibility",
|
|
1814
|
+
type: "string",
|
|
1815
|
+
enum: ["private", "public"]
|
|
1816
|
+
},
|
|
1647
1817
|
defaultBranch: {
|
|
1648
1818
|
title: "Default Branch",
|
|
1649
1819
|
type: "string",
|
|
1650
1820
|
description: `Sets the default branch on the repository. The default value is 'master'`
|
|
1651
1821
|
},
|
|
1822
|
+
sourcePath: {
|
|
1823
|
+
title: "Source Path",
|
|
1824
|
+
description: "Path within the workspace that will be used as the repository root. If omitted, the entire workspace will be published as the repository.",
|
|
1825
|
+
type: "string"
|
|
1826
|
+
},
|
|
1827
|
+
enableLFS: {
|
|
1828
|
+
title: "Enable LFS?",
|
|
1829
|
+
description: "Enable LFS for the repository. Only available for hosted Bitbucket.",
|
|
1830
|
+
type: "boolean"
|
|
1831
|
+
},
|
|
1832
|
+
token: {
|
|
1833
|
+
title: "Authentication Token",
|
|
1834
|
+
type: "string",
|
|
1835
|
+
description: "The token to use for authorization to BitBucket"
|
|
1836
|
+
},
|
|
1652
1837
|
gitCommitMessage: {
|
|
1653
1838
|
title: "Git Commit Message",
|
|
1654
1839
|
type: "string",
|
|
@@ -1681,98 +1866,140 @@ function createPublishGerritAction(options) {
|
|
|
1681
1866
|
}
|
|
1682
1867
|
},
|
|
1683
1868
|
async handler(ctx) {
|
|
1869
|
+
var _a;
|
|
1870
|
+
ctx.logger.warn(`[Deprecated] Please migrate the use of action "publish:bitbucket" to "publish:bitbucketCloud" or "publish:bitbucketServer".`);
|
|
1684
1871
|
const {
|
|
1685
1872
|
repoUrl,
|
|
1686
1873
|
description,
|
|
1687
1874
|
defaultBranch = "master",
|
|
1875
|
+
repoVisibility = "private",
|
|
1876
|
+
enableLFS = false,
|
|
1877
|
+
gitCommitMessage = "initial commit",
|
|
1688
1878
|
gitAuthorName,
|
|
1689
|
-
gitAuthorEmail
|
|
1690
|
-
gitCommitMessage = "initial commit"
|
|
1879
|
+
gitAuthorEmail
|
|
1691
1880
|
} = ctx.input;
|
|
1692
|
-
const {
|
|
1693
|
-
|
|
1694
|
-
|
|
1695
|
-
|
|
1881
|
+
const { workspace, project, repo, host } = parseRepoUrl(repoUrl, integrations);
|
|
1882
|
+
if (host === "bitbucket.org") {
|
|
1883
|
+
if (!workspace) {
|
|
1884
|
+
throw new errors.InputError(`Invalid URL provider was included in the repo URL to create ${ctx.input.repoUrl}, missing workspace`);
|
|
1885
|
+
}
|
|
1696
1886
|
}
|
|
1697
|
-
if (!
|
|
1698
|
-
throw new errors.InputError(`Invalid URL provider was included in the repo URL to create ${ctx.input.repoUrl}, missing
|
|
1887
|
+
if (!project) {
|
|
1888
|
+
throw new errors.InputError(`Invalid URL provider was included in the repo URL to create ${ctx.input.repoUrl}, missing project`);
|
|
1699
1889
|
}
|
|
1700
|
-
|
|
1701
|
-
|
|
1890
|
+
const integrationConfig = integrations.bitbucket.byHost(host);
|
|
1891
|
+
if (!integrationConfig) {
|
|
1892
|
+
throw new errors.InputError(`No matching integration configuration for host ${host}, please check your integrations config`);
|
|
1702
1893
|
}
|
|
1703
|
-
|
|
1894
|
+
const authorization = getAuthorizationHeader$2(ctx.input.token ? {
|
|
1895
|
+
host: integrationConfig.config.host,
|
|
1896
|
+
apiBaseUrl: integrationConfig.config.apiBaseUrl,
|
|
1897
|
+
token: ctx.input.token
|
|
1898
|
+
} : integrationConfig.config);
|
|
1899
|
+
const apiBaseUrl = integrationConfig.config.apiBaseUrl;
|
|
1900
|
+
const createMethod = host === "bitbucket.org" ? createBitbucketCloudRepository : createBitbucketServerRepository;
|
|
1901
|
+
const { remoteUrl, repoContentsUrl } = await createMethod({
|
|
1902
|
+
authorization,
|
|
1903
|
+
workspace: workspace || "",
|
|
1904
|
+
project,
|
|
1905
|
+
repo,
|
|
1906
|
+
repoVisibility,
|
|
1907
|
+
mainBranch: defaultBranch,
|
|
1704
1908
|
description,
|
|
1705
|
-
|
|
1706
|
-
projectName: repo,
|
|
1707
|
-
parent: workspace
|
|
1909
|
+
apiBaseUrl
|
|
1708
1910
|
});
|
|
1709
|
-
const auth = {
|
|
1710
|
-
username: integrationConfig.config.username,
|
|
1711
|
-
password: integrationConfig.config.password
|
|
1712
|
-
};
|
|
1713
1911
|
const gitAuthorInfo = {
|
|
1714
1912
|
name: gitAuthorName ? gitAuthorName : config.getOptionalString("scaffolder.defaultAuthor.name"),
|
|
1715
1913
|
email: gitAuthorEmail ? gitAuthorEmail : config.getOptionalString("scaffolder.defaultAuthor.email")
|
|
1716
1914
|
};
|
|
1717
|
-
|
|
1915
|
+
let auth;
|
|
1916
|
+
if (ctx.input.token) {
|
|
1917
|
+
auth = {
|
|
1918
|
+
username: "x-token-auth",
|
|
1919
|
+
password: ctx.input.token
|
|
1920
|
+
};
|
|
1921
|
+
} else {
|
|
1922
|
+
auth = {
|
|
1923
|
+
username: integrationConfig.config.username ? integrationConfig.config.username : "x-token-auth",
|
|
1924
|
+
password: integrationConfig.config.appPassword ? integrationConfig.config.appPassword : (_a = integrationConfig.config.token) != null ? _a : ""
|
|
1925
|
+
};
|
|
1926
|
+
}
|
|
1718
1927
|
await initRepoAndPush({
|
|
1719
|
-
dir: getRepoSourceDirectory(ctx.workspacePath,
|
|
1928
|
+
dir: getRepoSourceDirectory(ctx.workspacePath, ctx.input.sourcePath),
|
|
1720
1929
|
remoteUrl,
|
|
1721
1930
|
auth,
|
|
1722
1931
|
defaultBranch,
|
|
1723
1932
|
logger: ctx.logger,
|
|
1724
|
-
commitMessage:
|
|
1933
|
+
commitMessage: gitCommitMessage ? gitCommitMessage : config.getOptionalString("scaffolder.defaultCommitMessage"),
|
|
1725
1934
|
gitAuthorInfo
|
|
1726
1935
|
});
|
|
1727
|
-
|
|
1936
|
+
if (enableLFS && host !== "bitbucket.org") {
|
|
1937
|
+
await performEnableLFS$1({ authorization, host, project, repo });
|
|
1938
|
+
}
|
|
1728
1939
|
ctx.output("remoteUrl", remoteUrl);
|
|
1729
1940
|
ctx.output("repoContentsUrl", repoContentsUrl);
|
|
1730
1941
|
}
|
|
1731
1942
|
});
|
|
1732
1943
|
}
|
|
1733
1944
|
|
|
1734
|
-
const
|
|
1735
|
-
|
|
1736
|
-
|
|
1737
|
-
|
|
1738
|
-
|
|
1739
|
-
|
|
1740
|
-
|
|
1945
|
+
const createRepository$1 = async (opts) => {
|
|
1946
|
+
const {
|
|
1947
|
+
workspace,
|
|
1948
|
+
project,
|
|
1949
|
+
repo,
|
|
1950
|
+
description,
|
|
1951
|
+
repoVisibility,
|
|
1952
|
+
mainBranch,
|
|
1953
|
+
authorization,
|
|
1954
|
+
apiBaseUrl
|
|
1955
|
+
} = opts;
|
|
1956
|
+
const options = {
|
|
1957
|
+
method: "POST",
|
|
1958
|
+
body: JSON.stringify({
|
|
1959
|
+
scm: "git",
|
|
1960
|
+
description,
|
|
1961
|
+
is_private: repoVisibility === "private",
|
|
1962
|
+
project: { key: project }
|
|
1963
|
+
}),
|
|
1964
|
+
headers: {
|
|
1965
|
+
Authorization: authorization,
|
|
1966
|
+
"Content-Type": "application/json"
|
|
1967
|
+
}
|
|
1741
1968
|
};
|
|
1742
|
-
|
|
1743
|
-
|
|
1969
|
+
let response;
|
|
1970
|
+
try {
|
|
1971
|
+
response = await fetch__default["default"](`${apiBaseUrl}/repositories/${workspace}/${repo}`, options);
|
|
1972
|
+
} catch (e) {
|
|
1973
|
+
throw new Error(`Unable to create repository, ${e}`);
|
|
1744
1974
|
}
|
|
1745
|
-
|
|
1746
|
-
|
|
1747
|
-
throw new errors.InputError(`No integration for host ${host}`);
|
|
1975
|
+
if (response.status !== 200) {
|
|
1976
|
+
throw new Error(`Unable to create repository, ${response.status} ${response.statusText}, ${await response.text()}`);
|
|
1748
1977
|
}
|
|
1749
|
-
|
|
1750
|
-
|
|
1751
|
-
|
|
1752
|
-
|
|
1753
|
-
|
|
1754
|
-
|
|
1755
|
-
};
|
|
1978
|
+
const r = await response.json();
|
|
1979
|
+
let remoteUrl = "";
|
|
1980
|
+
for (const link of r.links.clone) {
|
|
1981
|
+
if (link.name === "https") {
|
|
1982
|
+
remoteUrl = link.href;
|
|
1983
|
+
}
|
|
1756
1984
|
}
|
|
1757
|
-
const
|
|
1758
|
-
|
|
1759
|
-
|
|
1760
|
-
|
|
1761
|
-
if (
|
|
1762
|
-
|
|
1985
|
+
const repoContentsUrl = `${r.links.html.href}/src/${mainBranch}`;
|
|
1986
|
+
return { remoteUrl, repoContentsUrl };
|
|
1987
|
+
};
|
|
1988
|
+
const getAuthorizationHeader$1 = (config) => {
|
|
1989
|
+
if (config.username && config.appPassword) {
|
|
1990
|
+
const buffer = Buffer.from(`${config.username}:${config.appPassword}`, "utf8");
|
|
1991
|
+
return `Basic ${buffer.toString("base64")}`;
|
|
1763
1992
|
}
|
|
1764
|
-
|
|
1765
|
-
|
|
1766
|
-
|
|
1767
|
-
|
|
1768
|
-
|
|
1769
|
-
|
|
1770
|
-
|
|
1771
|
-
function createPublishGithubAction(options) {
|
|
1772
|
-
const { integrations, config, githubCredentialsProvider } = options;
|
|
1993
|
+
if (config.token) {
|
|
1994
|
+
return `Bearer ${config.token}`;
|
|
1995
|
+
}
|
|
1996
|
+
throw new Error(`Authorization has not been provided for Bitbucket Cloud. Please add either username + appPassword to the Integrations config or a user login auth token`);
|
|
1997
|
+
};
|
|
1998
|
+
function createPublishBitbucketCloudAction(options) {
|
|
1999
|
+
const { integrations, config } = options;
|
|
1773
2000
|
return createTemplateAction({
|
|
1774
|
-
id: "publish:
|
|
1775
|
-
description: "Initializes a git repository of
|
|
2001
|
+
id: "publish:bitbucketCloud",
|
|
2002
|
+
description: "Initializes a git repository of the content in the workspace, and publishes it to Bitbucket Cloud.",
|
|
1776
2003
|
schema: {
|
|
1777
2004
|
input: {
|
|
1778
2005
|
type: "object",
|
|
@@ -1780,131 +2007,31 @@ function createPublishGithubAction(options) {
|
|
|
1780
2007
|
properties: {
|
|
1781
2008
|
repoUrl: {
|
|
1782
2009
|
title: "Repository Location",
|
|
1783
|
-
description: `Accepts the format 'github.com?repo=reponame&owner=owner' where 'reponame' is the new repository name and 'owner' is an organization or username`,
|
|
1784
2010
|
type: "string"
|
|
1785
2011
|
},
|
|
1786
2012
|
description: {
|
|
1787
2013
|
title: "Repository Description",
|
|
1788
2014
|
type: "string"
|
|
1789
2015
|
},
|
|
1790
|
-
access: {
|
|
1791
|
-
title: "Repository Access",
|
|
1792
|
-
description: `Sets an admin collaborator on the repository. Can either be a user reference different from 'owner' in 'repoUrl' or team reference, eg. 'org/team-name'`,
|
|
1793
|
-
type: "string"
|
|
1794
|
-
},
|
|
1795
|
-
requireCodeOwnerReviews: {
|
|
1796
|
-
title: "Require CODEOWNER Reviews?",
|
|
1797
|
-
description: "Require an approved review in PR including files with a designated Code Owner",
|
|
1798
|
-
type: "boolean"
|
|
1799
|
-
},
|
|
1800
|
-
requiredStatusCheckContexts: {
|
|
1801
|
-
title: "Required Status Check Contexts",
|
|
1802
|
-
description: "The list of status checks to require in order to merge into this branch",
|
|
1803
|
-
type: "array",
|
|
1804
|
-
items: {
|
|
1805
|
-
type: "string"
|
|
1806
|
-
}
|
|
1807
|
-
},
|
|
1808
2016
|
repoVisibility: {
|
|
1809
2017
|
title: "Repository Visibility",
|
|
1810
2018
|
type: "string",
|
|
1811
|
-
enum: ["private", "public"
|
|
2019
|
+
enum: ["private", "public"]
|
|
1812
2020
|
},
|
|
1813
2021
|
defaultBranch: {
|
|
1814
2022
|
title: "Default Branch",
|
|
1815
2023
|
type: "string",
|
|
1816
2024
|
description: `Sets the default branch on the repository. The default value is 'master'`
|
|
1817
2025
|
},
|
|
1818
|
-
protectDefaultBranch: {
|
|
1819
|
-
title: "Protect Default Branch",
|
|
1820
|
-
type: "boolean",
|
|
1821
|
-
description: `Protect the default branch after creating the repository. The default value is 'true'`
|
|
1822
|
-
},
|
|
1823
|
-
deleteBranchOnMerge: {
|
|
1824
|
-
title: "Delete Branch On Merge",
|
|
1825
|
-
type: "boolean",
|
|
1826
|
-
description: `Delete the branch after merging the PR. The default value is 'false'`
|
|
1827
|
-
},
|
|
1828
|
-
gitCommitMessage: {
|
|
1829
|
-
title: "Git Commit Message",
|
|
1830
|
-
type: "string",
|
|
1831
|
-
description: `Sets the commit message on the repository. The default value is 'initial commit'`
|
|
1832
|
-
},
|
|
1833
|
-
gitAuthorName: {
|
|
1834
|
-
title: "Default Author Name",
|
|
1835
|
-
type: "string",
|
|
1836
|
-
description: `Sets the default author name for the commit. The default value is 'Scaffolder'`
|
|
1837
|
-
},
|
|
1838
|
-
gitAuthorEmail: {
|
|
1839
|
-
title: "Default Author Email",
|
|
1840
|
-
type: "string",
|
|
1841
|
-
description: `Sets the default author email for the commit.`
|
|
1842
|
-
},
|
|
1843
|
-
allowMergeCommit: {
|
|
1844
|
-
title: "Allow Merge Commits",
|
|
1845
|
-
type: "boolean",
|
|
1846
|
-
description: `Allow merge commits. The default value is 'true'`
|
|
1847
|
-
},
|
|
1848
|
-
allowSquashMerge: {
|
|
1849
|
-
title: "Allow Squash Merges",
|
|
1850
|
-
type: "boolean",
|
|
1851
|
-
description: `Allow squash merges. The default value is 'true'`
|
|
1852
|
-
},
|
|
1853
|
-
allowRebaseMerge: {
|
|
1854
|
-
title: "Allow Rebase Merges",
|
|
1855
|
-
type: "boolean",
|
|
1856
|
-
description: `Allow rebase merges. The default value is 'true'`
|
|
1857
|
-
},
|
|
1858
2026
|
sourcePath: {
|
|
1859
2027
|
title: "Source Path",
|
|
1860
2028
|
description: "Path within the workspace that will be used as the repository root. If omitted, the entire workspace will be published as the repository.",
|
|
1861
2029
|
type: "string"
|
|
1862
2030
|
},
|
|
1863
|
-
collaborators: {
|
|
1864
|
-
title: "Collaborators",
|
|
1865
|
-
description: "Provide additional users or teams with permissions",
|
|
1866
|
-
type: "array",
|
|
1867
|
-
items: {
|
|
1868
|
-
type: "object",
|
|
1869
|
-
additionalProperties: false,
|
|
1870
|
-
required: ["access"],
|
|
1871
|
-
properties: {
|
|
1872
|
-
access: {
|
|
1873
|
-
type: "string",
|
|
1874
|
-
description: "The type of access for the user",
|
|
1875
|
-
enum: ["push", "pull", "admin", "maintain", "triage"]
|
|
1876
|
-
},
|
|
1877
|
-
user: {
|
|
1878
|
-
type: "string",
|
|
1879
|
-
description: "The name of the user that will be added as a collaborator"
|
|
1880
|
-
},
|
|
1881
|
-
username: {
|
|
1882
|
-
type: "string",
|
|
1883
|
-
description: "Deprecated. Use the `team` or `user` field instead."
|
|
1884
|
-
},
|
|
1885
|
-
team: {
|
|
1886
|
-
type: "string",
|
|
1887
|
-
description: "The name of the team that will be added as a collaborator"
|
|
1888
|
-
}
|
|
1889
|
-
},
|
|
1890
|
-
oneOf: [
|
|
1891
|
-
{ required: ["user"] },
|
|
1892
|
-
{ required: ["username"] },
|
|
1893
|
-
{ required: ["team"] }
|
|
1894
|
-
]
|
|
1895
|
-
}
|
|
1896
|
-
},
|
|
1897
2031
|
token: {
|
|
1898
2032
|
title: "Authentication Token",
|
|
1899
2033
|
type: "string",
|
|
1900
|
-
description: "The token to use for authorization to
|
|
1901
|
-
},
|
|
1902
|
-
topics: {
|
|
1903
|
-
title: "Topics",
|
|
1904
|
-
type: "array",
|
|
1905
|
-
items: {
|
|
1906
|
-
type: "string"
|
|
1907
|
-
}
|
|
2034
|
+
description: "The token to use for authorization to BitBucket Cloud"
|
|
1908
2035
|
}
|
|
1909
2036
|
}
|
|
1910
2037
|
},
|
|
@@ -1926,370 +2053,297 @@ function createPublishGithubAction(options) {
|
|
|
1926
2053
|
const {
|
|
1927
2054
|
repoUrl,
|
|
1928
2055
|
description,
|
|
1929
|
-
access,
|
|
1930
|
-
requireCodeOwnerReviews = false,
|
|
1931
|
-
requiredStatusCheckContexts = [],
|
|
1932
|
-
repoVisibility = "private",
|
|
1933
2056
|
defaultBranch = "master",
|
|
1934
|
-
|
|
1935
|
-
deleteBranchOnMerge = false,
|
|
1936
|
-
gitCommitMessage = "initial commit",
|
|
1937
|
-
gitAuthorName,
|
|
1938
|
-
gitAuthorEmail,
|
|
1939
|
-
allowMergeCommit = true,
|
|
1940
|
-
allowSquashMerge = true,
|
|
1941
|
-
allowRebaseMerge = true,
|
|
1942
|
-
collaborators,
|
|
1943
|
-
topics,
|
|
1944
|
-
token: providedToken
|
|
2057
|
+
repoVisibility = "private"
|
|
1945
2058
|
} = ctx.input;
|
|
1946
|
-
const {
|
|
1947
|
-
if (!
|
|
1948
|
-
throw new errors.InputError(
|
|
1949
|
-
}
|
|
1950
|
-
const octokitOptions = await getOctokitOptions({
|
|
1951
|
-
integrations,
|
|
1952
|
-
credentialsProvider: githubCredentialsProvider,
|
|
1953
|
-
token: providedToken,
|
|
1954
|
-
repoUrl
|
|
1955
|
-
});
|
|
1956
|
-
const client = new octokit.Octokit(octokitOptions);
|
|
1957
|
-
const user = await client.rest.users.getByUsername({
|
|
1958
|
-
username: owner
|
|
1959
|
-
});
|
|
1960
|
-
const repoCreationPromise = user.data.type === "Organization" ? client.rest.repos.createInOrg({
|
|
1961
|
-
name: repo,
|
|
1962
|
-
org: owner,
|
|
1963
|
-
private: repoVisibility === "private",
|
|
1964
|
-
visibility: repoVisibility,
|
|
1965
|
-
description,
|
|
1966
|
-
delete_branch_on_merge: deleteBranchOnMerge,
|
|
1967
|
-
allow_merge_commit: allowMergeCommit,
|
|
1968
|
-
allow_squash_merge: allowSquashMerge,
|
|
1969
|
-
allow_rebase_merge: allowRebaseMerge
|
|
1970
|
-
}) : client.rest.repos.createForAuthenticatedUser({
|
|
1971
|
-
name: repo,
|
|
1972
|
-
private: repoVisibility === "private",
|
|
1973
|
-
description,
|
|
1974
|
-
delete_branch_on_merge: deleteBranchOnMerge,
|
|
1975
|
-
allow_merge_commit: allowMergeCommit,
|
|
1976
|
-
allow_squash_merge: allowSquashMerge,
|
|
1977
|
-
allow_rebase_merge: allowRebaseMerge
|
|
1978
|
-
});
|
|
1979
|
-
let newRepo;
|
|
1980
|
-
try {
|
|
1981
|
-
newRepo = (await repoCreationPromise).data;
|
|
1982
|
-
} catch (e) {
|
|
1983
|
-
errors.assertError(e);
|
|
1984
|
-
if (e.message === "Resource not accessible by integration") {
|
|
1985
|
-
ctx.logger.warn(`The GitHub app or token provided may not have the required permissions to create the ${user.data.type} repository ${owner}/${repo}.`);
|
|
1986
|
-
}
|
|
1987
|
-
throw new Error(`Failed to create the ${user.data.type} repository ${owner}/${repo}, ${e.message}`);
|
|
1988
|
-
}
|
|
1989
|
-
if (access == null ? void 0 : access.startsWith(`${owner}/`)) {
|
|
1990
|
-
const [, team] = access.split("/");
|
|
1991
|
-
await client.rest.teams.addOrUpdateRepoPermissionsInOrg({
|
|
1992
|
-
org: owner,
|
|
1993
|
-
team_slug: team,
|
|
1994
|
-
owner,
|
|
1995
|
-
repo,
|
|
1996
|
-
permission: "admin"
|
|
1997
|
-
});
|
|
1998
|
-
} else if (access && access !== owner) {
|
|
1999
|
-
await client.rest.repos.addCollaborator({
|
|
2000
|
-
owner,
|
|
2001
|
-
repo,
|
|
2002
|
-
username: access,
|
|
2003
|
-
permission: "admin"
|
|
2004
|
-
});
|
|
2059
|
+
const { workspace, project, repo, host } = parseRepoUrl(repoUrl, integrations);
|
|
2060
|
+
if (!workspace) {
|
|
2061
|
+
throw new errors.InputError(`Invalid URL provider was included in the repo URL to create ${ctx.input.repoUrl}, missing workspace`);
|
|
2005
2062
|
}
|
|
2006
|
-
if (
|
|
2007
|
-
|
|
2008
|
-
try {
|
|
2009
|
-
if ("user" in collaborator) {
|
|
2010
|
-
await client.rest.repos.addCollaborator({
|
|
2011
|
-
owner,
|
|
2012
|
-
repo,
|
|
2013
|
-
username: collaborator.user,
|
|
2014
|
-
permission: collaborator.access
|
|
2015
|
-
});
|
|
2016
|
-
} else if ("username" in collaborator) {
|
|
2017
|
-
ctx.logger.warn("The field `username` is deprecated in favor of `team` and will be removed in the future.");
|
|
2018
|
-
await client.rest.teams.addOrUpdateRepoPermissionsInOrg({
|
|
2019
|
-
org: owner,
|
|
2020
|
-
team_slug: collaborator.username,
|
|
2021
|
-
owner,
|
|
2022
|
-
repo,
|
|
2023
|
-
permission: collaborator.access
|
|
2024
|
-
});
|
|
2025
|
-
} else if ("team" in collaborator) {
|
|
2026
|
-
await client.rest.teams.addOrUpdateRepoPermissionsInOrg({
|
|
2027
|
-
org: owner,
|
|
2028
|
-
team_slug: collaborator.team,
|
|
2029
|
-
owner,
|
|
2030
|
-
repo,
|
|
2031
|
-
permission: collaborator.access
|
|
2032
|
-
});
|
|
2033
|
-
}
|
|
2034
|
-
} catch (e) {
|
|
2035
|
-
errors.assertError(e);
|
|
2036
|
-
const name = extractCollaboratorName(collaborator);
|
|
2037
|
-
ctx.logger.warn(`Skipping ${collaborator.access} access for ${name}, ${e.message}`);
|
|
2038
|
-
}
|
|
2039
|
-
}
|
|
2063
|
+
if (!project) {
|
|
2064
|
+
throw new errors.InputError(`Invalid URL provider was included in the repo URL to create ${ctx.input.repoUrl}, missing project`);
|
|
2040
2065
|
}
|
|
2041
|
-
|
|
2042
|
-
|
|
2043
|
-
|
|
2044
|
-
owner,
|
|
2045
|
-
repo,
|
|
2046
|
-
names: topics.map((t) => t.toLowerCase())
|
|
2047
|
-
});
|
|
2048
|
-
} catch (e) {
|
|
2049
|
-
errors.assertError(e);
|
|
2050
|
-
ctx.logger.warn(`Skipping topics ${topics.join(" ")}, ${e.message}`);
|
|
2051
|
-
}
|
|
2066
|
+
const integrationConfig = integrations.bitbucketCloud.byHost(host);
|
|
2067
|
+
if (!integrationConfig) {
|
|
2068
|
+
throw new errors.InputError(`No matching integration configuration for host ${host}, please check your integrations config`);
|
|
2052
2069
|
}
|
|
2053
|
-
const
|
|
2054
|
-
const
|
|
2070
|
+
const authorization = getAuthorizationHeader$1(ctx.input.token ? { token: ctx.input.token } : integrationConfig.config);
|
|
2071
|
+
const apiBaseUrl = integrationConfig.config.apiBaseUrl;
|
|
2072
|
+
const { remoteUrl, repoContentsUrl } = await createRepository$1({
|
|
2073
|
+
authorization,
|
|
2074
|
+
workspace: workspace || "",
|
|
2075
|
+
project,
|
|
2076
|
+
repo,
|
|
2077
|
+
repoVisibility,
|
|
2078
|
+
mainBranch: defaultBranch,
|
|
2079
|
+
description,
|
|
2080
|
+
apiBaseUrl
|
|
2081
|
+
});
|
|
2055
2082
|
const gitAuthorInfo = {
|
|
2056
|
-
name:
|
|
2057
|
-
email:
|
|
2083
|
+
name: config.getOptionalString("scaffolder.defaultAuthor.name"),
|
|
2084
|
+
email: config.getOptionalString("scaffolder.defaultAuthor.email")
|
|
2058
2085
|
};
|
|
2086
|
+
let auth;
|
|
2087
|
+
if (ctx.input.token) {
|
|
2088
|
+
auth = {
|
|
2089
|
+
username: "x-token-auth",
|
|
2090
|
+
password: ctx.input.token
|
|
2091
|
+
};
|
|
2092
|
+
} else {
|
|
2093
|
+
if (!integrationConfig.config.username || !integrationConfig.config.appPassword) {
|
|
2094
|
+
throw new Error("Credentials for Bitbucket Cloud integration required for this action.");
|
|
2095
|
+
}
|
|
2096
|
+
auth = {
|
|
2097
|
+
username: integrationConfig.config.username,
|
|
2098
|
+
password: integrationConfig.config.appPassword
|
|
2099
|
+
};
|
|
2100
|
+
}
|
|
2059
2101
|
await initRepoAndPush({
|
|
2060
2102
|
dir: getRepoSourceDirectory(ctx.workspacePath, ctx.input.sourcePath),
|
|
2061
2103
|
remoteUrl,
|
|
2104
|
+
auth,
|
|
2062
2105
|
defaultBranch,
|
|
2063
|
-
auth: {
|
|
2064
|
-
username: "x-access-token",
|
|
2065
|
-
password: octokitOptions.auth
|
|
2066
|
-
},
|
|
2067
2106
|
logger: ctx.logger,
|
|
2068
|
-
commitMessage:
|
|
2107
|
+
commitMessage: config.getOptionalString("scaffolder.defaultCommitMessage"),
|
|
2069
2108
|
gitAuthorInfo
|
|
2070
2109
|
});
|
|
2071
|
-
if (protectDefaultBranch) {
|
|
2072
|
-
try {
|
|
2073
|
-
await enableBranchProtectionOnDefaultRepoBranch({
|
|
2074
|
-
owner,
|
|
2075
|
-
client,
|
|
2076
|
-
repoName: newRepo.name,
|
|
2077
|
-
logger: ctx.logger,
|
|
2078
|
-
defaultBranch,
|
|
2079
|
-
requireCodeOwnerReviews,
|
|
2080
|
-
requiredStatusCheckContexts
|
|
2081
|
-
});
|
|
2082
|
-
} catch (e) {
|
|
2083
|
-
errors.assertError(e);
|
|
2084
|
-
ctx.logger.warn(`Skipping: default branch protection on '${newRepo.name}', ${e.message}`);
|
|
2085
|
-
}
|
|
2086
|
-
}
|
|
2087
2110
|
ctx.output("remoteUrl", remoteUrl);
|
|
2088
|
-
ctx.output("repoContentsUrl", repoContentsUrl);
|
|
2089
|
-
}
|
|
2090
|
-
});
|
|
2091
|
-
}
|
|
2092
|
-
function extractCollaboratorName(collaborator) {
|
|
2093
|
-
if ("username" in collaborator)
|
|
2094
|
-
return collaborator.username;
|
|
2095
|
-
if ("user" in collaborator)
|
|
2096
|
-
return collaborator.user;
|
|
2097
|
-
return collaborator.team;
|
|
2098
|
-
}
|
|
2099
|
-
|
|
2100
|
-
const DEFAULT_GLOB_PATTERNS = ["./**", "!.git"];
|
|
2101
|
-
const isExecutable = (fileMode) => {
|
|
2102
|
-
if (!fileMode) {
|
|
2103
|
-
return false;
|
|
2104
|
-
}
|
|
2105
|
-
const executeBitMask = 73;
|
|
2106
|
-
const res = fileMode & executeBitMask;
|
|
2107
|
-
return res > 0;
|
|
2108
|
-
};
|
|
2109
|
-
async function serializeDirectoryContents(sourcePath, options) {
|
|
2110
|
-
var _a;
|
|
2111
|
-
const paths = await globby__default["default"]((_a = options == null ? void 0 : options.globPatterns) != null ? _a : DEFAULT_GLOB_PATTERNS, {
|
|
2112
|
-
cwd: sourcePath,
|
|
2113
|
-
dot: true,
|
|
2114
|
-
gitignore: options == null ? void 0 : options.gitignore,
|
|
2115
|
-
followSymbolicLinks: false,
|
|
2116
|
-
objectMode: true,
|
|
2117
|
-
stats: true
|
|
2118
|
-
});
|
|
2119
|
-
const limiter = limiterFactory__default["default"](10);
|
|
2120
|
-
return Promise.all(paths.map(async ({ path: path$1, stats }) => ({
|
|
2121
|
-
path: path$1,
|
|
2122
|
-
content: await limiter(async () => fs__default["default"].readFile(path.join(sourcePath, path$1))),
|
|
2123
|
-
executable: isExecutable(stats == null ? void 0 : stats.mode)
|
|
2124
|
-
})));
|
|
2111
|
+
ctx.output("repoContentsUrl", repoContentsUrl);
|
|
2112
|
+
}
|
|
2113
|
+
});
|
|
2125
2114
|
}
|
|
2126
2115
|
|
|
2127
|
-
async
|
|
2128
|
-
|
|
2129
|
-
|
|
2130
|
-
|
|
2131
|
-
|
|
2116
|
+
const createRepository = async (opts) => {
|
|
2117
|
+
const {
|
|
2118
|
+
project,
|
|
2119
|
+
repo,
|
|
2120
|
+
description,
|
|
2121
|
+
authorization,
|
|
2122
|
+
repoVisibility,
|
|
2123
|
+
apiBaseUrl
|
|
2124
|
+
} = opts;
|
|
2125
|
+
let response;
|
|
2126
|
+
const options = {
|
|
2127
|
+
method: "POST",
|
|
2128
|
+
body: JSON.stringify({
|
|
2129
|
+
name: repo,
|
|
2130
|
+
description,
|
|
2131
|
+
public: repoVisibility === "public"
|
|
2132
|
+
}),
|
|
2133
|
+
headers: {
|
|
2134
|
+
Authorization: authorization,
|
|
2135
|
+
"Content-Type": "application/json"
|
|
2136
|
+
}
|
|
2137
|
+
};
|
|
2138
|
+
try {
|
|
2139
|
+
response = await fetch__default["default"](`${apiBaseUrl}/projects/${project}/repos`, options);
|
|
2140
|
+
} catch (e) {
|
|
2141
|
+
throw new Error(`Unable to create repository, ${e}`);
|
|
2132
2142
|
}
|
|
2133
|
-
|
|
2134
|
-
|
|
2135
|
-
|
|
2136
|
-
|
|
2137
|
-
|
|
2138
|
-
|
|
2139
|
-
|
|
2140
|
-
|
|
2141
|
-
|
|
2142
|
-
|
|
2143
|
-
|
|
2144
|
-
|
|
2145
|
-
const [encodedHost, encodedOwner, encodedRepo] = [host, owner, repo].map(encodeURIComponent);
|
|
2146
|
-
const octokitOptions = await getOctokitOptions({
|
|
2147
|
-
integrations,
|
|
2148
|
-
credentialsProvider: githubCredentialsProvider,
|
|
2149
|
-
repoUrl: `${encodedHost}?owner=${encodedOwner}&repo=${encodedRepo}`,
|
|
2150
|
-
token: providedToken
|
|
2151
|
-
});
|
|
2152
|
-
const OctokitPR = octokit.Octokit.plugin(octokitPluginCreatePullRequest.createPullRequest);
|
|
2153
|
-
return new OctokitPR(octokitOptions);
|
|
2143
|
+
if (response.status !== 201) {
|
|
2144
|
+
throw new Error(`Unable to create repository, ${response.status} ${response.statusText}, ${await response.text()}`);
|
|
2145
|
+
}
|
|
2146
|
+
const r = await response.json();
|
|
2147
|
+
let remoteUrl = "";
|
|
2148
|
+
for (const link of r.links.clone) {
|
|
2149
|
+
if (link.name === "http") {
|
|
2150
|
+
remoteUrl = link.href;
|
|
2151
|
+
}
|
|
2152
|
+
}
|
|
2153
|
+
const repoContentsUrl = `${r.links.self[0].href}`;
|
|
2154
|
+
return { remoteUrl, repoContentsUrl };
|
|
2154
2155
|
};
|
|
2155
|
-
const
|
|
2156
|
-
|
|
2157
|
-
|
|
2158
|
-
|
|
2159
|
-
}
|
|
2156
|
+
const getAuthorizationHeader = (config) => {
|
|
2157
|
+
return `Bearer ${config.token}`;
|
|
2158
|
+
};
|
|
2159
|
+
const performEnableLFS = async (opts) => {
|
|
2160
|
+
const { authorization, host, project, repo } = opts;
|
|
2161
|
+
const options = {
|
|
2162
|
+
method: "PUT",
|
|
2163
|
+
headers: {
|
|
2164
|
+
Authorization: authorization
|
|
2165
|
+
}
|
|
2166
|
+
};
|
|
2167
|
+
const { ok, status, statusText } = await fetch__default["default"](`https://${host}/rest/git-lfs/admin/projects/${project}/repos/${repo}/enabled`, options);
|
|
2168
|
+
if (!ok)
|
|
2169
|
+
throw new Error(`Failed to enable LFS in the repository, ${status}: ${statusText}`);
|
|
2170
|
+
};
|
|
2171
|
+
function createPublishBitbucketServerAction(options) {
|
|
2172
|
+
const { integrations, config } = options;
|
|
2160
2173
|
return createTemplateAction({
|
|
2161
|
-
id: "publish:
|
|
2174
|
+
id: "publish:bitbucketServer",
|
|
2175
|
+
description: "Initializes a git repository of the content in the workspace, and publishes it to Bitbucket Server.",
|
|
2162
2176
|
schema: {
|
|
2163
2177
|
input: {
|
|
2164
|
-
required: ["repoUrl", "title", "description", "branchName"],
|
|
2165
2178
|
type: "object",
|
|
2179
|
+
required: ["repoUrl"],
|
|
2166
2180
|
properties: {
|
|
2167
2181
|
repoUrl: {
|
|
2168
2182
|
title: "Repository Location",
|
|
2169
|
-
description: `Accepts the format 'github.com?repo=reponame&owner=owner' where 'reponame' is the repository name and 'owner' is an organization or username`,
|
|
2170
2183
|
type: "string"
|
|
2171
2184
|
},
|
|
2172
|
-
|
|
2173
|
-
|
|
2174
|
-
|
|
2175
|
-
description: "The name for the branch"
|
|
2185
|
+
description: {
|
|
2186
|
+
title: "Repository Description",
|
|
2187
|
+
type: "string"
|
|
2176
2188
|
},
|
|
2177
|
-
|
|
2189
|
+
repoVisibility: {
|
|
2190
|
+
title: "Repository Visibility",
|
|
2178
2191
|
type: "string",
|
|
2179
|
-
|
|
2180
|
-
description: "The name for the pull request"
|
|
2192
|
+
enum: ["private", "public"]
|
|
2181
2193
|
},
|
|
2182
|
-
|
|
2194
|
+
defaultBranch: {
|
|
2195
|
+
title: "Default Branch",
|
|
2183
2196
|
type: "string",
|
|
2184
|
-
|
|
2185
|
-
description: "The description of the pull request"
|
|
2186
|
-
},
|
|
2187
|
-
draft: {
|
|
2188
|
-
type: "boolean",
|
|
2189
|
-
title: "Create as Draft",
|
|
2190
|
-
description: "Create a draft pull request"
|
|
2197
|
+
description: `Sets the default branch on the repository. The default value is 'master'`
|
|
2191
2198
|
},
|
|
2192
2199
|
sourcePath: {
|
|
2193
|
-
|
|
2194
|
-
|
|
2195
|
-
|
|
2200
|
+
title: "Source Path",
|
|
2201
|
+
description: "Path within the workspace that will be used as the repository root. If omitted, the entire workspace will be published as the repository.",
|
|
2202
|
+
type: "string"
|
|
2196
2203
|
},
|
|
2197
|
-
|
|
2198
|
-
|
|
2199
|
-
|
|
2200
|
-
|
|
2204
|
+
enableLFS: {
|
|
2205
|
+
title: "Enable LFS?",
|
|
2206
|
+
description: "Enable LFS for the repository.",
|
|
2207
|
+
type: "boolean"
|
|
2201
2208
|
},
|
|
2202
2209
|
token: {
|
|
2203
2210
|
title: "Authentication Token",
|
|
2204
2211
|
type: "string",
|
|
2205
|
-
description: "The token to use for authorization to
|
|
2212
|
+
description: "The token to use for authorization to BitBucket Server"
|
|
2206
2213
|
}
|
|
2207
2214
|
}
|
|
2208
2215
|
},
|
|
2209
2216
|
output: {
|
|
2210
|
-
required: ["remoteUrl"],
|
|
2211
2217
|
type: "object",
|
|
2212
2218
|
properties: {
|
|
2213
2219
|
remoteUrl: {
|
|
2214
|
-
|
|
2215
|
-
|
|
2216
|
-
description: "Link to the pull request in Github"
|
|
2220
|
+
title: "A URL to the repository with the provider",
|
|
2221
|
+
type: "string"
|
|
2217
2222
|
},
|
|
2218
|
-
|
|
2219
|
-
|
|
2220
|
-
|
|
2221
|
-
description: "The pull request number"
|
|
2223
|
+
repoContentsUrl: {
|
|
2224
|
+
title: "A URL to the root of the repository",
|
|
2225
|
+
type: "string"
|
|
2222
2226
|
}
|
|
2223
2227
|
}
|
|
2224
2228
|
}
|
|
2225
2229
|
},
|
|
2226
2230
|
async handler(ctx) {
|
|
2231
|
+
var _a;
|
|
2227
2232
|
const {
|
|
2228
2233
|
repoUrl,
|
|
2229
|
-
branchName,
|
|
2230
|
-
title,
|
|
2231
2234
|
description,
|
|
2232
|
-
|
|
2233
|
-
|
|
2234
|
-
|
|
2235
|
-
token: providedToken
|
|
2235
|
+
defaultBranch = "master",
|
|
2236
|
+
repoVisibility = "private",
|
|
2237
|
+
enableLFS = false
|
|
2236
2238
|
} = ctx.input;
|
|
2237
|
-
const {
|
|
2238
|
-
if (!
|
|
2239
|
-
throw new errors.InputError(`
|
|
2239
|
+
const { project, repo, host } = parseRepoUrl(repoUrl, integrations);
|
|
2240
|
+
if (!project) {
|
|
2241
|
+
throw new errors.InputError(`Invalid URL provider was included in the repo URL to create ${ctx.input.repoUrl}, missing project`);
|
|
2240
2242
|
}
|
|
2241
|
-
const
|
|
2242
|
-
|
|
2243
|
-
|
|
2244
|
-
|
|
2245
|
-
|
|
2243
|
+
const integrationConfig = integrations.bitbucketServer.byHost(host);
|
|
2244
|
+
if (!integrationConfig) {
|
|
2245
|
+
throw new errors.InputError(`No matching integration configuration for host ${host}, please check your integrations config`);
|
|
2246
|
+
}
|
|
2247
|
+
const token = (_a = ctx.input.token) != null ? _a : integrationConfig.config.token;
|
|
2248
|
+
if (!token) {
|
|
2249
|
+
throw new Error(`Authorization has not been provided for ${integrationConfig.config.host}. Please add either token to the Integrations config or a user login auth token`);
|
|
2250
|
+
}
|
|
2251
|
+
const authorization = getAuthorizationHeader({ token });
|
|
2252
|
+
const apiBaseUrl = integrationConfig.config.apiBaseUrl;
|
|
2253
|
+
const { remoteUrl, repoContentsUrl } = await createRepository({
|
|
2254
|
+
authorization,
|
|
2255
|
+
project,
|
|
2246
2256
|
repo,
|
|
2247
|
-
|
|
2257
|
+
repoVisibility,
|
|
2258
|
+
description,
|
|
2259
|
+
apiBaseUrl
|
|
2248
2260
|
});
|
|
2249
|
-
const
|
|
2250
|
-
|
|
2251
|
-
|
|
2261
|
+
const gitAuthorInfo = {
|
|
2262
|
+
name: config.getOptionalString("scaffolder.defaultAuthor.name"),
|
|
2263
|
+
email: config.getOptionalString("scaffolder.defaultAuthor.email")
|
|
2264
|
+
};
|
|
2265
|
+
const auth = {
|
|
2266
|
+
username: "x-token-auth",
|
|
2267
|
+
password: token
|
|
2268
|
+
};
|
|
2269
|
+
await initRepoAndPush({
|
|
2270
|
+
dir: getRepoSourceDirectory(ctx.workspacePath, ctx.input.sourcePath),
|
|
2271
|
+
remoteUrl,
|
|
2272
|
+
auth,
|
|
2273
|
+
defaultBranch,
|
|
2274
|
+
logger: ctx.logger,
|
|
2275
|
+
commitMessage: config.getOptionalString("scaffolder.defaultCommitMessage"),
|
|
2276
|
+
gitAuthorInfo
|
|
2252
2277
|
});
|
|
2253
|
-
|
|
2254
|
-
|
|
2255
|
-
|
|
2256
|
-
|
|
2257
|
-
|
|
2258
|
-
|
|
2259
|
-
|
|
2260
|
-
|
|
2261
|
-
|
|
2262
|
-
|
|
2263
|
-
|
|
2264
|
-
|
|
2265
|
-
|
|
2266
|
-
|
|
2267
|
-
|
|
2268
|
-
|
|
2269
|
-
|
|
2270
|
-
|
|
2271
|
-
|
|
2272
|
-
|
|
2273
|
-
|
|
2274
|
-
|
|
2275
|
-
}
|
|
2276
|
-
|
|
2277
|
-
|
|
2278
|
-
|
|
2279
|
-
|
|
2280
|
-
|
|
2281
|
-
|
|
2282
|
-
throw new
|
|
2278
|
+
if (enableLFS) {
|
|
2279
|
+
await performEnableLFS({ authorization, host, project, repo });
|
|
2280
|
+
}
|
|
2281
|
+
ctx.output("remoteUrl", remoteUrl);
|
|
2282
|
+
ctx.output("repoContentsUrl", repoContentsUrl);
|
|
2283
|
+
}
|
|
2284
|
+
});
|
|
2285
|
+
}
|
|
2286
|
+
|
|
2287
|
+
function createPublishFileAction() {
|
|
2288
|
+
return createTemplateAction({
|
|
2289
|
+
id: "publish:file",
|
|
2290
|
+
description: "Writes contents of the workspace to a local directory",
|
|
2291
|
+
schema: {
|
|
2292
|
+
input: {
|
|
2293
|
+
type: "object",
|
|
2294
|
+
required: ["path"],
|
|
2295
|
+
properties: {
|
|
2296
|
+
path: {
|
|
2297
|
+
title: "Path to a directory where the output will be written",
|
|
2298
|
+
type: "string"
|
|
2299
|
+
}
|
|
2300
|
+
}
|
|
2301
|
+
}
|
|
2302
|
+
},
|
|
2303
|
+
async handler(ctx) {
|
|
2304
|
+
const { path: path$1 } = ctx.input;
|
|
2305
|
+
const exists = await fs__default["default"].pathExists(path$1);
|
|
2306
|
+
if (exists) {
|
|
2307
|
+
throw new errors.InputError("Output path already exists");
|
|
2283
2308
|
}
|
|
2309
|
+
await fs__default["default"].ensureDir(path.dirname(path$1));
|
|
2310
|
+
await fs__default["default"].copy(ctx.workspacePath, path$1);
|
|
2284
2311
|
}
|
|
2285
2312
|
});
|
|
2313
|
+
}
|
|
2314
|
+
|
|
2315
|
+
const createGerritProject = async (config, options) => {
|
|
2316
|
+
const { projectName, parent, owner, description } = options;
|
|
2317
|
+
const fetchOptions = {
|
|
2318
|
+
method: "PUT",
|
|
2319
|
+
body: JSON.stringify({
|
|
2320
|
+
parent,
|
|
2321
|
+
description,
|
|
2322
|
+
owners: owner ? [owner] : [],
|
|
2323
|
+
create_empty_commit: false
|
|
2324
|
+
}),
|
|
2325
|
+
headers: {
|
|
2326
|
+
...integration.getGerritRequestOptions(config).headers,
|
|
2327
|
+
"Content-Type": "application/json"
|
|
2328
|
+
}
|
|
2329
|
+
};
|
|
2330
|
+
const response = await fetch__default["default"](`${config.baseUrl}/a/projects/${encodeURIComponent(projectName)}`, fetchOptions);
|
|
2331
|
+
if (response.status !== 201) {
|
|
2332
|
+
throw new Error(`Unable to create repository, ${response.status} ${response.statusText}, ${await response.text()}`);
|
|
2333
|
+
}
|
|
2286
2334
|
};
|
|
2335
|
+
const generateCommitMessage = (config, commitSubject) => {
|
|
2336
|
+
const changeId = crypto__default["default"].randomBytes(20).toString("hex");
|
|
2337
|
+
const msg = `${config.getOptionalString("scaffolder.defaultCommitMessage") || commitSubject}
|
|
2287
2338
|
|
|
2288
|
-
|
|
2339
|
+
Change-Id: I${changeId}`;
|
|
2340
|
+
return msg;
|
|
2341
|
+
};
|
|
2342
|
+
function createPublishGerritAction(options) {
|
|
2289
2343
|
const { integrations, config } = options;
|
|
2290
2344
|
return createTemplateAction({
|
|
2291
|
-
id: "publish:
|
|
2292
|
-
description: "Initializes a git repository of the content in the workspace, and publishes it to
|
|
2345
|
+
id: "publish:gerrit",
|
|
2346
|
+
description: "Initializes a git repository of the content in the workspace, and publishes it to Gerrit.",
|
|
2293
2347
|
schema: {
|
|
2294
2348
|
input: {
|
|
2295
2349
|
type: "object",
|
|
@@ -2299,10 +2353,9 @@ function createPublishGitlabAction(options) {
|
|
|
2299
2353
|
title: "Repository Location",
|
|
2300
2354
|
type: "string"
|
|
2301
2355
|
},
|
|
2302
|
-
|
|
2303
|
-
title: "Repository
|
|
2304
|
-
type: "string"
|
|
2305
|
-
enum: ["private", "public", "internal"]
|
|
2356
|
+
description: {
|
|
2357
|
+
title: "Repository Description",
|
|
2358
|
+
type: "string"
|
|
2306
2359
|
},
|
|
2307
2360
|
defaultBranch: {
|
|
2308
2361
|
title: "Default Branch",
|
|
@@ -2326,13 +2379,8 @@ function createPublishGitlabAction(options) {
|
|
|
2326
2379
|
},
|
|
2327
2380
|
sourcePath: {
|
|
2328
2381
|
title: "Source Path",
|
|
2329
|
-
description: "Path within the workspace that will be used as the repository root. If omitted, the entire workspace will be published as the repository.",
|
|
2330
|
-
type: "string"
|
|
2331
|
-
},
|
|
2332
|
-
token: {
|
|
2333
|
-
title: "Authentication Token",
|
|
2334
2382
|
type: "string",
|
|
2335
|
-
description:
|
|
2383
|
+
description: `Path within the workspace that will be used as the repository root. If omitted, the entire workspace will be published as the repository.`
|
|
2336
2384
|
}
|
|
2337
2385
|
}
|
|
2338
2386
|
},
|
|
@@ -2353,223 +2401,257 @@ function createPublishGitlabAction(options) {
|
|
|
2353
2401
|
async handler(ctx) {
|
|
2354
2402
|
const {
|
|
2355
2403
|
repoUrl,
|
|
2356
|
-
|
|
2404
|
+
description,
|
|
2357
2405
|
defaultBranch = "master",
|
|
2358
|
-
gitCommitMessage = "initial commit",
|
|
2359
2406
|
gitAuthorName,
|
|
2360
|
-
gitAuthorEmail
|
|
2407
|
+
gitAuthorEmail,
|
|
2408
|
+
gitCommitMessage = "initial commit",
|
|
2409
|
+
sourcePath
|
|
2361
2410
|
} = ctx.input;
|
|
2362
|
-
const {
|
|
2363
|
-
|
|
2364
|
-
throw new errors.InputError(`No owner provided for host: ${host}, and repo ${repo}`);
|
|
2365
|
-
}
|
|
2366
|
-
const integrationConfig = integrations.gitlab.byHost(host);
|
|
2411
|
+
const { repo, host, owner, workspace } = parseRepoUrl(repoUrl, integrations);
|
|
2412
|
+
const integrationConfig = integrations.gerrit.byHost(host);
|
|
2367
2413
|
if (!integrationConfig) {
|
|
2368
2414
|
throw new errors.InputError(`No matching integration configuration for host ${host}, please check your integrations config`);
|
|
2369
2415
|
}
|
|
2370
|
-
if (!
|
|
2371
|
-
throw new errors.InputError(`
|
|
2372
|
-
}
|
|
2373
|
-
const token = ctx.input.token || integrationConfig.config.token;
|
|
2374
|
-
const tokenType = ctx.input.token ? "oauthToken" : "token";
|
|
2375
|
-
const client = new node.Gitlab({
|
|
2376
|
-
host: integrationConfig.config.baseUrl,
|
|
2377
|
-
[tokenType]: token
|
|
2378
|
-
});
|
|
2379
|
-
let { id: targetNamespace } = await client.Namespaces.show(owner);
|
|
2380
|
-
if (!targetNamespace) {
|
|
2381
|
-
const { id } = await client.Users.current();
|
|
2382
|
-
targetNamespace = id;
|
|
2416
|
+
if (!workspace) {
|
|
2417
|
+
throw new errors.InputError(`Invalid URL provider was included in the repo URL to create ${ctx.input.repoUrl}, missing workspace`);
|
|
2383
2418
|
}
|
|
2384
|
-
|
|
2385
|
-
|
|
2386
|
-
|
|
2387
|
-
|
|
2419
|
+
await createGerritProject(integrationConfig.config, {
|
|
2420
|
+
description,
|
|
2421
|
+
owner,
|
|
2422
|
+
projectName: repo,
|
|
2423
|
+
parent: workspace
|
|
2388
2424
|
});
|
|
2389
|
-
const
|
|
2390
|
-
|
|
2425
|
+
const auth = {
|
|
2426
|
+
username: integrationConfig.config.username,
|
|
2427
|
+
password: integrationConfig.config.password
|
|
2428
|
+
};
|
|
2391
2429
|
const gitAuthorInfo = {
|
|
2392
2430
|
name: gitAuthorName ? gitAuthorName : config.getOptionalString("scaffolder.defaultAuthor.name"),
|
|
2393
2431
|
email: gitAuthorEmail ? gitAuthorEmail : config.getOptionalString("scaffolder.defaultAuthor.email")
|
|
2394
2432
|
};
|
|
2433
|
+
const remoteUrl = `${integrationConfig.config.cloneUrl}/a/${repo}`;
|
|
2395
2434
|
await initRepoAndPush({
|
|
2396
|
-
dir: getRepoSourceDirectory(ctx.workspacePath,
|
|
2397
|
-
remoteUrl
|
|
2435
|
+
dir: getRepoSourceDirectory(ctx.workspacePath, sourcePath),
|
|
2436
|
+
remoteUrl,
|
|
2437
|
+
auth,
|
|
2398
2438
|
defaultBranch,
|
|
2399
|
-
auth: {
|
|
2400
|
-
username: "oauth2",
|
|
2401
|
-
password: token
|
|
2402
|
-
},
|
|
2403
2439
|
logger: ctx.logger,
|
|
2404
|
-
commitMessage:
|
|
2440
|
+
commitMessage: generateCommitMessage(config, gitCommitMessage),
|
|
2405
2441
|
gitAuthorInfo
|
|
2406
2442
|
});
|
|
2443
|
+
const repoContentsUrl = `${integrationConfig.config.gitilesBaseUrl}/${repo}/+/refs/heads/${defaultBranch}`;
|
|
2407
2444
|
ctx.output("remoteUrl", remoteUrl);
|
|
2408
2445
|
ctx.output("repoContentsUrl", repoContentsUrl);
|
|
2409
2446
|
}
|
|
2410
2447
|
});
|
|
2411
2448
|
}
|
|
2412
2449
|
|
|
2413
|
-
|
|
2414
|
-
const { integrations } = options;
|
|
2450
|
+
function createPublishGithubAction(options) {
|
|
2451
|
+
const { integrations, config, githubCredentialsProvider } = options;
|
|
2415
2452
|
return createTemplateAction({
|
|
2416
|
-
id: "publish:
|
|
2453
|
+
id: "publish:github",
|
|
2454
|
+
description: "Initializes a git repository of contents in workspace and publishes it to GitHub.",
|
|
2417
2455
|
schema: {
|
|
2418
2456
|
input: {
|
|
2419
|
-
required: ["repoUrl", "targetPath", "branchName"],
|
|
2420
2457
|
type: "object",
|
|
2458
|
+
required: ["repoUrl"],
|
|
2421
2459
|
properties: {
|
|
2422
|
-
repoUrl:
|
|
2423
|
-
|
|
2424
|
-
|
|
2425
|
-
|
|
2426
|
-
|
|
2427
|
-
|
|
2428
|
-
|
|
2429
|
-
|
|
2430
|
-
|
|
2431
|
-
|
|
2432
|
-
|
|
2433
|
-
|
|
2434
|
-
|
|
2435
|
-
|
|
2436
|
-
|
|
2437
|
-
|
|
2438
|
-
|
|
2439
|
-
|
|
2440
|
-
|
|
2441
|
-
|
|
2442
|
-
branchName: {
|
|
2443
|
-
type: "string",
|
|
2444
|
-
title: "Destination Branch name",
|
|
2445
|
-
description: "The description of the merge request"
|
|
2446
|
-
},
|
|
2447
|
-
targetPath: {
|
|
2448
|
-
type: "string",
|
|
2449
|
-
title: "Repository Subdirectory",
|
|
2450
|
-
description: "Subdirectory of repository to apply changes to"
|
|
2451
|
-
},
|
|
2452
|
-
token: {
|
|
2453
|
-
title: "Authentication Token",
|
|
2454
|
-
type: "string",
|
|
2455
|
-
description: "The token to use for authorization to GitLab"
|
|
2456
|
-
}
|
|
2460
|
+
repoUrl: repoUrl,
|
|
2461
|
+
description: description,
|
|
2462
|
+
access: access,
|
|
2463
|
+
requireCodeOwnerReviews: requireCodeOwnerReviews,
|
|
2464
|
+
requiredStatusCheckContexts: requiredStatusCheckContexts,
|
|
2465
|
+
repoVisibility: repoVisibility,
|
|
2466
|
+
defaultBranch: defaultBranch,
|
|
2467
|
+
protectDefaultBranch: protectDefaultBranch,
|
|
2468
|
+
protectEnforceAdmins: protectEnforceAdmins,
|
|
2469
|
+
deleteBranchOnMerge: deleteBranchOnMerge,
|
|
2470
|
+
gitCommitMessage: gitCommitMessage,
|
|
2471
|
+
gitAuthorName: gitAuthorName,
|
|
2472
|
+
gitAuthorEmail: gitAuthorEmail,
|
|
2473
|
+
allowMergeCommit: allowMergeCommit,
|
|
2474
|
+
allowSquashMerge: allowSquashMerge,
|
|
2475
|
+
allowRebaseMerge: allowRebaseMerge,
|
|
2476
|
+
sourcePath: sourcePath,
|
|
2477
|
+
collaborators: collaborators,
|
|
2478
|
+
token: token,
|
|
2479
|
+
topics: topics
|
|
2457
2480
|
}
|
|
2458
2481
|
},
|
|
2459
2482
|
output: {
|
|
2460
2483
|
type: "object",
|
|
2461
2484
|
properties: {
|
|
2462
|
-
|
|
2463
|
-
|
|
2464
|
-
type: "string"
|
|
2465
|
-
},
|
|
2466
|
-
projectPath: {
|
|
2467
|
-
title: "Gitlab Project path",
|
|
2468
|
-
type: "string"
|
|
2469
|
-
},
|
|
2470
|
-
mergeRequestURL: {
|
|
2471
|
-
title: "MergeRequest(MR) URL",
|
|
2472
|
-
type: "string",
|
|
2473
|
-
description: "Link to the merge request in GitLab"
|
|
2474
|
-
}
|
|
2485
|
+
remoteUrl: remoteUrl,
|
|
2486
|
+
repoContentsUrl: repoContentsUrl
|
|
2475
2487
|
}
|
|
2476
2488
|
}
|
|
2477
2489
|
},
|
|
2478
2490
|
async handler(ctx) {
|
|
2479
|
-
|
|
2480
|
-
|
|
2481
|
-
|
|
2482
|
-
|
|
2483
|
-
|
|
2484
|
-
|
|
2485
|
-
|
|
2486
|
-
|
|
2487
|
-
|
|
2488
|
-
|
|
2489
|
-
|
|
2490
|
-
|
|
2491
|
-
|
|
2492
|
-
|
|
2493
|
-
|
|
2494
|
-
|
|
2495
|
-
|
|
2496
|
-
|
|
2497
|
-
|
|
2498
|
-
|
|
2499
|
-
|
|
2500
|
-
|
|
2501
|
-
|
|
2502
|
-
|
|
2503
|
-
|
|
2504
|
-
|
|
2491
|
+
const {
|
|
2492
|
+
repoUrl,
|
|
2493
|
+
description,
|
|
2494
|
+
access,
|
|
2495
|
+
requireCodeOwnerReviews = false,
|
|
2496
|
+
requiredStatusCheckContexts = [],
|
|
2497
|
+
repoVisibility = "private",
|
|
2498
|
+
defaultBranch = "master",
|
|
2499
|
+
protectDefaultBranch = true,
|
|
2500
|
+
protectEnforceAdmins = true,
|
|
2501
|
+
deleteBranchOnMerge = false,
|
|
2502
|
+
gitCommitMessage = "initial commit",
|
|
2503
|
+
gitAuthorName,
|
|
2504
|
+
gitAuthorEmail,
|
|
2505
|
+
allowMergeCommit = true,
|
|
2506
|
+
allowSquashMerge = true,
|
|
2507
|
+
allowRebaseMerge = true,
|
|
2508
|
+
collaborators,
|
|
2509
|
+
topics,
|
|
2510
|
+
token: providedToken
|
|
2511
|
+
} = ctx.input;
|
|
2512
|
+
const octokitOptions = await getOctokitOptions({
|
|
2513
|
+
integrations,
|
|
2514
|
+
credentialsProvider: githubCredentialsProvider,
|
|
2515
|
+
token: providedToken,
|
|
2516
|
+
repoUrl
|
|
2505
2517
|
});
|
|
2506
|
-
const
|
|
2507
|
-
|
|
2508
|
-
|
|
2509
|
-
|
|
2510
|
-
content: file.content.toString("base64"),
|
|
2511
|
-
execute_filemode: file.executable
|
|
2512
|
-
}));
|
|
2513
|
-
const projects = await api.Projects.show(projectPath);
|
|
2514
|
-
const { default_branch: defaultBranch } = projects;
|
|
2515
|
-
try {
|
|
2516
|
-
await api.Branches.create(projectPath, destinationBranch, String(defaultBranch));
|
|
2517
|
-
} catch (e) {
|
|
2518
|
-
throw new errors.InputError(`The branch creation failed ${e}`);
|
|
2519
|
-
}
|
|
2520
|
-
try {
|
|
2521
|
-
await api.Commits.create(projectPath, destinationBranch, ctx.input.title, actions);
|
|
2522
|
-
} catch (e) {
|
|
2523
|
-
throw new errors.InputError(`Committing the changes to ${destinationBranch} failed ${e}`);
|
|
2524
|
-
}
|
|
2525
|
-
try {
|
|
2526
|
-
const mergeRequestUrl = await api.MergeRequests.create(projectPath, destinationBranch, String(defaultBranch), ctx.input.title, { description: ctx.input.description }).then((mergeRequest) => {
|
|
2527
|
-
return mergeRequest.web_url;
|
|
2528
|
-
});
|
|
2529
|
-
ctx.output("projectid", projectPath);
|
|
2530
|
-
ctx.output("projectPath", projectPath);
|
|
2531
|
-
ctx.output("mergeRequestUrl", mergeRequestUrl);
|
|
2532
|
-
} catch (e) {
|
|
2533
|
-
throw new errors.InputError(`Merge request creation failed${e}`);
|
|
2518
|
+
const client = new octokit.Octokit(octokitOptions);
|
|
2519
|
+
const { owner, repo } = parseRepoUrl(repoUrl, integrations);
|
|
2520
|
+
if (!owner) {
|
|
2521
|
+
throw new errors.InputError("Invalid repository owner provided in repoUrl");
|
|
2534
2522
|
}
|
|
2523
|
+
const newRepo = await createGithubRepoWithCollaboratorsAndTopics(client, repo, owner, repoVisibility, description, deleteBranchOnMerge, allowMergeCommit, allowSquashMerge, allowRebaseMerge, access, collaborators, topics, ctx.logger);
|
|
2524
|
+
const remoteUrl = newRepo.clone_url;
|
|
2525
|
+
const repoContentsUrl = `${newRepo.html_url}/blob/${defaultBranch}`;
|
|
2526
|
+
await initRepoPushAndProtect(remoteUrl, octokitOptions.auth, ctx.workspacePath, ctx.input.sourcePath, defaultBranch, protectDefaultBranch, protectEnforceAdmins, owner, client, repo, requireCodeOwnerReviews, requiredStatusCheckContexts, config, ctx.logger, gitCommitMessage, gitAuthorName, gitAuthorEmail);
|
|
2527
|
+
ctx.output("remoteUrl", remoteUrl);
|
|
2528
|
+
ctx.output("repoContentsUrl", repoContentsUrl);
|
|
2535
2529
|
}
|
|
2536
2530
|
});
|
|
2531
|
+
}
|
|
2532
|
+
|
|
2533
|
+
const DEFAULT_GLOB_PATTERNS = ["./**", "!.git"];
|
|
2534
|
+
const isExecutable = (fileMode) => {
|
|
2535
|
+
if (!fileMode) {
|
|
2536
|
+
return false;
|
|
2537
|
+
}
|
|
2538
|
+
const executeBitMask = 73;
|
|
2539
|
+
const res = fileMode & executeBitMask;
|
|
2540
|
+
return res > 0;
|
|
2537
2541
|
};
|
|
2542
|
+
async function serializeDirectoryContents(sourcePath, options) {
|
|
2543
|
+
var _a;
|
|
2544
|
+
const paths = await globby__default["default"]((_a = options == null ? void 0 : options.globPatterns) != null ? _a : DEFAULT_GLOB_PATTERNS, {
|
|
2545
|
+
cwd: sourcePath,
|
|
2546
|
+
dot: true,
|
|
2547
|
+
gitignore: options == null ? void 0 : options.gitignore,
|
|
2548
|
+
followSymbolicLinks: false,
|
|
2549
|
+
objectMode: true,
|
|
2550
|
+
stats: true
|
|
2551
|
+
});
|
|
2552
|
+
const limiter = limiterFactory__default["default"](10);
|
|
2553
|
+
return Promise.all(paths.map(async ({ path: path$1, stats }) => ({
|
|
2554
|
+
path: path$1,
|
|
2555
|
+
content: await limiter(async () => fs__default["default"].readFile(path.join(sourcePath, path$1))),
|
|
2556
|
+
executable: isExecutable(stats == null ? void 0 : stats.mode)
|
|
2557
|
+
})));
|
|
2558
|
+
}
|
|
2538
2559
|
|
|
2539
|
-
function
|
|
2540
|
-
const
|
|
2560
|
+
async function deserializeDirectoryContents(targetPath, files) {
|
|
2561
|
+
for (const file of files) {
|
|
2562
|
+
const filePath = backendCommon.resolveSafeChildPath(targetPath, file.path);
|
|
2563
|
+
await fs__default["default"].ensureDir(path.dirname(filePath));
|
|
2564
|
+
await fs__default["default"].writeFile(filePath, file.content);
|
|
2565
|
+
}
|
|
2566
|
+
}
|
|
2567
|
+
|
|
2568
|
+
class GithubResponseError extends errors.CustomErrorBase {
|
|
2569
|
+
}
|
|
2570
|
+
const defaultClientFactory = async ({
|
|
2571
|
+
integrations,
|
|
2572
|
+
githubCredentialsProvider,
|
|
2573
|
+
owner,
|
|
2574
|
+
repo,
|
|
2575
|
+
host = "github.com",
|
|
2576
|
+
token: providedToken
|
|
2577
|
+
}) => {
|
|
2578
|
+
const [encodedHost, encodedOwner, encodedRepo] = [host, owner, repo].map(encodeURIComponent);
|
|
2579
|
+
const octokitOptions = await getOctokitOptions({
|
|
2580
|
+
integrations,
|
|
2581
|
+
credentialsProvider: githubCredentialsProvider,
|
|
2582
|
+
repoUrl: `${encodedHost}?owner=${encodedOwner}&repo=${encodedRepo}`,
|
|
2583
|
+
token: providedToken
|
|
2584
|
+
});
|
|
2585
|
+
const OctokitPR = octokit.Octokit.plugin(octokitPluginCreatePullRequest.createPullRequest);
|
|
2586
|
+
return new OctokitPR(octokitOptions);
|
|
2587
|
+
};
|
|
2588
|
+
const createPublishGithubPullRequestAction = ({
|
|
2589
|
+
integrations,
|
|
2590
|
+
githubCredentialsProvider,
|
|
2591
|
+
clientFactory = defaultClientFactory
|
|
2592
|
+
}) => {
|
|
2541
2593
|
return createTemplateAction({
|
|
2542
|
-
id: "github:
|
|
2543
|
-
description: "Dispatches a GitHub Action workflow for a given branch or tag",
|
|
2594
|
+
id: "publish:github:pull-request",
|
|
2544
2595
|
schema: {
|
|
2545
2596
|
input: {
|
|
2597
|
+
required: ["repoUrl", "title", "description", "branchName"],
|
|
2546
2598
|
type: "object",
|
|
2547
|
-
required: ["repoUrl", "workflowId", "branchOrTagName"],
|
|
2548
2599
|
properties: {
|
|
2549
2600
|
repoUrl: {
|
|
2550
2601
|
title: "Repository Location",
|
|
2551
|
-
description: `Accepts the format 'github.com?repo=reponame&owner=owner' where 'reponame' is the
|
|
2602
|
+
description: `Accepts the format 'github.com?repo=reponame&owner=owner' where 'reponame' is the repository name and 'owner' is an organization or username`,
|
|
2552
2603
|
type: "string"
|
|
2553
2604
|
},
|
|
2554
|
-
|
|
2555
|
-
|
|
2556
|
-
|
|
2557
|
-
|
|
2605
|
+
branchName: {
|
|
2606
|
+
type: "string",
|
|
2607
|
+
title: "Branch Name",
|
|
2608
|
+
description: "The name for the branch"
|
|
2558
2609
|
},
|
|
2559
|
-
|
|
2560
|
-
|
|
2561
|
-
|
|
2562
|
-
|
|
2610
|
+
title: {
|
|
2611
|
+
type: "string",
|
|
2612
|
+
title: "Pull Request Name",
|
|
2613
|
+
description: "The name for the pull request"
|
|
2563
2614
|
},
|
|
2564
|
-
|
|
2565
|
-
|
|
2566
|
-
|
|
2567
|
-
|
|
2615
|
+
description: {
|
|
2616
|
+
type: "string",
|
|
2617
|
+
title: "Pull Request Description",
|
|
2618
|
+
description: "The description of the pull request"
|
|
2619
|
+
},
|
|
2620
|
+
draft: {
|
|
2621
|
+
type: "boolean",
|
|
2622
|
+
title: "Create as Draft",
|
|
2623
|
+
description: "Create a draft pull request"
|
|
2624
|
+
},
|
|
2625
|
+
sourcePath: {
|
|
2626
|
+
type: "string",
|
|
2627
|
+
title: "Working Subdirectory",
|
|
2628
|
+
description: "Subdirectory of working directory to copy changes from"
|
|
2629
|
+
},
|
|
2630
|
+
targetPath: {
|
|
2631
|
+
type: "string",
|
|
2632
|
+
title: "Repository Subdirectory",
|
|
2633
|
+
description: "Subdirectory of repository to apply changes to"
|
|
2568
2634
|
},
|
|
2569
2635
|
token: {
|
|
2570
2636
|
title: "Authentication Token",
|
|
2571
2637
|
type: "string",
|
|
2572
|
-
description: "The
|
|
2638
|
+
description: "The token to use for authorization to GitHub"
|
|
2639
|
+
}
|
|
2640
|
+
}
|
|
2641
|
+
},
|
|
2642
|
+
output: {
|
|
2643
|
+
required: ["remoteUrl"],
|
|
2644
|
+
type: "object",
|
|
2645
|
+
properties: {
|
|
2646
|
+
remoteUrl: {
|
|
2647
|
+
type: "string",
|
|
2648
|
+
title: "Pull Request URL",
|
|
2649
|
+
description: "Link to the pull request in Github"
|
|
2650
|
+
},
|
|
2651
|
+
pullRequestNumber: {
|
|
2652
|
+
type: "number",
|
|
2653
|
+
title: "Pull Request Number",
|
|
2654
|
+
description: "The pull request number"
|
|
2573
2655
|
}
|
|
2574
2656
|
}
|
|
2575
2657
|
}
|
|
@@ -2577,99 +2659,131 @@ function createGithubActionsDispatchAction(options) {
|
|
|
2577
2659
|
async handler(ctx) {
|
|
2578
2660
|
const {
|
|
2579
2661
|
repoUrl,
|
|
2580
|
-
|
|
2581
|
-
|
|
2582
|
-
|
|
2662
|
+
branchName,
|
|
2663
|
+
title,
|
|
2664
|
+
description,
|
|
2665
|
+
draft,
|
|
2666
|
+
targetPath,
|
|
2667
|
+
sourcePath,
|
|
2583
2668
|
token: providedToken
|
|
2584
2669
|
} = ctx.input;
|
|
2585
|
-
|
|
2586
|
-
const { owner, repo } = parseRepoUrl(repoUrl, integrations);
|
|
2670
|
+
const { owner, repo, host } = parseRepoUrl(repoUrl, integrations);
|
|
2587
2671
|
if (!owner) {
|
|
2588
|
-
throw new errors.InputError(
|
|
2672
|
+
throw new errors.InputError(`No owner provided for host: ${host}, and repo ${repo}`);
|
|
2589
2673
|
}
|
|
2590
|
-
const client =
|
|
2674
|
+
const client = await clientFactory({
|
|
2591
2675
|
integrations,
|
|
2592
|
-
|
|
2593
|
-
|
|
2594
|
-
token: providedToken
|
|
2595
|
-
}));
|
|
2596
|
-
await client.rest.actions.createWorkflowDispatch({
|
|
2676
|
+
githubCredentialsProvider,
|
|
2677
|
+
host,
|
|
2597
2678
|
owner,
|
|
2598
2679
|
repo,
|
|
2599
|
-
|
|
2600
|
-
ref: branchOrTagName,
|
|
2601
|
-
inputs: workflowInputs
|
|
2680
|
+
token: providedToken
|
|
2602
2681
|
});
|
|
2603
|
-
ctx.
|
|
2682
|
+
const fileRoot = sourcePath ? backendCommon.resolveSafeChildPath(ctx.workspacePath, sourcePath) : ctx.workspacePath;
|
|
2683
|
+
const directoryContents = await serializeDirectoryContents(fileRoot, {
|
|
2684
|
+
gitignore: true
|
|
2685
|
+
});
|
|
2686
|
+
const files = Object.fromEntries(directoryContents.map((file) => [
|
|
2687
|
+
targetPath ? path__default["default"].posix.join(targetPath, file.path) : file.path,
|
|
2688
|
+
{
|
|
2689
|
+
mode: file.executable ? "100755" : "100644",
|
|
2690
|
+
encoding: "base64",
|
|
2691
|
+
content: file.content.toString("base64")
|
|
2692
|
+
}
|
|
2693
|
+
]));
|
|
2694
|
+
try {
|
|
2695
|
+
const response = await client.createPullRequest({
|
|
2696
|
+
owner,
|
|
2697
|
+
repo,
|
|
2698
|
+
title,
|
|
2699
|
+
changes: [
|
|
2700
|
+
{
|
|
2701
|
+
files,
|
|
2702
|
+
commit: title
|
|
2703
|
+
}
|
|
2704
|
+
],
|
|
2705
|
+
body: description,
|
|
2706
|
+
head: branchName,
|
|
2707
|
+
draft
|
|
2708
|
+
});
|
|
2709
|
+
if (!response) {
|
|
2710
|
+
throw new GithubResponseError("null response from Github");
|
|
2711
|
+
}
|
|
2712
|
+
ctx.output("remoteUrl", response.data.html_url);
|
|
2713
|
+
ctx.output("pullRequestNumber", response.data.number);
|
|
2714
|
+
} catch (e) {
|
|
2715
|
+
throw new GithubResponseError("Pull request creation failed", e);
|
|
2716
|
+
}
|
|
2604
2717
|
}
|
|
2605
2718
|
});
|
|
2606
|
-
}
|
|
2719
|
+
};
|
|
2607
2720
|
|
|
2608
|
-
function
|
|
2609
|
-
const { integrations,
|
|
2610
|
-
const eventNames = webhooks.emitterEventNames.filter((event) => !event.includes("."));
|
|
2721
|
+
function createPublishGitlabAction(options) {
|
|
2722
|
+
const { integrations, config } = options;
|
|
2611
2723
|
return createTemplateAction({
|
|
2612
|
-
id: "
|
|
2613
|
-
description: "
|
|
2724
|
+
id: "publish:gitlab",
|
|
2725
|
+
description: "Initializes a git repository of the content in the workspace, and publishes it to GitLab.",
|
|
2614
2726
|
schema: {
|
|
2615
2727
|
input: {
|
|
2616
2728
|
type: "object",
|
|
2617
|
-
required: ["repoUrl"
|
|
2729
|
+
required: ["repoUrl"],
|
|
2618
2730
|
properties: {
|
|
2619
2731
|
repoUrl: {
|
|
2620
2732
|
title: "Repository Location",
|
|
2621
|
-
description: `Accepts the format 'github.com?repo=reponame&owner=owner' where 'reponame' is the new repository name and 'owner' is an organization or username`,
|
|
2622
2733
|
type: "string"
|
|
2623
2734
|
},
|
|
2624
|
-
|
|
2625
|
-
title: "
|
|
2626
|
-
|
|
2627
|
-
|
|
2735
|
+
repoVisibility: {
|
|
2736
|
+
title: "Repository Visibility",
|
|
2737
|
+
type: "string",
|
|
2738
|
+
enum: ["private", "public", "internal"]
|
|
2628
2739
|
},
|
|
2629
|
-
|
|
2630
|
-
title: "
|
|
2631
|
-
|
|
2632
|
-
|
|
2740
|
+
defaultBranch: {
|
|
2741
|
+
title: "Default Branch",
|
|
2742
|
+
type: "string",
|
|
2743
|
+
description: `Sets the default branch on the repository. The default value is 'master'`
|
|
2633
2744
|
},
|
|
2634
|
-
|
|
2635
|
-
title: "
|
|
2636
|
-
|
|
2637
|
-
|
|
2638
|
-
oneOf: [
|
|
2639
|
-
{
|
|
2640
|
-
items: {
|
|
2641
|
-
type: "string",
|
|
2642
|
-
enum: eventNames
|
|
2643
|
-
}
|
|
2644
|
-
},
|
|
2645
|
-
{
|
|
2646
|
-
items: {
|
|
2647
|
-
type: "string",
|
|
2648
|
-
const: "*"
|
|
2649
|
-
}
|
|
2650
|
-
}
|
|
2651
|
-
]
|
|
2745
|
+
gitCommitMessage: {
|
|
2746
|
+
title: "Git Commit Message",
|
|
2747
|
+
type: "string",
|
|
2748
|
+
description: `Sets the commit message on the repository. The default value is 'initial commit'`
|
|
2652
2749
|
},
|
|
2653
|
-
|
|
2654
|
-
title: "
|
|
2655
|
-
type: "
|
|
2656
|
-
description: `
|
|
2750
|
+
gitAuthorName: {
|
|
2751
|
+
title: "Default Author Name",
|
|
2752
|
+
type: "string",
|
|
2753
|
+
description: `Sets the default author name for the commit. The default value is 'Scaffolder'`
|
|
2657
2754
|
},
|
|
2658
|
-
|
|
2659
|
-
title: "
|
|
2755
|
+
gitAuthorEmail: {
|
|
2756
|
+
title: "Default Author Email",
|
|
2660
2757
|
type: "string",
|
|
2661
|
-
|
|
2662
|
-
description: `The media type used to serialize the payloads. The default is 'form'`
|
|
2758
|
+
description: `Sets the default author email for the commit.`
|
|
2663
2759
|
},
|
|
2664
|
-
|
|
2665
|
-
title: "
|
|
2666
|
-
|
|
2667
|
-
|
|
2760
|
+
sourcePath: {
|
|
2761
|
+
title: "Source Path",
|
|
2762
|
+
description: "Path within the workspace that will be used as the repository root. If omitted, the entire workspace will be published as the repository.",
|
|
2763
|
+
type: "string"
|
|
2668
2764
|
},
|
|
2669
2765
|
token: {
|
|
2670
2766
|
title: "Authentication Token",
|
|
2671
2767
|
type: "string",
|
|
2672
|
-
description: "The
|
|
2768
|
+
description: "The token to use for authorization to GitLab"
|
|
2769
|
+
},
|
|
2770
|
+
setUserAsOwner: {
|
|
2771
|
+
title: "Set User As Owner",
|
|
2772
|
+
type: "boolean",
|
|
2773
|
+
description: "Set the token user as owner of the newly created repository. Requires a token authorized to do the edit in the integration configuration for the matching host"
|
|
2774
|
+
}
|
|
2775
|
+
}
|
|
2776
|
+
},
|
|
2777
|
+
output: {
|
|
2778
|
+
type: "object",
|
|
2779
|
+
properties: {
|
|
2780
|
+
remoteUrl: {
|
|
2781
|
+
title: "A URL to the repository with the provider",
|
|
2782
|
+
type: "string"
|
|
2783
|
+
},
|
|
2784
|
+
repoContentsUrl: {
|
|
2785
|
+
title: "A URL to the root of the repository",
|
|
2786
|
+
type: "string"
|
|
2673
2787
|
}
|
|
2674
2788
|
}
|
|
2675
2789
|
}
|
|
@@ -2677,111 +2791,220 @@ function createGithubWebhookAction(options) {
|
|
|
2677
2791
|
async handler(ctx) {
|
|
2678
2792
|
const {
|
|
2679
2793
|
repoUrl,
|
|
2680
|
-
|
|
2681
|
-
|
|
2682
|
-
|
|
2683
|
-
|
|
2684
|
-
|
|
2685
|
-
|
|
2686
|
-
token: providedToken
|
|
2794
|
+
repoVisibility = "private",
|
|
2795
|
+
defaultBranch = "master",
|
|
2796
|
+
gitCommitMessage = "initial commit",
|
|
2797
|
+
gitAuthorName,
|
|
2798
|
+
gitAuthorEmail,
|
|
2799
|
+
setUserAsOwner = false
|
|
2687
2800
|
} = ctx.input;
|
|
2688
|
-
|
|
2689
|
-
const { owner, repo } = parseRepoUrl(repoUrl, integrations);
|
|
2801
|
+
const { owner, repo, host } = parseRepoUrl(repoUrl, integrations);
|
|
2690
2802
|
if (!owner) {
|
|
2691
|
-
throw new errors.InputError(
|
|
2803
|
+
throw new errors.InputError(`No owner provided for host: ${host}, and repo ${repo}`);
|
|
2692
2804
|
}
|
|
2693
|
-
const
|
|
2694
|
-
|
|
2695
|
-
|
|
2696
|
-
|
|
2697
|
-
|
|
2698
|
-
|
|
2699
|
-
|
|
2700
|
-
|
|
2701
|
-
|
|
2702
|
-
|
|
2703
|
-
|
|
2704
|
-
|
|
2705
|
-
|
|
2706
|
-
|
|
2707
|
-
|
|
2708
|
-
|
|
2709
|
-
|
|
2710
|
-
|
|
2711
|
-
|
|
2805
|
+
const integrationConfig = integrations.gitlab.byHost(host);
|
|
2806
|
+
if (!integrationConfig) {
|
|
2807
|
+
throw new errors.InputError(`No matching integration configuration for host ${host}, please check your integrations config`);
|
|
2808
|
+
}
|
|
2809
|
+
if (!integrationConfig.config.token && !ctx.input.token) {
|
|
2810
|
+
throw new errors.InputError(`No token available for host ${host}`);
|
|
2811
|
+
}
|
|
2812
|
+
const token = ctx.input.token || integrationConfig.config.token;
|
|
2813
|
+
const tokenType = ctx.input.token ? "oauthToken" : "token";
|
|
2814
|
+
const client = new node.Gitlab({
|
|
2815
|
+
host: integrationConfig.config.baseUrl,
|
|
2816
|
+
[tokenType]: token
|
|
2817
|
+
});
|
|
2818
|
+
let { id: targetNamespace } = await client.Namespaces.show(owner);
|
|
2819
|
+
const { id: userId } = await client.Users.current();
|
|
2820
|
+
if (!targetNamespace) {
|
|
2821
|
+
targetNamespace = userId;
|
|
2822
|
+
}
|
|
2823
|
+
const { id: projectId, http_url_to_repo } = await client.Projects.create({
|
|
2824
|
+
namespace_id: targetNamespace,
|
|
2825
|
+
name: repo,
|
|
2826
|
+
visibility: repoVisibility
|
|
2827
|
+
});
|
|
2828
|
+
if (setUserAsOwner && integrationConfig.config.token) {
|
|
2829
|
+
const adminClient = new node.Gitlab({
|
|
2830
|
+
host: integrationConfig.config.baseUrl,
|
|
2831
|
+
token: integrationConfig.config.token
|
|
2712
2832
|
});
|
|
2713
|
-
|
|
2714
|
-
} catch (e) {
|
|
2715
|
-
errors.assertError(e);
|
|
2716
|
-
ctx.logger.warn(`Failed: create webhook '${webhookUrl}' on repo: '${repo}', ${e.message}`);
|
|
2833
|
+
await adminClient.ProjectMembers.add(projectId, userId, 50);
|
|
2717
2834
|
}
|
|
2835
|
+
const remoteUrl = http_url_to_repo.replace(/\.git$/, "");
|
|
2836
|
+
const repoContentsUrl = `${remoteUrl}/-/blob/${defaultBranch}`;
|
|
2837
|
+
const gitAuthorInfo = {
|
|
2838
|
+
name: gitAuthorName ? gitAuthorName : config.getOptionalString("scaffolder.defaultAuthor.name"),
|
|
2839
|
+
email: gitAuthorEmail ? gitAuthorEmail : config.getOptionalString("scaffolder.defaultAuthor.email")
|
|
2840
|
+
};
|
|
2841
|
+
await initRepoAndPush({
|
|
2842
|
+
dir: getRepoSourceDirectory(ctx.workspacePath, ctx.input.sourcePath),
|
|
2843
|
+
remoteUrl: http_url_to_repo,
|
|
2844
|
+
defaultBranch,
|
|
2845
|
+
auth: {
|
|
2846
|
+
username: "oauth2",
|
|
2847
|
+
password: token
|
|
2848
|
+
},
|
|
2849
|
+
logger: ctx.logger,
|
|
2850
|
+
commitMessage: gitCommitMessage ? gitCommitMessage : config.getOptionalString("scaffolder.defaultCommitMessage"),
|
|
2851
|
+
gitAuthorInfo
|
|
2852
|
+
});
|
|
2853
|
+
ctx.output("remoteUrl", remoteUrl);
|
|
2854
|
+
ctx.output("repoContentsUrl", repoContentsUrl);
|
|
2718
2855
|
}
|
|
2719
2856
|
});
|
|
2720
2857
|
}
|
|
2721
2858
|
|
|
2722
|
-
|
|
2723
|
-
const { integrations
|
|
2859
|
+
const createPublishGitlabMergeRequestAction = (options) => {
|
|
2860
|
+
const { integrations } = options;
|
|
2724
2861
|
return createTemplateAction({
|
|
2725
|
-
id: "
|
|
2726
|
-
description: "Adds labels to a pull request or issue on GitHub.",
|
|
2862
|
+
id: "publish:gitlab:merge-request",
|
|
2727
2863
|
schema: {
|
|
2728
2864
|
input: {
|
|
2865
|
+
required: ["repoUrl", "targetPath", "branchName"],
|
|
2729
2866
|
type: "object",
|
|
2730
|
-
required: ["repoUrl", "number", "labels"],
|
|
2731
2867
|
properties: {
|
|
2732
2868
|
repoUrl: {
|
|
2869
|
+
type: "string",
|
|
2733
2870
|
title: "Repository Location",
|
|
2734
|
-
description: `Accepts the format '
|
|
2735
|
-
type: "string"
|
|
2871
|
+
description: `Accepts the format 'gitlab.com/group_name/project_name' where 'project_name' is the repository name and 'group_name' is a group or username`
|
|
2736
2872
|
},
|
|
2737
|
-
|
|
2738
|
-
|
|
2739
|
-
|
|
2740
|
-
|
|
2873
|
+
projectid: {
|
|
2874
|
+
type: "string",
|
|
2875
|
+
title: "projectid",
|
|
2876
|
+
description: "Project ID/Name(slug) of the Gitlab Project"
|
|
2741
2877
|
},
|
|
2742
|
-
|
|
2743
|
-
|
|
2744
|
-
|
|
2745
|
-
|
|
2746
|
-
|
|
2747
|
-
|
|
2748
|
-
|
|
2878
|
+
title: {
|
|
2879
|
+
type: "string",
|
|
2880
|
+
title: "Merge Request Name",
|
|
2881
|
+
description: "The name for the merge request"
|
|
2882
|
+
},
|
|
2883
|
+
description: {
|
|
2884
|
+
type: "string",
|
|
2885
|
+
title: "Merge Request Description",
|
|
2886
|
+
description: "The description of the merge request"
|
|
2887
|
+
},
|
|
2888
|
+
branchName: {
|
|
2889
|
+
type: "string",
|
|
2890
|
+
title: "Destination Branch name",
|
|
2891
|
+
description: "The description of the merge request"
|
|
2892
|
+
},
|
|
2893
|
+
targetPath: {
|
|
2894
|
+
type: "string",
|
|
2895
|
+
title: "Repository Subdirectory",
|
|
2896
|
+
description: "Subdirectory of repository to apply changes to"
|
|
2749
2897
|
},
|
|
2750
2898
|
token: {
|
|
2751
2899
|
title: "Authentication Token",
|
|
2752
2900
|
type: "string",
|
|
2753
|
-
description: "The
|
|
2901
|
+
description: "The token to use for authorization to GitLab"
|
|
2902
|
+
},
|
|
2903
|
+
removeSourceBranch: {
|
|
2904
|
+
title: "Delete source branch",
|
|
2905
|
+
type: "boolean",
|
|
2906
|
+
description: "Option to delete source branch once the MR has been merged. Default: false"
|
|
2907
|
+
},
|
|
2908
|
+
assignee: {
|
|
2909
|
+
title: "Merge Request Assignee",
|
|
2910
|
+
type: "string",
|
|
2911
|
+
description: "User this merge request will be assigned to"
|
|
2912
|
+
}
|
|
2913
|
+
}
|
|
2914
|
+
},
|
|
2915
|
+
output: {
|
|
2916
|
+
type: "object",
|
|
2917
|
+
properties: {
|
|
2918
|
+
projectid: {
|
|
2919
|
+
title: "Gitlab Project id/Name(slug)",
|
|
2920
|
+
type: "string"
|
|
2921
|
+
},
|
|
2922
|
+
projectPath: {
|
|
2923
|
+
title: "Gitlab Project path",
|
|
2924
|
+
type: "string"
|
|
2925
|
+
},
|
|
2926
|
+
mergeRequestURL: {
|
|
2927
|
+
title: "MergeRequest(MR) URL",
|
|
2928
|
+
type: "string",
|
|
2929
|
+
description: "Link to the merge request in GitLab"
|
|
2754
2930
|
}
|
|
2755
2931
|
}
|
|
2756
2932
|
}
|
|
2757
2933
|
},
|
|
2758
2934
|
async handler(ctx) {
|
|
2759
|
-
|
|
2760
|
-
const
|
|
2761
|
-
|
|
2762
|
-
|
|
2763
|
-
|
|
2935
|
+
var _a;
|
|
2936
|
+
const repoUrl = ctx.input.repoUrl;
|
|
2937
|
+
const { host, owner, repo } = parseRepoUrl(repoUrl, integrations);
|
|
2938
|
+
const projectPath = `${owner}/${repo}`;
|
|
2939
|
+
if (ctx.input.projectid) {
|
|
2940
|
+
const deprecationWarning = `Property "projectid" is deprecated and no longer to needed to create a MR`;
|
|
2941
|
+
ctx.logger.warn(deprecationWarning);
|
|
2942
|
+
console.warn(deprecationWarning);
|
|
2764
2943
|
}
|
|
2765
|
-
const
|
|
2766
|
-
|
|
2767
|
-
|
|
2768
|
-
|
|
2769
|
-
|
|
2944
|
+
const integrationConfig = integrations.gitlab.byHost(host);
|
|
2945
|
+
const destinationBranch = ctx.input.branchName;
|
|
2946
|
+
if (!integrationConfig) {
|
|
2947
|
+
throw new errors.InputError(`No matching integration configuration for host ${host}, please check your integrations config`);
|
|
2948
|
+
}
|
|
2949
|
+
if (!integrationConfig.config.token && !ctx.input.token) {
|
|
2950
|
+
throw new errors.InputError(`No token available for host ${host}`);
|
|
2951
|
+
}
|
|
2952
|
+
const token = (_a = ctx.input.token) != null ? _a : integrationConfig.config.token;
|
|
2953
|
+
const tokenType = ctx.input.token ? "oauthToken" : "token";
|
|
2954
|
+
const api = new node.Gitlab({
|
|
2955
|
+
host: integrationConfig.config.baseUrl,
|
|
2956
|
+
[tokenType]: token
|
|
2957
|
+
});
|
|
2958
|
+
const assignee = ctx.input.assignee;
|
|
2959
|
+
let assigneeId = void 0;
|
|
2960
|
+
if (assignee !== void 0) {
|
|
2961
|
+
try {
|
|
2962
|
+
const assigneeUser = await api.Users.username(assignee);
|
|
2963
|
+
assigneeId = assigneeUser[0].id;
|
|
2964
|
+
} catch (e) {
|
|
2965
|
+
ctx.logger.warn(`Failed to find gitlab user id for ${assignee}: ${e}. Proceeding with MR creation without an assignee.`);
|
|
2966
|
+
}
|
|
2967
|
+
}
|
|
2968
|
+
const targetPath = backendCommon.resolveSafeChildPath(ctx.workspacePath, ctx.input.targetPath);
|
|
2969
|
+
const fileContents = await serializeDirectoryContents(targetPath, {
|
|
2970
|
+
gitignore: true
|
|
2971
|
+
});
|
|
2972
|
+
const actions = fileContents.map((file) => ({
|
|
2973
|
+
action: "create",
|
|
2974
|
+
filePath: path__default["default"].posix.join(ctx.input.targetPath, file.path),
|
|
2975
|
+
encoding: "base64",
|
|
2976
|
+
content: file.content.toString("base64"),
|
|
2977
|
+
execute_filemode: file.executable
|
|
2770
2978
|
}));
|
|
2979
|
+
const projects = await api.Projects.show(projectPath);
|
|
2980
|
+
const { default_branch: defaultBranch } = projects;
|
|
2771
2981
|
try {
|
|
2772
|
-
await
|
|
2773
|
-
|
|
2774
|
-
|
|
2775
|
-
|
|
2776
|
-
|
|
2982
|
+
await api.Branches.create(projectPath, destinationBranch, String(defaultBranch));
|
|
2983
|
+
} catch (e) {
|
|
2984
|
+
throw new errors.InputError(`The branch creation failed ${e}`);
|
|
2985
|
+
}
|
|
2986
|
+
try {
|
|
2987
|
+
await api.Commits.create(projectPath, destinationBranch, ctx.input.title, actions);
|
|
2988
|
+
} catch (e) {
|
|
2989
|
+
throw new errors.InputError(`Committing the changes to ${destinationBranch} failed ${e}`);
|
|
2990
|
+
}
|
|
2991
|
+
try {
|
|
2992
|
+
const mergeRequestUrl = await api.MergeRequests.create(projectPath, destinationBranch, String(defaultBranch), ctx.input.title, {
|
|
2993
|
+
description: ctx.input.description,
|
|
2994
|
+
removeSourceBranch: ctx.input.removeSourceBranch ? ctx.input.removeSourceBranch : false,
|
|
2995
|
+
assigneeId
|
|
2996
|
+
}).then((mergeRequest) => {
|
|
2997
|
+
return mergeRequest.web_url;
|
|
2777
2998
|
});
|
|
2999
|
+
ctx.output("projectid", projectPath);
|
|
3000
|
+
ctx.output("projectPath", projectPath);
|
|
3001
|
+
ctx.output("mergeRequestUrl", mergeRequestUrl);
|
|
2778
3002
|
} catch (e) {
|
|
2779
|
-
errors.
|
|
2780
|
-
ctx.logger.warn(`Failed: adding labels to issue: '${number}' on repo: '${repo}', ${e.message}`);
|
|
3003
|
+
throw new errors.InputError(`Merge request creation failed${e}`);
|
|
2781
3004
|
}
|
|
2782
3005
|
}
|
|
2783
3006
|
});
|
|
2784
|
-
}
|
|
3007
|
+
};
|
|
2785
3008
|
|
|
2786
3009
|
const createBuiltinActions = (options) => {
|
|
2787
3010
|
const {
|
|
@@ -2854,6 +3077,15 @@ const createBuiltinActions = (options) => {
|
|
|
2854
3077
|
createGithubIssuesLabelAction({
|
|
2855
3078
|
integrations,
|
|
2856
3079
|
githubCredentialsProvider
|
|
3080
|
+
}),
|
|
3081
|
+
createGithubRepoCreateAction({
|
|
3082
|
+
integrations,
|
|
3083
|
+
githubCredentialsProvider
|
|
3084
|
+
}),
|
|
3085
|
+
createGithubRepoPushAction({
|
|
3086
|
+
integrations,
|
|
3087
|
+
config,
|
|
3088
|
+
githubCredentialsProvider
|
|
2857
3089
|
})
|
|
2858
3090
|
];
|
|
2859
3091
|
return actions;
|
|
@@ -3709,6 +3941,11 @@ async function createRouter(options) {
|
|
|
3709
3941
|
});
|
|
3710
3942
|
const { token, entityRef: userEntityRef } = parseBearerToken(req.headers.authorization);
|
|
3711
3943
|
const userEntity = userEntityRef ? await catalogClient.getEntityByRef(userEntityRef, { token }) : void 0;
|
|
3944
|
+
let auditLog = `Scaffolding task for ${templateRef}`;
|
|
3945
|
+
if (userEntityRef) {
|
|
3946
|
+
auditLog += ` created by ${userEntityRef}`;
|
|
3947
|
+
}
|
|
3948
|
+
logger.info(auditLog);
|
|
3712
3949
|
const values = req.body.values;
|
|
3713
3950
|
const template = await findTemplate({
|
|
3714
3951
|
catalogApi: catalogClient,
|
|
@@ -3970,6 +4207,21 @@ class ScaffolderEntitiesProcessor {
|
|
|
3970
4207
|
}
|
|
3971
4208
|
}
|
|
3972
4209
|
|
|
4210
|
+
const scaffolderCatalogModule = backendPluginApi.createBackendModule({
|
|
4211
|
+
moduleId: "scaffolder.module",
|
|
4212
|
+
pluginId: "catalog",
|
|
4213
|
+
register(env) {
|
|
4214
|
+
env.registerInit({
|
|
4215
|
+
deps: {
|
|
4216
|
+
catalogProcessingExtensionPoint: pluginCatalogNode.catalogProcessingExtentionPoint
|
|
4217
|
+
},
|
|
4218
|
+
async init({ catalogProcessingExtensionPoint }) {
|
|
4219
|
+
catalogProcessingExtensionPoint.addProcessor(new ScaffolderEntitiesProcessor());
|
|
4220
|
+
}
|
|
4221
|
+
});
|
|
4222
|
+
}
|
|
4223
|
+
});
|
|
4224
|
+
|
|
3973
4225
|
exports.DatabaseTaskStore = DatabaseTaskStore;
|
|
3974
4226
|
exports.ScaffolderEntitiesProcessor = ScaffolderEntitiesProcessor;
|
|
3975
4227
|
exports.TaskManager = TaskManager;
|
|
@@ -3985,6 +4237,8 @@ exports.createFilesystemDeleteAction = createFilesystemDeleteAction;
|
|
|
3985
4237
|
exports.createFilesystemRenameAction = createFilesystemRenameAction;
|
|
3986
4238
|
exports.createGithubActionsDispatchAction = createGithubActionsDispatchAction;
|
|
3987
4239
|
exports.createGithubIssuesLabelAction = createGithubIssuesLabelAction;
|
|
4240
|
+
exports.createGithubRepoCreateAction = createGithubRepoCreateAction;
|
|
4241
|
+
exports.createGithubRepoPushAction = createGithubRepoPushAction;
|
|
3988
4242
|
exports.createGithubWebhookAction = createGithubWebhookAction;
|
|
3989
4243
|
exports.createPublishAzureAction = createPublishAzureAction;
|
|
3990
4244
|
exports.createPublishBitbucketAction = createPublishBitbucketAction;
|
|
@@ -4000,4 +4254,5 @@ exports.createRouter = createRouter;
|
|
|
4000
4254
|
exports.createTemplateAction = createTemplateAction;
|
|
4001
4255
|
exports.executeShellCommand = executeShellCommand;
|
|
4002
4256
|
exports.fetchContents = fetchContents;
|
|
4257
|
+
exports.scaffolderCatalogModule = scaffolderCatalogModule;
|
|
4003
4258
|
//# sourceMappingURL=index.cjs.js.map
|