diff --git a/plugins/catalog-import/src/api/AzureDevops.ts b/plugins/catalog-import/src/api/AzureDevops.ts
index d2cf9eacd7..d028c75129 100644
--- a/plugins/catalog-import/src/api/AzureDevops.ts
+++ b/plugins/catalog-import/src/api/AzureDevops.ts
@@ -13,12 +13,11 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-import { AzureIntegration } from '@backstage/integration';
import { ScmAuthApi } from '@backstage/integration-react';
import { ConfigApi } from '@backstage/core-plugin-api';
import { getBranchName, getCatalogFilename } from '../components/helpers';
import { createAzurePullRequest } from './AzureRepoApiClient';
-import parseGitUrl from 'git-url-parse';
+import { parseRepoUrl } from './util';
export interface AzureRepoParts {
tenantUrl: string;
@@ -26,17 +25,19 @@ export interface AzureRepoParts {
project: string;
}
-export function parseAzureUrl(
- repoUrl: string,
- integration: AzureIntegration,
-): AzureRepoParts {
- const { organization, owner, name } = parseGitUrl(repoUrl);
- const tenantUrl = `https://${integration.config.host}/${organization}`;
- return { tenantUrl, repoName: name, project: owner };
+export function parseAzureUrl(repoUrl: string): AzureRepoParts {
+ const { org, repo, project, host } = parseRepoUrl(repoUrl);
+ if (!org || !repo || !project) {
+ throw new Error(
+ 'Invalid AzureDevops Repository. Please use a valid repository url and try again ',
+ );
+ }
+ const tenantUrl = `https://${host}/${org}`;
+
+ return { tenantUrl, repoName: repo, project: project };
}
export async function submitAzurePrToRepo(
- integration: AzureIntegration,
options: {
title: string;
body: string;
@@ -57,10 +58,7 @@ export async function submitAzurePrToRepo(
repoWrite: true,
},
});
- const { tenantUrl, repoName, project } = parseAzureUrl(
- repositoryUrl,
- integration,
- );
+ const { tenantUrl, repoName, project } = parseAzureUrl(repositoryUrl);
const result = await createAzurePullRequest({
token,
fileContent,
diff --git a/plugins/catalog-import/src/api/CatalogImportClient.ts b/plugins/catalog-import/src/api/CatalogImportClient.ts
index e05e3882c4..fe7fc5c59e 100644
--- a/plugins/catalog-import/src/api/CatalogImportClient.ts
+++ b/plugins/catalog-import/src/api/CatalogImportClient.ts
@@ -21,7 +21,6 @@ import {
IdentityApi,
} from '@backstage/core-plugin-api';
import {
- AzureIntegration,
GithubIntegration,
ScmIntegrationRegistry,
} from '@backstage/integration';
@@ -208,7 +207,6 @@ the component will become available.\n\nFor more information, read an \
}
case 'azure': {
return submitAzurePrToRepo(
- provider as AzureIntegration,
{
repositoryUrl,
fileContent,
diff --git a/plugins/catalog-import/src/api/util.test.ts b/plugins/catalog-import/src/api/util.test.ts
new file mode 100644
index 0000000000..3a14a2e7ae
--- /dev/null
+++ b/plugins/catalog-import/src/api/util.test.ts
@@ -0,0 +1,51 @@
+/*
+ * Copyright 2024 The Backstage Authors
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+import { parseRepoUrl } from './util';
+
+describe('parseRepoUrl', () => {
+ it('parses Azure DevOps Cloud url', async () => {
+ const result = parseRepoUrl(
+ 'https://dev.azure.com/organization/project/_git/repository?path=%2Fcatalog-info.yaml',
+ );
+
+ expect(result.host).toEqual('dev.azure.com');
+ expect(result.org).toEqual('organization');
+ expect(result.project).toEqual('project');
+ expect(result.repo).toEqual('repository');
+ });
+
+ it('parses Azure DevOps Server url', async () => {
+ const result = parseRepoUrl(
+ 'https://server.com/organization/project/_git/repository?path=%2Fcatalog-info.yaml',
+ );
+
+ expect(result.host).toEqual('server.com');
+ expect(result.org).toEqual('organization');
+ expect(result.project).toEqual('project');
+ expect(result.repo).toEqual('repository');
+ });
+
+ it('parses TFS subpath Url', async () => {
+ const result = parseRepoUrl(
+ 'https://server.com/tfs/organization/project/_git/repository?path=%2Fcatalog-info.yaml',
+ );
+
+ expect(result.host).toEqual('server.com/tfs');
+ expect(result.org).toEqual('organization');
+ expect(result.project).toEqual('project');
+ expect(result.repo).toEqual('repository');
+ });
+});
diff --git a/plugins/catalog-import/src/api/util.ts b/plugins/catalog-import/src/api/util.ts
new file mode 100644
index 0000000000..dc6b4ae2a6
--- /dev/null
+++ b/plugins/catalog-import/src/api/util.ts
@@ -0,0 +1,40 @@
+/*
+ * Copyright 2024 The Backstage Authors
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+export function parseRepoUrl(sourceUrl: string) {
+ const url = new URL(sourceUrl);
+
+ let host = url.host;
+ let org;
+ let project;
+ let repo;
+
+ const parts = url.pathname.split('/').map(part => decodeURIComponent(part));
+ if (parts[2] === '_git') {
+ org = parts[1];
+ project = repo = parts[3];
+ } else if (parts[3] === '_git') {
+ org = parts[1];
+ project = parts[2];
+ repo = parts[4];
+ } else if (parts[4] === '_git') {
+ host = `${host}/${parts[1]}`;
+ org = parts[2];
+ project = parts[3];
+ repo = parts[5];
+ }
+
+ return { host, org, project, repo };
+}
diff --git a/plugins/catalog-import/src/components/ImportInfoCard/ImportInfoCard.tsx b/plugins/catalog-import/src/components/ImportInfoCard/ImportInfoCard.tsx
index 67979f1a3d..be8a457572 100644
--- a/plugins/catalog-import/src/components/ImportInfoCard/ImportInfoCard.tsx
+++ b/plugins/catalog-import/src/components/ImportInfoCard/ImportInfoCard.tsx
@@ -29,8 +29,7 @@ import { useCatalogFilename } from '../../hooks';
*/
export interface ImportInfoCardProps {
exampleLocationUrl?: string;
- exampleGitRepositoryUrl?: string;
- exampleAzureRepositoryUrl?: string;
+ exampleRepositoryUrl?: string;
}
/**
@@ -41,8 +40,7 @@ export interface ImportInfoCardProps {
export const ImportInfoCard = (props: ImportInfoCardProps) => {
const {
exampleLocationUrl = 'https://github.com/backstage/backstage/blob/master/catalog-info.yaml',
- exampleGitRepositoryUrl = 'https://github.com/backstage/backstage',
- exampleAzureRepositoryUrl = 'https://dev.azure.com/org-name/project-name/_git/repo-name,
+ exampleRepositoryUrl = 'https://github.com/backstage/backstage',
} = props;
const configApi = useApi(configApiRef);
@@ -79,18 +77,10 @@ export const ImportInfoCard = (props: ImportInfoCardProps) => {
<>
Link to a repository{' '}
-
-
-
-
- GitHub Example: {exampleGitRepositoryUrl}
+
- Azure Example: {exampleAzureRepositoryUrl}
+ Example: {exampleRepositoryUrl}
The wizard discovers all {catalogFilename} files in the