@backstage/integration 1.2.2 → 1.3.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 +40 -0
- package/config.d.ts +10 -0
- package/dist/index.cjs.js +234 -104
- package/dist/index.cjs.js.map +1 -1
- package/dist/index.d.ts +17 -1
- package/dist/index.esm.js +234 -104
- package/dist/index.esm.js.map +1 -1
- package/package.json +5 -5
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
|
}
|
|
@@ -1236,11 +1363,8 @@ function getGitLabIntegrationRelativePath(config) {
|
|
|
1236
1363
|
}
|
|
1237
1364
|
|
|
1238
1365
|
async function getGitLabFileFetchUrl(url, config) {
|
|
1239
|
-
|
|
1240
|
-
|
|
1241
|
-
return buildProjectUrl(url, projectID, config).toString();
|
|
1242
|
-
}
|
|
1243
|
-
return buildRawUrl(url).toString();
|
|
1366
|
+
const projectID = await getProjectId(url, config);
|
|
1367
|
+
return buildProjectUrl(url, projectID, config).toString();
|
|
1244
1368
|
}
|
|
1245
1369
|
function getGitLabRequestOptions(config) {
|
|
1246
1370
|
const { token = "" } = config;
|
|
@@ -1250,29 +1374,10 @@ function getGitLabRequestOptions(config) {
|
|
|
1250
1374
|
}
|
|
1251
1375
|
};
|
|
1252
1376
|
}
|
|
1253
|
-
function buildRawUrl(target) {
|
|
1254
|
-
try {
|
|
1255
|
-
const url = new URL(target);
|
|
1256
|
-
const splitPath = url.pathname.split("/").filter(Boolean);
|
|
1257
|
-
const blobIndex = splitPath.indexOf("blob", 2);
|
|
1258
|
-
if (blobIndex < 2 || blobIndex === splitPath.length - 1) {
|
|
1259
|
-
throw new errors.InputError("Wrong GitLab URL");
|
|
1260
|
-
}
|
|
1261
|
-
const repoPath = splitPath.slice(0, blobIndex);
|
|
1262
|
-
const restOfPath = splitPath.slice(blobIndex + 1);
|
|
1263
|
-
if (!restOfPath.join("/").match(/\.(yaml|yml)$/)) {
|
|
1264
|
-
throw new errors.InputError("Wrong GitLab URL");
|
|
1265
|
-
}
|
|
1266
|
-
url.pathname = [...repoPath, "raw", ...restOfPath].join("/");
|
|
1267
|
-
return url;
|
|
1268
|
-
} catch (e) {
|
|
1269
|
-
throw new errors.InputError(`Incorrect url: ${target}, ${e}`);
|
|
1270
|
-
}
|
|
1271
|
-
}
|
|
1272
1377
|
function buildProjectUrl(target, projectID, config) {
|
|
1273
1378
|
try {
|
|
1274
1379
|
const url = new URL(target);
|
|
1275
|
-
const branchAndFilePath = url.pathname.split("
|
|
1380
|
+
const branchAndFilePath = url.pathname.split("/blob/").slice(1).join("/blob/");
|
|
1276
1381
|
const [branch, ...filePath] = branchAndFilePath.split("/");
|
|
1277
1382
|
const relativePath = getGitLabIntegrationRelativePath(config);
|
|
1278
1383
|
url.pathname = [
|
|
@@ -1291,20 +1396,29 @@ function buildProjectUrl(target, projectID, config) {
|
|
|
1291
1396
|
}
|
|
1292
1397
|
async function getProjectId(target, config) {
|
|
1293
1398
|
const url = new URL(target);
|
|
1294
|
-
if (!url.pathname.includes("
|
|
1399
|
+
if (!url.pathname.includes("/blob/")) {
|
|
1295
1400
|
throw new Error("Please provide full path to yaml file from GitLab");
|
|
1296
1401
|
}
|
|
1297
1402
|
try {
|
|
1298
|
-
let repo = url.pathname.split("/-/blob/")[0];
|
|
1403
|
+
let repo = url.pathname.split("/-/blob/")[0].split("/blob/")[0];
|
|
1299
1404
|
const relativePath = getGitLabIntegrationRelativePath(config);
|
|
1300
1405
|
if (relativePath) {
|
|
1301
1406
|
repo = repo.replace(relativePath, "");
|
|
1302
1407
|
}
|
|
1303
|
-
const repoIDLookup = new URL(
|
|
1304
|
-
|
|
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
|
+
);
|
|
1305
1417
|
const data = await response.json();
|
|
1306
1418
|
if (!response.ok) {
|
|
1307
|
-
throw new Error(
|
|
1419
|
+
throw new Error(
|
|
1420
|
+
`GitLab Error '${data.error}', ${data.error_description}`
|
|
1421
|
+
);
|
|
1308
1422
|
}
|
|
1309
1423
|
return Number(data.id);
|
|
1310
1424
|
} catch (e) {
|
|
@@ -1335,8 +1449,13 @@ const _GitLabIntegration = class {
|
|
|
1335
1449
|
let GitLabIntegration = _GitLabIntegration;
|
|
1336
1450
|
GitLabIntegration.factory = ({ config }) => {
|
|
1337
1451
|
var _a;
|
|
1338
|
-
const configs = readGitLabIntegrationConfigs(
|
|
1339
|
-
|
|
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
|
+
);
|
|
1340
1459
|
};
|
|
1341
1460
|
function replaceGitLabUrlType(url, type) {
|
|
1342
1461
|
return url.replace(/\/\-\/(blob|tree|edit)\//, `/-/${type}/`);
|
|
@@ -1395,10 +1514,21 @@ class ScmIntegrations {
|
|
|
1395
1514
|
return this.byType.gitlab;
|
|
1396
1515
|
}
|
|
1397
1516
|
list() {
|
|
1398
|
-
return Object.values(this.byType).flatMap(
|
|
1517
|
+
return Object.values(this.byType).flatMap(
|
|
1518
|
+
(i) => i.list()
|
|
1519
|
+
);
|
|
1399
1520
|
}
|
|
1400
1521
|
byUrl(url) {
|
|
1401
|
-
|
|
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];
|
|
1402
1532
|
}
|
|
1403
1533
|
byHost(host) {
|
|
1404
1534
|
return Object.values(this.byType).map((i) => i.byHost(host)).find(Boolean);
|