GitHub Actions are great at enabling you to automate workflows directly from within a GitHub repository. The workflows are stored in a YAML definition file located within the .github/workflows
directory within the repository. GitHub Actions can be used to configure workflows that can perform a variety of actions to perform build and release steps. On rare occasions, it might be necessary to have the workflow make file changes that are then committed back to the GitHub repository itself. Luckily, GitHub Actions support the use of Git CLI commands within the workflow. This article walks through the steps to configure a GitHub Actions workflow that can commit file changes made in a workflow action back to the GitHub repository the workflow is a part of.
Let’s dive in!
Table of Contents
GitHub Action Steps to Commit Changes to Repository
There are actions available in GitHub Action workflows for performing all sorts of tasks. GitHub Actions even supports extensibility so custom actions can be built to extend it with any functionality necessary. However, no custom actions are needed to make Git commits back to the repo. A script action can be added that simply makes git
CLI commands.
The following are the minimum tasks to add to a GitHub Actions workflow to affectively make file changes and commit them back to the repository:
Step 1: Checkout code
The first thing that’s needed is a task that checks out the contents of the GitHub repository. To do this, the actions/checkout
action can be used.
# Checkout code
- uses: actions/checkout@v4
Step 2: Make File Changes
Once the contents of the GitHub repository have been checked out, then the files within the repository are available to the workflow. This is where you’ll normally perform all your necessary build and release actions in the workflow. This is also where you can write actions and other code that makes the necessary changes to the files that you wish to commit back to the GitHub repository.
# Add actions that modify files here
- name: Modify files
run: |
echo "change files here"
Step 3: Commit and Push Changes
Once the files changes are made, then an action can be added that makes the necessary git
CLI commands to commit and push the changes back up to the source GitHub repository.
To do this, the usual git
steps to make a commit will need to be performed:
- Use
git config
to configure theuser.name
anduser.email
of the user that is making the commit. - Use
git add
to stage file changes to be committed. - Use
git commit
to commit the changes to the Git repository. - Use
git push
to push the recent commit up to the GitHub repository.
The following is an example action with these git
commands all setup as necessary:
# Commit and Push Changes
- name: Commit and Push Changes
run: |
# configure user
git config --global user.name "${{ github.actor }}"
git config --global user.email "${{ github.actor }}@users.noreply.github.com"
# stage any file changes to be committed
git add .
# make commit with staged changes
git commit -m 'files changed'
# push the commit back up to source GitHub repository
git push
Configure GitHub Actor as Git User and Email for Commits
Before the git commit
call can be made, Git needs to be configured with the name (user.name
) and email (user.email
) for the user that will be making the commit. To do this, the GitHub context variable that represents the username of the GitHub account that triggered the workflow needs to be referenced.
To configure the Git user.name
, make a call to the git config
command including a reference to set it’s value to the github.actor
GitHub context variable. This is done as follows:
git config --global user.name "${{ github.actor }}"
To configure the Git user.email
, make a call to the git config
command including a reference to set it’s value to the github.actor
GitHub context variable with the @users.noreply.github.com
text appended after it. This is done as follows:
git config --global user.email "${{ github.actor }}@users.noreply.github.com"
The Git user.email
config must be set to an email address that GitHub will recognize for the user. Although the GitHub context variables do not allow the workflow to access the registered email addresses of the GitHub user account. Luckily, GitHub has a default email associated with each GitHub user account that is in the format of <github-username>@users.noreply.github.com
. The github.actor
is the GitHub username and can be filled in to form the required email address that Git requires.
By default, the GitgHub Actions workflow will run with the privileges of the github.actor
that triggered the workflow. This is where the workflow will get it’s permissions to be able to make Git commits back to the GitHub repository.
Add Step to Check of Changes Have Been Made
If no file changes have been made, then the action that calls Git to make the commit and push against the GitHub repository may fail. To keep the GitHub Actions workflow from failing if no changes are detected, then an additional action must be added immediately prior to the commit action to detect if changes have been made.
This change detection action will perform these steps:
- Call
git diff
to detect if changes have been made. - Set an output variable from the action that can be referenced by the commit action to conditionally perform the commit and push only if changes have been made.
This is necessary if the workflow code makes changes to the files that end up with identical file contents, or it simply doesn’t make any file changes. If the workflow attempts to make a commit with no changes, this can result in the workflow execution to fail. This may not be the desired behavior depending on the requirements of the GitHub Actions workflow.
The following is an example action that will detect if any uncommitted changes have been made to the repository:
- name: Check for Changes
id: check_changes
run: |
if [[ -n "$(git diff --exit-code)" ]]; then
echo "Changes detected."
echo "::set-output name=has_changes::true"
else
echo "No changes detected."
echo "::set-output name=has_changes::false"
fi
This action is named check_changes
and will output a variable named has_changes
that will contain a boolean true or false value whether changes have been detected or not. Following actions will then be able to reference this variable to perform conditional logic.
The easiest method to conditionally perform the action that makes the commit and push to the GitHub repository, is to add an if
condition to the action itself. This condition can then perform a simple check that the has_changes
output variable from the check_changes
action is equal to true
.
The following is an example of how to configure the if
condition on the Git commit action:
- name: Commit and Push Changes
if: steps.check_changes.outputs.has_changes == 'true'
run: |
# perform git actions
...
Once this is configured, with the check for changes, then the GitHub Actions workflow will run and only perform the Git commit and push operations if there are actually file changes to be committed.
Note that this example is truncated, but does include the if
condition check needed. The full code example putting this whole GitHub Actions workflow together is below.
Full GitHub Actions Workflow to Commit Changes Back to GitHub Repository
The following is a full code example of a GitHub Actions workflow with all of the above steps that can be used to commit and push file changes made within the workflow back up to the source GitHub repository:
on:
workflow_dispatch:
name: Make File Changes to GitHub Repo
jobs:
make-file-changes:
runs-on: ubuntu-latest
steps:
# Checkout code
- uses: actions/checkout@v4
# Add actions that modify files here
- name: Modify files
run: |
echo "change files here"
- name: Check for Changes
id: check_changes
run: |
if [[ -n "$(git diff --exit-code)" ]]; then
echo "Changes detected."
echo "::set-output name=has_changes::true"
else
echo "No changes detected."
echo "::set-output name=has_changes::false"
fi
- name: Commit and Push Changes
if: steps.check_changes.outputs.has_changes == 'true'
run: |
# configure user
git config --global user.name "${{ github.actor }}"
git config --global user.email "${{ github.actor }}@users.noreply.github.com"
# stage any file changes to be committed
git add .
# make commit with staged changes
git commit -m 'files changed'
# push the commit back up to source GitHub repository
git push
I hope this helps with building your own GitHub Actions workflows!