@backstage/integration 1.2.2-next.3 → 1.3.0-next.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +48 -0
- package/config.d.ts +10 -0
- package/dist/index.cjs.js +250 -106
- package/dist/index.cjs.js.map +1 -1
- package/dist/index.d.ts +26 -2
- package/dist/index.esm.js +250 -107
- package/dist/index.esm.js.map +1 -1
- package/package.json +9 -9
package/dist/index.cjs.js
CHANGED
|
@@ -8,7 +8,6 @@ var fetch = require('cross-fetch');
|
|
|
8
8
|
var authApp = require('@octokit/auth-app');
|
|
9
9
|
var rest = require('@octokit/rest');
|
|
10
10
|
var luxon = require('luxon');
|
|
11
|
-
var errors = require('@backstage/errors');
|
|
12
11
|
|
|
13
12
|
function _interopDefaultLegacy (e) { return e && typeof e === 'object' && 'default' in e ? e : { 'default': e }; }
|
|
14
13
|
|
|
@@ -57,7 +56,10 @@ function defaultScmResolveUrl(options) {
|
|
|
57
56
|
if (url.startsWith("/")) {
|
|
58
57
|
const { filepath } = parseGitUrl__default["default"](base);
|
|
59
58
|
updated = new URL(base);
|
|
60
|
-
const repoRootPath = lodash.trimEnd(
|
|
59
|
+
const repoRootPath = lodash.trimEnd(
|
|
60
|
+
updated.pathname.substring(0, updated.pathname.length - filepath.length),
|
|
61
|
+
"/"
|
|
62
|
+
);
|
|
61
63
|
updated.pathname = `${repoRootPath}${url}`;
|
|
62
64
|
} else {
|
|
63
65
|
updated = new URL(url, base);
|
|
@@ -82,10 +84,14 @@ function readAwsS3IntegrationConfig(config) {
|
|
|
82
84
|
host = url.host;
|
|
83
85
|
pathname = url.pathname;
|
|
84
86
|
} catch {
|
|
85
|
-
throw new Error(
|
|
87
|
+
throw new Error(
|
|
88
|
+
`invalid awsS3 integration config, endpoint '${endpoint}' is not a valid URL`
|
|
89
|
+
);
|
|
86
90
|
}
|
|
87
91
|
if (pathname !== "/") {
|
|
88
|
-
throw new Error(
|
|
92
|
+
throw new Error(
|
|
93
|
+
`invalid awsS3 integration config, endpoints cannot contain path, got '${endpoint}'`
|
|
94
|
+
);
|
|
89
95
|
}
|
|
90
96
|
} else {
|
|
91
97
|
host = AMAZON_AWS_HOST;
|
|
@@ -138,8 +144,13 @@ const _AwsS3Integration = class {
|
|
|
138
144
|
let AwsS3Integration = _AwsS3Integration;
|
|
139
145
|
AwsS3Integration.factory = ({ config }) => {
|
|
140
146
|
var _a;
|
|
141
|
-
const configs = readAwsS3IntegrationConfigs(
|
|
142
|
-
|
|
147
|
+
const configs = readAwsS3IntegrationConfigs(
|
|
148
|
+
(_a = config.getOptionalConfigArray("integrations.awsS3")) != null ? _a : []
|
|
149
|
+
);
|
|
150
|
+
return basicIntegrations(
|
|
151
|
+
configs.map((c) => new _AwsS3Integration(c)),
|
|
152
|
+
(i) => i.config.host
|
|
153
|
+
);
|
|
143
154
|
};
|
|
144
155
|
|
|
145
156
|
var __accessCheck = (obj, member, msg) => {
|
|
@@ -233,7 +244,9 @@ const _AzureUrl = class {
|
|
|
233
244
|
}
|
|
234
245
|
toFileUrl() {
|
|
235
246
|
if (!__privateGet(this, _path)) {
|
|
236
|
-
throw new Error(
|
|
247
|
+
throw new Error(
|
|
248
|
+
"Azure URL must point to a specific path to be able to download a file"
|
|
249
|
+
);
|
|
237
250
|
}
|
|
238
251
|
const url = __privateGet(this, _baseUrl).call(this, __privateGet(this, _owner), __privateGet(this, _project), "_apis", "git", "repositories", __privateGet(this, _repo), "items");
|
|
239
252
|
url.searchParams.set("api-version", "6.0");
|
|
@@ -295,7 +308,9 @@ function readAzureIntegrationConfig(config) {
|
|
|
295
308
|
const host = (_a = config.getOptionalString("host")) != null ? _a : AZURE_HOST;
|
|
296
309
|
const token = config.getOptionalString("token");
|
|
297
310
|
if (!isValidHost(host)) {
|
|
298
|
-
throw new Error(
|
|
311
|
+
throw new Error(
|
|
312
|
+
`Invalid Azure integration config, '${host}' is not a valid host`
|
|
313
|
+
);
|
|
299
314
|
}
|
|
300
315
|
return { host, token };
|
|
301
316
|
}
|
|
@@ -350,8 +365,13 @@ const _AzureIntegration = class {
|
|
|
350
365
|
let AzureIntegration = _AzureIntegration;
|
|
351
366
|
AzureIntegration.factory = ({ config }) => {
|
|
352
367
|
var _a;
|
|
353
|
-
const configs = readAzureIntegrationConfigs(
|
|
354
|
-
|
|
368
|
+
const configs = readAzureIntegrationConfigs(
|
|
369
|
+
(_a = config.getOptionalConfigArray("integrations.azure")) != null ? _a : []
|
|
370
|
+
);
|
|
371
|
+
return basicIntegrations(
|
|
372
|
+
configs.map((c) => new _AzureIntegration(c)),
|
|
373
|
+
(i) => i.config.host
|
|
374
|
+
);
|
|
355
375
|
};
|
|
356
376
|
|
|
357
377
|
function getAzureFileFetchUrl(url) {
|
|
@@ -382,7 +402,9 @@ function readBitbucketIntegrationConfig(config) {
|
|
|
382
402
|
const username = config.getOptionalString("username");
|
|
383
403
|
const appPassword = config.getOptionalString("appPassword");
|
|
384
404
|
if (!isValidHost(host)) {
|
|
385
|
-
throw new Error(
|
|
405
|
+
throw new Error(
|
|
406
|
+
`Invalid Bitbucket integration config, '${host}' is not a valid host`
|
|
407
|
+
);
|
|
386
408
|
}
|
|
387
409
|
if (apiBaseUrl) {
|
|
388
410
|
apiBaseUrl = lodash.trimEnd(apiBaseUrl, "/");
|
|
@@ -450,11 +472,16 @@ BitbucketIntegration.factory = ({
|
|
|
450
472
|
config
|
|
451
473
|
}) => {
|
|
452
474
|
var _a, _b, _c;
|
|
453
|
-
const configs = readBitbucketIntegrationConfigs(
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
|
|
457
|
-
|
|
475
|
+
const configs = readBitbucketIntegrationConfigs(
|
|
476
|
+
(_c = config.getOptionalConfigArray("integrations.bitbucket")) != null ? _c : [
|
|
477
|
+
...(_a = config.getOptionalConfigArray("integrations.bitbucketCloud")) != null ? _a : [],
|
|
478
|
+
...(_b = config.getOptionalConfigArray("integrations.bitbucketServer")) != null ? _b : []
|
|
479
|
+
]
|
|
480
|
+
);
|
|
481
|
+
return basicIntegrations(
|
|
482
|
+
configs.map((c) => new _BitbucketIntegration(c)),
|
|
483
|
+
(i) => i.config.host
|
|
484
|
+
);
|
|
458
485
|
};
|
|
459
486
|
|
|
460
487
|
async function getBitbucketDefaultBranch(url, config) {
|
|
@@ -479,7 +506,9 @@ async function getBitbucketDefaultBranch(url, config) {
|
|
|
479
506
|
defaultBranch = displayId;
|
|
480
507
|
}
|
|
481
508
|
if (!defaultBranch) {
|
|
482
|
-
throw new Error(
|
|
509
|
+
throw new Error(
|
|
510
|
+
`Failed to read default branch from ${branchUrl}. Response ${response.status} ${response.json()}`
|
|
511
|
+
);
|
|
483
512
|
}
|
|
484
513
|
return defaultBranch;
|
|
485
514
|
}
|
|
@@ -497,7 +526,7 @@ async function getBitbucketDownloadUrl(url, config) {
|
|
|
497
526
|
if (!branch) {
|
|
498
527
|
branch = await getBitbucketDefaultBranch(url, config);
|
|
499
528
|
}
|
|
500
|
-
const path = filepath ? `&path=${encodeURIComponent(filepath)}` : "";
|
|
529
|
+
const path = filepath ? `&path=${encodeURIComponent(decodeURIComponent(filepath))}` : "";
|
|
501
530
|
const archiveUrl = isHosted ? `${protocol}://${resource}/${project}/${repoName}/get/${branch}.tar.gz` : `${config.apiBaseUrl}/projects/${project}/repos/${repoName}/archive?format=tgz&at=${branch}&prefix=${project}-${repoName}${path}`;
|
|
502
531
|
return archiveUrl;
|
|
503
532
|
}
|
|
@@ -524,7 +553,10 @@ function getBitbucketRequestOptions(config) {
|
|
|
524
553
|
if (config.token) {
|
|
525
554
|
headers.Authorization = `Bearer ${config.token}`;
|
|
526
555
|
} else if (config.username && config.appPassword) {
|
|
527
|
-
const buffer = Buffer.from(
|
|
556
|
+
const buffer = Buffer.from(
|
|
557
|
+
`${config.username}:${config.appPassword}`,
|
|
558
|
+
"utf8"
|
|
559
|
+
);
|
|
528
560
|
headers.Authorization = `Basic ${buffer.toString("base64")}`;
|
|
529
561
|
}
|
|
530
562
|
return {
|
|
@@ -592,14 +624,22 @@ BitbucketCloudIntegration.factory = ({
|
|
|
592
624
|
config
|
|
593
625
|
}) => {
|
|
594
626
|
var _a;
|
|
595
|
-
const configs = readBitbucketCloudIntegrationConfigs(
|
|
596
|
-
|
|
627
|
+
const configs = readBitbucketCloudIntegrationConfigs(
|
|
628
|
+
(_a = config.getOptionalConfigArray("integrations.bitbucketCloud")) != null ? _a : []
|
|
629
|
+
);
|
|
630
|
+
return basicIntegrations(
|
|
631
|
+
configs.map((c) => new _BitbucketCloudIntegration(c)),
|
|
632
|
+
(i) => i.config.host
|
|
633
|
+
);
|
|
597
634
|
};
|
|
598
635
|
|
|
599
636
|
async function getBitbucketCloudDefaultBranch(url, config) {
|
|
600
637
|
const { name: repoName, owner: project } = parseGitUrl__default["default"](url);
|
|
601
638
|
const branchUrl = `${config.apiBaseUrl}/repositories/${project}/${repoName}`;
|
|
602
|
-
const response = await fetch__default["default"](
|
|
639
|
+
const response = await fetch__default["default"](
|
|
640
|
+
branchUrl,
|
|
641
|
+
getBitbucketCloudRequestOptions(config)
|
|
642
|
+
);
|
|
603
643
|
if (!response.ok) {
|
|
604
644
|
const message = `Failed to retrieve default branch from ${branchUrl}, ${response.status} ${response.statusText}`;
|
|
605
645
|
throw new Error(message);
|
|
@@ -607,7 +647,9 @@ async function getBitbucketCloudDefaultBranch(url, config) {
|
|
|
607
647
|
const repoInfo = await response.json();
|
|
608
648
|
const defaultBranch = repoInfo.mainbranch.name;
|
|
609
649
|
if (!defaultBranch) {
|
|
610
|
-
throw new Error(
|
|
650
|
+
throw new Error(
|
|
651
|
+
`Failed to read default branch from ${branchUrl}. Response ${response.status} ${response.json()}`
|
|
652
|
+
);
|
|
611
653
|
}
|
|
612
654
|
return defaultBranch;
|
|
613
655
|
}
|
|
@@ -643,7 +685,10 @@ function getBitbucketCloudFileFetchUrl(url, config) {
|
|
|
643
685
|
function getBitbucketCloudRequestOptions(config) {
|
|
644
686
|
const headers = {};
|
|
645
687
|
if (config.username && config.appPassword) {
|
|
646
|
-
const buffer = Buffer.from(
|
|
688
|
+
const buffer = Buffer.from(
|
|
689
|
+
`${config.username}:${config.appPassword}`,
|
|
690
|
+
"utf8"
|
|
691
|
+
);
|
|
647
692
|
headers.Authorization = `Basic ${buffer.toString("base64")}`;
|
|
648
693
|
}
|
|
649
694
|
return {
|
|
@@ -655,8 +700,12 @@ function readBitbucketServerIntegrationConfig(config) {
|
|
|
655
700
|
const host = config.getString("host");
|
|
656
701
|
let apiBaseUrl = config.getOptionalString("apiBaseUrl");
|
|
657
702
|
const token = config.getOptionalString("token");
|
|
703
|
+
const username = config.getOptionalString("username");
|
|
704
|
+
const password = config.getOptionalString("password");
|
|
658
705
|
if (!isValidHost(host)) {
|
|
659
|
-
throw new Error(
|
|
706
|
+
throw new Error(
|
|
707
|
+
`Invalid Bitbucket Server integration config, '${host}' is not a valid host`
|
|
708
|
+
);
|
|
660
709
|
}
|
|
661
710
|
if (apiBaseUrl) {
|
|
662
711
|
apiBaseUrl = lodash.trimEnd(apiBaseUrl, "/");
|
|
@@ -666,7 +715,9 @@ function readBitbucketServerIntegrationConfig(config) {
|
|
|
666
715
|
return {
|
|
667
716
|
host,
|
|
668
717
|
apiBaseUrl,
|
|
669
|
-
token
|
|
718
|
+
token,
|
|
719
|
+
username,
|
|
720
|
+
password
|
|
670
721
|
};
|
|
671
722
|
}
|
|
672
723
|
function readBitbucketServerIntegrationConfigs(configs) {
|
|
@@ -690,19 +741,16 @@ const _BitbucketServerIntegration = class {
|
|
|
690
741
|
const resolved = defaultScmResolveUrl(options);
|
|
691
742
|
if (options.lineNumber) {
|
|
692
743
|
const url = new URL(resolved);
|
|
693
|
-
|
|
694
|
-
url.hash = `${filename}-${options.lineNumber}`;
|
|
744
|
+
url.hash = options.lineNumber.toString();
|
|
695
745
|
return url.toString();
|
|
696
746
|
}
|
|
697
747
|
return resolved;
|
|
698
748
|
}
|
|
699
749
|
resolveEditUrl(url) {
|
|
700
|
-
|
|
701
|
-
|
|
702
|
-
|
|
703
|
-
|
|
704
|
-
editUrl.searchParams.set("at", urlData.ref);
|
|
705
|
-
return editUrl.toString();
|
|
750
|
+
if (url.includes("?")) {
|
|
751
|
+
return url.substring(0, url.indexOf("?"));
|
|
752
|
+
}
|
|
753
|
+
return url;
|
|
706
754
|
}
|
|
707
755
|
};
|
|
708
756
|
let BitbucketServerIntegration = _BitbucketServerIntegration;
|
|
@@ -710,14 +758,22 @@ BitbucketServerIntegration.factory = ({
|
|
|
710
758
|
config
|
|
711
759
|
}) => {
|
|
712
760
|
var _a;
|
|
713
|
-
const configs = readBitbucketServerIntegrationConfigs(
|
|
714
|
-
|
|
761
|
+
const configs = readBitbucketServerIntegrationConfigs(
|
|
762
|
+
(_a = config.getOptionalConfigArray("integrations.bitbucketServer")) != null ? _a : []
|
|
763
|
+
);
|
|
764
|
+
return basicIntegrations(
|
|
765
|
+
configs.map((c) => new _BitbucketServerIntegration(c)),
|
|
766
|
+
(i) => i.config.host
|
|
767
|
+
);
|
|
715
768
|
};
|
|
716
769
|
|
|
717
770
|
async function getBitbucketServerDefaultBranch(url, config) {
|
|
718
771
|
const { name: repoName, owner: project } = parseGitUrl__default["default"](url);
|
|
719
772
|
let branchUrl = `${config.apiBaseUrl}/projects/${project}/repos/${repoName}/default-branch`;
|
|
720
|
-
let response = await fetch__default["default"](
|
|
773
|
+
let response = await fetch__default["default"](
|
|
774
|
+
branchUrl,
|
|
775
|
+
getBitbucketServerRequestOptions(config)
|
|
776
|
+
);
|
|
721
777
|
if (response.status === 404) {
|
|
722
778
|
branchUrl = `${config.apiBaseUrl}/projects/${project}/repos/${repoName}/branches/default`;
|
|
723
779
|
response = await fetch__default["default"](branchUrl, getBitbucketServerRequestOptions(config));
|
|
@@ -729,7 +785,9 @@ async function getBitbucketServerDefaultBranch(url, config) {
|
|
|
729
785
|
const { displayId } = await response.json();
|
|
730
786
|
const defaultBranch = displayId;
|
|
731
787
|
if (!defaultBranch) {
|
|
732
|
-
throw new Error(
|
|
788
|
+
throw new Error(
|
|
789
|
+
`Failed to read default branch from ${branchUrl}. Response ${response.status} ${response.json()}`
|
|
790
|
+
);
|
|
733
791
|
}
|
|
734
792
|
return defaultBranch;
|
|
735
793
|
}
|
|
@@ -759,6 +817,10 @@ function getBitbucketServerRequestOptions(config) {
|
|
|
759
817
|
if (config.token) {
|
|
760
818
|
headers.Authorization = `Bearer ${config.token}`;
|
|
761
819
|
}
|
|
820
|
+
if (config.username && config.password) {
|
|
821
|
+
const buffer = Buffer.from(`${config.username}:${config.password}`, "utf8");
|
|
822
|
+
headers.Authorization = `Basic ${buffer.toString("base64")}`;
|
|
823
|
+
}
|
|
762
824
|
return {
|
|
763
825
|
headers
|
|
764
826
|
};
|
|
@@ -772,13 +834,21 @@ function readGerritIntegrationConfig(config) {
|
|
|
772
834
|
const username = config.getOptionalString("username");
|
|
773
835
|
const password = config.getOptionalString("password");
|
|
774
836
|
if (!isValidHost(host)) {
|
|
775
|
-
throw new Error(
|
|
837
|
+
throw new Error(
|
|
838
|
+
`Invalid Gerrit integration config, '${host}' is not a valid host`
|
|
839
|
+
);
|
|
776
840
|
} else if (baseUrl && !isValidUrl(baseUrl)) {
|
|
777
|
-
throw new Error(
|
|
841
|
+
throw new Error(
|
|
842
|
+
`Invalid Gerrit integration config, '${baseUrl}' is not a valid baseUrl`
|
|
843
|
+
);
|
|
778
844
|
} else if (cloneUrl && !isValidUrl(cloneUrl)) {
|
|
779
|
-
throw new Error(
|
|
845
|
+
throw new Error(
|
|
846
|
+
`Invalid Gerrit integration config, '${cloneUrl}' is not a valid cloneUrl`
|
|
847
|
+
);
|
|
780
848
|
} else if (gitilesBaseUrl && !isValidUrl(gitilesBaseUrl)) {
|
|
781
|
-
throw new Error(
|
|
849
|
+
throw new Error(
|
|
850
|
+
`Invalid Gerrit integration config, '${gitilesBaseUrl}' is not a valid gitilesBaseUrl`
|
|
851
|
+
);
|
|
782
852
|
}
|
|
783
853
|
if (baseUrl) {
|
|
784
854
|
baseUrl = lodash.trimEnd(baseUrl, "/");
|
|
@@ -837,7 +907,9 @@ function getAuthenticationPrefix(config) {
|
|
|
837
907
|
}
|
|
838
908
|
function getGerritBranchApiUrl(config, url) {
|
|
839
909
|
const { branch, project } = parseGerritGitilesUrl(config, url);
|
|
840
|
-
return `${config.baseUrl}${getAuthenticationPrefix(
|
|
910
|
+
return `${config.baseUrl}${getAuthenticationPrefix(
|
|
911
|
+
config
|
|
912
|
+
)}projects/${encodeURIComponent(project)}/branches/${branch}`;
|
|
841
913
|
}
|
|
842
914
|
function getGerritCloneRepoUrl(config, url) {
|
|
843
915
|
const { project } = parseGerritGitilesUrl(config, url);
|
|
@@ -845,7 +917,11 @@ function getGerritCloneRepoUrl(config, url) {
|
|
|
845
917
|
}
|
|
846
918
|
function getGerritFileContentsApiUrl(config, url) {
|
|
847
919
|
const { branch, filePath, project } = parseGerritGitilesUrl(config, url);
|
|
848
|
-
return `${config.baseUrl}${getAuthenticationPrefix(
|
|
920
|
+
return `${config.baseUrl}${getAuthenticationPrefix(
|
|
921
|
+
config
|
|
922
|
+
)}projects/${encodeURIComponent(
|
|
923
|
+
project
|
|
924
|
+
)}/branches/${branch}/files/${encodeURIComponent(filePath)}/content`;
|
|
849
925
|
}
|
|
850
926
|
function getGerritProjectsApiUrl(config) {
|
|
851
927
|
return `${config.baseUrl}${getAuthenticationPrefix(config)}projects/`;
|
|
@@ -867,10 +943,14 @@ async function parseGerritJsonResponse(response) {
|
|
|
867
943
|
try {
|
|
868
944
|
return JSON.parse(responseBody.slice(GERRIT_BODY_PREFIX.length));
|
|
869
945
|
} catch (ex) {
|
|
870
|
-
throw new Error(
|
|
946
|
+
throw new Error(
|
|
947
|
+
`Invalid response from Gerrit: ${responseBody.slice(0, 10)} - ${ex}`
|
|
948
|
+
);
|
|
871
949
|
}
|
|
872
950
|
}
|
|
873
|
-
throw new Error(
|
|
951
|
+
throw new Error(
|
|
952
|
+
`Gerrit JSON body prefix missing. Found: ${responseBody.slice(0, 10)}`
|
|
953
|
+
);
|
|
874
954
|
}
|
|
875
955
|
|
|
876
956
|
const _GerritIntegration = class {
|
|
@@ -910,8 +990,13 @@ const _GerritIntegration = class {
|
|
|
910
990
|
let GerritIntegration = _GerritIntegration;
|
|
911
991
|
GerritIntegration.factory = ({ config }) => {
|
|
912
992
|
var _a;
|
|
913
|
-
const configs = readGerritIntegrationConfigs(
|
|
914
|
-
|
|
993
|
+
const configs = readGerritIntegrationConfigs(
|
|
994
|
+
(_a = config.getOptionalConfigArray("integrations.gerrit")) != null ? _a : []
|
|
995
|
+
);
|
|
996
|
+
return basicIntegrations(
|
|
997
|
+
configs.map((c) => new _GerritIntegration(c)),
|
|
998
|
+
(i) => i.config.host
|
|
999
|
+
);
|
|
915
1000
|
};
|
|
916
1001
|
|
|
917
1002
|
const GITHUB_HOST = "github.com";
|
|
@@ -929,10 +1014,14 @@ function readGitHubIntegrationConfig(config) {
|
|
|
929
1014
|
clientSecret: c.getString("clientSecret"),
|
|
930
1015
|
webhookSecret: c.getString("webhookSecret"),
|
|
931
1016
|
privateKey: c.getString("privateKey"),
|
|
932
|
-
allowedInstallationOwners: c.getOptionalStringArray(
|
|
1017
|
+
allowedInstallationOwners: c.getOptionalStringArray(
|
|
1018
|
+
"allowedInstallationOwners"
|
|
1019
|
+
)
|
|
933
1020
|
}));
|
|
934
1021
|
if (!isValidHost(host)) {
|
|
935
|
-
throw new Error(
|
|
1022
|
+
throw new Error(
|
|
1023
|
+
`Invalid GitHub integration config, '${host}' is not a valid host`
|
|
1024
|
+
);
|
|
936
1025
|
}
|
|
937
1026
|
if (apiBaseUrl) {
|
|
938
1027
|
apiBaseUrl = lodash.trimEnd(apiBaseUrl, "/");
|
|
@@ -1037,6 +1126,7 @@ class GithubAppManager {
|
|
|
1037
1126
|
}
|
|
1038
1127
|
const cacheKey = repo ? `${owner}/${repo}` : owner;
|
|
1039
1128
|
return this.cache.getOrCreateToken(cacheKey, async () => {
|
|
1129
|
+
var _a2;
|
|
1040
1130
|
const result = await this.appClient.apps.createInstallationAccessToken({
|
|
1041
1131
|
installation_id: installationId,
|
|
1042
1132
|
headers: HEADERS
|
|
@@ -1046,12 +1136,17 @@ class GithubAppManager {
|
|
|
1046
1136
|
baseUrl: this.baseUrl,
|
|
1047
1137
|
auth: result.data.token
|
|
1048
1138
|
});
|
|
1049
|
-
const repos = await installationClient.paginate(
|
|
1050
|
-
|
|
1139
|
+
const repos = await installationClient.paginate(
|
|
1140
|
+
installationClient.apps.listReposAccessibleToInstallation
|
|
1141
|
+
);
|
|
1142
|
+
const repositories = (_a2 = repos.repositories) != null ? _a2 : repos;
|
|
1143
|
+
const hasRepo = repositories.some((repository) => {
|
|
1051
1144
|
return repository.name === repo;
|
|
1052
1145
|
});
|
|
1053
1146
|
if (!hasRepo) {
|
|
1054
|
-
throw new Error(
|
|
1147
|
+
throw new Error(
|
|
1148
|
+
`The Backstage GitHub application used in the ${owner} organization does not have access to a repository with the name ${repo}`
|
|
1149
|
+
);
|
|
1055
1150
|
}
|
|
1056
1151
|
}
|
|
1057
1152
|
return {
|
|
@@ -1065,17 +1160,21 @@ class GithubAppManager {
|
|
|
1065
1160
|
}
|
|
1066
1161
|
async getInstallationData(owner) {
|
|
1067
1162
|
const allInstallations = await this.getInstallations();
|
|
1068
|
-
const installation = allInstallations.find(
|
|
1069
|
-
|
|
1070
|
-
|
|
1071
|
-
|
|
1163
|
+
const installation = allInstallations.find(
|
|
1164
|
+
(inst) => {
|
|
1165
|
+
var _a, _b;
|
|
1166
|
+
return ((_b = (_a = inst.account) == null ? void 0 : _a.login) == null ? void 0 : _b.toLocaleLowerCase("en-US")) === owner.toLocaleLowerCase("en-US");
|
|
1167
|
+
}
|
|
1168
|
+
);
|
|
1072
1169
|
if (installation) {
|
|
1073
1170
|
return {
|
|
1074
1171
|
installationId: installation.id,
|
|
1075
1172
|
suspended: Boolean(installation.suspended_by)
|
|
1076
1173
|
};
|
|
1077
1174
|
}
|
|
1078
|
-
const notFoundError = new Error(
|
|
1175
|
+
const notFoundError = new Error(
|
|
1176
|
+
`No app installation found for ${owner} in ${this.baseAuthConfig.appId}`
|
|
1177
|
+
);
|
|
1079
1178
|
notFoundError.name = "NotFoundError";
|
|
1080
1179
|
throw notFoundError;
|
|
1081
1180
|
}
|
|
@@ -1089,14 +1188,23 @@ class GithubAppCredentialsMux {
|
|
|
1089
1188
|
if (!this.apps.length) {
|
|
1090
1189
|
return [];
|
|
1091
1190
|
}
|
|
1092
|
-
const installs = await Promise.all(
|
|
1191
|
+
const installs = await Promise.all(
|
|
1192
|
+
this.apps.map((app) => app.getInstallations())
|
|
1193
|
+
);
|
|
1093
1194
|
return installs.flat();
|
|
1094
1195
|
}
|
|
1095
1196
|
async getAppToken(owner, repo) {
|
|
1096
1197
|
if (this.apps.length === 0) {
|
|
1097
1198
|
return void 0;
|
|
1098
1199
|
}
|
|
1099
|
-
const results = await Promise.all(
|
|
1200
|
+
const results = await Promise.all(
|
|
1201
|
+
this.apps.map(
|
|
1202
|
+
(app) => app.getInstallationCredentials(owner, repo).then(
|
|
1203
|
+
(credentials) => ({ credentials, error: void 0 }),
|
|
1204
|
+
(error) => ({ credentials: void 0, error })
|
|
1205
|
+
)
|
|
1206
|
+
)
|
|
1207
|
+
);
|
|
1100
1208
|
const result = results.find((resultItem) => resultItem.credentials);
|
|
1101
1209
|
if (result) {
|
|
1102
1210
|
return result.credentials.accessToken;
|
|
@@ -1133,7 +1241,10 @@ const _SingleInstanceGithubCredentialsProvider = class {
|
|
|
1133
1241
|
};
|
|
1134
1242
|
let SingleInstanceGithubCredentialsProvider = _SingleInstanceGithubCredentialsProvider;
|
|
1135
1243
|
SingleInstanceGithubCredentialsProvider.create = (config) => {
|
|
1136
|
-
return new _SingleInstanceGithubCredentialsProvider(
|
|
1244
|
+
return new _SingleInstanceGithubCredentialsProvider(
|
|
1245
|
+
new GithubAppCredentialsMux(config),
|
|
1246
|
+
config.token
|
|
1247
|
+
);
|
|
1137
1248
|
};
|
|
1138
1249
|
|
|
1139
1250
|
class DefaultGithubCredentialsProvider {
|
|
@@ -1152,7 +1263,9 @@ class DefaultGithubCredentialsProvider {
|
|
|
1152
1263
|
const parsed = new URL(opts.url);
|
|
1153
1264
|
const provider = this.providers.get(parsed.host);
|
|
1154
1265
|
if (!provider) {
|
|
1155
|
-
throw new Error(
|
|
1266
|
+
throw new Error(
|
|
1267
|
+
`There is no GitHub integration that matches ${opts.url}. Please add a configuration for an integration.`
|
|
1268
|
+
);
|
|
1156
1269
|
}
|
|
1157
1270
|
return provider.getCredentials(opts);
|
|
1158
1271
|
}
|
|
@@ -1181,13 +1294,21 @@ const _GitHubIntegration = class {
|
|
|
1181
1294
|
let GitHubIntegration = _GitHubIntegration;
|
|
1182
1295
|
GitHubIntegration.factory = ({ config }) => {
|
|
1183
1296
|
var _a;
|
|
1184
|
-
const configs = readGitHubIntegrationConfigs(
|
|
1185
|
-
|
|
1297
|
+
const configs = readGitHubIntegrationConfigs(
|
|
1298
|
+
(_a = config.getOptionalConfigArray("integrations.github")) != null ? _a : []
|
|
1299
|
+
);
|
|
1300
|
+
return basicIntegrations(
|
|
1301
|
+
configs.map((c) => new _GitHubIntegration(c)),
|
|
1302
|
+
(i) => i.config.host
|
|
1303
|
+
);
|
|
1186
1304
|
};
|
|
1187
1305
|
function replaceGitHubUrlType(url, type) {
|
|
1188
|
-
return url.replace(
|
|
1189
|
-
|
|
1190
|
-
|
|
1306
|
+
return url.replace(
|
|
1307
|
+
/\/\/([^/]+)\/([^/]+)\/([^/]+)\/(blob|tree|edit)\//,
|
|
1308
|
+
(_, host, owner, repo) => {
|
|
1309
|
+
return `//${host}/${owner}/${repo}/${type}/`;
|
|
1310
|
+
}
|
|
1311
|
+
);
|
|
1191
1312
|
}
|
|
1192
1313
|
|
|
1193
1314
|
const GITLAB_HOST = "gitlab.com";
|
|
@@ -1208,11 +1329,17 @@ function readGitLabIntegrationConfig(config) {
|
|
|
1208
1329
|
baseUrl = `https://${host}`;
|
|
1209
1330
|
}
|
|
1210
1331
|
if (!isValidHost(host)) {
|
|
1211
|
-
throw new Error(
|
|
1332
|
+
throw new Error(
|
|
1333
|
+
`Invalid GitLab integration config, '${host}' is not a valid host`
|
|
1334
|
+
);
|
|
1212
1335
|
} else if (!apiBaseUrl || !isValidUrl(apiBaseUrl)) {
|
|
1213
|
-
throw new Error(
|
|
1336
|
+
throw new Error(
|
|
1337
|
+
`Invalid GitLab integration config, '${apiBaseUrl}' is not a valid apiBaseUrl`
|
|
1338
|
+
);
|
|
1214
1339
|
} else if (!isValidUrl(baseUrl)) {
|
|
1215
|
-
throw new Error(
|
|
1340
|
+
throw new Error(
|
|
1341
|
+
`Invalid GitLab integration config, '${baseUrl}' is not a valid baseUrl`
|
|
1342
|
+
);
|
|
1216
1343
|
}
|
|
1217
1344
|
return { host, token, apiBaseUrl, baseUrl };
|
|
1218
1345
|
}
|
|
@@ -1227,13 +1354,17 @@ function readGitLabIntegrationConfigs(configs) {
|
|
|
1227
1354
|
}
|
|
1228
1355
|
return result;
|
|
1229
1356
|
}
|
|
1357
|
+
function getGitLabIntegrationRelativePath(config) {
|
|
1358
|
+
let relativePath = "";
|
|
1359
|
+
if (config.host !== GITLAB_HOST) {
|
|
1360
|
+
relativePath = new URL(config.baseUrl).pathname;
|
|
1361
|
+
}
|
|
1362
|
+
return lodash.trimEnd(relativePath, "/");
|
|
1363
|
+
}
|
|
1230
1364
|
|
|
1231
1365
|
async function getGitLabFileFetchUrl(url, config) {
|
|
1232
|
-
|
|
1233
|
-
|
|
1234
|
-
return buildProjectUrl(url, projectID).toString();
|
|
1235
|
-
}
|
|
1236
|
-
return buildRawUrl(url).toString();
|
|
1366
|
+
const projectID = await getProjectId(url, config);
|
|
1367
|
+
return buildProjectUrl(url, projectID, config).toString();
|
|
1237
1368
|
}
|
|
1238
1369
|
function getGitLabRequestOptions(config) {
|
|
1239
1370
|
const { token = "" } = config;
|
|
@@ -1243,32 +1374,15 @@ function getGitLabRequestOptions(config) {
|
|
|
1243
1374
|
}
|
|
1244
1375
|
};
|
|
1245
1376
|
}
|
|
1246
|
-
function
|
|
1377
|
+
function buildProjectUrl(target, projectID, config) {
|
|
1247
1378
|
try {
|
|
1248
1379
|
const url = new URL(target);
|
|
1249
|
-
const
|
|
1250
|
-
const blobIndex = splitPath.indexOf("blob", 2);
|
|
1251
|
-
if (blobIndex < 2 || blobIndex === splitPath.length - 1) {
|
|
1252
|
-
throw new errors.InputError("Wrong GitLab URL");
|
|
1253
|
-
}
|
|
1254
|
-
const repoPath = splitPath.slice(0, blobIndex);
|
|
1255
|
-
const restOfPath = splitPath.slice(blobIndex + 1);
|
|
1256
|
-
if (!restOfPath.join("/").match(/\.(yaml|yml)$/)) {
|
|
1257
|
-
throw new errors.InputError("Wrong GitLab URL");
|
|
1258
|
-
}
|
|
1259
|
-
url.pathname = [...repoPath, "raw", ...restOfPath].join("/");
|
|
1260
|
-
return url;
|
|
1261
|
-
} catch (e) {
|
|
1262
|
-
throw new errors.InputError(`Incorrect url: ${target}, ${e}`);
|
|
1263
|
-
}
|
|
1264
|
-
}
|
|
1265
|
-
function buildProjectUrl(target, projectID) {
|
|
1266
|
-
try {
|
|
1267
|
-
const url = new URL(target);
|
|
1268
|
-
const branchAndFilePath = url.pathname.split("/-/blob/")[1];
|
|
1380
|
+
const branchAndFilePath = url.pathname.split("/blob/").slice(1).join("/blob/");
|
|
1269
1381
|
const [branch, ...filePath] = branchAndFilePath.split("/");
|
|
1382
|
+
const relativePath = getGitLabIntegrationRelativePath(config);
|
|
1270
1383
|
url.pathname = [
|
|
1271
|
-
|
|
1384
|
+
...relativePath ? [relativePath] : [],
|
|
1385
|
+
"api/v4/projects",
|
|
1272
1386
|
projectID,
|
|
1273
1387
|
"repository/files",
|
|
1274
1388
|
encodeURIComponent(decodeURIComponent(filePath.join("/"))),
|
|
@@ -1282,16 +1396,29 @@ function buildProjectUrl(target, projectID) {
|
|
|
1282
1396
|
}
|
|
1283
1397
|
async function getProjectId(target, config) {
|
|
1284
1398
|
const url = new URL(target);
|
|
1285
|
-
if (!url.pathname.includes("
|
|
1399
|
+
if (!url.pathname.includes("/blob/")) {
|
|
1286
1400
|
throw new Error("Please provide full path to yaml file from GitLab");
|
|
1287
1401
|
}
|
|
1288
1402
|
try {
|
|
1289
|
-
|
|
1290
|
-
const
|
|
1291
|
-
|
|
1403
|
+
let repo = url.pathname.split("/-/blob/")[0].split("/blob/")[0];
|
|
1404
|
+
const relativePath = getGitLabIntegrationRelativePath(config);
|
|
1405
|
+
if (relativePath) {
|
|
1406
|
+
repo = repo.replace(relativePath, "");
|
|
1407
|
+
}
|
|
1408
|
+
const repoIDLookup = new URL(
|
|
1409
|
+
`${url.origin}${relativePath}/api/v4/projects/${encodeURIComponent(
|
|
1410
|
+
repo.replace(/^\//, "")
|
|
1411
|
+
)}`
|
|
1412
|
+
);
|
|
1413
|
+
const response = await fetch__default["default"](
|
|
1414
|
+
repoIDLookup.toString(),
|
|
1415
|
+
getGitLabRequestOptions(config)
|
|
1416
|
+
);
|
|
1292
1417
|
const data = await response.json();
|
|
1293
1418
|
if (!response.ok) {
|
|
1294
|
-
throw new Error(
|
|
1419
|
+
throw new Error(
|
|
1420
|
+
`GitLab Error '${data.error}', ${data.error_description}`
|
|
1421
|
+
);
|
|
1295
1422
|
}
|
|
1296
1423
|
return Number(data.id);
|
|
1297
1424
|
} catch (e) {
|
|
@@ -1322,8 +1449,13 @@ const _GitLabIntegration = class {
|
|
|
1322
1449
|
let GitLabIntegration = _GitLabIntegration;
|
|
1323
1450
|
GitLabIntegration.factory = ({ config }) => {
|
|
1324
1451
|
var _a;
|
|
1325
|
-
const configs = readGitLabIntegrationConfigs(
|
|
1326
|
-
|
|
1452
|
+
const configs = readGitLabIntegrationConfigs(
|
|
1453
|
+
(_a = config.getOptionalConfigArray("integrations.gitlab")) != null ? _a : []
|
|
1454
|
+
);
|
|
1455
|
+
return basicIntegrations(
|
|
1456
|
+
configs.map((c) => new _GitLabIntegration(c)),
|
|
1457
|
+
(i) => i.config.host
|
|
1458
|
+
);
|
|
1327
1459
|
};
|
|
1328
1460
|
function replaceGitLabUrlType(url, type) {
|
|
1329
1461
|
return url.replace(/\/\-\/(blob|tree|edit)\//, `/-/${type}/`);
|
|
@@ -1382,10 +1514,21 @@ class ScmIntegrations {
|
|
|
1382
1514
|
return this.byType.gitlab;
|
|
1383
1515
|
}
|
|
1384
1516
|
list() {
|
|
1385
|
-
return Object.values(this.byType).flatMap(
|
|
1517
|
+
return Object.values(this.byType).flatMap(
|
|
1518
|
+
(i) => i.list()
|
|
1519
|
+
);
|
|
1386
1520
|
}
|
|
1387
1521
|
byUrl(url) {
|
|
1388
|
-
|
|
1522
|
+
let candidates = Object.values(this.byType).map((i) => i.byUrl(url)).filter(Boolean);
|
|
1523
|
+
if (candidates.length > 1) {
|
|
1524
|
+
const filteredCandidates = candidates.filter(
|
|
1525
|
+
(x) => !(x instanceof BitbucketIntegration)
|
|
1526
|
+
);
|
|
1527
|
+
if (filteredCandidates.length !== 0) {
|
|
1528
|
+
candidates = filteredCandidates;
|
|
1529
|
+
}
|
|
1530
|
+
}
|
|
1531
|
+
return candidates[0];
|
|
1389
1532
|
}
|
|
1390
1533
|
byHost(host) {
|
|
1391
1534
|
return Object.values(this.byType).map((i) => i.byHost(host)).find(Boolean);
|
|
@@ -1443,6 +1586,7 @@ exports.getGerritRequestOptions = getGerritRequestOptions;
|
|
|
1443
1586
|
exports.getGitHubFileFetchUrl = getGitHubFileFetchUrl;
|
|
1444
1587
|
exports.getGitHubRequestOptions = getGitHubRequestOptions;
|
|
1445
1588
|
exports.getGitLabFileFetchUrl = getGitLabFileFetchUrl;
|
|
1589
|
+
exports.getGitLabIntegrationRelativePath = getGitLabIntegrationRelativePath;
|
|
1446
1590
|
exports.getGitLabRequestOptions = getGitLabRequestOptions;
|
|
1447
1591
|
exports.parseGerritGitilesUrl = parseGerritGitilesUrl;
|
|
1448
1592
|
exports.parseGerritJsonResponse = parseGerritJsonResponse;
|