Distribute Patches to many Git repositories with Patchbot

tl;dr Patchbot is a tool to automate the distribution of patches to your own Git repositories and helps to reduce technical debt.

Technical debt

Technical debt is a common problem in software development and just like financial debt there may be good or bad reasons for it, but an accumulation of technical debt may lead to unrecoverable risks.

Innovation and rapid development is a catch-22 situation here, since this may increase technical debt causing a loss on innovation and development speed.

There are many ways to acknowledge, address, and pay off technical debt eventually. However, the hardest part is to work against the “No, not right now“ conation.

Real World Examples

I had to face this issue some time ago, when numerous Git repositories I manage were affected by some general decisions.

Required changes:

  • Streamline the name of the Changelog file to comply with recommended naming patterns
  • Add a configuration file needed to introduce a new code quality tool
  • The main domain address of the organisation changed - reflect this in documentation and meta files

Ideas how solve these:

  • Open an issue for each project repository, wait until solved – Large overhead, what happens if the project is quiet?
  • Add these tasks to a project health checklist, and one of these tasks needs to be done every time a project is opened
    • How to check that a task is solved everywhere and no longer necessary?
  • Do a walkthrough - get it done
    • This… is… monotonous… and requires all attention over a period of time

Those are small issues. But a lot of work to implement them in all projects. All proposed solutions have a great potential for the omnious “No, not right now” excuse.

However, there was a keyword: »monotonous«… Wait… 🤔 This could be automated!

Automate

First step: Write a patch script to apply the changes.

A patch is a set of changes to a computer program or its supporting data designed to update, fix, or improve it.

It is not possible solve the requirements with a Git patch file, since they require conditions or depend on other tools. A custom migration script will do however.

Second step: Write a bot to run the patch script against all repositories.

A (internet) bot is a software application that runs automated tasks (scripts) over the Internet.

The bot needs to checkout every selected repository, create a feature branch, run the patch script, commit the changes, push the branch. Let's do it.

Patchbot

I build a prototype and used it to solve the given tasks.

Patch + Bot = Patchbot

Now, this a not a completely new area. I write migration scripts all the time when I refactor projects. I delete them as soon as they are executed though. Tomas Votruba calls these one-time migration scripts »Mandala Scripts«. I love this synonym.

Since the purpose of my patch scripts was to reuse them across many repositories I designed a lean file structure which encourages this concept. ♻

Usage

Setup

Create a new repository which will contain a collection of all patch scripts.

composer create-project pixelbrackets/patchbot-skeleton my-patches
cd my-patches

Each patch directory always contains at least a patch script and a commit message named commit-message.txt. Patch scripts can be written in different languages: PHP (patch.php), Shell (patch.sh), Python (patch.py), or even as a plain Git diff file (patch.diff). You can create a new patch using the create command:

./vendor/bin/patchbot create

Import Patches

Instead of writing every patch from scratch, you can import ready-to-use patches from Git repositories or Gists.

There is a patchbot-examples repository which covers several real-world use cases like find-and-replace, adding packages, introducing config files or renaming files.

For a quick start, you can import all example patches at once and customize them to your needs:

./vendor/bin/patchbot import https://github.com/pixelbrackets/patchbot-examples

Apply a Patch

Run the following command to apply the patch template to a repository:

./vendor/bin/patchbot patch template git@gitlab.com:user/repo.git

Repeat the same command with different repository URLs to distribute the patch to them as well - or use patch:many to apply a patch to all repositories listed in a repositories.json file at once:

./vendor/bin/patchbot patch:many template

Patchbot creates a random name for the feature branch and uses the default branch as base. When you want to create the feature branch based on a different branch, like development, and declare a fixed name for the feature branch, like feature-1337, you may run this command:

./vendor/bin/patchbot patch template git@gitlab.com:user/repo.git --source-branch=development --branch-name=feature-1337

Hint: Run ./vendor/bin/patchbot discover to quickly create a first repositories.json storage file.

Merge

Patchbot uses a feature branch and does not commit into existing branches right away. When you reviewed the feature branch and all tests are successful then you can use Patchbot again to merge the branch.

Example command to merge branch bugfix-add-missing-lock-file into branch main:

./vendor/bin/patchbot merge bugfix-add-missing-lock-file main git@gitlab.com:user/repo.git 

Passing the option --create-mr can start a GitLab merge request automatically.

Contributions welcome

This script is Open Source, so please use, patch, extend or fork it.

Contributions are welcome!

Update: Patchbot now supports import of patches from Git repositories and Gists, and patches written in different languages. The above post was changed accordingly.

⌛ Warning! This post is old. The information may be outdated.

No comments on this notepad. If you would like to add something, then don't hesitate to contact me via E-Mail, Mastodon or other social profiles.