Create or Update PR Action
Tags: github, githubactions
Published: 2021-04-16 17:48:46

---
title: Create or Update PR Action
published: true
date: 2021-04-16 16:45:23 UTC
tags: github, githubactions
canonical_url: https://michaelheap.com/create-or-update-pr/
series: Action Spotlight
cover_image: https://michaelheapcom-og-image.s3.amazonaws.com/action-spotlight/gr2m/create-or-update-pr.png

---

Following on from our theme of actions to commit and push changes, today’s action can commit and push any local edits _and_ raise a pull request automatically.

| Fact Sheet | |
| --- | --- |
| **Author** | [gr2m](https://twitter.com/gr2m) |
| **Contributors** | 8 |
| **Stars** | 31 |
| **Repo** | [https://github.com/gr2m/create-or-update-pull-request-action](https://github.com/gr2m/create-or-update-pull-request-action) |
| **Marketplace** | [https://github.com/marketplace/actions/create-or-update-pull-request](https://github.com/marketplace/actions/create-or-update-pull-request) |

## What does it do?

> A GitHub Action to create or update a pull request based on local changes

You can use the action to add, commit and push changes to a remote branch and create a pull request for those changes automatically. It can add all your files in a single commit, add multiple commits to a single PR and automatically add labels and assignees to any pull requests that it raises.

Oh, and it’s built by [Gregor](https://github.com/gr2m) which means I instantly trust it.

## How does it work?

1. If there any uncommitted changes, files matching the pattern provided in the `path` input supplied are added and committed. You can customise the commit with the `commit-message` and `author` inputs
2. If there are any local commits (created by step 1, or by any other means) the action pushes changes to a remote branch with the name specified in the `branch` input
3. Search for any open pull requests with `branch` as the HEAD commit using the search query `head:${inputs.branch} type:pr is:open repo:${process.env.GITHUB_REPOSITORY}`
4. If there is an existing pull request, the action stops execution. Otherwise, a new pull request is created with the `title` and `body` inputs provided. Any `labels` and `assignees` provided as inputs are automatically added

Using the search API to check if there is already an open pull request for a branch is a fantastic solution. When I needed to do the same, I uses the `pulls.list` endpoint and passed in the `head` parameter like so:

```javascript
let pr = ( await tools.github.pulls.list({ owner, repo, head: `${owner}:${automationBranchName}`, })).data[0];if (!pr) { // Create a PR}
```

What I find interesting about this action is that it only uses the GitHub API for actions that it cannot perform using the `git` binary. There is a `runShellCommand` function that is used to run `git fetch`, `git commit` etc on the runner directly. When automating updates I’ve used the GitHub API to get and modify file contents to remove the requirement to use `actions/checkout` as part of your workflow, but in this context you’re likely to have a workspace so it makes sense to interact directly with the files.

Another interesting choice is that the action does not configure a remote or add authentication details, instead opting to specify both values directly in the `git` commit like so:

```javascript
await runShellCommand( `git push -f https://x-access-token:${process.env.GITHUB_TOKEN}@github.com/${process.env.GITHUB_REPOSITORY}.git HEAD:refs/heads/${inputs.branch}`);
```

There’s nothing in the commit history to explain why, but if I had to hazard a guess it’s due to the fact that other processes can interact with `git` config files, so by passing the details directly to the command it reduces the chances of anything interfering.

Finally, this action provides an example of how to provide lists of values in `inputs`. The action uses a comma-separated list of values for `labels` and `assignees` to provide multiple values. There still isn’t consensus how to provide multiple values (some actions provide a JSON list and deserialise), but this is another vote for the “comma-separated” camp.

## Common use cases

This action is most useful when you’ve got a task running on a schedule that fetches and commits updated data to the repo. If there are no changes, no commit or pull request will be created.

The README itself shows that it’s being used to keep track of [Chinese Starbucks stores](https://github.com/sinchang/cn-starbucks-stores-data/blob/master/.github/workflows/update.yml), [download JSON schema updates periodically](https://github.com/ergebnis/composer-normalize/blob/69ec6fd9a87cbb16badf2a988f4372221592b05e/.github/workflows/schema.yml#L25-L38) and even [by the Node project itself](https://github.com/nodejs/node/blob/master/.github/workflows/license-builder.yml) to keep the license file up to date.

If you’re interested in learning more about this pattern, there’s a great post by [Simon Willison](https://simonwillison.net/2020/Oct/9/git-scraping/) on `git scraping` as a concept.