From 093d799ba0dcdc8ab6399bdab151b425480c4700 Mon Sep 17 00:00:00 2001 From: lelemm Date: Thu, 19 Jun 2025 17:22:44 -0300 Subject: [PATCH] Auto generate Release Notes (#5182) * AI Generated release notes * lint * removedverbose console.log --- .../check-first-comment.js | 75 +++++++++++ .../check-release-notes-exists.js | 76 +++++++++++ .../comment-on-pr.js | 76 +++++++++++ .../create-release-notes-file.js | 96 ++++++++++++++ .../determine-category.js | 118 ++++++++++++++++++ .../generate-summary.js | 97 ++++++++++++++ .../ai-generated-release-notes/pr-details.js | 59 +++++++++ .../workflows/ai-generated-release-notes.yml | 89 +++++++++++++ upcoming-release-notes/5182.md | 7 ++ 9 files changed, 693 insertions(+) create mode 100755 .github/actions/ai-generated-release-notes/check-first-comment.js create mode 100755 .github/actions/ai-generated-release-notes/check-release-notes-exists.js create mode 100755 .github/actions/ai-generated-release-notes/comment-on-pr.js create mode 100755 .github/actions/ai-generated-release-notes/create-release-notes-file.js create mode 100755 .github/actions/ai-generated-release-notes/determine-category.js create mode 100755 .github/actions/ai-generated-release-notes/generate-summary.js create mode 100755 .github/actions/ai-generated-release-notes/pr-details.js create mode 100644 .github/workflows/ai-generated-release-notes.yml create mode 100644 upcoming-release-notes/5182.md diff --git a/.github/actions/ai-generated-release-notes/check-first-comment.js b/.github/actions/ai-generated-release-notes/check-first-comment.js new file mode 100755 index 0000000000..7a2dd328fd --- /dev/null +++ b/.github/actions/ai-generated-release-notes/check-first-comment.js @@ -0,0 +1,75 @@ +#!/usr/bin/env node + +import { Octokit } from '@octokit/rest'; +import fs from 'fs'; + +const token = process.env.GITHUB_TOKEN; +const repo = process.env.GITHUB_REPOSITORY; +const issueNumber = process.env.GITHUB_EVENT_ISSUE_NUMBER; +const commentId = process.env.GITHUB_EVENT_COMMENT_ID; + +if (!token || !repo || !issueNumber || !commentId) { + console.log('Missing required environment variables'); + process.exit(1); +} + +const [owner, repoName] = repo.split('/'); +const octokit = new Octokit({ auth: token }); + +function setOutput(name, value) { + fs.appendFileSync(process.env.GITHUB_OUTPUT, `${name}=${value}\n`); +} + +async function checkFirstComment() { + try { + console.log('Fetching comments with Octokit...'); + + // Get all comments with automatic pagination + const comments = await octokit.paginate(octokit.rest.issues.listComments, { + owner, + repo: repoName, + issue_number: issueNumber, + }); + + console.log(`Total comments found: ${comments.length}`); + + // Filter for CodeRabbit summary comments (containing the specific marker) + const coderabbitSummaryComments = comments.filter(comment => { + const isCodeRabbit = comment.user.login === 'coderabbitai[bot]'; + const hasSummaryMarker = comment.body.includes( + '', + ); + + if (isCodeRabbit) { + console.log( + `CodeRabbit comment found (ID: ${comment.id}), has summary marker: ${hasSummaryMarker}`, + ); + } + + return isCodeRabbit && hasSummaryMarker; + }); + + const isFirstSummaryComment = + coderabbitSummaryComments.length === 1 && + coderabbitSummaryComments[0].id == commentId; + + console.log( + `CodeRabbit summary comments found: ${coderabbitSummaryComments.length}`, + ); + console.log(`Current comment ID: ${commentId}`); + console.log(`Is first summary comment: ${isFirstSummaryComment}`); + setOutput('result', isFirstSummaryComment); + } catch (error) { + console.log('Error checking CodeRabbit comment:', error.message); + console.log('Stack:', error.stack); + setOutput('result', 'false'); + process.exit(1); + } +} + +checkFirstComment().catch(error => { + console.log('Unhandled error:', error.message); + console.log('Stack:', error.stack); + setOutput('result', 'false'); + process.exit(1); +}); diff --git a/.github/actions/ai-generated-release-notes/check-release-notes-exists.js b/.github/actions/ai-generated-release-notes/check-release-notes-exists.js new file mode 100755 index 0000000000..2f42bd367c --- /dev/null +++ b/.github/actions/ai-generated-release-notes/check-release-notes-exists.js @@ -0,0 +1,76 @@ +#!/usr/bin/env node + +import { Octokit } from '@octokit/rest'; +import fs from 'fs'; + +const token = process.env.GITHUB_TOKEN; +const repo = process.env.GITHUB_REPOSITORY; +const issueNumber = process.env.GITHUB_EVENT_ISSUE_NUMBER; +const prDetailsJson = process.env.PR_DETAILS; + +if (!token || !repo || !issueNumber || !prDetailsJson) { + console.log('Missing required environment variables'); + process.exit(1); +} + +const [owner, repoName] = repo.split('/'); +const octokit = new Octokit({ auth: token }); + +function setOutput(name, value) { + fs.appendFileSync(process.env.GITHUB_OUTPUT, `${name}=${value}\n`); +} + +async function checkReleaseNotesExists() { + try { + const prDetails = JSON.parse(prDetailsJson); + if (!prDetails) { + console.log('No PR details available, skipping file check'); + setOutput('result', 'false'); + return; + } + + const fileName = `upcoming-release-notes/${prDetails.number}.md`; + + // Get PR info to get head SHA + const { data: pr } = await octokit.rest.pulls.get({ + owner, + repo: repoName, + pull_number: issueNumber, + }); + + const prHeadSha = pr.head.sha; + console.log( + `Checking for file on PR branch: ${pr.head.ref} (${prHeadSha})`, + ); + + // Check if file exists + try { + await octokit.rest.repos.getContent({ + owner, + repo: repoName, + path: fileName, + ref: prHeadSha, + }); + + console.log( + `Release notes file already exists on PR branch: ${fileName}`, + ); + setOutput('result', 'true'); + } catch (error) { + if (error.status === 404) { + console.log( + `No existing release notes file found on PR branch: ${fileName}`, + ); + setOutput('result', 'false'); + } else { + console.log('Error checking file existence:', error.message); + setOutput('result', 'false'); + } + } + } catch (error) { + console.log('Error in file existence check:', error.message); + setOutput('result', 'false'); + } +} + +checkReleaseNotesExists(); diff --git a/.github/actions/ai-generated-release-notes/comment-on-pr.js b/.github/actions/ai-generated-release-notes/comment-on-pr.js new file mode 100755 index 0000000000..a2a424f5dd --- /dev/null +++ b/.github/actions/ai-generated-release-notes/comment-on-pr.js @@ -0,0 +1,76 @@ +#!/usr/bin/env node + +import { Octokit } from '@octokit/rest'; + +const token = process.env.GITHUB_TOKEN; +const repo = process.env.GITHUB_REPOSITORY; +const issueNumber = process.env.GITHUB_EVENT_ISSUE_NUMBER; +const summaryDataJson = process.env.SUMMARY_DATA; +const category = process.env.CATEGORY; + +if (!token || !repo || !issueNumber || !summaryDataJson || !category) { + console.log('Missing required environment variables'); + process.exit(1); +} + +const [owner, repoName] = repo.split('/'); +const octokit = new Octokit({ auth: token }); + +async function commentOnPR() { + try { + const summaryData = JSON.parse(summaryDataJson); + + if (!summaryData) { + console.log('No summary data available, skipping comment'); + return; + } + + if (!category || category === 'null') { + console.log('No valid category available, skipping comment'); + return; + } + + // Clean category for display + const cleanCategory = + typeof category === 'string' + ? category.replace(/^["']|["']$/g, '') + : category; + + // Get PR info for the file URL + const { data: pr } = await octokit.rest.pulls.get({ + owner, + repo: repoName, + pull_number: issueNumber, + }); + + const prBranch = pr.head.ref; + const headOwner = pr.head.repo.owner.login; + const headRepo = pr.head.repo.name; + const fileUrl = `https://github.com/${headOwner}/${headRepo}/blob/${prBranch}/upcoming-release-notes/${summaryData.prNumber}.md`; + + const commentBody = [ + '🤖 **Auto-generated Release Notes**', + '', + `Hey @${summaryData.author}! I've automatically created a release notes file based on CodeRabbit's analysis:`, + '', + `**Category:** ${cleanCategory}`, + `**Summary:** ${summaryData.summary}`, + `**File:** [upcoming-release-notes/${summaryData.prNumber}.md](${fileUrl})`, + '', + 'The release notes file has been committed to the repository. You can edit it if needed before merging.', + ].join('\n'); + + await octokit.rest.issues.createComment({ + owner, + repo: repoName, + issue_number: issueNumber, + body: commentBody, + }); + + console.log('✅ Successfully commented on PR'); + } catch (error) { + console.log('Error commenting on PR:', error.message); + } +} + +commentOnPR(); diff --git a/.github/actions/ai-generated-release-notes/create-release-notes-file.js b/.github/actions/ai-generated-release-notes/create-release-notes-file.js new file mode 100755 index 0000000000..c902f375fd --- /dev/null +++ b/.github/actions/ai-generated-release-notes/create-release-notes-file.js @@ -0,0 +1,96 @@ +#!/usr/bin/env node + +import { Octokit } from '@octokit/rest'; + +const token = process.env.GITHUB_TOKEN; +const repo = process.env.GITHUB_REPOSITORY; +const issueNumber = process.env.GITHUB_EVENT_ISSUE_NUMBER; +const summaryDataJson = process.env.SUMMARY_DATA; +const category = process.env.CATEGORY; + +if (!token || !repo || !issueNumber || !summaryDataJson || !category) { + console.log('Missing required environment variables'); + process.exit(1); +} + +const [owner, repoName] = repo.split('/'); +const octokit = new Octokit({ auth: token }); + +async function createReleaseNotesFile() { + try { + const summaryData = JSON.parse(summaryDataJson); + + console.log('Debug - Category value:', category); + console.log('Debug - Category type:', typeof category); + console.log('Debug - Category JSON stringified:', JSON.stringify(category)); + + if (!summaryData) { + console.log('No summary data available, cannot create file'); + return; + } + + if (!category || category === 'null') { + console.log('No valid category available, cannot create file'); + return; + } + + // Create file content - ensure category is not quoted + const cleanCategory = + typeof category === 'string' + ? category.replace(/^["']|["']$/g, '') + : category; + console.log('Debug - Clean category:', cleanCategory); + + const fileContent = `--- +category: ${cleanCategory} +authors: [${summaryData.author}] +--- + +${summaryData.summary}`; + + const fileName = `upcoming-release-notes/${summaryData.prNumber}.md`; + + console.log(`Creating release notes file: ${fileName}`); + console.log('File content:'); + console.log(fileContent); + + // Get PR info + const { data: pr } = await octokit.rest.pulls.get({ + owner, + repo: repoName, + pull_number: issueNumber, + }); + + const prBranch = pr.head.ref; + const headOwner = pr.head.repo.owner.login; + const headRepo = pr.head.repo.name; + + console.log( + `Committing to PR branch: ${headOwner}/${headRepo}:${prBranch}`, + ); + + // Create the file via GitHub API on the PR branch + await octokit.rest.repos.createOrUpdateFileContents({ + owner: headOwner, + repo: headRepo, + path: fileName, + message: `Add release notes for PR #${summaryData.prNumber}`, + content: Buffer.from(`${fileContent}\n\n`).toString('base64'), + branch: prBranch, + committer: { + name: 'github-actions[bot]', + email: 'github-actions[bot]@users.noreply.github.com', + }, + author: { + name: 'github-actions[bot]', + email: 'github-actions[bot]@users.noreply.github.com', + }, + }); + + console.log(`✅ Successfully created release notes file: ${fileName}`); + } catch (error) { + console.log('Error creating release notes file:', error.message); + } +} + +createReleaseNotesFile(); diff --git a/.github/actions/ai-generated-release-notes/determine-category.js b/.github/actions/ai-generated-release-notes/determine-category.js new file mode 100755 index 0000000000..cca4037580 --- /dev/null +++ b/.github/actions/ai-generated-release-notes/determine-category.js @@ -0,0 +1,118 @@ +#!/usr/bin/env node + +const https = require('https'); +const fs = require('fs'); + +const commentBody = process.env.GITHUB_EVENT_COMMENT_BODY; +const prDetailsJson = process.env.PR_DETAILS; +const summaryDataJson = process.env.SUMMARY_DATA; +const openaiApiKey = process.env.OPENAI_API_KEY; + +if (!commentBody || !prDetailsJson || !summaryDataJson || !openaiApiKey) { + console.log('Missing required environment variables'); + process.exit(1); +} + +function setOutput(name, value) { + fs.appendFileSync(process.env.GITHUB_OUTPUT, `${name}=${value}\n`); +} + +try { + const prDetails = JSON.parse(prDetailsJson); + const summaryData = JSON.parse(summaryDataJson); + + if (!summaryData || !prDetails) { + console.log('Missing data for categorization'); + setOutput('result', 'null'); + process.exit(0); + } + + const data = JSON.stringify({ + model: 'gpt-4o-mini', + messages: [ + { + role: 'system', + content: + 'You are categorizing pull requests for release notes. You must respond with exactly one of these categories: "Features", "Enhancements", "Bugfix", or "Maintenance". No other text or explanation.', + }, + { + role: 'user', + content: `PR Title: ${prDetails.title}\n\nGenerated Summary: ${summaryData.summary}\n\nCodeRabbit Analysis:\n${commentBody}\n\nCategories:\n- Features: New functionality or capabilities\n- Bugfix: Fixes for broken or incorrect behavior\n- Enhancements: Improvements to existing functionality\n- Maintenance: Code cleanup, refactoring, dependencies, etc.\n\nWhat category does this PR belong to?`, + }, + ], + max_tokens: 10, + temperature: 0.1, + }); + + const options = { + hostname: 'api.openai.com', + path: '/v1/chat/completions', + method: 'POST', + headers: { + Authorization: `Bearer ${openaiApiKey}`, + 'Content-Type': 'application/json', + }, + }; + + const req = https.request(options, res => { + let responseData = ''; + res.on('data', chunk => (responseData += chunk)); + res.on('end', () => { + if (res.statusCode !== 200) { + console.log('OpenAI API error for categorization'); + setOutput('result', 'null'); + return; + } + + try { + const response = JSON.parse(responseData); + console.log('OpenAI raw response:', JSON.stringify(response, null, 2)); + + const rawContent = response.choices[0].message.content.trim(); + console.log('Raw content from OpenAI:', rawContent); + + let category; + try { + category = JSON.parse(rawContent); + console.log('Parsed category:', category); + } catch (parseError) { + console.log( + 'JSON parse error, using raw content:', + parseError.message, + ); + category = rawContent; + } + + // Validate the category response + const validCategories = [ + 'Features', + 'Bugfix', + 'Enhancements', + 'Maintenance', + ]; + if (validCategories.includes(category)) { + console.log('OpenAI categorized as:', category); + setOutput('result', category); + } else { + console.log('Invalid category from OpenAI:', category); + console.log('Valid categories are:', validCategories); + setOutput('result', 'null'); + } + } catch (error) { + console.log('Error parsing OpenAI response:', error.message); + setOutput('result', 'null'); + } + }); + }); + + req.on('error', error => { + console.log('Error in categorization:', error.message); + setOutput('result', 'null'); + }); + + req.write(data); + req.end(); +} catch (error) { + console.log('Error in categorization:', error.message); + setOutput('result', 'null'); +} diff --git a/.github/actions/ai-generated-release-notes/generate-summary.js b/.github/actions/ai-generated-release-notes/generate-summary.js new file mode 100755 index 0000000000..056cf571ea --- /dev/null +++ b/.github/actions/ai-generated-release-notes/generate-summary.js @@ -0,0 +1,97 @@ +#!/usr/bin/env node + +const https = require('https'); +const fs = require('fs'); + +const commentBody = process.env.GITHUB_EVENT_COMMENT_BODY; +const prDetailsJson = process.env.PR_DETAILS; +const openaiApiKey = process.env.OPENAI_API_KEY; + +if (!commentBody || !prDetailsJson || !openaiApiKey) { + console.log('Missing required environment variables'); + process.exit(1); +} + +function setOutput(name, value) { + fs.appendFileSync(process.env.GITHUB_OUTPUT, `${name}=${value}\n`); +} + +try { + const prDetails = JSON.parse(prDetailsJson); + + if (!prDetails) { + console.log('No PR details available, cannot generate summary'); + setOutput('result', 'null'); + process.exit(0); + } + + console.log('CodeRabbit comment body:', commentBody); + + const data = JSON.stringify({ + model: 'gpt-4o-mini', + messages: [ + { + role: 'system', + content: + 'You are a technical writer helping to create concise release notes. Generate a maximum 15-word summary that describes what this PR does. Focus on the user-facing changes or bug fixes. Do not include "This PR" or similar phrases - just describe the change directly. Start with a base form verb (e.g., "Add" not "Adds", "Fix" not "Fixes", "Introduce" not "Introduces").', + }, + { + role: 'user', + content: `PR Title: ${prDetails.title}\n\nCodeRabbit Analysis:\n${commentBody}\n\nPlease provide a concise summary (max 15 words) of what this PR accomplishes.`, + }, + ], + max_tokens: 50, + temperature: 0.3, + }); + + const options = { + hostname: 'api.openai.com', + path: '/v1/chat/completions', + method: 'POST', + headers: { + Authorization: `Bearer ${openaiApiKey}`, + 'Content-Type': 'application/json', + }, + }; + + const req = https.request(options, res => { + let responseData = ''; + res.on('data', chunk => (responseData += chunk)); + res.on('end', () => { + if (res.statusCode !== 200) { + console.log(`OpenAI API error: ${res.statusCode} ${res.statusMessage}`); + setOutput('result', 'null'); + return; + } + + try { + const response = JSON.parse(responseData); + const summary = response.choices[0].message.content.trim(); + + console.log('Generated summary:', summary); + + const result = { + summary: summary, + prNumber: prDetails.number, + author: prDetails.author, + }; + + setOutput('result', JSON.stringify(result)); + } catch (error) { + console.log('Error parsing OpenAI response:', error.message); + setOutput('result', 'null'); + } + }); + }); + + req.on('error', error => { + console.log('Error generating summary:', error.message); + setOutput('result', 'null'); + }); + + req.write(data); + req.end(); +} catch (error) { + console.log('Error generating summary:', error.message); + setOutput('result', 'null'); +} diff --git a/.github/actions/ai-generated-release-notes/pr-details.js b/.github/actions/ai-generated-release-notes/pr-details.js new file mode 100755 index 0000000000..1708d595ae --- /dev/null +++ b/.github/actions/ai-generated-release-notes/pr-details.js @@ -0,0 +1,59 @@ +#!/usr/bin/env node + +import { Octokit } from '@octokit/rest'; +import fs from 'fs'; + +const token = process.env.GITHUB_TOKEN; +const repo = process.env.GITHUB_REPOSITORY; +const issueNumber = process.env.GITHUB_EVENT_ISSUE_NUMBER; + +if (!token || !repo || !issueNumber) { + console.log('Missing required environment variables'); + process.exit(1); +} + +const [owner, repoName] = repo.split('/'); +const octokit = new Octokit({ auth: token }); + +function setOutput(name, value) { + fs.appendFileSync(process.env.GITHUB_OUTPUT, `${name}=${value}\n`); +} + +async function getPRDetails() { + try { + console.log( + `Fetching PR details for ${owner}/${repoName}#${issueNumber}...`, + ); + + const { data: pr } = await octokit.rest.pulls.get({ + owner, + repo: repoName, + pull_number: issueNumber, + }); + + console.log('PR details fetched successfully'); + console.log('- PR Number:', pr.number); + console.log('- PR Author:', pr.user.login); + console.log('- PR Title:', pr.title); + + const result = { + number: pr.number, + author: pr.user.login, + title: pr.title, + }; + + setOutput('result', JSON.stringify(result)); + } catch (error) { + console.log('Error getting PR details:', error.message); + console.log('Stack:', error.stack); + setOutput('result', 'null'); + process.exit(1); + } +} + +getPRDetails().catch(error => { + console.log('Unhandled error:', error.message); + console.log('Stack:', error.stack); + setOutput('result', 'null'); + process.exit(1); +}); diff --git a/.github/workflows/ai-generated-release-notes.yml b/.github/workflows/ai-generated-release-notes.yml new file mode 100644 index 0000000000..2ed789ffe5 --- /dev/null +++ b/.github/workflows/ai-generated-release-notes.yml @@ -0,0 +1,89 @@ +name: Generate Release Notes from CodeRabbit summary + +on: + issue_comment: + types: [created] + +jobs: + generate-release-notes: + # Only run on PR comments from CodeRabbit bot + if: github.event.issue.pull_request && github.event.comment.user.login == 'coderabbitai[bot]' + runs-on: ubuntu-latest + timeout-minutes: 10 + permissions: + contents: write + pull-requests: read + issues: read + + steps: + - name: Checkout repository + uses: actions/checkout@v4 + - name: Set up environment + uses: ./.github/actions/setup + + - name: Check if this is CodeRabbit's first comment + id: check-first-comment + run: node .github/actions/ai-generated-release-notes/check-first-comment.js + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + GITHUB_REPOSITORY: ${{ github.repository }} + GITHUB_EVENT_ISSUE_NUMBER: ${{ github.event.issue.number }} + GITHUB_EVENT_COMMENT_ID: ${{ github.event.comment.id }} + + - name: Get PR details + if: steps.check-first-comment.outputs.result == 'true' + id: pr-details + run: node .github/actions/ai-generated-release-notes/pr-details.js + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + GITHUB_REPOSITORY: ${{ github.repository }} + GITHUB_EVENT_ISSUE_NUMBER: ${{ github.event.issue.number }} + + - name: Check if release notes file already exists + if: steps.check-first-comment.outputs.result == 'true' && steps.pr-details.outputs.result != 'null' + id: check-release-notes-exists + run: node .github/actions/ai-generated-release-notes/check-release-notes-exists.js + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + GITHUB_REPOSITORY: ${{ github.repository }} + GITHUB_EVENT_ISSUE_NUMBER: ${{ github.event.issue.number }} + PR_DETAILS: ${{ steps.pr-details.outputs.result }} + + - name: Generate summary with OpenAI + if: steps.check-first-comment.outputs.result == 'true' && steps.check-release-notes-exists.outputs.result == 'false' + id: generate-summary + run: node .github/actions/ai-generated-release-notes/generate-summary.js + env: + OPENAI_API_KEY: ${{ secrets.OPENAI_API_KEY }} + GITHUB_EVENT_COMMENT_BODY: ${{ github.event.comment.body }} + PR_DETAILS: ${{ steps.pr-details.outputs.result }} + + - name: Determine category with OpenAI + if: steps.check-first-comment.outputs.result == 'true' && steps.check-release-notes-exists.outputs.result == 'false' && steps.generate-summary.outputs.result != 'null' + id: determine-category + run: node .github/actions/ai-generated-release-notes/determine-category.js + env: + OPENAI_API_KEY: ${{ secrets.OPENAI_API_KEY }} + GITHUB_EVENT_COMMENT_BODY: ${{ github.event.comment.body }} + PR_DETAILS: ${{ steps.pr-details.outputs.result }} + SUMMARY_DATA: ${{ steps.generate-summary.outputs.result }} + + - name: Create and commit release notes file via GitHub API + if: steps.check-first-comment.outputs.result == 'true' && steps.check-release-notes-exists.outputs.result == 'false' && steps.generate-summary.outputs.result != 'null' && steps.determine-category.outputs.result != 'null' && steps.determine-category.outputs.result != '' + run: node .github/actions/ai-generated-release-notes/create-release-notes-file.js + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + GITHUB_REPOSITORY: ${{ github.repository }} + GITHUB_EVENT_ISSUE_NUMBER: ${{ github.event.issue.number }} + SUMMARY_DATA: ${{ steps.generate-summary.outputs.result }} + CATEGORY: ${{ steps.determine-category.outputs.result }} + + - name: Comment on PR + if: steps.check-first-comment.outputs.result == 'true' && steps.check-release-notes-exists.outputs.result == 'false' && steps.generate-summary.outputs.result != 'null' && steps.determine-category.outputs.result != 'null' && steps.determine-category.outputs.result != '' + run: node .github/actions/ai-generated-release-notes/comment-on-pr.js + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + GITHUB_REPOSITORY: ${{ github.repository }} + GITHUB_EVENT_ISSUE_NUMBER: ${{ github.event.issue.number }} + SUMMARY_DATA: ${{ steps.generate-summary.outputs.result }} + CATEGORY: ${{ steps.determine-category.outputs.result }} diff --git a/upcoming-release-notes/5182.md b/upcoming-release-notes/5182.md new file mode 100644 index 0000000000..0b36593061 --- /dev/null +++ b/upcoming-release-notes/5182.md @@ -0,0 +1,7 @@ +--- +category: Maintenance +authors: [lelemm] +--- + +Automate release notes generation for pull requests using GitHub Actions and OpenAI API. +