@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.esm.js
CHANGED
|
@@ -4,7 +4,6 @@ import fetch from 'cross-fetch';
|
|
|
4
4
|
import { createAppAuth } from '@octokit/auth-app';
|
|
5
5
|
import { Octokit } from '@octokit/rest';
|
|
6
6
|
import { DateTime } from 'luxon';
|
|
7
|
-
import { InputError } from '@backstage/errors';
|
|
8
7
|
|
|
9
8
|
function isValidHost(host) {
|
|
10
9
|
const check = new URL("http://example.com");
|
|
@@ -48,7 +47,10 @@ function defaultScmResolveUrl(options) {
|
|
|
48
47
|
if (url.startsWith("/")) {
|
|
49
48
|
const { filepath } = parseGitUrl(base);
|
|
50
49
|
updated = new URL(base);
|
|
51
|
-
const repoRootPath = trimEnd(
|
|
50
|
+
const repoRootPath = trimEnd(
|
|
51
|
+
updated.pathname.substring(0, updated.pathname.length - filepath.length),
|
|
52
|
+
"/"
|
|
53
|
+
);
|
|
52
54
|
updated.pathname = `${repoRootPath}${url}`;
|
|
53
55
|
} else {
|
|
54
56
|
updated = new URL(url, base);
|
|
@@ -73,10 +75,14 @@ function readAwsS3IntegrationConfig(config) {
|
|
|
73
75
|
host = url.host;
|
|
74
76
|
pathname = url.pathname;
|
|
75
77
|
} catch {
|
|
76
|
-
throw new Error(
|
|
78
|
+
throw new Error(
|
|
79
|
+
`invalid awsS3 integration config, endpoint '${endpoint}' is not a valid URL`
|
|
80
|
+
);
|
|
77
81
|
}
|
|
78
82
|
if (pathname !== "/") {
|
|
79
|
-
throw new Error(
|
|
83
|
+
throw new Error(
|
|
84
|
+
`invalid awsS3 integration config, endpoints cannot contain path, got '${endpoint}'`
|
|
85
|
+
);
|
|
80
86
|
}
|
|
81
87
|
} else {
|
|
82
88
|
host = AMAZON_AWS_HOST;
|
|
@@ -129,8 +135,13 @@ const _AwsS3Integration = class {
|
|
|
129
135
|
let AwsS3Integration = _AwsS3Integration;
|
|
130
136
|
AwsS3Integration.factory = ({ config }) => {
|
|
131
137
|
var _a;
|
|
132
|
-
const configs = readAwsS3IntegrationConfigs(
|
|
133
|
-
|
|
138
|
+
const configs = readAwsS3IntegrationConfigs(
|
|
139
|
+
(_a = config.getOptionalConfigArray("integrations.awsS3")) != null ? _a : []
|
|
140
|
+
);
|
|
141
|
+
return basicIntegrations(
|
|
142
|
+
configs.map((c) => new _AwsS3Integration(c)),
|
|
143
|
+
(i) => i.config.host
|
|
144
|
+
);
|
|
134
145
|
};
|
|
135
146
|
|
|
136
147
|
var __accessCheck = (obj, member, msg) => {
|
|
@@ -224,7 +235,9 @@ const _AzureUrl = class {
|
|
|
224
235
|
}
|
|
225
236
|
toFileUrl() {
|
|
226
237
|
if (!__privateGet(this, _path)) {
|
|
227
|
-
throw new Error(
|
|
238
|
+
throw new Error(
|
|
239
|
+
"Azure URL must point to a specific path to be able to download a file"
|
|
240
|
+
);
|
|
228
241
|
}
|
|
229
242
|
const url = __privateGet(this, _baseUrl).call(this, __privateGet(this, _owner), __privateGet(this, _project), "_apis", "git", "repositories", __privateGet(this, _repo), "items");
|
|
230
243
|
url.searchParams.set("api-version", "6.0");
|
|
@@ -286,7 +299,9 @@ function readAzureIntegrationConfig(config) {
|
|
|
286
299
|
const host = (_a = config.getOptionalString("host")) != null ? _a : AZURE_HOST;
|
|
287
300
|
const token = config.getOptionalString("token");
|
|
288
301
|
if (!isValidHost(host)) {
|
|
289
|
-
throw new Error(
|
|
302
|
+
throw new Error(
|
|
303
|
+
`Invalid Azure integration config, '${host}' is not a valid host`
|
|
304
|
+
);
|
|
290
305
|
}
|
|
291
306
|
return { host, token };
|
|
292
307
|
}
|
|
@@ -341,8 +356,13 @@ const _AzureIntegration = class {
|
|
|
341
356
|
let AzureIntegration = _AzureIntegration;
|
|
342
357
|
AzureIntegration.factory = ({ config }) => {
|
|
343
358
|
var _a;
|
|
344
|
-
const configs = readAzureIntegrationConfigs(
|
|
345
|
-
|
|
359
|
+
const configs = readAzureIntegrationConfigs(
|
|
360
|
+
(_a = config.getOptionalConfigArray("integrations.azure")) != null ? _a : []
|
|
361
|
+
);
|
|
362
|
+
return basicIntegrations(
|
|
363
|
+
configs.map((c) => new _AzureIntegration(c)),
|
|
364
|
+
(i) => i.config.host
|
|
365
|
+
);
|
|
346
366
|
};
|
|
347
367
|
|
|
348
368
|
function getAzureFileFetchUrl(url) {
|
|
@@ -373,7 +393,9 @@ function readBitbucketIntegrationConfig(config) {
|
|
|
373
393
|
const username = config.getOptionalString("username");
|
|
374
394
|
const appPassword = config.getOptionalString("appPassword");
|
|
375
395
|
if (!isValidHost(host)) {
|
|
376
|
-
throw new Error(
|
|
396
|
+
throw new Error(
|
|
397
|
+
`Invalid Bitbucket integration config, '${host}' is not a valid host`
|
|
398
|
+
);
|
|
377
399
|
}
|
|
378
400
|
if (apiBaseUrl) {
|
|
379
401
|
apiBaseUrl = trimEnd(apiBaseUrl, "/");
|
|
@@ -441,11 +463,16 @@ BitbucketIntegration.factory = ({
|
|
|
441
463
|
config
|
|
442
464
|
}) => {
|
|
443
465
|
var _a, _b, _c;
|
|
444
|
-
const configs = readBitbucketIntegrationConfigs(
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
|
|
466
|
+
const configs = readBitbucketIntegrationConfigs(
|
|
467
|
+
(_c = config.getOptionalConfigArray("integrations.bitbucket")) != null ? _c : [
|
|
468
|
+
...(_a = config.getOptionalConfigArray("integrations.bitbucketCloud")) != null ? _a : [],
|
|
469
|
+
...(_b = config.getOptionalConfigArray("integrations.bitbucketServer")) != null ? _b : []
|
|
470
|
+
]
|
|
471
|
+
);
|
|
472
|
+
return basicIntegrations(
|
|
473
|
+
configs.map((c) => new _BitbucketIntegration(c)),
|
|
474
|
+
(i) => i.config.host
|
|
475
|
+
);
|
|
449
476
|
};
|
|
450
477
|
|
|
451
478
|
async function getBitbucketDefaultBranch(url, config) {
|
|
@@ -470,7 +497,9 @@ async function getBitbucketDefaultBranch(url, config) {
|
|
|
470
497
|
defaultBranch = displayId;
|
|
471
498
|
}
|
|
472
499
|
if (!defaultBranch) {
|
|
473
|
-
throw new Error(
|
|
500
|
+
throw new Error(
|
|
501
|
+
`Failed to read default branch from ${branchUrl}. Response ${response.status} ${response.json()}`
|
|
502
|
+
);
|
|
474
503
|
}
|
|
475
504
|
return defaultBranch;
|
|
476
505
|
}
|
|
@@ -488,7 +517,7 @@ async function getBitbucketDownloadUrl(url, config) {
|
|
|
488
517
|
if (!branch) {
|
|
489
518
|
branch = await getBitbucketDefaultBranch(url, config);
|
|
490
519
|
}
|
|
491
|
-
const path = filepath ? `&path=${encodeURIComponent(filepath)}` : "";
|
|
520
|
+
const path = filepath ? `&path=${encodeURIComponent(decodeURIComponent(filepath))}` : "";
|
|
492
521
|
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}`;
|
|
493
522
|
return archiveUrl;
|
|
494
523
|
}
|
|
@@ -515,7 +544,10 @@ function getBitbucketRequestOptions(config) {
|
|
|
515
544
|
if (config.token) {
|
|
516
545
|
headers.Authorization = `Bearer ${config.token}`;
|
|
517
546
|
} else if (config.username && config.appPassword) {
|
|
518
|
-
const buffer = Buffer.from(
|
|
547
|
+
const buffer = Buffer.from(
|
|
548
|
+
`${config.username}:${config.appPassword}`,
|
|
549
|
+
"utf8"
|
|
550
|
+
);
|
|
519
551
|
headers.Authorization = `Basic ${buffer.toString("base64")}`;
|
|
520
552
|
}
|
|
521
553
|
return {
|
|
@@ -583,14 +615,22 @@ BitbucketCloudIntegration.factory = ({
|
|
|
583
615
|
config
|
|
584
616
|
}) => {
|
|
585
617
|
var _a;
|
|
586
|
-
const configs = readBitbucketCloudIntegrationConfigs(
|
|
587
|
-
|
|
618
|
+
const configs = readBitbucketCloudIntegrationConfigs(
|
|
619
|
+
(_a = config.getOptionalConfigArray("integrations.bitbucketCloud")) != null ? _a : []
|
|
620
|
+
);
|
|
621
|
+
return basicIntegrations(
|
|
622
|
+
configs.map((c) => new _BitbucketCloudIntegration(c)),
|
|
623
|
+
(i) => i.config.host
|
|
624
|
+
);
|
|
588
625
|
};
|
|
589
626
|
|
|
590
627
|
async function getBitbucketCloudDefaultBranch(url, config) {
|
|
591
628
|
const { name: repoName, owner: project } = parseGitUrl(url);
|
|
592
629
|
const branchUrl = `${config.apiBaseUrl}/repositories/${project}/${repoName}`;
|
|
593
|
-
const response = await fetch(
|
|
630
|
+
const response = await fetch(
|
|
631
|
+
branchUrl,
|
|
632
|
+
getBitbucketCloudRequestOptions(config)
|
|
633
|
+
);
|
|
594
634
|
if (!response.ok) {
|
|
595
635
|
const message = `Failed to retrieve default branch from ${branchUrl}, ${response.status} ${response.statusText}`;
|
|
596
636
|
throw new Error(message);
|
|
@@ -598,7 +638,9 @@ async function getBitbucketCloudDefaultBranch(url, config) {
|
|
|
598
638
|
const repoInfo = await response.json();
|
|
599
639
|
const defaultBranch = repoInfo.mainbranch.name;
|
|
600
640
|
if (!defaultBranch) {
|
|
601
|
-
throw new Error(
|
|
641
|
+
throw new Error(
|
|
642
|
+
`Failed to read default branch from ${branchUrl}. Response ${response.status} ${response.json()}`
|
|
643
|
+
);
|
|
602
644
|
}
|
|
603
645
|
return defaultBranch;
|
|
604
646
|
}
|
|
@@ -634,7 +676,10 @@ function getBitbucketCloudFileFetchUrl(url, config) {
|
|
|
634
676
|
function getBitbucketCloudRequestOptions(config) {
|
|
635
677
|
const headers = {};
|
|
636
678
|
if (config.username && config.appPassword) {
|
|
637
|
-
const buffer = Buffer.from(
|
|
679
|
+
const buffer = Buffer.from(
|
|
680
|
+
`${config.username}:${config.appPassword}`,
|
|
681
|
+
"utf8"
|
|
682
|
+
);
|
|
638
683
|
headers.Authorization = `Basic ${buffer.toString("base64")}`;
|
|
639
684
|
}
|
|
640
685
|
return {
|
|
@@ -646,8 +691,12 @@ function readBitbucketServerIntegrationConfig(config) {
|
|
|
646
691
|
const host = config.getString("host");
|
|
647
692
|
let apiBaseUrl = config.getOptionalString("apiBaseUrl");
|
|
648
693
|
const token = config.getOptionalString("token");
|
|
694
|
+
const username = config.getOptionalString("username");
|
|
695
|
+
const password = config.getOptionalString("password");
|
|
649
696
|
if (!isValidHost(host)) {
|
|
650
|
-
throw new Error(
|
|
697
|
+
throw new Error(
|
|
698
|
+
`Invalid Bitbucket Server integration config, '${host}' is not a valid host`
|
|
699
|
+
);
|
|
651
700
|
}
|
|
652
701
|
if (apiBaseUrl) {
|
|
653
702
|
apiBaseUrl = trimEnd(apiBaseUrl, "/");
|
|
@@ -657,7 +706,9 @@ function readBitbucketServerIntegrationConfig(config) {
|
|
|
657
706
|
return {
|
|
658
707
|
host,
|
|
659
708
|
apiBaseUrl,
|
|
660
|
-
token
|
|
709
|
+
token,
|
|
710
|
+
username,
|
|
711
|
+
password
|
|
661
712
|
};
|
|
662
713
|
}
|
|
663
714
|
function readBitbucketServerIntegrationConfigs(configs) {
|
|
@@ -681,19 +732,16 @@ const _BitbucketServerIntegration = class {
|
|
|
681
732
|
const resolved = defaultScmResolveUrl(options);
|
|
682
733
|
if (options.lineNumber) {
|
|
683
734
|
const url = new URL(resolved);
|
|
684
|
-
|
|
685
|
-
url.hash = `${filename}-${options.lineNumber}`;
|
|
735
|
+
url.hash = options.lineNumber.toString();
|
|
686
736
|
return url.toString();
|
|
687
737
|
}
|
|
688
738
|
return resolved;
|
|
689
739
|
}
|
|
690
740
|
resolveEditUrl(url) {
|
|
691
|
-
|
|
692
|
-
|
|
693
|
-
|
|
694
|
-
|
|
695
|
-
editUrl.searchParams.set("at", urlData.ref);
|
|
696
|
-
return editUrl.toString();
|
|
741
|
+
if (url.includes("?")) {
|
|
742
|
+
return url.substring(0, url.indexOf("?"));
|
|
743
|
+
}
|
|
744
|
+
return url;
|
|
697
745
|
}
|
|
698
746
|
};
|
|
699
747
|
let BitbucketServerIntegration = _BitbucketServerIntegration;
|
|
@@ -701,14 +749,22 @@ BitbucketServerIntegration.factory = ({
|
|
|
701
749
|
config
|
|
702
750
|
}) => {
|
|
703
751
|
var _a;
|
|
704
|
-
const configs = readBitbucketServerIntegrationConfigs(
|
|
705
|
-
|
|
752
|
+
const configs = readBitbucketServerIntegrationConfigs(
|
|
753
|
+
(_a = config.getOptionalConfigArray("integrations.bitbucketServer")) != null ? _a : []
|
|
754
|
+
);
|
|
755
|
+
return basicIntegrations(
|
|
756
|
+
configs.map((c) => new _BitbucketServerIntegration(c)),
|
|
757
|
+
(i) => i.config.host
|
|
758
|
+
);
|
|
706
759
|
};
|
|
707
760
|
|
|
708
761
|
async function getBitbucketServerDefaultBranch(url, config) {
|
|
709
762
|
const { name: repoName, owner: project } = parseGitUrl(url);
|
|
710
763
|
let branchUrl = `${config.apiBaseUrl}/projects/${project}/repos/${repoName}/default-branch`;
|
|
711
|
-
let response = await fetch(
|
|
764
|
+
let response = await fetch(
|
|
765
|
+
branchUrl,
|
|
766
|
+
getBitbucketServerRequestOptions(config)
|
|
767
|
+
);
|
|
712
768
|
if (response.status === 404) {
|
|
713
769
|
branchUrl = `${config.apiBaseUrl}/projects/${project}/repos/${repoName}/branches/default`;
|
|
714
770
|
response = await fetch(branchUrl, getBitbucketServerRequestOptions(config));
|
|
@@ -720,7 +776,9 @@ async function getBitbucketServerDefaultBranch(url, config) {
|
|
|
720
776
|
const { displayId } = await response.json();
|
|
721
777
|
const defaultBranch = displayId;
|
|
722
778
|
if (!defaultBranch) {
|
|
723
|
-
throw new Error(
|
|
779
|
+
throw new Error(
|
|
780
|
+
`Failed to read default branch from ${branchUrl}. Response ${response.status} ${response.json()}`
|
|
781
|
+
);
|
|
724
782
|
}
|
|
725
783
|
return defaultBranch;
|
|
726
784
|
}
|
|
@@ -750,6 +808,10 @@ function getBitbucketServerRequestOptions(config) {
|
|
|
750
808
|
if (config.token) {
|
|
751
809
|
headers.Authorization = `Bearer ${config.token}`;
|
|
752
810
|
}
|
|
811
|
+
if (config.username && config.password) {
|
|
812
|
+
const buffer = Buffer.from(`${config.username}:${config.password}`, "utf8");
|
|
813
|
+
headers.Authorization = `Basic ${buffer.toString("base64")}`;
|
|
814
|
+
}
|
|
753
815
|
return {
|
|
754
816
|
headers
|
|
755
817
|
};
|
|
@@ -763,13 +825,21 @@ function readGerritIntegrationConfig(config) {
|
|
|
763
825
|
const username = config.getOptionalString("username");
|
|
764
826
|
const password = config.getOptionalString("password");
|
|
765
827
|
if (!isValidHost(host)) {
|
|
766
|
-
throw new Error(
|
|
828
|
+
throw new Error(
|
|
829
|
+
`Invalid Gerrit integration config, '${host}' is not a valid host`
|
|
830
|
+
);
|
|
767
831
|
} else if (baseUrl && !isValidUrl(baseUrl)) {
|
|
768
|
-
throw new Error(
|
|
832
|
+
throw new Error(
|
|
833
|
+
`Invalid Gerrit integration config, '${baseUrl}' is not a valid baseUrl`
|
|
834
|
+
);
|
|
769
835
|
} else if (cloneUrl && !isValidUrl(cloneUrl)) {
|
|
770
|
-
throw new Error(
|
|
836
|
+
throw new Error(
|
|
837
|
+
`Invalid Gerrit integration config, '${cloneUrl}' is not a valid cloneUrl`
|
|
838
|
+
);
|
|
771
839
|
} else if (gitilesBaseUrl && !isValidUrl(gitilesBaseUrl)) {
|
|
772
|
-
throw new Error(
|
|
840
|
+
throw new Error(
|
|
841
|
+
`Invalid Gerrit integration config, '${gitilesBaseUrl}' is not a valid gitilesBaseUrl`
|
|
842
|
+
);
|
|
773
843
|
}
|
|
774
844
|
if (baseUrl) {
|
|
775
845
|
baseUrl = trimEnd(baseUrl, "/");
|
|
@@ -828,7 +898,9 @@ function getAuthenticationPrefix(config) {
|
|
|
828
898
|
}
|
|
829
899
|
function getGerritBranchApiUrl(config, url) {
|
|
830
900
|
const { branch, project } = parseGerritGitilesUrl(config, url);
|
|
831
|
-
return `${config.baseUrl}${getAuthenticationPrefix(
|
|
901
|
+
return `${config.baseUrl}${getAuthenticationPrefix(
|
|
902
|
+
config
|
|
903
|
+
)}projects/${encodeURIComponent(project)}/branches/${branch}`;
|
|
832
904
|
}
|
|
833
905
|
function getGerritCloneRepoUrl(config, url) {
|
|
834
906
|
const { project } = parseGerritGitilesUrl(config, url);
|
|
@@ -836,7 +908,11 @@ function getGerritCloneRepoUrl(config, url) {
|
|
|
836
908
|
}
|
|
837
909
|
function getGerritFileContentsApiUrl(config, url) {
|
|
838
910
|
const { branch, filePath, project } = parseGerritGitilesUrl(config, url);
|
|
839
|
-
return `${config.baseUrl}${getAuthenticationPrefix(
|
|
911
|
+
return `${config.baseUrl}${getAuthenticationPrefix(
|
|
912
|
+
config
|
|
913
|
+
)}projects/${encodeURIComponent(
|
|
914
|
+
project
|
|
915
|
+
)}/branches/${branch}/files/${encodeURIComponent(filePath)}/content`;
|
|
840
916
|
}
|
|
841
917
|
function getGerritProjectsApiUrl(config) {
|
|
842
918
|
return `${config.baseUrl}${getAuthenticationPrefix(config)}projects/`;
|
|
@@ -858,10 +934,14 @@ async function parseGerritJsonResponse(response) {
|
|
|
858
934
|
try {
|
|
859
935
|
return JSON.parse(responseBody.slice(GERRIT_BODY_PREFIX.length));
|
|
860
936
|
} catch (ex) {
|
|
861
|
-
throw new Error(
|
|
937
|
+
throw new Error(
|
|
938
|
+
`Invalid response from Gerrit: ${responseBody.slice(0, 10)} - ${ex}`
|
|
939
|
+
);
|
|
862
940
|
}
|
|
863
941
|
}
|
|
864
|
-
throw new Error(
|
|
942
|
+
throw new Error(
|
|
943
|
+
`Gerrit JSON body prefix missing. Found: ${responseBody.slice(0, 10)}`
|
|
944
|
+
);
|
|
865
945
|
}
|
|
866
946
|
|
|
867
947
|
const _GerritIntegration = class {
|
|
@@ -901,8 +981,13 @@ const _GerritIntegration = class {
|
|
|
901
981
|
let GerritIntegration = _GerritIntegration;
|
|
902
982
|
GerritIntegration.factory = ({ config }) => {
|
|
903
983
|
var _a;
|
|
904
|
-
const configs = readGerritIntegrationConfigs(
|
|
905
|
-
|
|
984
|
+
const configs = readGerritIntegrationConfigs(
|
|
985
|
+
(_a = config.getOptionalConfigArray("integrations.gerrit")) != null ? _a : []
|
|
986
|
+
);
|
|
987
|
+
return basicIntegrations(
|
|
988
|
+
configs.map((c) => new _GerritIntegration(c)),
|
|
989
|
+
(i) => i.config.host
|
|
990
|
+
);
|
|
906
991
|
};
|
|
907
992
|
|
|
908
993
|
const GITHUB_HOST = "github.com";
|
|
@@ -920,10 +1005,14 @@ function readGitHubIntegrationConfig(config) {
|
|
|
920
1005
|
clientSecret: c.getString("clientSecret"),
|
|
921
1006
|
webhookSecret: c.getString("webhookSecret"),
|
|
922
1007
|
privateKey: c.getString("privateKey"),
|
|
923
|
-
allowedInstallationOwners: c.getOptionalStringArray(
|
|
1008
|
+
allowedInstallationOwners: c.getOptionalStringArray(
|
|
1009
|
+
"allowedInstallationOwners"
|
|
1010
|
+
)
|
|
924
1011
|
}));
|
|
925
1012
|
if (!isValidHost(host)) {
|
|
926
|
-
throw new Error(
|
|
1013
|
+
throw new Error(
|
|
1014
|
+
`Invalid GitHub integration config, '${host}' is not a valid host`
|
|
1015
|
+
);
|
|
927
1016
|
}
|
|
928
1017
|
if (apiBaseUrl) {
|
|
929
1018
|
apiBaseUrl = trimEnd(apiBaseUrl, "/");
|
|
@@ -1028,6 +1117,7 @@ class GithubAppManager {
|
|
|
1028
1117
|
}
|
|
1029
1118
|
const cacheKey = repo ? `${owner}/${repo}` : owner;
|
|
1030
1119
|
return this.cache.getOrCreateToken(cacheKey, async () => {
|
|
1120
|
+
var _a2;
|
|
1031
1121
|
const result = await this.appClient.apps.createInstallationAccessToken({
|
|
1032
1122
|
installation_id: installationId,
|
|
1033
1123
|
headers: HEADERS
|
|
@@ -1037,12 +1127,17 @@ class GithubAppManager {
|
|
|
1037
1127
|
baseUrl: this.baseUrl,
|
|
1038
1128
|
auth: result.data.token
|
|
1039
1129
|
});
|
|
1040
|
-
const repos = await installationClient.paginate(
|
|
1041
|
-
|
|
1130
|
+
const repos = await installationClient.paginate(
|
|
1131
|
+
installationClient.apps.listReposAccessibleToInstallation
|
|
1132
|
+
);
|
|
1133
|
+
const repositories = (_a2 = repos.repositories) != null ? _a2 : repos;
|
|
1134
|
+
const hasRepo = repositories.some((repository) => {
|
|
1042
1135
|
return repository.name === repo;
|
|
1043
1136
|
});
|
|
1044
1137
|
if (!hasRepo) {
|
|
1045
|
-
throw new Error(
|
|
1138
|
+
throw new Error(
|
|
1139
|
+
`The Backstage GitHub application used in the ${owner} organization does not have access to a repository with the name ${repo}`
|
|
1140
|
+
);
|
|
1046
1141
|
}
|
|
1047
1142
|
}
|
|
1048
1143
|
return {
|
|
@@ -1056,17 +1151,21 @@ class GithubAppManager {
|
|
|
1056
1151
|
}
|
|
1057
1152
|
async getInstallationData(owner) {
|
|
1058
1153
|
const allInstallations = await this.getInstallations();
|
|
1059
|
-
const installation = allInstallations.find(
|
|
1060
|
-
|
|
1061
|
-
|
|
1062
|
-
|
|
1154
|
+
const installation = allInstallations.find(
|
|
1155
|
+
(inst) => {
|
|
1156
|
+
var _a, _b;
|
|
1157
|
+
return ((_b = (_a = inst.account) == null ? void 0 : _a.login) == null ? void 0 : _b.toLocaleLowerCase("en-US")) === owner.toLocaleLowerCase("en-US");
|
|
1158
|
+
}
|
|
1159
|
+
);
|
|
1063
1160
|
if (installation) {
|
|
1064
1161
|
return {
|
|
1065
1162
|
installationId: installation.id,
|
|
1066
1163
|
suspended: Boolean(installation.suspended_by)
|
|
1067
1164
|
};
|
|
1068
1165
|
}
|
|
1069
|
-
const notFoundError = new Error(
|
|
1166
|
+
const notFoundError = new Error(
|
|
1167
|
+
`No app installation found for ${owner} in ${this.baseAuthConfig.appId}`
|
|
1168
|
+
);
|
|
1070
1169
|
notFoundError.name = "NotFoundError";
|
|
1071
1170
|
throw notFoundError;
|
|
1072
1171
|
}
|
|
@@ -1080,14 +1179,23 @@ class GithubAppCredentialsMux {
|
|
|
1080
1179
|
if (!this.apps.length) {
|
|
1081
1180
|
return [];
|
|
1082
1181
|
}
|
|
1083
|
-
const installs = await Promise.all(
|
|
1182
|
+
const installs = await Promise.all(
|
|
1183
|
+
this.apps.map((app) => app.getInstallations())
|
|
1184
|
+
);
|
|
1084
1185
|
return installs.flat();
|
|
1085
1186
|
}
|
|
1086
1187
|
async getAppToken(owner, repo) {
|
|
1087
1188
|
if (this.apps.length === 0) {
|
|
1088
1189
|
return void 0;
|
|
1089
1190
|
}
|
|
1090
|
-
const results = await Promise.all(
|
|
1191
|
+
const results = await Promise.all(
|
|
1192
|
+
this.apps.map(
|
|
1193
|
+
(app) => app.getInstallationCredentials(owner, repo).then(
|
|
1194
|
+
(credentials) => ({ credentials, error: void 0 }),
|
|
1195
|
+
(error) => ({ credentials: void 0, error })
|
|
1196
|
+
)
|
|
1197
|
+
)
|
|
1198
|
+
);
|
|
1091
1199
|
const result = results.find((resultItem) => resultItem.credentials);
|
|
1092
1200
|
if (result) {
|
|
1093
1201
|
return result.credentials.accessToken;
|
|
@@ -1124,7 +1232,10 @@ const _SingleInstanceGithubCredentialsProvider = class {
|
|
|
1124
1232
|
};
|
|
1125
1233
|
let SingleInstanceGithubCredentialsProvider = _SingleInstanceGithubCredentialsProvider;
|
|
1126
1234
|
SingleInstanceGithubCredentialsProvider.create = (config) => {
|
|
1127
|
-
return new _SingleInstanceGithubCredentialsProvider(
|
|
1235
|
+
return new _SingleInstanceGithubCredentialsProvider(
|
|
1236
|
+
new GithubAppCredentialsMux(config),
|
|
1237
|
+
config.token
|
|
1238
|
+
);
|
|
1128
1239
|
};
|
|
1129
1240
|
|
|
1130
1241
|
class DefaultGithubCredentialsProvider {
|
|
@@ -1143,7 +1254,9 @@ class DefaultGithubCredentialsProvider {
|
|
|
1143
1254
|
const parsed = new URL(opts.url);
|
|
1144
1255
|
const provider = this.providers.get(parsed.host);
|
|
1145
1256
|
if (!provider) {
|
|
1146
|
-
throw new Error(
|
|
1257
|
+
throw new Error(
|
|
1258
|
+
`There is no GitHub integration that matches ${opts.url}. Please add a configuration for an integration.`
|
|
1259
|
+
);
|
|
1147
1260
|
}
|
|
1148
1261
|
return provider.getCredentials(opts);
|
|
1149
1262
|
}
|
|
@@ -1172,13 +1285,21 @@ const _GitHubIntegration = class {
|
|
|
1172
1285
|
let GitHubIntegration = _GitHubIntegration;
|
|
1173
1286
|
GitHubIntegration.factory = ({ config }) => {
|
|
1174
1287
|
var _a;
|
|
1175
|
-
const configs = readGitHubIntegrationConfigs(
|
|
1176
|
-
|
|
1288
|
+
const configs = readGitHubIntegrationConfigs(
|
|
1289
|
+
(_a = config.getOptionalConfigArray("integrations.github")) != null ? _a : []
|
|
1290
|
+
);
|
|
1291
|
+
return basicIntegrations(
|
|
1292
|
+
configs.map((c) => new _GitHubIntegration(c)),
|
|
1293
|
+
(i) => i.config.host
|
|
1294
|
+
);
|
|
1177
1295
|
};
|
|
1178
1296
|
function replaceGitHubUrlType(url, type) {
|
|
1179
|
-
return url.replace(
|
|
1180
|
-
|
|
1181
|
-
|
|
1297
|
+
return url.replace(
|
|
1298
|
+
/\/\/([^/]+)\/([^/]+)\/([^/]+)\/(blob|tree|edit)\//,
|
|
1299
|
+
(_, host, owner, repo) => {
|
|
1300
|
+
return `//${host}/${owner}/${repo}/${type}/`;
|
|
1301
|
+
}
|
|
1302
|
+
);
|
|
1182
1303
|
}
|
|
1183
1304
|
|
|
1184
1305
|
const GITLAB_HOST = "gitlab.com";
|
|
@@ -1199,11 +1320,17 @@ function readGitLabIntegrationConfig(config) {
|
|
|
1199
1320
|
baseUrl = `https://${host}`;
|
|
1200
1321
|
}
|
|
1201
1322
|
if (!isValidHost(host)) {
|
|
1202
|
-
throw new Error(
|
|
1323
|
+
throw new Error(
|
|
1324
|
+
`Invalid GitLab integration config, '${host}' is not a valid host`
|
|
1325
|
+
);
|
|
1203
1326
|
} else if (!apiBaseUrl || !isValidUrl(apiBaseUrl)) {
|
|
1204
|
-
throw new Error(
|
|
1327
|
+
throw new Error(
|
|
1328
|
+
`Invalid GitLab integration config, '${apiBaseUrl}' is not a valid apiBaseUrl`
|
|
1329
|
+
);
|
|
1205
1330
|
} else if (!isValidUrl(baseUrl)) {
|
|
1206
|
-
throw new Error(
|
|
1331
|
+
throw new Error(
|
|
1332
|
+
`Invalid GitLab integration config, '${baseUrl}' is not a valid baseUrl`
|
|
1333
|
+
);
|
|
1207
1334
|
}
|
|
1208
1335
|
return { host, token, apiBaseUrl, baseUrl };
|
|
1209
1336
|
}
|
|
@@ -1218,13 +1345,17 @@ function readGitLabIntegrationConfigs(configs) {
|
|
|
1218
1345
|
}
|
|
1219
1346
|
return result;
|
|
1220
1347
|
}
|
|
1348
|
+
function getGitLabIntegrationRelativePath(config) {
|
|
1349
|
+
let relativePath = "";
|
|
1350
|
+
if (config.host !== GITLAB_HOST) {
|
|
1351
|
+
relativePath = new URL(config.baseUrl).pathname;
|
|
1352
|
+
}
|
|
1353
|
+
return trimEnd(relativePath, "/");
|
|
1354
|
+
}
|
|
1221
1355
|
|
|
1222
1356
|
async function getGitLabFileFetchUrl(url, config) {
|
|
1223
|
-
|
|
1224
|
-
|
|
1225
|
-
return buildProjectUrl(url, projectID).toString();
|
|
1226
|
-
}
|
|
1227
|
-
return buildRawUrl(url).toString();
|
|
1357
|
+
const projectID = await getProjectId(url, config);
|
|
1358
|
+
return buildProjectUrl(url, projectID, config).toString();
|
|
1228
1359
|
}
|
|
1229
1360
|
function getGitLabRequestOptions(config) {
|
|
1230
1361
|
const { token = "" } = config;
|
|
@@ -1234,32 +1365,15 @@ function getGitLabRequestOptions(config) {
|
|
|
1234
1365
|
}
|
|
1235
1366
|
};
|
|
1236
1367
|
}
|
|
1237
|
-
function
|
|
1368
|
+
function buildProjectUrl(target, projectID, config) {
|
|
1238
1369
|
try {
|
|
1239
1370
|
const url = new URL(target);
|
|
1240
|
-
const
|
|
1241
|
-
const blobIndex = splitPath.indexOf("blob", 2);
|
|
1242
|
-
if (blobIndex < 2 || blobIndex === splitPath.length - 1) {
|
|
1243
|
-
throw new InputError("Wrong GitLab URL");
|
|
1244
|
-
}
|
|
1245
|
-
const repoPath = splitPath.slice(0, blobIndex);
|
|
1246
|
-
const restOfPath = splitPath.slice(blobIndex + 1);
|
|
1247
|
-
if (!restOfPath.join("/").match(/\.(yaml|yml)$/)) {
|
|
1248
|
-
throw new InputError("Wrong GitLab URL");
|
|
1249
|
-
}
|
|
1250
|
-
url.pathname = [...repoPath, "raw", ...restOfPath].join("/");
|
|
1251
|
-
return url;
|
|
1252
|
-
} catch (e) {
|
|
1253
|
-
throw new InputError(`Incorrect url: ${target}, ${e}`);
|
|
1254
|
-
}
|
|
1255
|
-
}
|
|
1256
|
-
function buildProjectUrl(target, projectID) {
|
|
1257
|
-
try {
|
|
1258
|
-
const url = new URL(target);
|
|
1259
|
-
const branchAndFilePath = url.pathname.split("/-/blob/")[1];
|
|
1371
|
+
const branchAndFilePath = url.pathname.split("/blob/").slice(1).join("/blob/");
|
|
1260
1372
|
const [branch, ...filePath] = branchAndFilePath.split("/");
|
|
1373
|
+
const relativePath = getGitLabIntegrationRelativePath(config);
|
|
1261
1374
|
url.pathname = [
|
|
1262
|
-
|
|
1375
|
+
...relativePath ? [relativePath] : [],
|
|
1376
|
+
"api/v4/projects",
|
|
1263
1377
|
projectID,
|
|
1264
1378
|
"repository/files",
|
|
1265
1379
|
encodeURIComponent(decodeURIComponent(filePath.join("/"))),
|
|
@@ -1273,16 +1387,29 @@ function buildProjectUrl(target, projectID) {
|
|
|
1273
1387
|
}
|
|
1274
1388
|
async function getProjectId(target, config) {
|
|
1275
1389
|
const url = new URL(target);
|
|
1276
|
-
if (!url.pathname.includes("
|
|
1390
|
+
if (!url.pathname.includes("/blob/")) {
|
|
1277
1391
|
throw new Error("Please provide full path to yaml file from GitLab");
|
|
1278
1392
|
}
|
|
1279
1393
|
try {
|
|
1280
|
-
|
|
1281
|
-
const
|
|
1282
|
-
|
|
1394
|
+
let repo = url.pathname.split("/-/blob/")[0].split("/blob/")[0];
|
|
1395
|
+
const relativePath = getGitLabIntegrationRelativePath(config);
|
|
1396
|
+
if (relativePath) {
|
|
1397
|
+
repo = repo.replace(relativePath, "");
|
|
1398
|
+
}
|
|
1399
|
+
const repoIDLookup = new URL(
|
|
1400
|
+
`${url.origin}${relativePath}/api/v4/projects/${encodeURIComponent(
|
|
1401
|
+
repo.replace(/^\//, "")
|
|
1402
|
+
)}`
|
|
1403
|
+
);
|
|
1404
|
+
const response = await fetch(
|
|
1405
|
+
repoIDLookup.toString(),
|
|
1406
|
+
getGitLabRequestOptions(config)
|
|
1407
|
+
);
|
|
1283
1408
|
const data = await response.json();
|
|
1284
1409
|
if (!response.ok) {
|
|
1285
|
-
throw new Error(
|
|
1410
|
+
throw new Error(
|
|
1411
|
+
`GitLab Error '${data.error}', ${data.error_description}`
|
|
1412
|
+
);
|
|
1286
1413
|
}
|
|
1287
1414
|
return Number(data.id);
|
|
1288
1415
|
} catch (e) {
|
|
@@ -1313,8 +1440,13 @@ const _GitLabIntegration = class {
|
|
|
1313
1440
|
let GitLabIntegration = _GitLabIntegration;
|
|
1314
1441
|
GitLabIntegration.factory = ({ config }) => {
|
|
1315
1442
|
var _a;
|
|
1316
|
-
const configs = readGitLabIntegrationConfigs(
|
|
1317
|
-
|
|
1443
|
+
const configs = readGitLabIntegrationConfigs(
|
|
1444
|
+
(_a = config.getOptionalConfigArray("integrations.gitlab")) != null ? _a : []
|
|
1445
|
+
);
|
|
1446
|
+
return basicIntegrations(
|
|
1447
|
+
configs.map((c) => new _GitLabIntegration(c)),
|
|
1448
|
+
(i) => i.config.host
|
|
1449
|
+
);
|
|
1318
1450
|
};
|
|
1319
1451
|
function replaceGitLabUrlType(url, type) {
|
|
1320
1452
|
return url.replace(/\/\-\/(blob|tree|edit)\//, `/-/${type}/`);
|
|
@@ -1373,10 +1505,21 @@ class ScmIntegrations {
|
|
|
1373
1505
|
return this.byType.gitlab;
|
|
1374
1506
|
}
|
|
1375
1507
|
list() {
|
|
1376
|
-
return Object.values(this.byType).flatMap(
|
|
1508
|
+
return Object.values(this.byType).flatMap(
|
|
1509
|
+
(i) => i.list()
|
|
1510
|
+
);
|
|
1377
1511
|
}
|
|
1378
1512
|
byUrl(url) {
|
|
1379
|
-
|
|
1513
|
+
let candidates = Object.values(this.byType).map((i) => i.byUrl(url)).filter(Boolean);
|
|
1514
|
+
if (candidates.length > 1) {
|
|
1515
|
+
const filteredCandidates = candidates.filter(
|
|
1516
|
+
(x) => !(x instanceof BitbucketIntegration)
|
|
1517
|
+
);
|
|
1518
|
+
if (filteredCandidates.length !== 0) {
|
|
1519
|
+
candidates = filteredCandidates;
|
|
1520
|
+
}
|
|
1521
|
+
}
|
|
1522
|
+
return candidates[0];
|
|
1380
1523
|
}
|
|
1381
1524
|
byHost(host) {
|
|
1382
1525
|
return Object.values(this.byType).map((i) => i.byHost(host)).find(Boolean);
|
|
@@ -1397,5 +1540,5 @@ class ScmIntegrations {
|
|
|
1397
1540
|
}
|
|
1398
1541
|
}
|
|
1399
1542
|
|
|
1400
|
-
export { AwsS3Integration, AzureIntegration, BitbucketCloudIntegration, BitbucketIntegration, BitbucketServerIntegration, DefaultGithubCredentialsProvider, GerritIntegration, GitHubIntegration, GitLabIntegration, GithubAppCredentialsMux, ScmIntegrations, SingleInstanceGithubCredentialsProvider, defaultScmResolveUrl, getAzureCommitsUrl, getAzureDownloadUrl, getAzureFileFetchUrl, getAzureRequestOptions, getBitbucketCloudDefaultBranch, getBitbucketCloudDownloadUrl, getBitbucketCloudFileFetchUrl, getBitbucketCloudRequestOptions, getBitbucketDefaultBranch, getBitbucketDownloadUrl, getBitbucketFileFetchUrl, getBitbucketRequestOptions, getBitbucketServerDefaultBranch, getBitbucketServerDownloadUrl, getBitbucketServerFileFetchUrl, getBitbucketServerRequestOptions, getGerritBranchApiUrl, getGerritCloneRepoUrl, getGerritFileContentsApiUrl, getGerritProjectsApiUrl, getGerritRequestOptions, getGitHubFileFetchUrl, getGitHubRequestOptions, getGitLabFileFetchUrl, getGitLabRequestOptions, parseGerritGitilesUrl, parseGerritJsonResponse, readAwsS3IntegrationConfig, readAwsS3IntegrationConfigs, readAzureIntegrationConfig, readAzureIntegrationConfigs, readBitbucketCloudIntegrationConfig, readBitbucketCloudIntegrationConfigs, readBitbucketIntegrationConfig, readBitbucketIntegrationConfigs, readBitbucketServerIntegrationConfig, readBitbucketServerIntegrationConfigs, readGerritIntegrationConfig, readGerritIntegrationConfigs, readGitHubIntegrationConfig, readGitHubIntegrationConfigs, readGitLabIntegrationConfig, readGitLabIntegrationConfigs, readGoogleGcsIntegrationConfig, replaceGitHubUrlType, replaceGitLabUrlType };
|
|
1543
|
+
export { AwsS3Integration, AzureIntegration, BitbucketCloudIntegration, BitbucketIntegration, BitbucketServerIntegration, DefaultGithubCredentialsProvider, GerritIntegration, GitHubIntegration, GitLabIntegration, GithubAppCredentialsMux, ScmIntegrations, SingleInstanceGithubCredentialsProvider, defaultScmResolveUrl, getAzureCommitsUrl, getAzureDownloadUrl, getAzureFileFetchUrl, getAzureRequestOptions, getBitbucketCloudDefaultBranch, getBitbucketCloudDownloadUrl, getBitbucketCloudFileFetchUrl, getBitbucketCloudRequestOptions, getBitbucketDefaultBranch, getBitbucketDownloadUrl, getBitbucketFileFetchUrl, getBitbucketRequestOptions, getBitbucketServerDefaultBranch, getBitbucketServerDownloadUrl, getBitbucketServerFileFetchUrl, getBitbucketServerRequestOptions, getGerritBranchApiUrl, getGerritCloneRepoUrl, getGerritFileContentsApiUrl, getGerritProjectsApiUrl, getGerritRequestOptions, getGitHubFileFetchUrl, getGitHubRequestOptions, getGitLabFileFetchUrl, getGitLabIntegrationRelativePath, getGitLabRequestOptions, parseGerritGitilesUrl, parseGerritJsonResponse, readAwsS3IntegrationConfig, readAwsS3IntegrationConfigs, readAzureIntegrationConfig, readAzureIntegrationConfigs, readBitbucketCloudIntegrationConfig, readBitbucketCloudIntegrationConfigs, readBitbucketIntegrationConfig, readBitbucketIntegrationConfigs, readBitbucketServerIntegrationConfig, readBitbucketServerIntegrationConfigs, readGerritIntegrationConfig, readGerritIntegrationConfigs, readGitHubIntegrationConfig, readGitHubIntegrationConfigs, readGitLabIntegrationConfig, readGitLabIntegrationConfigs, readGoogleGcsIntegrationConfig, replaceGitHubUrlType, replaceGitLabUrlType };
|
|
1401
1544
|
//# sourceMappingURL=index.esm.js.map
|