From 8cfeb5fe08dd20d0fbeeaa0d2442ba6e8bff74a2 Mon Sep 17 00:00:00 2001 From: Patrik Oldsberg Date: Fri, 10 Sep 2021 12:08:08 +0200 Subject: [PATCH] backend-common: fix azure readTree subpath handling Signed-off-by: Patrik Oldsberg --- .../src/reading/AzureUrlReader.test.ts | 10 ++++----- .../src/reading/AzureUrlReader.ts | 22 +++++++++++++------ 2 files changed, 19 insertions(+), 13 deletions(-) diff --git a/packages/backend-common/src/reading/AzureUrlReader.test.ts b/packages/backend-common/src/reading/AzureUrlReader.test.ts index c7c8853929..e014fa2e75 100644 --- a/packages/backend-common/src/reading/AzureUrlReader.test.ts +++ b/packages/backend-common/src/reading/AzureUrlReader.test.ts @@ -81,14 +81,14 @@ describe('AzureUrlReader', () => { url: 'https://dev.azure.com/org-name/project-name/_git/repo-name?path=my-template.yaml&version=GBmaster', config: createConfig(), response: expect.objectContaining({ - url: 'https://dev.azure.com/org-name/project-name/_apis/git/repositories/repo-name/items?path=my-template.yaml&version=master', + url: 'https://dev.azure.com/org-name/project-name/_apis/git/repositories/repo-name/items?api-version=6.0&path=my-template.yaml&version=master', }), }, { url: 'https://dev.azure.com/org-name/project-name/_git/repo-name?path=my-template.yaml', config: createConfig(), response: expect.objectContaining({ - url: 'https://dev.azure.com/org-name/project-name/_apis/git/repositories/repo-name/items?path=my-template.yaml', + url: 'https://dev.azure.com/org-name/project-name/_apis/git/repositories/repo-name/items?api-version=6.0&path=my-template.yaml', }), }, { @@ -125,14 +125,12 @@ describe('AzureUrlReader', () => { { url: 'https://api.com/a/b/blob/master/path/to/c.yaml', config: createConfig(), - error: - 'Incorrect URL: https://api.com/a/b/blob/master/path/to/c.yaml, Error: Wrong Azure Devops URL or Invalid file path', + error: 'Azure URL must point to a git repository', }, { url: 'com/a/b/blob/master/path/to/c.yaml', config: createConfig(), - error: - 'Incorrect URL: com/a/b/blob/master/path/to/c.yaml, TypeError: Invalid URL: com/a/b/blob/master/path/to/c.yaml', + error: 'Invalid URL: com/a/b/blob/master/path/to/c.yaml', }, { url: '', diff --git a/packages/backend-common/src/reading/AzureUrlReader.ts b/packages/backend-common/src/reading/AzureUrlReader.ts index 910e8e04a5..6015b044bc 100644 --- a/packages/backend-common/src/reading/AzureUrlReader.ts +++ b/packages/backend-common/src/reading/AzureUrlReader.ts @@ -23,11 +23,9 @@ import { ScmIntegrations, } from '@backstage/integration'; import fetch from 'cross-fetch'; -import parseGitUrl from 'git-url-parse'; import { Minimatch } from 'minimatch'; import { Readable } from 'stream'; import { NotFoundError, NotModifiedError } from '@backstage/errors'; -import { stripFirstDirectoryFromPath } from './tree/util'; import { ReadTreeResponseFactory, ReaderFactory, @@ -129,28 +127,38 @@ export class AzureUrlReader implements UrlReader { throw new Error(message); } + // When downloading a zip archive from azure on a subpath we get an extra directory + // layer added at the top. With for example the file /a/b/c.txt and a download of + // /a/b, we'll see /b/c.txt in the zip archive. This picks out /b so that we can remove it. + let subpath; + const path = new URL(url).searchParams.get('path'); + if (path) { + subpath = path.split('/').filter(Boolean).slice(-1)[0]; + } + return await this.deps.treeResponseFactory.fromZipArchive({ stream: archiveAzureResponse.body as unknown as Readable, etag: commitSha, filter: options?.filter, + subpath, }); } async search(url: string, options?: SearchOptions): Promise { - const { filepath } = parseGitUrl(url); - const matcher = new Minimatch(filepath); + const treeUrl = new URL(url); + + const path = treeUrl.searchParams.get('path'); + const matcher = path && new Minimatch(path.replace(/^\/+/, '')); // TODO(freben): For now, read the entire repo and filter through that. In // a future improvement, we could be smart and try to deduce that non-glob // prefixes (like for filepaths such as some-prefix/**/a.yaml) can be used // to get just that part of the repo. - const treeUrl = new URL(url); treeUrl.searchParams.delete('path'); - treeUrl.pathname = treeUrl.pathname.replace(/\/+$/, ''); const tree = await this.readTree(treeUrl.toString(), { etag: options?.etag, - filter: path => matcher.match(stripFirstDirectoryFromPath(path)), + filter: p => (matcher ? matcher.match(p) : true), }); const files = await tree.files();