Learn through the super-clean Baeldung Pro experience:
>> Membership and Baeldung Pro.
No ads, dark-mode and 6 months free of IntelliJ Idea Ultimate to start with.
Last updated: June 17, 2025
GitHub Actions is a powerful automation platform for continuous integration and continuous delivery (CI/CD) workflows. One common requirement when working with GitHub repositories is to programmatically create a Pull Request (PR) as part of an automated update, whether it’s refreshing a timestamp, syncing generated files, or applying a bulk change across branches.
In this tutorial, we’ll explore multiple ways to create a Pull Request using GitHub Actions, including the GitHub CLI, REST API, and the peter-evans/create-pull-request action.
Before we can create pull requests from a GitHub Actions workflow, we must ensure Git is configured correctly and authenticated using a token.
Let’s start by creating a composite action that handles this setup:
$ cat .github/actions/setup-repo/action.yml
name: 'Setup Git Repository'
description: 'Configure Git user and authentication for commits'
inputs:
token:
description: 'GitHub token'
required: true
runs:
using: 'composite'
steps:
- name: Configure Git user
run: |
git config --global user.name "github-actions[bot]"
git config --global user.email "github-actions[bot]@users.noreply.github.com"
shell: bash
- name: Authenticate using token
run: |
git remote set-url origin https://x-access-token:${{ inputs.token }}@github.com/${{ github.repository }}
shell: bash
We’ll reuse this action to ensure that Git operations, such as committing and pushing, work correctly.
Next, we’ll define another composite action that updates a file and optionally pushes the changes to a new branch:
$ cat .github/actions/update-and-push/action.yml
name: 'Update File and Push Branch'
description: 'Creates a new branch, modifies a file, commits, and pushes'
inputs:
update_only:
required: false
default: false
outputs:
branch:
description: 'Branch name'
value: ${{ steps.push.outputs.branch }}
runs:
using: 'composite'
steps:
- name: Update file
run: echo "Updated at $(date -u --rfc-3339=ns)" > gh-update.txt
shell: bash
- name: Commit and push
id: push
run: |
branch="gh/update-$(date +%s)"
if [ "${{ inputs.update_only }}" = "false" ]; then
git checkout -b "$branch"
git add gh-update.txt
git commit -m "chore: update file"
echo "Pushing branch: $branch"
git push origin "$branch"
else
echo "Skipping push as update_only is true"
fi
echo "branch=$branch" >> "$GITHUB_OUTPUT"
shell: bash
We’ll use this action to handle file updates and create a uniquely named branch to hold the changes. Further, we’ve added the update_only flag to the composite action to skip committing to the branch.
Finally, we must create a personal access token with the “repo” scope and add it as a repository secret named GH_TOKEN.
The GitHub CLI provides a convenient way to interact with repositories from the command line. We can use it in a workflow to open a pull request after pushing changes to a branch.
First, let’s write the workflow in the create-pr-gh-cli.yml file under the .github/workflows directory:
$ cat .github/workflows/create-pr-gh-cli.yml
name: "Create Pull Request via GitHub CLI"
on:
workflow_dispatch:
jobs:
create-pr:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
with:
persist-credentials: false
- uses: ./.github/actions/setup-repo
with:
token: ${{ secrets.GH_TOKEN }}
- uses: ./.github/actions/update-and-push
- name: Open pull request using gh CLI
run: |
gh pr create \
--title "chore: update via gh cli" \
--body "This PR was created using the GitHub CLI." \
--base main \
--head $(git rev-parse --abbrev-ref HEAD)
env:
GITHUB_TOKEN: ${{ secrets.GH_TOKEN }}
We used the gh pr create command to open a pull request from the newly pushed branch. Further, we explicitly passed the GITHUB_TOKEN to allow the CLI to authenticate.
Next, let’s verify our workflow by running it manually:
The workflow executed successfully.
Lastly, we should see a new pull request in the list of open PRs:
Excellent! It looks like we nailed it.
If we prefer not to rely on external tools like the GitHub CLI, we can use the GitHub REST API directly to create a pull request from within a workflow.
Let’s start by writing the workflow in the .github/workflows/create-pr-rest.yml file:
$ cat .github/workflows/create-pr-rest.yml
name: "Create Pull Request via REST API"
on:
workflow_dispatch:
jobs:
create-pr:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
with:
persist-credentials: false
- uses: ./.github/actions/setup-repo
with:
token: ${{ secrets.GH_TOKEN }}
- uses: ./.github/actions/update-and-push
- name: Open pull request via REST API
run: |
curl -X POST \
-H "Authorization: token ${{ secrets.GH_TOKEN }}" \
-H "Accept: application/vnd.github+json" \
https://api.github.com/repos/${{ github.repository }}/pulls \
-d "$(jq -n \
--arg title 'chore: daily timestamp update' \
--arg head "$(git rev-parse --abbrev-ref HEAD)" \
--arg base 'main' \
--arg body 'This PR updates the timestamp file automatically via GitHub Actions.' \
'{title: $title, head: $head, base: $base, body: $body}')"
We used the jq command to construct a POST request to the /pulls endpoint with the necessary metadata to open a pull request from the new branch.
Now, we can trigger a workflow run and verify that a pull request is generated with the related changes:
Perfect! It worked as expected.
If we want a ready-to-use and well-maintained solution for creating pull requests, we can use the peter-evans/create-pull-request action. It handles the entire process, from committing changes to opening the pull request, without requiring custom logic.
Let’s define the workflow in the .github/workflows/create-pr-peter-evans.yml file:
$ cat .github/workflows/create-pr-peter-evans.yml
name: "Create Pull Request via peter-evans Action"
on:
workflow_dispatch:
jobs:
create-pr:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
with:
persist-credentials: false
- uses: ./.github/actions/setup-repo
with:
token: ${{ secrets.GH_TOKEN }}
- uses: ./.github/actions/update-and-push
id: update
with:
update_only: true
- name: Open pull request using peter-evans action
uses: peter-evans/create-pull-request@v5
with:
branch: ${{ steps.update.outputs.branch }}
base: main
commit-message: "chore: update via create-pull-request action"
title: "chore: update via create-pull-request action"
body: "This PR was created using the peter-evans/create-pull-request action."
token: ${{ secrets.GH_TOKEN }}
We used the peter-evans/create-pull-request action to automate the entire pull request creation process in a single step. Furthermore, we set the update_only flag to true for the update-and-push action because the create-pull-request action expects uncommitted changes in the branch.
Now, let’s run the workflow:
Fantastic! We got it working.
In this tutorial, we explored different ways to create a pull request using GitHub Actions, including the GitHub CLI, the REST API, and the peter-evans/create-pull-request action.
Each method serves a different use case, and we can choose the one that best fits our workflow.