feat(pr): default title and body to commit message for single commits

This commit is contained in:
BLACKBOX Agent 2025-11-06 17:05:50 +00:00
parent 0edc001d28
commit f0ea17c511
5 changed files with 482 additions and 0 deletions

89
EXAMPLE_WORKFLOW.yml Normal file
View File

@ -0,0 +1,89 @@
# Example workflow demonstrating the new feature from Issue #4111
# This workflow shows how PR title and body are automatically defaulted from commit message
name: Example - Auto PR Title/Body
on:
workflow_dispatch:
jobs:
create-pr-with-auto-defaults:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
# Make some changes
- name: Update files
run: |
echo "Updated content" > example.txt
date > timestamp.txt
# Create PR with automatic title/body from commit
# The PR will have:
# Title: "Update AGP sources to the latest"
# Body: "_Auto-generated by `dump-sources` Github workflow._"
- name: Create Pull Request (Auto Title/Body)
uses: peter-evans/create-pull-request@v7
with:
commit-message: |
Update AGP sources to the latest
_Auto-generated by `dump-sources` Github workflow._
branch: actions/dump-sources
delete-branch: true
# Note: title and body are NOT specified, so they will be
# automatically set from the commit message above
create-pr-with-custom-values:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
# Make some changes
- name: Update files
run: |
echo "Updated content" > example.txt
# Create PR with custom title/body (overrides auto-default)
# The PR will have the custom values below, NOT the commit message
- name: Create Pull Request (Custom Title/Body)
uses: peter-evans/create-pull-request@v7
with:
commit-message: |
Update AGP sources to the latest
_Auto-generated by `dump-sources` Github workflow._
title: Custom PR Title
body: Custom PR Description
branch: actions/custom-pr
delete-branch: true
create-pr-multiple-commits:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
# Make multiple commits
- name: Create multiple commits
run: |
git config user.name "github-actions[bot]"
git config user.email "41898282+github-actions[bot]@users.noreply.github.com"
echo "First change" > file1.txt
git add file1.txt
git commit -m "First commit"
echo "Second change" > file2.txt
git add file2.txt
git commit -m "Second commit"
# Create PR - will use default title/body since there are multiple commits
# The PR will have:
# Title: "Changes by create-pull-request action"
# Body: "Automated changes by [create-pull-request](...) GitHub action"
- name: Create Pull Request (Multiple Commits)
uses: peter-evans/create-pull-request@v7
with:
branch: actions/multiple-commits
delete-branch: true
# Note: Multiple commits means auto-default doesn't apply

View File

@ -0,0 +1,218 @@
import {Commit} from '../lib/git-command-manager'
describe('create-pull-request PR title and body defaulting', () => {
const defaultTitle = 'Changes by create-pull-request action'
const defaultBody =
'Automated changes by [create-pull-request](https://github.com/peter-evans/create-pull-request) GitHub action'
test('should use commit subject as title for single commit when title is default', () => {
const commit: Commit = {
sha: 'abc123',
tree: 'tree123',
parents: ['parent123'],
signed: false,
subject: 'Update AGP sources to the latest',
body: '_Auto-generated by `dump-sources` Github workflow._',
changes: [],
unparsedChanges: []
}
const branchCommits = [commit]
let title = defaultTitle
let body = defaultBody
// Simulate the logic from create-pull-request.ts
if (branchCommits.length === 1) {
const singleCommit = branchCommits[0]
if (title === defaultTitle) {
title = singleCommit.subject
}
if (body === defaultBody) {
if (singleCommit.body && singleCommit.body.trim().length > 0) {
body = singleCommit.body
}
}
}
expect(title).toEqual('Update AGP sources to the latest')
expect(body).toEqual('_Auto-generated by `dump-sources` Github workflow._')
})
test('should not override custom title and body', () => {
const commit: Commit = {
sha: 'abc123',
tree: 'tree123',
parents: ['parent123'],
signed: false,
subject: 'Update AGP sources to the latest',
body: '_Auto-generated by `dump-sources` Github workflow._',
changes: [],
unparsedChanges: []
}
const branchCommits = [commit]
let title = 'Custom Title'
let body = 'Custom Body'
// Simulate the logic from create-pull-request.ts
if (branchCommits.length === 1) {
const singleCommit = branchCommits[0]
if (title === defaultTitle) {
title = singleCommit.subject
}
if (body === defaultBody) {
if (singleCommit.body && singleCommit.body.trim().length > 0) {
body = singleCommit.body
}
}
}
expect(title).toEqual('Custom Title')
expect(body).toEqual('Custom Body')
})
test('should not default for multiple commits', () => {
const commit1: Commit = {
sha: 'abc123',
tree: 'tree123',
parents: ['parent123'],
signed: false,
subject: 'First commit',
body: 'First commit body',
changes: [],
unparsedChanges: []
}
const commit2: Commit = {
sha: 'def456',
tree: 'tree456',
parents: ['abc123'],
signed: false,
subject: 'Second commit',
body: 'Second commit body',
changes: [],
unparsedChanges: []
}
const branchCommits = [commit1, commit2]
let title = defaultTitle
let body = defaultBody
// Simulate the logic from create-pull-request.ts
if (branchCommits.length === 1) {
const singleCommit = branchCommits[0]
if (title === defaultTitle) {
title = singleCommit.subject
}
if (body === defaultBody) {
if (singleCommit.body && singleCommit.body.trim().length > 0) {
body = singleCommit.body
}
}
}
expect(title).toEqual(defaultTitle)
expect(body).toEqual(defaultBody)
})
test('should handle commit with empty body', () => {
const commit: Commit = {
sha: 'abc123',
tree: 'tree123',
parents: ['parent123'],
signed: false,
subject: 'Update files',
body: '',
changes: [],
unparsedChanges: []
}
const branchCommits = [commit]
let title = defaultTitle
let body = defaultBody
// Simulate the logic from create-pull-request.ts
if (branchCommits.length === 1) {
const singleCommit = branchCommits[0]
if (title === defaultTitle) {
title = singleCommit.subject
}
if (body === defaultBody) {
if (singleCommit.body && singleCommit.body.trim().length > 0) {
body = singleCommit.body
}
}
}
expect(title).toEqual('Update files')
expect(body).toEqual(defaultBody) // Should keep default when commit body is empty
})
test('should handle commit with multi-line body', () => {
const commit: Commit = {
sha: 'abc123',
tree: 'tree123',
parents: ['parent123'],
signed: false,
subject: 'Update documentation',
body: 'This is a detailed commit message.\n\nIt has multiple paragraphs.\n\n- Item 1\n- Item 2',
changes: [],
unparsedChanges: []
}
const branchCommits = [commit]
let title = defaultTitle
let body = defaultBody
// Simulate the logic from create-pull-request.ts
if (branchCommits.length === 1) {
const singleCommit = branchCommits[0]
if (title === defaultTitle) {
title = singleCommit.subject
}
if (body === defaultBody) {
if (singleCommit.body && singleCommit.body.trim().length > 0) {
body = singleCommit.body
}
}
}
expect(title).toEqual('Update documentation')
expect(body).toEqual(
'This is a detailed commit message.\n\nIt has multiple paragraphs.\n\n- Item 1\n- Item 2'
)
})
test('should handle commit with whitespace-only body', () => {
const commit: Commit = {
sha: 'abc123',
tree: 'tree123',
parents: ['parent123'],
signed: false,
subject: 'Update files',
body: ' \n\n ',
changes: [],
unparsedChanges: []
}
const branchCommits = [commit]
let title = defaultTitle
let body = defaultBody
// Simulate the logic from create-pull-request.ts
if (branchCommits.length === 1) {
const singleCommit = branchCommits[0]
if (title === defaultTitle) {
title = singleCommit.subject
}
if (body === defaultBody) {
if (singleCommit.body && singleCommit.body.trim().length > 0) {
body = singleCommit.body
}
}
}
expect(title).toEqual('Update files')
expect(body).toEqual(defaultBody) // Should keep default when commit body is only whitespace
})
})

20
dist/index.js vendored
View File

@ -535,6 +535,26 @@ function createPullRequest(inputs) {
core.endGroup();
}
if (result.hasDiffWithBase) {
// Default PR title and body from commit message for single-commit PRs
// This mimics GitHub's web interface behavior
const defaultTitle = 'Changes by create-pull-request action';
const defaultBody = 'Automated changes by [create-pull-request](https://github.com/peter-evans/create-pull-request) GitHub action';
if (result.branchCommits.length === 1) {
const commit = result.branchCommits[0];
// Use commit subject as title if title is at default value
if (inputs.title === defaultTitle) {
inputs.title = commit.subject;
core.info(`Defaulting pull request title to commit subject: '${inputs.title}'`);
}
// Use commit body as PR body if body is at default value
if (inputs.body === defaultBody) {
// Use commit body if it exists, otherwise keep the default
if (commit.body && commit.body.trim().length > 0) {
inputs.body = commit.body;
core.info(`Defaulting pull request body to commit body (${commit.body.length} characters)`);
}
}
}
core.startGroup('Create or update the pull request');
const pull = yield ghPull.createOrUpdatePullRequest(inputs, baseRemote.repository, branchRepository);
outputs.set('pull-request-number', pull.number.toString());

View File

@ -0,0 +1,128 @@
# Default PR Title and Body from Commit Message
When creating a pull request with a single commit, the action will automatically default the PR title and body to match the commit message, similar to how GitHub's web interface behaves.
## Behavior
### Single Commit PRs
When the following conditions are met:
- The pull request branch contains exactly **one commit**
- The `title` input is not provided (uses default value)
- The `body` input is not provided (uses default value)
The action will:
- Use the commit's **subject line** as the PR title
- Use the commit's **body** as the PR description (if the commit has a body)
### Example
Given a commit with this message:
```
Update AGP sources to the latest
_Auto-generated by `dump-sources` Github workflow._
```
The following workflow configuration:
```yml
- uses: peter-evans/create-pull-request@v7
with:
commit-message: |
Update AGP sources to the latest
_Auto-generated by `dump-sources` Github workflow._
branch: actions/dump-sources
delete-branch: true
```
Will create a pull request with:
- **Title**: `Update AGP sources to the latest`
- **Body**: `_Auto-generated by `dump-sources` Github workflow._`
### Multiple Commits
When the pull request branch contains **multiple commits**, the action will use the default values:
- **Title**: `Changes by create-pull-request action`
- **Body**: `Automated changes by [create-pull-request](https://github.com/peter-evans/create-pull-request) GitHub action`
### Custom Title and Body
If you explicitly provide `title` or `body` inputs, those values will always be used regardless of the number of commits:
```yml
- uses: peter-evans/create-pull-request@v7
with:
commit-message: |
Update AGP sources to the latest
_Auto-generated by `dump-sources` Github workflow._
title: Custom PR Title
body: Custom PR Description
branch: actions/dump-sources
```
This will create a PR with your custom title and body, ignoring the commit message.
## Benefits
This feature eliminates the need to duplicate commit messages in the workflow configuration:
### Before (Duplicated Content)
```yml
- uses: peter-evans/create-pull-request@v7
with:
commit-message: |
Update AGP sources to the latest
_Auto-generated by `dump-sources` Github workflow._
title: Update AGP sources to the latest
body: _Auto-generated by `dump-sources` Github workflow._
branch: actions/dump-sources
```
### After (Simplified)
```yml
- uses: peter-evans/create-pull-request@v7
with:
commit-message: |
Update AGP sources to the latest
_Auto-generated by `dump-sources` Github workflow._
branch: actions/dump-sources
```
## Edge Cases
### Commit with Empty Body
If a commit has only a subject line (no body), the PR will use:
- **Title**: The commit subject
- **Body**: The default body text
Example commit:
```
Update files
```
Results in:
- **Title**: `Update files`
- **Body**: `Automated changes by [create-pull-request](https://github.com/peter-evans/create-pull-request) GitHub action`
### Commit with Multi-line Body
Multi-line commit bodies are fully preserved in the PR description:
Example commit:
```
Update documentation
This is a detailed commit message.
It has multiple paragraphs.
- Item 1
- Item 2
```
Results in a PR with the full multi-line body as the description.

View File

@ -238,6 +238,33 @@ export async function createPullRequest(inputs: Inputs): Promise<void> {
}
if (result.hasDiffWithBase) {
// Default PR title and body from commit message for single-commit PRs
// This mimics GitHub's web interface behavior
const defaultTitle = 'Changes by create-pull-request action'
const defaultBody =
'Automated changes by [create-pull-request](https://github.com/peter-evans/create-pull-request) GitHub action'
if (result.branchCommits.length === 1) {
const commit = result.branchCommits[0]
// Use commit subject as title if title is at default value
if (inputs.title === defaultTitle) {
inputs.title = commit.subject
core.info(
`Defaulting pull request title to commit subject: '${inputs.title}'`
)
}
// Use commit body as PR body if body is at default value
if (inputs.body === defaultBody) {
// Use commit body if it exists, otherwise keep the default
if (commit.body && commit.body.trim().length > 0) {
inputs.body = commit.body
core.info(
`Defaulting pull request body to commit body (${commit.body.length} characters)`
)
}
}
}
core.startGroup('Create or update the pull request')
const pull = await ghPull.createOrUpdatePullRequest(
inputs,