feat(gerrit-integration): use gitiles archive for readTree if configured

If Gitiles is configured, leverage it to fetch an archive instead
of cloning the repository.

Co-Authored-By: Andy Ladjadj <andy.ladjadj@adevinta.com>
Signed-off-by: Thomas Cardonne <thomas.cardonne@adevinta.com>
This commit is contained in:
Thomas Cardonne
2023-04-24 14:42:22 +02:00
parent b3021ef892
commit 443afcf7f5
14 changed files with 339 additions and 23 deletions
+8
View File
@@ -172,6 +172,14 @@ export type BitbucketServerIntegrationConfig = {
password?: string;
};
// @public
export function buildGerritGitilesArchiveUrl(
config: GerritIntegrationConfig,
project: string,
branch: string,
filePath: string,
): string;
// @public
export class DefaultGithubCredentialsProvider
implements GithubCredentialsProvider
@@ -20,6 +20,7 @@ import fetch from 'cross-fetch';
import { setupRequestMockHandlers } from '../helpers';
import { GerritIntegrationConfig } from './config';
import {
buildGerritGitilesArchiveUrl,
buildGerritGitilesUrl,
getGerritBranchApiUrl,
getGerritCloneRepoUrl,
@@ -33,6 +34,41 @@ describe('gerrit core', () => {
const worker = setupServer();
setupRequestMockHandlers(worker);
describe('buildGerritGitilesArchiveUrl', () => {
const config: GerritIntegrationConfig = {
host: 'gerrit.com',
gitilesBaseUrl: 'https://gerrit.com/gitiles',
};
it('can create an archive url for a branch', () => {
expect(buildGerritGitilesArchiveUrl(config, 'repo', 'dev', '')).toEqual(
'https://gerrit.com/gitiles/repo/+archive/refs/heads/dev.tar.gz',
);
expect(buildGerritGitilesArchiveUrl(config, 'repo', 'dev', '/')).toEqual(
'https://gerrit.com/gitiles/repo/+archive/refs/heads/dev.tar.gz',
);
});
it('can create an archive url for a specific directory', () => {
expect(
buildGerritGitilesArchiveUrl(config, 'repo', 'dev', 'docs'),
).toEqual(
'https://gerrit.com/gitiles/repo/+archive/refs/heads/dev/docs.tar.gz',
);
});
it('can create an authenticated url when auth is enabled', () => {
const authConfig = {
...config,
username: 'username',
password: 'password',
};
expect(
buildGerritGitilesArchiveUrl(authConfig, 'repo', 'dev', 'docs'),
).toEqual(
'https://gerrit.com/a/gitiles/repo/+archive/refs/heads/dev/docs.tar.gz',
);
});
});
describe('buildGerritGitilesUrl', () => {
it('can create an url from arguments', () => {
const config: GerritIntegrationConfig = {
@@ -86,6 +122,25 @@ describe('gerrit core', () => {
);
expect(rootPath).toEqual('/');
});
it('can parse a valid authenticated gitiles url.', () => {
const config: GerritIntegrationConfig = {
host: 'gerrit.com',
gitilesBaseUrl: 'https://gerrit.com/gitiles',
};
const { branch, filePath, project } = parseGerritGitilesUrl(
config,
'https://gerrit.com/a/gitiles/web/project/+/refs/heads/master/README.md',
);
expect(project).toEqual('web/project');
expect(branch).toEqual('master');
expect(filePath).toEqual('README.md');
const { filePath: rootPath } = parseGerritGitilesUrl(
config,
'https://gerrit.com/gitiles/web/project/+/refs/heads/master',
);
expect(rootPath).toEqual('/');
});
it('throws on incorrect gitiles urls.', () => {
const config: GerritIntegrationConfig = {
host: 'gerrit.com',
+55 -1
View File
@@ -37,6 +37,7 @@ const GERRIT_BODY_PREFIX = ")]}'";
*
* Gitiles url:
* https://g.com/optional_path/\{project\}/+/refs/heads/\{branch\}/\{filePath\}
* https://g.com/a/optional_path/\{project\}/+/refs/heads/\{branch\}/\{filePath\}
*
*
* @param url - An URL pointing to a file stored in git.
@@ -47,7 +48,17 @@ export function parseGerritGitilesUrl(
config: GerritIntegrationConfig,
url: string,
): { branch: string; filePath: string; project: string } {
const urlPath = url.replace(config.gitilesBaseUrl!, '');
const baseUrlParse = new URL(config.gitilesBaseUrl!);
const urlParse = new URL(url);
// Remove the gerrit authentication prefix '/a/' from the url
// In case of the gitilesBaseUrl is https://review.gerrit.com/plugins/gitiles
// and the url provided is https://review.gerrit.com/a/plugins/gitiles/...
// remove the prefix only if the pathname start with '/a/'
const urlPath = urlParse.pathname
.substring(urlParse.pathname.startsWith('/a/') ? 2 : 0)
.replace(baseUrlParse.pathname, '');
const parts = urlPath.split('/').filter(p => !!p);
const projectEndIndex = parts.indexOf('+');
@@ -91,6 +102,28 @@ export function buildGerritGitilesUrl(
}/${project}/+/refs/heads/${branch}/${trimStart(filePath, '/')}`;
}
/**
* Build a Gerrit Gitiles archive url that targets a specific branch and path
*
* @param config - A Gerrit provider config.
* @param project - The name of the git project
* @param branch - The branch we will target.
* @param filePath - The absolute file path.
* @public
*/
export function buildGerritGitilesArchiveUrl(
config: GerritIntegrationConfig,
project: string,
branch: string,
filePath: string,
): string {
const archiveName =
filePath === '/' || filePath === '' ? '.tar.gz' : `/${filePath}.tar.gz`;
return `${getGitilesAuthenticationUrl(
config,
)}/${project}/+archive/refs/heads/${branch}${archiveName}`;
}
/**
* Return the authentication prefix.
*
@@ -109,6 +142,27 @@ export function getAuthenticationPrefix(
return config.password ? '/a/' : '/';
}
/**
* Return the authentication gitiles url.
*
* @remarks
*
* To authenticate with a password the API url must be prefixed with "/a/".
* If no password is set anonymous access (without the prefix) will
* be used.
*
* @param config - A Gerrit provider config.
* @public
*/
export function getGitilesAuthenticationUrl(
config: GerritIntegrationConfig,
): string {
const parsedUrl = new URL(config.gitilesBaseUrl!);
return `${parsedUrl.protocol}//${parsedUrl.host}${getAuthenticationPrefix(
config,
)}${parsedUrl.pathname.substring(1)}`;
}
/**
* Return the url to get branch info from the Gerrit API.
*
+1
View File
@@ -19,6 +19,7 @@ export {
readGerritIntegrationConfigs,
} from './config';
export {
buildGerritGitilesArchiveUrl,
getGerritBranchApiUrl,
getGerritCloneRepoUrl,
getGerritFileContentsApiUrl,