From 162081c68e0be2b3044c837bb1bbdcd273d3b946 Mon Sep 17 00:00:00 2001 From: Stephan Schroevers Date: Sat, 15 Nov 2025 21:21:45 +0100 Subject: [PATCH 01/14] Have OpenAI's Codex suggest commit messages This new GitHub Actions workflow upserts a pull request comment any time the PR is updated, to suggest an appropriate commit message for the squash-and-merge workflow. The workflow can also be triggered manually to simplify testing. --- .github/workflows/suggest-commit-message.yml | 272 +++++++++++++++++++ 1 file changed, 272 insertions(+) create mode 100644 .github/workflows/suggest-commit-message.yml diff --git a/.github/workflows/suggest-commit-message.yml b/.github/workflows/suggest-commit-message.yml new file mode 100644 index 0000000000..4d2f572e7e --- /dev/null +++ b/.github/workflows/suggest-commit-message.yml @@ -0,0 +1,272 @@ +name: Suggest PR commit message +on: + pull_request: + types: + - edited + - opened + - reopened + - synchronize + workflow_dispatch: + inputs: + pr_number: + description: Pull request number of interest + required: true + type: number +permissions: + contents: read +concurrency: + group: suggest-commit-message-${{ github.event.pull_request.number || github.event.inputs.pr_number }} + cancel-in-progress: true +jobs: + suggest: + permissions: + contents: read + pull-requests: write + runs-on: ubuntu-24.04 + environment: codex + steps: + - name: Install Harden-Runner + uses: step-security/harden-runner@95d9a5deda9de15063e7595e9719c11c38c90ae2 # v2.13.2 + with: + # We can't disable `sudo`, as `openai/codex-action` unconditionally + # invokes `sudo`. That step does disable `sudo` for itself and + # subsequent steps. + # XXX: Consider splitting this workflow into two jobs, with + # `openai/codex-action` being the first step of the second job. + disable-sudo-and-containers: false + # XXX: Change to `egress-policy: block` once we better understand + # whether Codex attempts to access arbitrary URLs. + egress-policy: audit + - name: Resolve pull request metadata + id: pr-details + uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8.0.0 + with: + github-token: ${{ secrets.GITHUB_TOKEN }} + script: | + const prNumber = Number(context.payload.pull_request?.number ?? context.payload.inputs?.pr_number); + if (!Number.isFinite(prNumber) || prNumber <= 0) { + throw new Error('Unable to determine pull request number'); + } + + const { data: pr } = await github.rest.pulls.get({ + owner: context.repo.owner, + repo: context.repo.repo, + pull_number: prNumber, + }); + + core.setOutput('number', String(pr.number)); + core.setOutput('title', pr.title ?? ''); + core.setOutput('body', pr.body ?? ''); + core.setOutput('author', pr.user?.login ?? ''); + core.setOutput('baseRef', pr.base.ref ?? ''); + core.setOutput('baseSha', pr.base.sha ?? ''); + core.setOutput('headRef', pr.head.ref ?? ''); + core.setOutput('headSha', pr.head.sha ?? ''); + - name: Check out pull request head + uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0 + with: + ref: ${{ steps.pr-details.outputs.headSha }} + fetch-depth: 0 + - name: Prepare Codex prompt + id: prompt + uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8.0.0 + env: + PR_NUMBER: ${{ steps.pr-details.outputs.number }} + PR_TITLE: ${{ steps.pr-details.outputs.title }} + PR_BODY: ${{ steps.pr-details.outputs.body }} + PR_AUTHOR: ${{ steps.pr-details.outputs.author }} + BASE_REF: ${{ steps.pr-details.outputs.baseRef }} + BASE_SHA: ${{ steps.pr-details.outputs.baseSha }} + HEAD_REF: ${{ steps.pr-details.outputs.headRef }} + HEAD_SHA: ${{ steps.pr-details.outputs.headSha }} + with: + script: | + const { execFileSync } = require('child_process'); + const fs = require('fs'); + + const git = (args, limit) => { + const output = execFileSync('git', args, { encoding: 'utf8' }).trim(); + if (!limit || !output) { + return output; + } + + const lines = output.split(/\r?\n/); + if (lines.length <= limit) { + return output; + } + + const truncated = lines.slice(0, limit).join('\n'); + return `${truncated}\n... (${limit} of ${lines.length} lines shown)`; + }; + + const env = process.env; + const prNumber = env.PR_NUMBER; + const title = env.PR_TITLE; + const body = env.PR_BODY; + const author = env.PR_AUTHOR; + const baseRef = env.BASE_REF; + const baseSha = env.BASE_SHA; + const headRef = env.HEAD_REF; + const headSha = env.HEAD_SHA; + + const diffStat = git(['diff', '--name-status', `${baseSha}...${headSha}`]) || ''; + const diffExcerpt = git(['diff', '--unified=3', `${baseSha}...${headSha}`], 500) || ''; + const recentCommits = git(['log', '--pretty=format:%h %s', '-n', '100']) || ''; + const upgradeExamples = + git(['log', '--grep', '^Upgrade', '--pretty=format:%h %s%n%b%n---', '-n', '100']) || + ''; + + const cleanedBody = (body || '').trim() || ''; + + const instructions = ` + You are an experienced maintainer helping to craft the squash commit message for PR #${prNumber} in the PicnicSupermarket/error-prone-support repository. + + Requirements: + 1. Write the summary line in the imperative mood. Keep it at or below 72 characters. + 2. Wrap each body paragraph at 72 characters. Focus on the "what" and "why" rather than implementation details. + 3. Keep the overall message concise. + 4. Match the established format used in similar past commits. + 5. Wrap code references in backticks. + 6. For dependency upgrades in particular, *very precisely* follow the pattern of past commit messages: reuse the summary wording (only adjust version numbers) and list updated changelog, release note, and diff URLs in the body. + 7. Don't hallucinate URLs, version numbers, or other factual information. + 8. Never split URLs across multiple lines, even if they exceed 72 characters. + + Some further guidelines to help you craft good upgrade commit messages: + - Unless highly salient, don't summarize code changes made as part of the upgrade. + - Don't bother linking to anchors within changelogs or release notes; just link to the main page. + - For GitHub-hosted projects, always link to all relevant GitHub release pages, including those for intermediate versions. + - This includes milestones and release candidates; if necessary, use the GitHub API to identify these. + - For GitHub-hosted projects, always link to the full diff between versions. + - Enumerate links in the following order: + 1. First, link to custom release note documents. + 2. Then list all GitHub release links in ascending order. + 3. Finally, provide the full diff link. + - If the upgrade involves multiple dependencies, group the links by dependency. + - When the Maven \u0060version.error-prone-orig\u0060 property is changed, this upgrades both Error Prone and Picnic's Error Prone fork. In this case: + 1. Make sure that the commit message includes a diff URL for the latter. + 2. Don't explicitly mention that \u0060version.error-prone-orig\u0060 got changed; just focus on the fact that Error Prone is being upgraded. + + Return a JSON object with the following shape: + { + "summary": "", + "body": "" + } + + Ensure the JSON is valid. Do not include additional commentary outside the JSON structure. + + Pull request metadata: + - Number: ${prNumber} + - Title: ${title} + - Author: ${author} + - Base branch: ${baseRef} (${baseSha}) + - Head branch: ${headRef} (${headSha}) + + Pull request description: + \u0060\u0060\u0060 + ${cleanedBody} + \u0060\u0060\u0060 + + Changed files (\u0060git diff --name-status ${baseSha}...${headSha}\u0060): + \u0060\u0060\u0060 + ${diffStat} + \u0060\u0060\u0060 + + Diff excerpt (\u0060git diff --unified=3 ${baseSha}...${headSha}\u0060, truncated to 500 lines when necessary): + \u0060\u0060\u0060 + ${diffExcerpt} + \u0060\u0060\u0060 + + Recent commit history (\u0060git log --pretty='format:%h %s' -n 100\u0060): + \u0060\u0060\u0060 + ${recentCommits} + \u0060\u0060\u0060 + + Recent upgrade commit examples (\u0060git log --grep '^Upgrade' --pretty='format:%h %s%n%b%n---' -n 100\u0060): + \u0060\u0060\u0060 + ${upgradeExamples} + \u0060\u0060\u0060 + `; + + const promptPath = '/tmp/codex-prompt-suggest-commit-message.md'; + fs.writeFileSync(promptPath, instructions.trim() + '\n', { encoding: 'utf8' }); + - name: Suggest commit message with Codex + id: codex + uses: openai/codex-action@02e7b2943818fbac9f077c3d1249a198ab358352 # v1.2 + with: + # XXX: Consider whether to set `safety-strategy: read-only`. In some + # cases the agent may be able to suggest a better commit message by + # following links or otherwise looking up information online. See + # also the `egress-policy` discussion further up. + sandbox: read-only + openai-api-key: ${{ secrets.OPENAI_API_KEY }} + prompt-file: /tmp/codex-prompt-suggest-commit-message.md + output-schema: | + { + "type": "object", + "properties": { + "summary": { + "type": "string", + "description": "Summary line in imperative mood, preferably at most 72 characters" + }, + "body": { + "type": "string", + "description": "Commit message body explaining what and why, wrapped at 72 characters" + } + }, + "required": ["summary", "body"], + "additionalProperties": false + } + - name: Upsert pull request comment + uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8.0.0 + env: + PR_NUMBER: ${{ steps.pr-details.outputs.number }} + CODEX_RESULT: ${{ steps.codex.outputs.final-message }} + with: + github-token: ${{ secrets.GITHUB_TOKEN }} + script: | + const prNumber = process.env.PR_NUMBER; + const codexResult = JSON.parse(process.env.CODEX_RESULT); + + // The PR summary may or may not already include the PR number; + // ensure that the correct number is present exactly once. + const summary = codexResult.summary.trim().replace(/(\s+\(#\d+\))*$/, ` (#${prNumber})`); + const body = codexResult.body.trim(); + const commitMessage = body ? `${summary}\n\n${body}` : summary; + + // The comment to be upserted includes a hidden marker to identify it. + const marker = ''; + const commentBody = `Suggested commit message:\n${marker}\n\n\u0060\u0060\u0060\n${commitMessage}\n\u0060\u0060\u0060\n`; + + const comments = await github.paginate(github.rest.issues.listComments, { + owner: context.repo.owner, + repo: context.repo.repo, + issue_number: prNumber, + per_page: 100, + }); + + const existing = comments.find((comment) => comment.body?.includes(marker)); + if (!existing) { + await github.rest.issues.createComment({ + owner: context.repo.owner, + repo: context.repo.repo, + issue_number: prNumber, + body: commentBody, + }); + core.info('Created new commit message suggestion comment.'); + return; + } + + if (existing.body === commentBody) { + core.info('Existing comment already up to date.'); + return; + } + + // XXX: Consider backing off if the comment has most recently been + // edited by another user. + await github.rest.issues.updateComment({ + owner: context.repo.owner, + repo: context.repo.repo, + comment_id: existing.id, + body: commentBody, + }); + core.info(`Updated comment ${existing.id}.`); From 632f5d02ec170e98206361d7b6188fae5e217387 Mon Sep 17 00:00:00 2001 From: Stephan Schroevers Date: Sun, 16 Nov 2025 20:53:17 +0100 Subject: [PATCH 02/14] More tweaks --- .github/workflows/suggest-commit-message.yml | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/.github/workflows/suggest-commit-message.yml b/.github/workflows/suggest-commit-message.yml index 4d2f572e7e..ffd2de850d 100644 --- a/.github/workflows/suggest-commit-message.yml +++ b/.github/workflows/suggest-commit-message.yml @@ -122,7 +122,8 @@ jobs: You are an experienced maintainer helping to craft the squash commit message for PR #${prNumber} in the PicnicSupermarket/error-prone-support repository. Requirements: - 1. Write the summary line in the imperative mood. Keep it at or below 72 characters. + 1. Write the summary line in the imperative mood. Try not to exceed 80 characters. + 2. End the summary line with the PR number in parentheses, i.e., " (#${prNumber})". 2. Wrap each body paragraph at 72 characters. Focus on the "what" and "why" rather than implementation details. 3. Keep the overall message concise. 4. Match the established format used in similar past commits. @@ -130,6 +131,7 @@ jobs: 6. For dependency upgrades in particular, *very precisely* follow the pattern of past commit messages: reuse the summary wording (only adjust version numbers) and list updated changelog, release note, and diff URLs in the body. 7. Don't hallucinate URLs, version numbers, or other factual information. 8. Never split URLs across multiple lines, even if they exceed 72 characters. + 9. If the pull request description already contains a suitable commit message, prefer using that as-is. Some further guidelines to help you craft good upgrade commit messages: - Unless highly salient, don't summarize code changes made as part of the upgrade. @@ -227,9 +229,7 @@ jobs: const prNumber = process.env.PR_NUMBER; const codexResult = JSON.parse(process.env.CODEX_RESULT); - // The PR summary may or may not already include the PR number; - // ensure that the correct number is present exactly once. - const summary = codexResult.summary.trim().replace(/(\s+\(#\d+\))*$/, ` (#${prNumber})`); + const summary = codexResult.summary.trim(); const body = codexResult.body.trim(); const commitMessage = body ? `${summary}\n\n${body}` : summary; From 0e20d3c6878454420fc6eb6fd924efd7e6bf6cee Mon Sep 17 00:00:00 2001 From: Stephan Schroevers Date: Sun, 16 Nov 2025 21:05:31 +0100 Subject: [PATCH 03/14] Don't overwrite edits --- .github/workflows/suggest-commit-message.yml | 21 ++++++++++++++++++-- 1 file changed, 19 insertions(+), 2 deletions(-) diff --git a/.github/workflows/suggest-commit-message.yml b/.github/workflows/suggest-commit-message.yml index ffd2de850d..8539b624aa 100644 --- a/.github/workflows/suggest-commit-message.yml +++ b/.github/workflows/suggest-commit-message.yml @@ -261,8 +261,25 @@ jobs: return; } - // XXX: Consider backing off if the comment has most recently been - // edited by another user. + const commentNode = await github.graphql( + `query ($id: ID!) { + node(id: $id) { + ... on IssueComment { + editor { + login + } + } + } + }`, + { id: existing.node_id }, + ); + + const lastEditor = commentNode.node.editor?.login; + if (lastEditor !== existing.user.login) { + core.info(`Skipping update because comment was last edited by ${lastEditor}.`); + return; + } + await github.rest.issues.updateComment({ owner: context.repo.owner, repo: context.repo.repo, From d9b7e93be7e3a5f930310bfd29eeb1d83ff100c7 Mon Sep 17 00:00:00 2001 From: Stephan Schroevers Date: Sun, 16 Nov 2025 21:09:41 +0100 Subject: [PATCH 04/14] Debug it --- .github/workflows/suggest-commit-message.yml | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/.github/workflows/suggest-commit-message.yml b/.github/workflows/suggest-commit-message.yml index 8539b624aa..1a1a2078c5 100644 --- a/.github/workflows/suggest-commit-message.yml +++ b/.github/workflows/suggest-commit-message.yml @@ -274,9 +274,10 @@ jobs: { id: existing.node_id }, ); + const workflowUser = existing.user.login; const lastEditor = commentNode.node.editor?.login; - if (lastEditor !== existing.user.login) { - core.info(`Skipping update because comment was last edited by ${lastEditor}.`); + if (lastEditor !== workflowUser) { + core.info(`Skipping update because comment was last edited by ${lastEditor} rather than ${workflowUser}.`); return; } From b24a94416c666e385214b4bbe22f5e4ee293e06a Mon Sep 17 00:00:00 2001 From: Stephan Schroevers Date: Sun, 16 Nov 2025 21:25:28 +0100 Subject: [PATCH 05/14] Another attempt --- .github/workflows/suggest-commit-message.yml | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/.github/workflows/suggest-commit-message.yml b/.github/workflows/suggest-commit-message.yml index 1a1a2078c5..72f2052604 100644 --- a/.github/workflows/suggest-commit-message.yml +++ b/.github/workflows/suggest-commit-message.yml @@ -274,10 +274,13 @@ jobs: { id: existing.node_id }, ); - const workflowUser = existing.user.login; + const originalCommenter = existing.user.login; + const workflowEditors = [originalCommenter, 'github-actions', 'github-actions[bot]']; const lastEditor = commentNode.node.editor?.login; - if (lastEditor !== workflowUser) { - core.info(`Skipping update because comment was last edited by ${lastEditor} rather than ${workflowUser}.`); + if (lastEditor && !workflowEditors.includes(lastEditor)) { + core.info( + `Skipping update because comment was last edited by ${lastEditor} rather than an allowed editor (${workflowEditors.join(', ')}).`, + ); return; } @@ -287,4 +290,4 @@ jobs: comment_id: existing.id, body: commentBody, }); - core.info(`Updated comment ${existing.id}.`); + core.info(`Updated comment ${existing.id} by ${originalCommenter}.`); From 92c41fe47c4fe4b7ec3c961b0b48dc23f8327ac5 Mon Sep 17 00:00:00 2001 From: Stephan Schroevers Date: Sun, 16 Nov 2025 21:52:59 +0100 Subject: [PATCH 06/14] Tweaks --- .github/workflows/suggest-commit-message.yml | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/.github/workflows/suggest-commit-message.yml b/.github/workflows/suggest-commit-message.yml index 72f2052604..f8ba510af3 100644 --- a/.github/workflows/suggest-commit-message.yml +++ b/.github/workflows/suggest-commit-message.yml @@ -261,6 +261,7 @@ jobs: return; } + // Determine who, if anybody, last edited the existing comment. const commentNode = await github.graphql( `query ($id: ID!) { node(id: $id) { @@ -274,12 +275,13 @@ jobs: { id: existing.node_id }, ); - const originalCommenter = existing.user.login; - const workflowEditors = [originalCommenter, 'github-actions', 'github-actions[bot]']; - const lastEditor = commentNode.node.editor?.login; - if (lastEditor && !workflowEditors.includes(lastEditor)) { + // If another user last edited the comment, skip the update. Note that the `[bot]` suffix is stripped + // because it does not seem to be present consistently. + const originalCommenter = existing.user.login.replace(/\[bot\]$/, ''); + const lastEditor = commentNode.node.editor?.login?.replace(/\[bot\]$/, ''); + if (lastEditor && lastEditor !== originalCommenter) { core.info( - `Skipping update because comment was last edited by ${lastEditor} rather than an allowed editor (${workflowEditors.join(', ')}).`, + `Skipping update because comment was last edited by ${lastEditor} rather than ${originalCommenter}.`, ); return; } From a9deb076eb270a5224d3aca062be3be2919db3de Mon Sep 17 00:00:00 2001 From: Stephan Schroevers Date: Sun, 16 Nov 2025 22:14:48 +0100 Subject: [PATCH 07/14] Try more nudging --- .github/workflows/suggest-commit-message.yml | 19 +++++++++++-------- 1 file changed, 11 insertions(+), 8 deletions(-) diff --git a/.github/workflows/suggest-commit-message.yml b/.github/workflows/suggest-commit-message.yml index f8ba510af3..feee7669da 100644 --- a/.github/workflows/suggest-commit-message.yml +++ b/.github/workflows/suggest-commit-message.yml @@ -111,9 +111,11 @@ jobs: const diffStat = git(['diff', '--name-status', `${baseSha}...${headSha}`]) || ''; const diffExcerpt = git(['diff', '--unified=3', `${baseSha}...${headSha}`], 500) || ''; - const recentCommits = git(['log', '--pretty=format:%h %s', '-n', '100']) || ''; - const upgradeExamples = - git(['log', '--grep', '^Upgrade', '--pretty=format:%h %s%n%b%n---', '-n', '100']) || + const nonUpgradeCommits = + git(['log', '--grep', '^Upgrade', '--invert-grep', '--pretty=format:%h %B%n---', '-n', '100', 'origin/HEAD']) || + ''; + const upgradeCommits = + git(['log', '--grep', '^Upgrade', '--pretty=format:%h %B%n---', '-n', '100', 'origin/HEAD']) || ''; const cleanedBody = (body || '').trim() || ''; @@ -147,6 +149,7 @@ jobs: - When the Maven \u0060version.error-prone-orig\u0060 property is changed, this upgrades both Error Prone and Picnic's Error Prone fork. In this case: 1. Make sure that the commit message includes a diff URL for the latter. 2. Don't explicitly mention that \u0060version.error-prone-orig\u0060 got changed; just focus on the fact that Error Prone is being upgraded. + - If the example upgrade commits shown below don't include at least one upgrade of the same dependency being upgraded in this pull request, check the full Git history to find relevant past upgrade commit messages to mimic. Return a JSON object with the following shape: { @@ -173,19 +176,19 @@ jobs: ${diffStat} \u0060\u0060\u0060 - Diff excerpt (\u0060git diff --unified=3 ${baseSha}...${headSha}\u0060, truncated to 500 lines when necessary): + Diff excerpt (\u0060git diff --unified=3 ${baseSha}...${headSha}\u0060, truncated to 500 lines if necessary): \u0060\u0060\u0060 ${diffExcerpt} \u0060\u0060\u0060 - Recent commit history (\u0060git log --pretty='format:%h %s' -n 100\u0060): + Recent non-upgrade commits examples (\u0060git log --grep '^Upgrade' --invert-grep --pretty='format:%h %B%n---' -n 100\u0060): \u0060\u0060\u0060 - ${recentCommits} + ${nonUpgradeCommits} \u0060\u0060\u0060 - Recent upgrade commit examples (\u0060git log --grep '^Upgrade' --pretty='format:%h %s%n%b%n---' -n 100\u0060): + Recent upgrade commit examples (\u0060git log --grep '^Upgrade' --pretty='format:%h %B%n---' -n 100\u0060): \u0060\u0060\u0060 - ${upgradeExamples} + ${upgradeCommits} \u0060\u0060\u0060 `; From 0a4f6ff6007a33ae53dd1d6e3d97c2d05279c610 Mon Sep 17 00:00:00 2001 From: Stephan Schroevers Date: Sun, 16 Nov 2025 22:16:30 +0100 Subject: [PATCH 08/14] Try another way --- .github/workflows/suggest-commit-message.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/suggest-commit-message.yml b/.github/workflows/suggest-commit-message.yml index feee7669da..8e09f110f8 100644 --- a/.github/workflows/suggest-commit-message.yml +++ b/.github/workflows/suggest-commit-message.yml @@ -112,10 +112,10 @@ jobs: const diffStat = git(['diff', '--name-status', `${baseSha}...${headSha}`]) || ''; const diffExcerpt = git(['diff', '--unified=3', `${baseSha}...${headSha}`], 500) || ''; const nonUpgradeCommits = - git(['log', '--grep', '^Upgrade', '--invert-grep', '--pretty=format:%h %B%n---', '-n', '100', 'origin/HEAD']) || + git(['log', '--grep', '^Upgrade', '--invert-grep', '--pretty=format:%h %B%n---', '-n', '100', 'master']) || ''; const upgradeCommits = - git(['log', '--grep', '^Upgrade', '--pretty=format:%h %B%n---', '-n', '100', 'origin/HEAD']) || + git(['log', '--grep', '^Upgrade', '--pretty=format:%h %B%n---', '-n', '100', 'master']) || ''; const cleanedBody = (body || '').trim() || ''; From dd7924c7968953033057fcd91872f84e728eeb6a Mon Sep 17 00:00:00 2001 From: Stephan Schroevers Date: Sun, 16 Nov 2025 22:20:25 +0100 Subject: [PATCH 09/14] Some other way --- .github/workflows/suggest-commit-message.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/suggest-commit-message.yml b/.github/workflows/suggest-commit-message.yml index 8e09f110f8..af7400072d 100644 --- a/.github/workflows/suggest-commit-message.yml +++ b/.github/workflows/suggest-commit-message.yml @@ -112,10 +112,10 @@ jobs: const diffStat = git(['diff', '--name-status', `${baseSha}...${headSha}`]) || ''; const diffExcerpt = git(['diff', '--unified=3', `${baseSha}...${headSha}`], 500) || ''; const nonUpgradeCommits = - git(['log', '--grep', '^Upgrade', '--invert-grep', '--pretty=format:%h %B%n---', '-n', '100', 'master']) || + git(['log', '--grep', '^Upgrade', '--invert-grep', '--pretty=format:%h %B%n---', '-n', '100', baseSha]) || ''; const upgradeCommits = - git(['log', '--grep', '^Upgrade', '--pretty=format:%h %B%n---', '-n', '100', 'master']) || + git(['log', '--grep', '^Upgrade', '--pretty=format:%h %B%n---', '-n', '100', baseSha]) || ''; const cleanedBody = (body || '').trim() || ''; From d2408d1eb903bb5ecd51b158884ac0626d1ad809 Mon Sep 17 00:00:00 2001 From: Stephan Schroevers Date: Sat, 22 Nov 2025 13:51:33 +0100 Subject: [PATCH 10/14] Apply tweaks --- .github/workflows/suggest-commit-message.yml | 20 +++++++++++--------- 1 file changed, 11 insertions(+), 9 deletions(-) diff --git a/.github/workflows/suggest-commit-message.yml b/.github/workflows/suggest-commit-message.yml index af7400072d..88c801ac06 100644 --- a/.github/workflows/suggest-commit-message.yml +++ b/.github/workflows/suggest-commit-message.yml @@ -71,6 +71,7 @@ jobs: id: prompt uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8.0.0 env: + REPOSITORY: ${{ github.repository }} PR_NUMBER: ${{ steps.pr-details.outputs.number }} PR_TITLE: ${{ steps.pr-details.outputs.title }} PR_BODY: ${{ steps.pr-details.outputs.body }} @@ -100,6 +101,7 @@ jobs: }; const env = process.env; + const repository = env.REPOSITORY; const prNumber = env.PR_NUMBER; const title = env.PR_TITLE; const body = env.PR_BODY; @@ -121,19 +123,19 @@ jobs: const cleanedBody = (body || '').trim() || ''; const instructions = ` - You are an experienced maintainer helping to craft the squash commit message for PR #${prNumber} in the PicnicSupermarket/error-prone-support repository. + You are an experienced maintainer helping to craft the squash commit message for PR #${prNumber} in the ${repository} repository. Requirements: 1. Write the summary line in the imperative mood. Try not to exceed 80 characters. 2. End the summary line with the PR number in parentheses, i.e., " (#${prNumber})". - 2. Wrap each body paragraph at 72 characters. Focus on the "what" and "why" rather than implementation details. - 3. Keep the overall message concise. - 4. Match the established format used in similar past commits. - 5. Wrap code references in backticks. - 6. For dependency upgrades in particular, *very precisely* follow the pattern of past commit messages: reuse the summary wording (only adjust version numbers) and list updated changelog, release note, and diff URLs in the body. - 7. Don't hallucinate URLs, version numbers, or other factual information. - 8. Never split URLs across multiple lines, even if they exceed 72 characters. - 9. If the pull request description already contains a suitable commit message, prefer using that as-is. + 3. Wrap each body paragraph at 72 characters. Focus on the "what" and "why" rather than implementation details. + 4. Keep the overall message concise. + 5. Match the established format used in similar past commits. + 6. Wrap code references in backticks. + 7. For dependency upgrades in particular, *very precisely* follow the pattern of past commit messages: reuse the summary wording (only adjust version numbers) and list updated changelog, release note, and diff URLs in the body. + 8. Don't hallucinate URLs, version numbers, or other factual information. + 9. Never split URLs across multiple lines, even if they exceed 72 characters. + 10. If the pull request description already contains a suitable commit message, prefer using that as-is. Some further guidelines to help you craft good upgrade commit messages: - Unless highly salient, don't summarize code changes made as part of the upgrade. From b8157ca813a726e8c971e4dc8f939eb85a47d57c Mon Sep 17 00:00:00 2001 From: Stephan Schroevers Date: Sat, 22 Nov 2025 14:53:57 +0100 Subject: [PATCH 11/14] Try to get those wiki links --- .github/workflows/suggest-commit-message.yml | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/.github/workflows/suggest-commit-message.yml b/.github/workflows/suggest-commit-message.yml index 88c801ac06..7ef7ebd961 100644 --- a/.github/workflows/suggest-commit-message.yml +++ b/.github/workflows/suggest-commit-message.yml @@ -149,9 +149,13 @@ jobs: 3. Finally, provide the full diff link. - If the upgrade involves multiple dependencies, group the links by dependency. - When the Maven \u0060version.error-prone-orig\u0060 property is changed, this upgrades both Error Prone and Picnic's Error Prone fork. In this case: - 1. Make sure that the commit message includes a diff URL for the latter. - 2. Don't explicitly mention that \u0060version.error-prone-orig\u0060 got changed; just focus on the fact that Error Prone is being upgraded. + - Make sure that the commit message includes a diff URL for the latter. + - Don't explicitly mention that \u0060version.error-prone-orig\u0060 got changed; just focus on the fact that Error Prone is being upgraded. - If the example upgrade commits shown below don't include at least one upgrade of the same dependency being upgraded in this pull request, check the full Git history to find relevant past upgrade commit messages to mimic. + - For major and minor version upgrades, check past dependency upgrade commit messages to infer blog or wiki URLs to which to link. Do this for at least the following libraries: + - Jackson + - Spring + - Spring Boot Return a JSON object with the following shape: { From 2fdac3194d420d31c470cde4ec58806bbd850cc3 Mon Sep 17 00:00:00 2001 From: Stephan Schroevers Date: Sat, 22 Nov 2025 15:27:26 +0100 Subject: [PATCH 12/14] Some more nudging --- .github/workflows/suggest-commit-message.yml | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/.github/workflows/suggest-commit-message.yml b/.github/workflows/suggest-commit-message.yml index 7ef7ebd961..332ff5ea7f 100644 --- a/.github/workflows/suggest-commit-message.yml +++ b/.github/workflows/suggest-commit-message.yml @@ -142,6 +142,14 @@ jobs: - Don't bother linking to anchors within changelogs or release notes; just link to the main page. - For GitHub-hosted projects, always link to all relevant GitHub release pages, including those for intermediate versions. - This includes milestones and release candidates; if necessary, use the GitHub API to identify these. + - Libraries that often use milestone and release candidates include, but are not limited to, the following: + - Jackson + - JUnit + - Micrometer + - Project Reactor + - Spring Framework + - Spring Boot + - Spring Security - For GitHub-hosted projects, always link to the full diff between versions. - Enumerate links in the following order: 1. First, link to custom release note documents. @@ -152,10 +160,11 @@ jobs: - Make sure that the commit message includes a diff URL for the latter. - Don't explicitly mention that \u0060version.error-prone-orig\u0060 got changed; just focus on the fact that Error Prone is being upgraded. - If the example upgrade commits shown below don't include at least one upgrade of the same dependency being upgraded in this pull request, check the full Git history to find relevant past upgrade commit messages to mimic. - - For major and minor version upgrades, check past dependency upgrade commit messages to infer blog or wiki URLs to which to link. Do this for at least the following libraries: + - For major and minor version upgrades, check past dependency upgrade commit messages to infer documentation, blog or wiki URLs to which to link. Do this for at least the following libraries: - Jackson - - Spring + - Spring Framework - Spring Boot + - Spring Security Return a JSON object with the following shape: { From f3a9d59028a02b0a45e751c1a194df8a44558506 Mon Sep 17 00:00:00 2001 From: Stephan Schroevers Date: Sat, 22 Nov 2025 15:46:35 +0100 Subject: [PATCH 13/14] Try to reduce the input size --- .github/workflows/suggest-commit-message.yml | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/.github/workflows/suggest-commit-message.yml b/.github/workflows/suggest-commit-message.yml index 332ff5ea7f..fd519545d2 100644 --- a/.github/workflows/suggest-commit-message.yml +++ b/.github/workflows/suggest-commit-message.yml @@ -114,10 +114,10 @@ jobs: const diffStat = git(['diff', '--name-status', `${baseSha}...${headSha}`]) || ''; const diffExcerpt = git(['diff', '--unified=3', `${baseSha}...${headSha}`], 500) || ''; const nonUpgradeCommits = - git(['log', '--grep', '^Upgrade', '--invert-grep', '--pretty=format:%h %B%n---', '-n', '100', baseSha]) || + git(['log', '--grep', '^Upgrade', '--invert-grep', '--pretty=format:%h %B%n---', '-n', '50', baseSha]) || ''; const upgradeCommits = - git(['log', '--grep', '^Upgrade', '--pretty=format:%h %B%n---', '-n', '100', baseSha]) || + git(['log', '--grep', '^Upgrade', '--pretty=format:%h %B%n---', '-n', '50', baseSha]) || ''; const cleanedBody = (body || '').trim() || ''; @@ -142,7 +142,7 @@ jobs: - Don't bother linking to anchors within changelogs or release notes; just link to the main page. - For GitHub-hosted projects, always link to all relevant GitHub release pages, including those for intermediate versions. - This includes milestones and release candidates; if necessary, use the GitHub API to identify these. - - Libraries that often use milestone and release candidates include, but are not limited to, the following: + - Libraries that often use milestone and release candidates include, but are not limited to: - Jackson - JUnit - Micrometer @@ -196,12 +196,12 @@ jobs: ${diffExcerpt} \u0060\u0060\u0060 - Recent non-upgrade commits examples (\u0060git log --grep '^Upgrade' --invert-grep --pretty='format:%h %B%n---' -n 100\u0060): + Recent non-upgrade commits examples (\u0060git log --grep '^Upgrade' --invert-grep --pretty='format:%h %B%n---' -n 50\u0060): \u0060\u0060\u0060 ${nonUpgradeCommits} \u0060\u0060\u0060 - Recent upgrade commit examples (\u0060git log --grep '^Upgrade' --pretty='format:%h %B%n---' -n 100\u0060): + Recent upgrade commit examples (\u0060git log --grep '^Upgrade' --pretty='format:%h %B%n---' -n 50\u0060): \u0060\u0060\u0060 ${upgradeCommits} \u0060\u0060\u0060 From d9613c5ce0d6551240ebe2baf9ce16c552b78edb Mon Sep 17 00:00:00 2001 From: Stephan Schroevers Date: Sat, 22 Nov 2025 15:54:42 +0100 Subject: [PATCH 14/14] More nudging may be better --- .github/workflows/suggest-commit-message.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/suggest-commit-message.yml b/.github/workflows/suggest-commit-message.yml index fd519545d2..36730972b7 100644 --- a/.github/workflows/suggest-commit-message.yml +++ b/.github/workflows/suggest-commit-message.yml @@ -117,7 +117,7 @@ jobs: git(['log', '--grep', '^Upgrade', '--invert-grep', '--pretty=format:%h %B%n---', '-n', '50', baseSha]) || ''; const upgradeCommits = - git(['log', '--grep', '^Upgrade', '--pretty=format:%h %B%n---', '-n', '50', baseSha]) || + git(['log', '--grep', '^Upgrade', '--pretty=format:%h %B%n---', '-n', '150', baseSha]) || ''; const cleanedBody = (body || '').trim() || ''; @@ -201,7 +201,7 @@ jobs: ${nonUpgradeCommits} \u0060\u0060\u0060 - Recent upgrade commit examples (\u0060git log --grep '^Upgrade' --pretty='format:%h %B%n---' -n 50\u0060): + Recent upgrade commit examples (\u0060git log --grep '^Upgrade' --pretty='format:%h %B%n---' -n 150\u0060): \u0060\u0060\u0060 ${upgradeCommits} \u0060\u0060\u0060