feat(pr): default title and body to commit message for single commits
This commit is contained in:
parent
0edc001d28
commit
f0ea17c511
89
EXAMPLE_WORKFLOW.yml
Normal file
89
EXAMPLE_WORKFLOW.yml
Normal 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
|
||||
218
__test__/create-pull-request.unit.test.ts
Normal file
218
__test__/create-pull-request.unit.test.ts
Normal 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
20
dist/index.js
vendored
@ -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());
|
||||
|
||||
128
docs/default-pr-title-body.md
Normal file
128
docs/default-pr-title-body.md
Normal 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.
|
||||
@ -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,
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user