Skip to content

Instantly share code, notes, and snippets.

@blanchardjeremy
Last active January 16, 2022 10:16
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save blanchardjeremy/07dbc9bd271bb9c8077c8c906307aaec to your computer and use it in GitHub Desktop.
Save blanchardjeremy/07dbc9bd271bb9c8077c8c906307aaec to your computer and use it in GitHub Desktop.
Workflow: Using a GitHub application to edit files, and auto-merge them

Workflow: Using a GitHub application to edit files, and auto-merge them

This gist offers a pattern to create a GitHub application (aka: bot) that serves as a bot to edit files and succesfully merge them into your repo, using GitHub actions & workflow files.

The end result is that you can have workflows that look like this:

image

Instead of this:

image

Context

This pattern is pretty easy if you are willing to use your own Personal Access Token (PAT) to do the commits. But that means your personal user account will show up as the actor for the commits and the pull request merge, which isn't ideal. Also, other workflows on your repo can access your PAT. If you're working on an open-source project, that could pose a security risk to your account.

A better solution is to use an Installation Access Token generated by a GitHub Application. The Application then becomes a "bot" that shows up as the user for all the actions.

If your repository has mandatory checks built-in, the built-in GITHUB_TOKEN that most actions use is configured by default to not initiate any further checks (workflows), so as to not create a recursive loop. Which is why we need an alternate approach (see a list of workarounds here).

For some reason, just running gh pr merge --auto --delete-branch --squash gives an error. (See my bug report here). If that gets fixes, this pattern will be much simpler (it can just be one workflow file).

Example

If you'd like to see how I used this pattern in my actual situation, you can see the relevant files here:

  1. Cron job workflow that initiates the daily file edits
  2. Script that gets called from the cron job that actually does the file edits
  3. Merge workflow that merges the pull request.

(These are permalinks to the files as they were at the time of this writing because I imagine we'll update them moving forward. Feel free to browse our repo to see how we're solving this problem now.)

This workflow does the following

  1. Trigger the workflow from a scheduled cron job (though you can have it run on other events if you wish, just modify the trigger event)
  2. Checks out your GitHub repo
  3. Makes edits to your files
  4. Create a new branch
  5. Commits the changes (as the bot)
  6. Create a pull request (as the bot)
  7. Merges the pull request (as the bot) after all checks are complete

Installation

  1. Create a GitHub application
    • You can give it a name. (In this example, it will be named "Repo Automation", but change it to what works for you.)
    • Uncheck Active under Webhook. You do not need to enter a Webhook URL.
    • Under Repository permissions: Contents select Access: Read & write.
    • Under Repository permissions: Pull request select Access: Read & write.
    • (optional) Upload a logo
  2. Create a Private key from the App settings page and store it securely.
  3. Install your application on the organization/user where your repo lives.
  4. Go to your repository > Secrets > New repository secret and create BOT_APP_ID (with the GitHub Application ID) and BOT_SECRET_KEY (with the contents of the secret key you generated)
  5. If you have branch protection rules on, go to your repo settings, add the application itself as an authorized committer.
  6. Use the two template files below as a basis for what your workflows need to do.
    • You can optionally change the pull request tag from auto-update to something else. If you do want to modify it, you need to change all instances of that label throughout both files for the script to function.
  7. Merge the files into your default branch for them to become available as options in the Actions tab of your repo.
    • After you merge the files in the first, they will become available in the Actions tab for you to run manually. Even if you only want to run them on a branch (where you're doing your development), you need to first commit them to your default branch.

Installation notes:

  • After you get this script working, you'll be able to look in the git log and discover the 12345678+repo-automation[bot]@users.noreply.github.com email that GitHub assigned your application. You can then go back and replace the 12345678 with the actual number that should be there. The commits will then show up with the logo that you set.

Usage

  • Option A: Let the cron job run at the specified time
  • Option B: Manually run it by going to Repo > Actions > Auto update > Run Workflow

References

Here are some of the references I found helpful in my quest to get this working.

name: Auto update
on:
schedule:
# 7:00am Pacific = 14:00 UTC
- cron: '0 14 * * *'
# Allows you to also run this workflow manually
workflow_dispatch:
jobs:
prevalence:
runs-on: ubuntu-latest
steps:
- name: Checkout git repo (with full history)
uses: actions/checkout@v2
with:
fetch-depth: 0
- uses: navikt/github-app-token-generator@v1
id: get-token
with:
private-key: ${{ secrets.BOT_PRIVATE_KEY }}
app-id: ${{ secrets.BOT_APP_ID }}
- name: Do the file edits
env:
GITHUB_TOKEN: ${{ steps.get-token.outputs.token }}
run: |
# Note: I haven't actually tested this exact code, but it's based scripts that do work (linked above). If you encounter bugs, please comment below.
# TODO: Replace 123456789+repo-automation[bot] with the actual address that GitHub assigns your app. You can find it in your commit log after this script runs & merges successfully for the first time.
git config --global user.name "Repo Automation[bot]"
git config --global user.email "123456789+repo-automation[bot]@users.noreply.github.com"
BRANCH=auto-update-$(date +%Y-%m-%d--%H-%M-%S)
git checkout -b $BRANCH
git push --set-upstream origin $BRANCH
echo "Created branch $BRANCH"
echo "Making file edits"
# Replace with your actual file edits
echo "Some new line" >> my-file.txt
TODAY=$(date +%Y-%m-%d)
git add -A
git commit -am "Automatic update $TODAY"
echo "Commit complete"
git push
echo "Pushing changes complete"
# Use the GitHub CLI to create a pull request and save the url
PR_URL=$(gh pr create --fill --label auto-prevalence-update)
echo "Pull request URL: $PR_URL"
# The `auto-update-merger.yml` file will complete the merge
name: "Auto update - merger"
on:
pull_request:
types: [opened, reopened, synchronize]
workflow_dispatch:
jobs:
automerge:
runs-on: ubuntu-latest
if: contains(github.event.pull_request.labels.*.name, 'auto-update')
steps:
- uses: navikt/github-app-token-generator@v1
id: get-token
with:
private-key: ${{ secrets.BOT_PRIVATE_KEY }}
app-id: ${{ secrets.BOT_APP_ID }}
- name: "Wait for status checks"
id: waitforstatuschecks
uses: WyriHaximus/github-action-wait-for-status@v1.4.0
with:
# Needs to not wait for its own action/workflow before proceeding
ignoreActions: automerge
checkInterval: 10
env:
GITHUB_TOKEN: ${{ steps.get-token.outputs.token }}
- name: "Merge"
uses: pascalgn/automerge-action@v0.14.2
if: steps.waitforstatuschecks.outputs.status == 'success'
env:
GITHUB_TOKEN: ${{ steps.get-token.outputs.token }}
MERGE_DELETE_BRANCH: true
MERGE_METHOD: squash
MERGE_LABELS: auto-update
UPDATE_LABELS: auto-update
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment