From 72a5af81d31e82dae5fef45b38807575ba70a430 Mon Sep 17 00:00:00 2001
From: RahulGautamSingh <rahultesnik@gmail.com>
Date: Fri, 28 Feb 2025 22:56:24 +0530
Subject: [PATCH] feat(github): support `automergeStrategy` (#34537)

---
 lib/config/options/index.ts               |  2 +-
 lib/modules/platform/github/common.ts     | 21 +++++++++++
 lib/modules/platform/github/index.spec.ts | 44 +++++++++++++++++++++++
 lib/modules/platform/github/index.ts      | 12 ++++---
 lib/modules/platform/github/readme.md     |  4 ---
 5 files changed, 74 insertions(+), 9 deletions(-)

diff --git a/lib/config/options/index.ts b/lib/config/options/index.ts
index 67fbcd41ef..a866567fac 100644
--- a/lib/config/options/index.ts
+++ b/lib/config/options/index.ts
@@ -2010,7 +2010,7 @@ const options: RenovateOptions[] = [
     type: 'string',
     allowedValues: ['auto', 'fast-forward', 'merge-commit', 'rebase', 'squash'],
     default: 'auto',
-    supportedPlatforms: ['azure', 'bitbucket', 'gitea'],
+    supportedPlatforms: ['azure', 'bitbucket', 'gitea', 'github'],
   },
   {
     name: 'automergeComment',
diff --git a/lib/modules/platform/github/common.ts b/lib/modules/platform/github/common.ts
index fc6a9d0f59..930513e19a 100644
--- a/lib/modules/platform/github/common.ts
+++ b/lib/modules/platform/github/common.ts
@@ -1,6 +1,8 @@
 import is from '@sindresorhus/is';
+import { logger } from '../../../logger';
 import { GithubHttp } from '../../../util/http/github';
 import { getPrBodyStruct } from '../pr-body';
+import type { MergePRConfig } from '../types';
 import type { GhPr, GhRestPr } from './types';
 
 export const githubApi = new GithubHttp();
@@ -57,3 +59,22 @@ export function coerceRestPr(pr: GhRestPr): GhPr {
 
   return result;
 }
+
+export function mapMergeStartegy(
+  strategy: MergePRConfig['strategy'],
+): string | undefined {
+  switch (strategy) {
+    case 'auto':
+      return undefined;
+    case 'fast-forward': {
+      logger.warn(
+        'Fast-forward merge strategy is not supported by Github. Falling back to merge strategy set for the repository.',
+      );
+      return undefined;
+    }
+    case 'merge-commit':
+      return 'merge';
+    default:
+      return strategy;
+  }
+}
diff --git a/lib/modules/platform/github/index.spec.ts b/lib/modules/platform/github/index.spec.ts
index d470d76315..0ebbdca7a7 100644
--- a/lib/modules/platform/github/index.spec.ts
+++ b/lib/modules/platform/github/index.spec.ts
@@ -3640,6 +3640,7 @@ describe('modules/platform/github/index', () => {
         await github.mergePr({
           branchName: '',
           id: pr.number,
+          strategy: 'merge-commit', // for coverage - has no effect on this test
         }),
       ).toBeFalse();
     });
@@ -3661,9 +3662,52 @@ describe('modules/platform/github/index', () => {
         await github.mergePr({
           branchName: '',
           id: pr.number,
+          strategy: 'auto', // for coverage -- has not effect on this test
         }),
       ).toBeFalse();
     });
+
+    it('should warn if automergeStrategy is not supported', async () => {
+      const scope = httpMock.scope(githubApiHost);
+      initRepoMock(scope, 'some/repo');
+      scope.put('/repos/some/repo/pulls/1234/merge').reply(200);
+      await github.initRepo({ repository: 'some/repo' });
+
+      const mergeResult = await github.mergePr({
+        id: 1234,
+        branchName: 'somebranch',
+        strategy: 'fast-forward',
+      });
+
+      expect(mergeResult).toBeTrue();
+      expect(logger.logger.warn).toHaveBeenCalledWith(
+        'Fast-forward merge strategy is not supported by Github. Falling back to merge strategy set for the repository.',
+      );
+    });
+
+    it('should use configured automergeStrategy', async () => {
+      const scope = httpMock.scope(githubApiHost);
+      initRepoMock(scope, 'some/repo');
+      scope.put('/repos/some/repo/pulls/1234/merge').reply(200);
+      await github.initRepo({ repository: 'some/repo' });
+
+      const mergeResult = await github.mergePr({
+        id: 1234,
+        branchName: 'somebranch',
+        strategy: 'rebase',
+      });
+
+      expect(mergeResult).toBeTrue();
+      expect(logger.logger.debug).toHaveBeenCalledWith(
+        {
+          options: {
+            body: { merge_method: 'rebase' },
+          },
+          url: 'repos/some/repo/pulls/1234/merge',
+        },
+        'mergePr',
+      );
+    });
   });
 
   describe('massageMarkdown(input)', () => {
diff --git a/lib/modules/platform/github/index.ts b/lib/modules/platform/github/index.ts
index 569110438b..5cd45277df 100644
--- a/lib/modules/platform/github/index.ts
+++ b/lib/modules/platform/github/index.ts
@@ -67,7 +67,7 @@ import { repoFingerprint } from '../util';
 import { normalizeNamePerEcosystem } from '../utils/github-alerts';
 import { smartTruncate } from '../utils/pr-body';
 import { remoteBranchExists } from './branch';
-import { coerceRestPr, githubApi } from './common';
+import { coerceRestPr, githubApi, mapMergeStartegy } from './common';
 import {
   enableAutoMergeMutation,
   getIssuesQuery,
@@ -1847,6 +1847,7 @@ export async function reattemptPlatformAutomerge({
 export async function mergePr({
   branchName,
   id: prNo,
+  strategy,
 }: MergePRConfig): Promise<boolean> {
   logger.debug(`mergePr(${prNo}, ${branchName})`);
   const url = `repos/${
@@ -1861,9 +1862,12 @@ export async function mergePr({
   }
   let automerged = false;
   let automergeResult: HttpResponse<unknown>;
-  if (config.mergeMethod) {
-    // This path is taken if we have auto-detected the allowed merge types from the repo
-    options.body.merge_method = config.mergeMethod;
+  const mergeStrategy = mapMergeStartegy(strategy) ?? config.mergeMethod;
+
+  if (mergeStrategy) {
+    // This path is taken if we have auto-detected the allowed merge types from the repo or
+    // automergeStrategy is configured by user
+    options.body.merge_method = mergeStrategy;
     try {
       logger.debug({ options, url }, `mergePr`);
       automergeResult = await githubApi.putJson(url, options);
diff --git a/lib/modules/platform/github/readme.md b/lib/modules/platform/github/readme.md
index 9e0dd06b1f..85908a1fa3 100644
--- a/lib/modules/platform/github/readme.md
+++ b/lib/modules/platform/github/readme.md
@@ -140,7 +140,3 @@ When Renovate runs against repositories on `github.com`, and the environment var
 <!-- prettier-ignore -->
 !!! warning
     We reverted the Package Registry Credentials feature to experimental mode, because users reported it's not working correctly with app tokens.
-
-## Features awaiting implementation
-
-- The `automergeStrategy` configuration option has not been implemented for this platform, and all values behave as if the value `auto` was used. Renovate will use the merge strategy configured in the GitHub repository itself, and this cannot be overridden yet
-- 
GitLab