From 6569194c3a9748ca564130cad84fe3c0d52250cc Mon Sep 17 00:00:00 2001 From: sredny buitrago Date: Wed, 5 Nov 2025 14:44:39 -0300 Subject: [PATCH 1/8] on suggest target branch give exact command for release bot --- .github/workflows/branch-suggestion.yml | 2 +- .../scripts/common/match-branches.js | 24 ++++++++++++++++--- 2 files changed, 22 insertions(+), 4 deletions(-) diff --git a/.github/workflows/branch-suggestion.yml b/.github/workflows/branch-suggestion.yml index 600a1f1..bd2a26c 100644 --- a/.github/workflows/branch-suggestion.yml +++ b/.github/workflows/branch-suggestion.yml @@ -22,7 +22,7 @@ jobs: uses: actions/checkout@v4 with: repository: TykTechnologies/github-actions - ref: main + ref: TT-15793-give-exact-instructions-on-merging-branches - name: Setup Node.js uses: actions/setup-node@v4 diff --git a/branch-suggestion/scripts/common/match-branches.js b/branch-suggestion/scripts/common/match-branches.js index 2dd6537..e75dc18 100644 --- a/branch-suggestion/scripts/common/match-branches.js +++ b/branch-suggestion/scripts/common/match-branches.js @@ -234,9 +234,27 @@ function formatBranchSuggestions(matchResults, jiraTicket = {}) { lines.push(''); lines.push('### 📋 Workflow'); lines.push('1. Merge this PR to `master` first'); - lines.push('2. After merging, comment on the **merged PR** with `/release to ` to cherry-pick to release branches'); - lines.push('3. Example: `/release to release-5.8`'); - lines.push('4. The bot will automatically create a backport PR to the specified release branch'); + + // Collect all non-master branches that need cherry-picking + const releaseBranches = []; + for (const result of matchResults) { + for (const branch of result.branches) { + if (branch.branch !== 'master' && !releaseBranches.includes(branch.branch)) { + releaseBranches.push(branch.branch); + } + } + } + + if (releaseBranches.length > 0) { + lines.push('2. After merging, cherry-pick to release branches by commenting on the **merged PR**:'); + lines.push(''); + for (const branch of releaseBranches) { + lines.push(` \`/release to ${branch}\``); + } + lines.push(''); + lines.push('3. The bot will automatically create backport PRs to the specified release branches'); + } + lines.push(''); lines.push(''); From d2448ae2200def1f0a2f71a81c54a13234f71211 Mon Sep 17 00:00:00 2001 From: sredny buitrago Date: Wed, 5 Nov 2025 14:58:08 -0300 Subject: [PATCH 2/8] improve formating of comment --- branch-suggestion/scripts/common/match-branches.js | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/branch-suggestion/scripts/common/match-branches.js b/branch-suggestion/scripts/common/match-branches.js index e75dc18..391124c 100644 --- a/branch-suggestion/scripts/common/match-branches.js +++ b/branch-suggestion/scripts/common/match-branches.js @@ -233,7 +233,9 @@ function formatBranchSuggestions(matchResults, jiraTicket = {}) { lines.push('---'); lines.push(''); lines.push('### 📋 Workflow'); - lines.push('1. Merge this PR to `master` first'); + lines.push(''); + lines.push('1. **Merge this PR to `master` first**'); + lines.push(''); // Collect all non-master branches that need cherry-picking const releaseBranches = []; @@ -246,13 +248,13 @@ function formatBranchSuggestions(matchResults, jiraTicket = {}) { } if (releaseBranches.length > 0) { - lines.push('2. After merging, cherry-pick to release branches by commenting on the **merged PR**:'); + lines.push('2. **Cherry-pick to release branches** by commenting on the **merged PR**:'); lines.push(''); for (const branch of releaseBranches) { - lines.push(` \`/release to ${branch}\``); + lines.push(` - \`/release to ${branch}\``); } lines.push(''); - lines.push('3. The bot will automatically create backport PRs to the specified release branches'); + lines.push('3. **Automated backport** - The bot will automatically create backport PRs to the specified release branches'); } lines.push(''); From 63f54a803a7298e0d3ea3c3865b86a0ea50e27f5 Mon Sep 17 00:00:00 2001 From: sredny buitrago Date: Wed, 5 Nov 2025 15:02:17 -0300 Subject: [PATCH 3/8] update ref --- .github/workflows/branch-suggestion.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/branch-suggestion.yml b/.github/workflows/branch-suggestion.yml index bd2a26c..600a1f1 100644 --- a/.github/workflows/branch-suggestion.yml +++ b/.github/workflows/branch-suggestion.yml @@ -22,7 +22,7 @@ jobs: uses: actions/checkout@v4 with: repository: TykTechnologies/github-actions - ref: TT-15793-give-exact-instructions-on-merging-branches + ref: main - name: Setup Node.js uses: actions/setup-node@v4 From de13a130f887bb24e0e69699caab40ed5a4d0281 Mon Sep 17 00:00:00 2001 From: sredny buitrago Date: Wed, 5 Nov 2025 15:15:33 -0300 Subject: [PATCH 4/8] address visor comments --- branch-suggestion/scripts/common/match-branches.js | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/branch-suggestion/scripts/common/match-branches.js b/branch-suggestion/scripts/common/match-branches.js index 391124c..92972f8 100644 --- a/branch-suggestion/scripts/common/match-branches.js +++ b/branch-suggestion/scripts/common/match-branches.js @@ -237,15 +237,16 @@ function formatBranchSuggestions(matchResults, jiraTicket = {}) { lines.push('1. **Merge this PR to `master` first**'); lines.push(''); - // Collect all non-master branches that need cherry-picking - const releaseBranches = []; + // Collect all non-master branches that need cherry-picking using Set for O(1) operations + const releaseBranchesSet = new Set(); for (const result of matchResults) { for (const branch of result.branches) { - if (branch.branch !== 'master' && !releaseBranches.includes(branch.branch)) { - releaseBranches.push(branch.branch); + if (branch.branch !== 'master') { + releaseBranchesSet.add(branch.branch); } } } + const releaseBranches = Array.from(releaseBranchesSet); if (releaseBranches.length > 0) { lines.push('2. **Cherry-pick to release branches** by commenting on the **merged PR**:'); From 38ba8a48fb17098f0d450758d8e906b3b32b0f0d Mon Sep 17 00:00:00 2001 From: sredny buitrago Date: Wed, 5 Nov 2025 15:19:37 -0300 Subject: [PATCH 5/8] revert ref for testing --- .github/workflows/branch-suggestion.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/branch-suggestion.yml b/.github/workflows/branch-suggestion.yml index 600a1f1..bd2a26c 100644 --- a/.github/workflows/branch-suggestion.yml +++ b/.github/workflows/branch-suggestion.yml @@ -22,7 +22,7 @@ jobs: uses: actions/checkout@v4 with: repository: TykTechnologies/github-actions - ref: main + ref: TT-15793-give-exact-instructions-on-merging-branches - name: Setup Node.js uses: actions/setup-node@v4 From c3d7081725e1db11528ff3bb82e938ef682c156b Mon Sep 17 00:00:00 2001 From: sredny buitrago Date: Wed, 5 Nov 2025 15:27:48 -0300 Subject: [PATCH 6/8] consider minor release in suggestions --- .../scripts/common/match-branches.js | 23 ++++++++++++++++--- 1 file changed, 20 insertions(+), 3 deletions(-) diff --git a/branch-suggestion/scripts/common/match-branches.js b/branch-suggestion/scripts/common/match-branches.js index 92972f8..08d14db 100644 --- a/branch-suggestion/scripts/common/match-branches.js +++ b/branch-suggestion/scripts/common/match-branches.js @@ -20,17 +20,22 @@ function generateBranchCandidates(parsedVersion) { const { major, minor, patch } = parsedVersion; const candidates = []; - // For patch releases (X.Y.Z where Z > 0): need release-X.Y + // First priority: exact match for the full version (X.Y.Z) + if (patch !== null && minor !== null) { + candidates.push(`release-${major}.${minor}.${patch}`); + } + + // Second priority: minor version branch (X.Y) for patch releases if (patch !== null && patch > 0 && minor !== null) { candidates.push(`release-${major}.${minor}`); } - // For minor releases (X.Y.0 or X.Y): need release-X.Y + // Third priority: minor version branch for minor releases (X.Y.0 or X.Y) if (minor !== null) { candidates.push(`release-${major}.${minor}`); } - // For any version: might need release-X (major version branch) + // Fourth priority: major version branch (X) if (major !== null) { candidates.push(`release-${major}`); } @@ -133,6 +138,12 @@ function getBranchReason(branch, fixVersion) { return 'Main development branch - ensures fix is in all future releases'; } + // Exact patch version match (e.g., release-5.10.1) + if (patch !== null && branch === `release-${major}.${minor}.${patch}`) { + return `Exact version branch for ${fixVersion.name} - specific patch release`; + } + + // Minor version branch (e.g., release-5.10) if (branch === `release-${major}.${minor}`) { if (patch > 0) { return `Minor version branch for ${major}.${minor}.x patches - required for creating ${fixVersion.name}`; @@ -141,6 +152,7 @@ function getBranchReason(branch, fixVersion) { } } + // Major version branch (e.g., release-5) if (branch === `release-${major}`) { return `Major version branch for all ${major}.x releases`; } @@ -162,6 +174,11 @@ function getBranchPriority(branch, fixVersion) { return 'required'; } + // Exact patch version match is required (e.g., release-5.10.1 for version 5.10.1) + if (patch !== null && branch === `release-${major}.${minor}.${patch}`) { + return 'required'; + } + // For patch releases (5.8.1), release-5.8 is required if (patch > 0 && branch === `release-${major}.${minor}`) { return 'required'; From ee0dc6e8269b6b88e0685fc0abd02d5f315c5892 Mon Sep 17 00:00:00 2001 From: sredny buitrago Date: Wed, 5 Nov 2025 15:31:00 -0300 Subject: [PATCH 7/8] set ref to point to main --- .github/workflows/branch-suggestion.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/branch-suggestion.yml b/.github/workflows/branch-suggestion.yml index bd2a26c..600a1f1 100644 --- a/.github/workflows/branch-suggestion.yml +++ b/.github/workflows/branch-suggestion.yml @@ -22,7 +22,7 @@ jobs: uses: actions/checkout@v4 with: repository: TykTechnologies/github-actions - ref: TT-15793-give-exact-instructions-on-merging-branches + ref: main - name: Setup Node.js uses: actions/setup-node@v4 From 6c4291086c9bfe6a0a2bf208e13892d6e6ecc138 Mon Sep 17 00:00:00 2001 From: sredny buitrago Date: Wed, 5 Nov 2025 15:40:18 -0300 Subject: [PATCH 8/8] update tests for matching branches --- .../common/__tests__/match-branches.test.js | 52 ++++++++++++++++++- 1 file changed, 50 insertions(+), 2 deletions(-) diff --git a/branch-suggestion/scripts/common/__tests__/match-branches.test.js b/branch-suggestion/scripts/common/__tests__/match-branches.test.js index af0e512..2840b4b 100644 --- a/branch-suggestion/scripts/common/__tests__/match-branches.test.js +++ b/branch-suggestion/scripts/common/__tests__/match-branches.test.js @@ -11,13 +11,13 @@ describe('generateBranchCandidates', () => { it('should generate candidates for patch release', () => { const parsed = { major: 5, minor: 8, patch: 1 }; const candidates = generateBranchCandidates(parsed); - expect(candidates).toEqual(['release-5.8', 'release-5', 'master']); + expect(candidates).toEqual(['release-5.8.1', 'release-5.8', 'release-5', 'master']); }); it('should generate candidates for minor release', () => { const parsed = { major: 5, minor: 8, patch: 0 }; const candidates = generateBranchCandidates(parsed); - expect(candidates).toEqual(['release-5.8', 'release-5', 'master']); + expect(candidates).toEqual(['release-5.8.0', 'release-5.8', 'release-5', 'master']); }); it('should generate candidates for major release', () => { @@ -91,6 +91,11 @@ describe('getBranchPriority', () => { expect(priority).toBe('required'); }); + it('should return required for exact patch version branch', () => { + const priority = getBranchPriority('release-5.8.1', { parsed: { major: 5, minor: 8, patch: 1 } }); + expect(priority).toBe('required'); + }); + it('should return required for patch release branch', () => { const priority = getBranchPriority('release-5.8', { parsed: { major: 5, minor: 8, patch: 1 } }); expect(priority).toBe('required'); @@ -113,6 +118,15 @@ describe('getBranchReason', () => { expect(reason).toContain('Main development branch'); }); + it('should return reason for exact patch version branch', () => { + const reason = getBranchReason('release-5.8.1', { + name: '5.8.1', + parsed: { major: 5, minor: 8, patch: 1 } + }); + expect(reason).toContain('Exact version branch'); + expect(reason).toContain('5.8.1'); + }); + it('should return reason for patch release', () => { const reason = getBranchReason('release-5.8', { name: '5.8.1', @@ -192,4 +206,38 @@ describe('matchBranches', () => { expect(results[0].branches).toHaveLength(1); expect(results[0].branches[0].branch).toBe('master'); }); + + it('should prefer exact version match when available', () => { + const fixVersions = [ + { name: '5.10.1', parsed: { major: 5, minor: 10, patch: 1, component: [] } } + ]; + const repoBranches = [ + { name: 'master' }, + { name: 'release-5.10' }, + { name: 'release-5.10.1' } + ]; + + const results = matchBranches(fixVersions, repoBranches); + expect(results[0].branches).toHaveLength(3); + expect(results[0].branches[0].branch).toBe('release-5.10.1'); + expect(results[0].branches[0].priority).toBe('required'); + expect(results[0].branches[1].branch).toBe('release-5.10'); + expect(results[0].branches[1].priority).toBe('required'); + expect(results[0].branches[2].branch).toBe('master'); + }); + + it('should fallback to minor version when exact match not available', () => { + const fixVersions = [ + { name: '5.10.1', parsed: { major: 5, minor: 10, patch: 1, component: [] } } + ]; + const repoBranches = [ + { name: 'master' }, + { name: 'release-5.10' } + ]; + + const results = matchBranches(fixVersions, repoBranches); + expect(results[0].branches).toHaveLength(2); + expect(results[0].branches[0].branch).toBe('release-5.10'); + expect(results[0].branches[0].priority).toBe('required'); + }); });