1+ # Copyright 2025 Google LLC
2+ #
3+ # Licensed under the Apache License, Version 2.0 (the "License");
4+ # you may not use this file except in compliance with the License.
5+ # You may obtain a copy of the License at
6+ #
7+ # http://www.apache.org/licenses/LICENSE-2.0
8+ #
9+ # Unless required by applicable law or agreed to in writing, software
10+ # distributed under the License is distributed on an "AS IS" BASIS,
11+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+ # See the License for the specific language governing permissions and
13+ # limitations under the License.
14+
15+ name : Mirror Toolbox Changelog
16+
17+ on :
18+ pull_request_target :
19+ types : [opened, edited]
20+
21+ jobs :
22+ add-release-notes :
23+ if : github.actor == 'renovate[bot]' && startsWith(github.head_ref, 'renovate/googleapis-genai-toolbox')
24+ runs-on : ubuntu-latest
25+ permissions :
26+ pull-requests : write
27+
28+ steps :
29+ - name : Add Toolbox Release Notes to PR Body
30+ uses : actions/github-script@v6
31+ env :
32+ REQUIRED_KEYWORDS : ' alloydb'
33+ with :
34+ script : |
35+ const requiredKeywordsEnv = process.env.REQUIRED_KEYWORDS;
36+ const requiredKeywords = requiredKeywordsEnv.split(',').map(kw => kw.trim()).filter(kw => kw.length > 0);
37+
38+ const prBody = context.payload.pull_request.body || '';
39+
40+ // Extract the relevant changelog section
41+ const startMarker = '<summary>googleapis/genai-toolbox';
42+ const endMarker = '</details>';
43+ const startIndex = prBody.indexOf(startMarker);
44+ const endIndex = prBody.indexOf(endMarker, startIndex);
45+
46+ if (startIndex === -1 || endIndex === -1) {
47+ console.log('Could not find the release notes section in the PR body. Exiting.');
48+ return;
49+ }
50+ const releaseNotesSection = prBody.substring(startIndex, endIndex);
51+
52+ // Parse, Filter, and transform
53+ const prefixesToFilter = ['source/', 'sources/', 'tool/', 'tools/'];
54+
55+ // Use a map for cleaner type switching
56+ const typeMap = {
57+ '##### ⚠ BREAKING CHANGES': 'feat!',
58+ '##### Features': 'feat',
59+ '##### Bug Fixes': 'fix',
60+ '##### Chores': 'ignore',
61+ '##### Miscellaneous Chores': 'ignore',
62+ '##### Documentation': 'ignore',
63+ };
64+
65+ let currentType = 'feat'; // Default
66+ const newChangelog = [];
67+
68+ for (const line of releaseNotesSection.split('\n')) {
69+ const trimmedLine = line.trim();
70+
71+ // Update current type if it's a header
72+ if (typeMap[trimmedLine]) {
73+ currentType = typeMap[trimmedLine];
74+ continue;
75+ }
76+
77+ // Skip ignored sections
78+ if (currentType === 'ignore') {
79+ continue;
80+ }
81+
82+ // Match and extract changelog item
83+ const itemMatch = trimmedLine.match(/^[*-]\s(.*)$/);
84+ if (itemMatch) {
85+ const originalContent = itemMatch[1];
86+ const lineAsLowerCase = originalContent.toLowerCase();
87+
88+ const hasPrefix = prefixesToFilter.some(prefix => lineAsLowerCase.includes(prefix));
89+
90+ // Check if the line includes ANY of the required keywords
91+ let hasAnyRequiredKeyword = false;
92+ if (requiredKeywords.length > 0) {
93+ hasAnyRequiredKeyword = requiredKeywords.some(keyword => lineAsLowerCase.includes(keyword));
94+ }
95+
96+ // Include if it doesn't have a prefix OR it has any of the required keywords
97+ if (!hasPrefix || hasAnyRequiredKeyword) {
98+ newChangelog.push(`- ${currentType}: ${originalContent}`);
99+ } else {
100+ console.log(`Filtering out: ${originalContent}`);
101+ }
102+ }
103+ }
104+
105+ if (newChangelog.length === 0) {
106+ console.log('Found no changelog items to add after filtering. Exiting.');
107+ return;
108+ }
109+
110+ // Construct the override block
111+ const overrideBlock = [
112+ '\n\nBEGIN_COMMIT_OVERRIDE',
113+ ...newChangelog,
114+ 'END_COMMIT_OVERRIDE'
115+ ].join('\n');
116+
117+ // Update PR body
118+ const baseBody = prBody.split('\n\nBEGIN_COMMIT_OVERRIDE')[0].trim();
119+ const finalBody = baseBody + overrideBlock;
120+
121+ if (finalBody === prBody) {
122+ console.log('The generated changelog is identical. No update needed.');
123+ return;
124+ }
125+
126+ // Update the PR
127+ await github.rest.pulls.update({
128+ owner: context.repo.owner,
129+ repo: context.repo.repo,
130+ pull_number: context.issue.number,
131+ body: finalBody,
132+ });
133+
134+ console.log('Successfully updated the PR body with filtered release notes.');
0 commit comments