From 2784016166bfa48630bba47f36bebb921eb98202 Mon Sep 17 00:00:00 2001 From: Jamie Magee <JamieMagee@users.noreply.github.com> Date: Fri, 18 Oct 2019 13:25:47 +0200 Subject: [PATCH] feat(azure): support different merge strategies for autocomplete (#4584) --- lib/platform/azure/azure-got-wrapper.ts | 6 +- lib/platform/azure/azure-helper.ts | 63 ++++++++----------- lib/platform/azure/index.ts | 10 +-- test/platform/azure/azure-got-wrapper.spec.ts | 3 +- test/platform/azure/azure-helper.spec.ts | 53 ++++++++++++++++ test/platform/azure/index.spec.ts | 2 +- 6 files changed, 93 insertions(+), 44 deletions(-) diff --git a/lib/platform/azure/azure-got-wrapper.ts b/lib/platform/azure/azure-got-wrapper.ts index f0d71ba340..3558cc1225 100644 --- a/lib/platform/azure/azure-got-wrapper.ts +++ b/lib/platform/azure/azure-got-wrapper.ts @@ -17,10 +17,14 @@ export function gitApi() { return azureObj().getGitApi(); } -export function getCoreApi() { +export function coreApi() { return azureObj().getCoreApi(); } +export function policyApi() { + return azureObj().getPolicyApi(); +} + export function setEndpoint(e: string) { endpoint = e; } diff --git a/lib/platform/azure/azure-helper.ts b/lib/platform/azure/azure-helper.ts index 6c16e4e2a7..b14d6b8d0c 100644 --- a/lib/platform/azure/azure-helper.ts +++ b/lib/platform/azure/azure-helper.ts @@ -1,10 +1,10 @@ +import { GitPullRequestMergeStrategy } from 'azure-devops-node-api/interfaces/GitInterfaces'; + import * as azureApi from './azure-got-wrapper'; import { logger } from '../../logger'; -/** - * - * @param {string} branchName - */ +const mergePolicyGuid = 'fa4e907d-c16b-4a4c-9dfa-4916e5d171ab'; // Magic GUID for merge strategy policy configurations + export function getNewBranchName(branchName?: string) { if (branchName && !branchName.startsWith('refs/heads/')) { return `refs/heads/${branchName}`; @@ -12,10 +12,6 @@ export function getNewBranchName(branchName?: string) { return branchName; } -/** - * - * @param {string} branchPath - */ export function getBranchNameWithoutRefsheadsPrefix(branchPath: string) { if (!branchPath) { logger.error(`getBranchNameWithoutRefsheadsPrefix(${branchPath})`); @@ -30,10 +26,6 @@ export function getBranchNameWithoutRefsheadsPrefix(branchPath: string) { return branchPath.substring(11, branchPath.length); } -/** - * - * @param {string} branchPath - */ function getBranchNameWithoutRefsPrefix(branchPath?: string) { if (!branchPath) { logger.error(`getBranchNameWithoutRefsPrefix(${branchPath})`); @@ -48,11 +40,6 @@ function getBranchNameWithoutRefsPrefix(branchPath?: string) { return branchPath.substring(5, branchPath.length); } -/** - * - * @param {string} repoId - * @param {string} branchName - */ export async function getRefs(repoId: string, branchName?: string) { logger.debug(`getRefs(${repoId}, ${branchName})`); const azureApiGit = await azureApi.gitApi(); @@ -64,12 +51,6 @@ export async function getRefs(repoId: string, branchName?: string) { return refs; } -/** - * - * @param repoId - * @param branchName - * @param from - */ export async function getAzureBranchObj( repoId: string, branchName: string, @@ -119,12 +100,7 @@ export async function getChanges( return changes; } -/** - * if no branchName, look globaly - * @param {string} repoId - * @param {string} filePath - * @param {string} branchName - */ +// if no branchName, look globaly export async function getFile( repoId: string, filePath: string, @@ -182,10 +158,6 @@ async function streamToString(stream: NodeJS.ReadableStream) { return p; } -/** - * - * @param {string} str - */ export function max4000Chars(str: string) { if (str && str.length >= 4000) { return str.substring(0, 3999); @@ -248,10 +220,6 @@ export async function getCommitDetails(commit: string, repoId: string) { return results; } -/** - * - * @param {string} str - */ export function getProjectAndRepo(str: string) { logger.trace(`getProjectAndRepo(${str})`); const strSplited = str.split(`/`); @@ -271,3 +239,24 @@ export function getProjectAndRepo(str: string) { logger.error(msg); throw new Error(msg); } + +export async function getMergeMethod( + repoId: string, + project: string +): Promise<GitPullRequestMergeStrategy> { + const policyConfigurations = (await (await azureApi.policyApi()).getPolicyConfigurations( + project + )) + .filter( + p => + p.settings.scope.some(s => s.repositoryId === repoId) && + p.type.id === mergePolicyGuid + ) + .map(p => p.settings)[0]; + + return ( + Object.keys(policyConfigurations) + .map(p => GitPullRequestMergeStrategy[p.slice(5)]) + .find(p => p) || GitPullRequestMergeStrategy.NoFastForward + ); +} diff --git a/lib/platform/azure/index.ts b/lib/platform/azure/index.ts index 677bb7f05e..367cb03d2b 100644 --- a/lib/platform/azure/index.ts +++ b/lib/platform/azure/index.ts @@ -1,3 +1,5 @@ +import { GitPullRequestMergeStrategy } from 'azure-devops-node-api/interfaces/GitInterfaces'; + import * as azureHelper from './azure-helper'; import * as azureApi from './azure-got-wrapper'; import * as hostRules from '../../util/host-rules'; @@ -11,7 +13,7 @@ import { smartTruncate } from '../utils/pr-body'; interface Config { storage: GitStorage; repoForceRebase: boolean; - mergeMethod: string; + mergeMethod: GitPullRequestMergeStrategy; baseCommitSHA: string | undefined; baseBranch: string; defaultBranch: string; @@ -86,7 +88,7 @@ export async function initRepo({ config.baseBranch = config.defaultBranch; logger.debug(`${repository} default branch = ${config.defaultBranch}`); config.baseCommitSHA = await getBranchCommit(config.baseBranch); - config.mergeMethod = 'merge'; + config.mergeMethod = await azureHelper.getMergeMethod(repo.id, names.project); config.repoForceRebase = false; if (optimizeForDisabled) { @@ -379,7 +381,7 @@ export async function createPr( id: pr.createdBy!.id, }, completionOptions: { - squashMerge: true, + mergeStrategy: config.mergeMethod, deleteSourceBranch: true, }, }, @@ -535,7 +537,7 @@ export async function addAssignees(issueNo: number, assignees: string[]) { export async function addReviewers(prNo: number, reviewers: string[]) { logger.trace(`addReviewers(${prNo}, ${reviewers})`); const azureApiGit = await azureApi.gitApi(); - const azureApiCore = await azureApi.getCoreApi(); + const azureApiCore = await azureApi.coreApi(); const repos = await azureApiGit.getRepositories(); const repo = repos.filter(c => c.id === config.repoId)[0]; const teams = await azureApiCore.getTeams(repo!.project!.id!); diff --git a/test/platform/azure/azure-got-wrapper.spec.ts b/test/platform/azure/azure-got-wrapper.spec.ts index 1350735b6e..85a2c4b1f0 100644 --- a/test/platform/azure/azure-got-wrapper.spec.ts +++ b/test/platform/azure/azure-got-wrapper.spec.ts @@ -13,7 +13,8 @@ describe('platform/azure/azure-got-wrapper', () => { describe('gitApi', () => { it('should throw an error if no token is provided', () => { expect(azure.gitApi).toThrow('No token found for azure'); - expect(azure.getCoreApi).toThrow('No token found for azure'); + expect(azure.coreApi).toThrow('No token found for azure'); + expect(azure.policyApi).toThrow('No token found for azure'); }); it('should set token and endpoint', async () => { hostRules.add({ diff --git a/test/platform/azure/azure-helper.spec.ts b/test/platform/azure/azure-helper.spec.ts index e3bb4ea18b..2fe6cd9503 100644 --- a/test/platform/azure/azure-helper.spec.ts +++ b/test/platform/azure/azure-helper.spec.ts @@ -1,4 +1,5 @@ import { Readable } from 'stream'; +import { GitPullRequestMergeStrategy } from 'azure-devops-node-api/interfaces/GitInterfaces'; describe('platform/azure/helpers', () => { let azureHelper: typeof import('../../../lib/platform/azure/azure-helper'); @@ -342,4 +343,56 @@ describe('platform/azure/helpers', () => { ); }); }); + + describe('getMergeMethod', () => { + it('should default to NoFastForward', async () => { + azureApi.policyApi.mockImplementationOnce( + () => + ({ + getPolicyConfigurations: jest.fn(() => [ + { + settings: { + scope: [ + { + repositoryId: '', + }, + ], + }, + type: { + id: 'fa4e907d-c16b-4a4c-9dfa-4916e5d171ab', + }, + }, + ]), + } as any) + ); + expect(await azureHelper.getMergeMethod('', '')).toEqual( + GitPullRequestMergeStrategy.NoFastForward + ); + }); + it('should return Squash', async () => { + azureApi.policyApi.mockImplementationOnce( + () => + ({ + getPolicyConfigurations: jest.fn(() => [ + { + settings: { + allowSquash: true, + scope: [ + { + repositoryId: '', + }, + ], + }, + type: { + id: 'fa4e907d-c16b-4a4c-9dfa-4916e5d171ab', + }, + }, + ]), + } as any) + ); + expect(await azureHelper.getMergeMethod('', '')).toEqual( + GitPullRequestMergeStrategy.Squash + ); + }); + }); }); diff --git a/test/platform/azure/index.spec.ts b/test/platform/azure/index.spec.ts index e93d28124d..9215c91b3c 100644 --- a/test/platform/azure/index.spec.ts +++ b/test/platform/azure/index.spec.ts @@ -622,7 +622,7 @@ describe('platform/azure', () => { createPullRequestReviewer: jest.fn(), } as any) ); - azureApi.getCoreApi.mockImplementation( + azureApi.coreApi.mockImplementation( () => ({ getTeams: jest.fn(() => [ -- GitLab