From 7ac003d3cbb9a9ec50163f07ce0fa17e4650d965 Mon Sep 17 00:00:00 2001 From: Twisha Bansal Date: Fri, 17 Oct 2025 13:13:27 +0530 Subject: [PATCH 1/5] ci: mirror toolbox changelog into extension changelog --- .github/renovate.json5 | 38 ++++++++ .github/workflows/mirror-changelog.yml | 129 +++++++++++++++++++++++++ 2 files changed, 167 insertions(+) create mode 100644 .github/renovate.json5 create mode 100644 .github/workflows/mirror-changelog.yml diff --git a/.github/renovate.json5 b/.github/renovate.json5 new file mode 100644 index 0000000..f69f79b --- /dev/null +++ b/.github/renovate.json5 @@ -0,0 +1,38 @@ +{ + extends: [ + 'config:recommended', + ':semanticCommitTypeAll(chore)', + ':ignoreUnstable', + ':separateMajorReleases', + ':prConcurrentLimitNone', + ':prHourlyLimitNone', + ':preserveSemverRanges', + ], + rebaseWhen: 'conflicted', + dependencyDashboardLabels: [ + 'type: process', + ], + packageRules: [ + { + groupName: 'GitHub Actions', + matchManagers: [ + 'github-actions', + ], + pinDigests: true, + }, + { + matchPackageNames: ['googleapis/genai-toolbox'], + 'semanticCommitType': 'feat' + } + ], + customManagers: [ + { + customType: "regex", + managerFilePatterns: ["/toolbox_version\\.txt$/"], + matchStrings: ["(?[\\d\\.]+)"], + datasourceTemplate: "github-releases", + packageNameTemplate: "googleapis/genai-toolbox", + extractVersionTemplate: "^v(?.*)$", + } + ] +} diff --git a/.github/workflows/mirror-changelog.yml b/.github/workflows/mirror-changelog.yml new file mode 100644 index 0000000..055ccca --- /dev/null +++ b/.github/workflows/mirror-changelog.yml @@ -0,0 +1,129 @@ +# Copyright 2025 Google LLC +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +name: Mirror Toolbox Changelog + +on: + pull_request_target: + types: [opened, edited] + +jobs: + add-release-notes: + if: github.actor == 'renovate[bot]' && startsWith(github.head_ref, 'renovate/googleapis-genai-toolbox') + runs-on: ubuntu-latest + permissions: + pull-requests: write + + steps: + - name: Add Toolbox Release Notes to PR Body + uses: actions/github-script@v6 + env: + REQUIRED_KEYWORD: 'alloydb' + with: + script: | + const requiredKeyword = process.env.REQUIRED_KEYWORD; + const prBody = context.payload.pull_request.body || ''; + + // Extract the relevant changelog section + const startMarker = 'googleapis/genai-toolbox'; + const endMarker = ''; + const startIndex = prBody.indexOf(startMarker); + const endIndex = prBody.indexOf(endMarker, startIndex); + + if (startIndex === -1 || endIndex === -1) { + console.log('Could not find the release notes section in the PR body. Exiting.'); + return; + } + const releaseNotesSection = prBody.substring(startIndex, endIndex); + + // Parse, Filter, and transform + const prefixesToFilter = ['source/', 'sources/', 'tool/', 'tools/']; + + // Use a map for cleaner type switching + const typeMap = { + '##### ⚠ BREAKING CHANGES': 'feat!', + '##### Features': 'feat', + '##### Bug Fixes': 'fix', + '##### Chores': 'ignore', + '##### Miscellaneous Chores': 'ignore', + '##### Documentation': 'ignore', + }; + + let currentType = 'feat'; // Default + const newChangelog = []; + + for (const line of releaseNotesSection.split('\n')) { + const trimmedLine = line.trim(); + + // Update current type if it's a header + if (typeMap[trimmedLine]) { + currentType = typeMap[trimmedLine]; + continue; + } + + // Skip ignored sections + if (currentType === 'ignore') { + continue; + } + + // Match and extract changelog item + const itemMatch = trimmedLine.match(/^[*-]\s(.*)$/); + if (itemMatch) { + // Use the raw content, exactly as the original script did + const originalContent = itemMatch[1]; + + const lineAsLowerCase = originalContent.toLowerCase(); + const hasPrefix = prefixesToFilter.some(prefix => lineAsLowerCase.includes(prefix)); + const hasKeyword = lineAsLowerCase.includes(requiredKeyword); + + // Include if it doesn't have a prefix OR it has the keyword + if (!hasPrefix || hasKeyword) { + // Use the original script's output format + newChangelog.push(`- ${currentType}: ${originalContent}`); + } else { + console.log(`Filtering out: ${originalContent}`); + } + } + } + + if (newChangelog.length === 0) { + console.log('Found no changelog items to add after filtering. Exiting.'); + return; + } + + // Construct the override block + const overrideBlock = [ + '\n\nBEGIN_COMMIT_OVERRIDE', + ...newChangelog, + 'END_COMMIT_OVERRIDE' + ].join('\n'); + + // Update PR body + const baseBody = prBody.split('\n\nBEGIN_COMMIT_OVERRIDE')[0].trim(); + const finalBody = baseBody + overrideBlock; + + if (finalBody === prBody) { + console.log('The generated changelog is identical. No update needed.'); + return; + } + + // 5. Update the PR + await github.rest.pulls.update({ + owner: context.repo.owner, + repo: context.repo.repo, + pull_number: context.issue.number, + body: finalBody, + }); + + console.log('Successfully updated the PR body with filtered release notes.'); From e058af4a71080f361d48f2ab14bac14ab7612e3c Mon Sep 17 00:00:00 2001 From: Twisha Bansal <58483338+twishabansal@users.noreply.github.com> Date: Fri, 17 Oct 2025 13:14:18 +0530 Subject: [PATCH 2/5] Update mirror-changelog.yml --- .github/workflows/mirror-changelog.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/mirror-changelog.yml b/.github/workflows/mirror-changelog.yml index 055ccca..2e47516 100644 --- a/.github/workflows/mirror-changelog.yml +++ b/.github/workflows/mirror-changelog.yml @@ -29,7 +29,7 @@ jobs: - name: Add Toolbox Release Notes to PR Body uses: actions/github-script@v6 env: - REQUIRED_KEYWORD: 'alloydb' + REQUIRED_KEYWORD: 'cloudsql' with: script: | const requiredKeyword = process.env.REQUIRED_KEYWORD; From 33c39e2b539f4d7f5e9bc5ee1e57712ebceacd70 Mon Sep 17 00:00:00 2001 From: Twisha Bansal <58483338+twishabansal@users.noreply.github.com> Date: Fri, 17 Oct 2025 13:19:30 +0530 Subject: [PATCH 3/5] Update mirror-changelog.yml --- .github/workflows/mirror-changelog.yml | 23 ++++++++++++++--------- 1 file changed, 14 insertions(+), 9 deletions(-) diff --git a/.github/workflows/mirror-changelog.yml b/.github/workflows/mirror-changelog.yml index 2e47516..1737e86 100644 --- a/.github/workflows/mirror-changelog.yml +++ b/.github/workflows/mirror-changelog.yml @@ -29,10 +29,12 @@ jobs: - name: Add Toolbox Release Notes to PR Body uses: actions/github-script@v6 env: - REQUIRED_KEYWORD: 'cloudsql' + REQUIRED_KEYWORDS: 'cloudsqlmysql,cloud-sql-mysql' with: script: | - const requiredKeyword = process.env.REQUIRED_KEYWORD; + const requiredKeywordsEnv = process.env.REQUIRED_KEYWORDS; + const requiredKeywords = requiredKeywordsEnv.split(',').map(kw => kw.trim()).filter(kw => kw.length > 0); + const prBody = context.payload.pull_request.body || ''; // Extract the relevant changelog section @@ -80,16 +82,19 @@ jobs: // Match and extract changelog item const itemMatch = trimmedLine.match(/^[*-]\s(.*)$/); if (itemMatch) { - // Use the raw content, exactly as the original script did const originalContent = itemMatch[1]; - const lineAsLowerCase = originalContent.toLowerCase(); + const hasPrefix = prefixesToFilter.some(prefix => lineAsLowerCase.includes(prefix)); - const hasKeyword = lineAsLowerCase.includes(requiredKeyword); - // Include if it doesn't have a prefix OR it has the keyword - if (!hasPrefix || hasKeyword) { - // Use the original script's output format + // Check if the line includes ANY of the required keywords + let hasAnyRequiredKeyword = false; + if (requiredKeywords.length > 0) { + hasAnyRequiredKeyword = requiredKeywords.some(keyword => lineAsLowerCase.includes(keyword)); + } + + // Include if it doesn't have a prefix OR it has any of the required keywords + if (!hasPrefix || hasAnyRequiredKeyword) { newChangelog.push(`- ${currentType}: ${originalContent}`); } else { console.log(`Filtering out: ${originalContent}`); @@ -118,7 +123,7 @@ jobs: return; } - // 5. Update the PR + // Update the PR await github.rest.pulls.update({ owner: context.repo.owner, repo: context.repo.repo, From 0ec67caf174466046c5a7d3f36e58b1529f28eaf Mon Sep 17 00:00:00 2001 From: Twisha Bansal <58483338+twishabansal@users.noreply.github.com> Date: Fri, 17 Oct 2025 13:20:54 +0530 Subject: [PATCH 4/5] Update mirror-changelog.yml --- .github/workflows/mirror-changelog.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/mirror-changelog.yml b/.github/workflows/mirror-changelog.yml index 1737e86..42001d2 100644 --- a/.github/workflows/mirror-changelog.yml +++ b/.github/workflows/mirror-changelog.yml @@ -29,7 +29,7 @@ jobs: - name: Add Toolbox Release Notes to PR Body uses: actions/github-script@v6 env: - REQUIRED_KEYWORDS: 'cloudsqlmysql,cloud-sql-mysql' + REQUIRED_KEYWORDS: 'mysql' with: script: | const requiredKeywordsEnv = process.env.REQUIRED_KEYWORDS; From e89df3c2a40054af760f8634c05ae41ea542172d Mon Sep 17 00:00:00 2001 From: Twisha Bansal <58483338+twishabansal@users.noreply.github.com> Date: Fri, 17 Oct 2025 23:05:56 +0530 Subject: [PATCH 5/5] docs: include mirror changelog info in developer docs (#57) Co-authored-by: Averi Kitsch --- DEVELOPER.md | 36 +++++++++++++++++++++++++++++++++++- 1 file changed, 35 insertions(+), 1 deletion(-) diff --git a/DEVELOPER.md b/DEVELOPER.md index 932c317..94f1fb1 100644 --- a/DEVELOPER.md +++ b/DEVELOPER.md @@ -97,7 +97,41 @@ The primary maintainers for this repository are defined in the ### Releasing -The release process is automated using `release-please`. +The release process is automated using `release-please`. It consists of an automated changelog preparation step followed by the manual merging of a Release PR. + +#### Automated Changelog Enrichment + +Before a Release PR is even created, a special workflow automatically mirrors +relevant changelogs from the core `googleapis/genai-toolbox` dependency. This +ensures that the release notes for this extension accurately reflect important +upstream changes. + +The process is handled by the [`mirror-changelog.yml`](.github/workflows/mirror-changelog.yml) workflow: + +1. **Trigger:** The workflow runs automatically on pull requests created by + Renovate for `toolbox` version updates. +2. **Parsing:** It reads the detailed release notes that Renovate includes in + the PR body. +3. **Filtering:** These release notes are filtered to include only changes + relevant to this extension. The relevance is determined by a keyword (e.g., + `mysql`), passed as an environment variable in the workflow file. +4. **Changelog Injection:** The script formats the filtered entries as + conventional commits and injects them into the PR body within a + `BEGIN_COMMIT_OVERRIDE` block. +5. **Release Please:** When the main Release PR is created, `release-please` + reads this override block instead of the standard `chore(deps): ...` commit + message, effectively mirroring the filtered upstream changelog into this + project's release notes. + +> **Note for Maintainers:** The filtering script is an automation aid, but it +> may occasionally produce "false positives" (e.g., an internal logging change +> that happens to contain the keyword). Before merging a `toolbox` dependency +> PR, maintainers must **review the generated `BEGIN_COMMIT_OVERRIDE` block** +> and manually delete any lines that are not relevant to the end-users of this +> extension. The curated override block is the final source of truth for the +> release changelog. + +#### Release Process 1. **Release PR:** When commits with conventional commit headers (e.g., `feat:`, `fix:`) are merged into the `main` branch, `release-please` will