🛡️ Automatically protect the default branch of new repositories in a GitHub organization

Overview

Branch Autoprotector

The Branch Autoprotector watches a GitHub organization and automatically protects the default branch in new repositories. This service notifies the creator of the default branch of this automatic branch protection setup by filing an issue in the repository.

Features

  • Protects the default branch of each new repository in an organization. In this way, commits can only be added to the default branch through pull requests with at least one approving review, while direct pushes are disallowed.
  • Leverages GitHub Apps, which is the recommended way of interfacing with GitHub.
  • Immediately reacts to newly created default branches by subscribing to GitHub webhook events.
  • Automatically retries failed requests as to be unaffected by sporadic network issues.
  • Automatically renews the GitHub App installation token after it expired to be able to run for long periods of time.
  • Verifies the signatures of incoming webhook payloads to verify that they were actually sent by the GitHub server.
  • Written in Rust with efficiency, robustness, and security in mind.

Limitations

  • Only the first branch pushed to the repository will be protected but not branches that are created afterward. If the default branch is changed later on, it will need to be protected manually.
  • Repository administrators and organization owners can still manually change the branch protection settings at any time regardless of how they have been initially set up by this service.
  • Branch protection rules are only set up after the first branch is pushed to a new repository. This is because empty repositories don’t have a default branch that could be protected via the GitHub API yet.
  • This service only supports a single organization at this time.
  • Currently, a GitHub Pro subscription is required on GitHub.com to support private repositories.

Development

Follow the steps in the next section if you want to deploy and run this service in production.

The Branch Autoprotector interacts with GitHub as a GitHub App, which you will need to create and configure first. As this service is written in Rust, you will further need to install the Rust toolchain. However, you don’t need to follow the deployment steps when you’re just developing and testing locally.

The GitHub server probably won’t be able to deliver webhook events to your local machine, as it’s very likely not to be publicly exposed to the internet for good reasons. To work around that, we recommend setting up a webhook proxy with smee.io for local development, which will forward webhook events to your local system.

  1. Create and install the GitHub App with the necessary permissions and event listeners. We recommend using a separate GitHub organization and GitHub App for testing.

  2. Install the Rust toolchain on your local system.

  3. Set up a webhook proxy with smee.io.

  4. In the settings of your GitHub App under General, update the Webhook URL field (which still contains a placeholder URL) with the webhook proxy URL that you generated on smee.io.

  5. Configure this service for development.

  6. Generate the source code documentation if you’d like to start working on this service:

    $ cargo doc

    Then, open target/docs/branch_autoprotector/index.html in your browser to have a look at the documentation.

  7. Build and run the service from the root of your working copy of this repository:

    $ RUST_LOG=debug cargo run

Your local machine will now receive and handle repository creation events in the organization the GitHub App has been installed to 🚀 . To test this, create a new repository in your organization and push some content to it—you should see a new issue being created! (Note that the issue won’t be created as long as the repository is empty, as it doesn’t have a default branch that could be protected yet.)

Installation and usage

The Branch Autoprotector interacts with GitHub as a GitHub App, which you will need to create and configure first. The service is designed to be installed on Debian systems and run as a systemd service. We don’t provide official builds at this time, but you can build a Debian package on your own with a few steps using the Rust toolchain.

  1. Create and install the GitHub App with the necessary permissions and event listeners.

  2. Ensure that the following Debian packages are installed on the system on which you’d like to build the Debian package (that could be your target system or another system with the same Debian version). These packages will be needed to build this service:

    $ sudo apt install curl build-essential pkg-config libssl-dev
  3. Install the Rust toolchain including cargo deb on the same system.

  4. Clone this repository and build the Debian package with cargo deb:

    $ git clone https://github.com/branch-autoprotector/branch-autoprotector
    # You can skip this step if you prefer building from the main branch
    $ git switch --detach <latest release tag>
    $ cd branch-autoprotector
    $ cargo deb

    The resulting Debian package can then be found in target/debian/branch-autoprotector_ _ .deb .

  5. Deploy this service by copying the Debian package to the target system and installing it:

    $ sudo apt install ./branch-autoprotector_<version>_<architecture>.deb
  6. Configure this service for production use.

  7. Set up a reverse proxy with Nginx serving the Branch Autoprotector at a publicly accessible location.

  8. In the settings of your GitHub App under General, update the Webhook URL field (which still contains a placeholder URL) with the URL that you configured in Nginx.

  9. Run and enable this service. This service runs as the github user you created earlier when you configured this service and not as root:

    $ sudo systemctl enable --now branch-autoprotector

    systemd ensures that services persist across sessions, that is, the service won’t terminate when you close your connection. By enabling it, this service will also be started automatically when your system boots. The --now flag lets the service start immediately and not just the next time you reboot.

Your server will now receive and handle repository creation events in the organization the GitHub App has been installed to 🚀 . To test this, create a new repository in your organization and push some content to it—you should see a new issue being created! (Note that the issue won’t be created as long as the repository is empty, as it doesn’t have a default branch that could be protected yet.)

Notes for new developers

If you’d like to adjust or extend this service, start by looking at main.rs. There, the route for handling incoming webhook events is defined as well as the handler for branch creation events.

All functionality related to making calls to the GitHub API, GitHub Apps authentication, and verifying payloads from webhook events delivered by GitHub is encapsulated in the github_api module. If you want to make calls to API endpoints not yet implemented, it’s likely that you won’t need to touch that module though. Instead, you can add new request and response types in models.rs, and the implementation of the GitHub API client will be able to handle those requests without relinquishing static type checking.

Notes concerning the assignment

  • No particular programming language, technology stack, or similar was requested. For this reason, I assumed the client to be interested in a service that’s reliable, secure, and has a small resource footprint on the server. To achieve that, I decided to build this service in Rust. I also presumed that the client requested robustness against sporadic network issues, automatic renewal of GitHub App installation tokens (to ensure the service can run for a long time unattended), and built-in webhook payload signature verification. Though there is no official GitHub API binding like OctoKit for Rust yet, I presumed that the client was aware of the trade-offs and finally decided to ask for a Rust implementation.
  • For deployment, I decided to install a systemd service (using a Debian package), which I find to be a lightweight way to manage services and make them persist across sessions and reboots nicely.
  • Using a personal access token and webhooks configured on the organization level rather than GitHub Apps would certainly have yielded a much simpler implementation. My assumption was, however, that this integration is meant to be used in production, and I don’t think that running integrations using personal access tokens from user accounts is a good choice. For this reason, I decided to implement this service using GitHub Apps.
  • The client explicitly asked for this this service to set up a branch protection rule whenever new repositories are created. I decided to slightly deviate from this by configuring the branch protection rule only when the default branch is pushed for the first time. The reason is that prior to pushing the first branch, the repository is empty, which would mean that we’d have to guess the name of the default branch that’s going to be used. Also, the GitHub API refuses setting up branch protection rules if there are no branches yet. I believe this adjustment to be in the client’s interests.

Resources used while developing this service

You might also like...
Gathering some metrics about github projects

rust-metrics This is an experimental project to start gathering metrics about github organizations and repositories. The goal is to get an idea of var

A Github Actions based CI release template for Rust binaries
A Github Actions based CI release template for Rust binaries

Rust CI Release Template A Github Actions based CI release template. This repo serves as a live template, and reference for building your own CI power

Help project managers and project owners with easy-to-understand views of github issue dependencies.
Help project managers and project owners with easy-to-understand views of github issue dependencies.

Help project managers and project owners with easy-to-understand views of github issue dependencies.

A Github webhook server to help with CI/CD written in Rust.
A Github webhook server to help with CI/CD written in Rust.

This application will automatically updates local GitHub repositories and triggers a command once the update is complete. This can be extremely useful

GitHub CLI extension to show & rename the default branch.

gh-default-branch GitHub CLI extension to show & rename the default branch. rename subcommand was inspired by this gist. ⚠️ Caution The rename subcomm

A simple web-app allowing you to batch archive groups of repositories from a given organization

ice-repos My goal here is to build a simple web-app allowing you to batch archive groups of repositories from a given organization, using Rust+Yew. As

A command-line tool and Docker image to automatically backup Git repositories from GitHub or anywhere

A command-line tool and Docker image to automatically backup Git repositories from GitHub or anywhere

Easy c̵̰͠r̵̛̠ö̴̪s̶̩̒s̵̭̀-t̶̲͝h̶̯̚r̵̺͐e̷̖̽ḁ̴̍d̶̖̔ ȓ̵͙ė̶͎ḟ̴͙e̸̖͛r̶̖͗ë̶̱́ṉ̵̒ĉ̷̥e̷͚̍ s̷̹͌h̷̲̉a̵̭͋r̷̫̊ḭ̵̊n̷̬͂g̵̦̃ f̶̻̊ơ̵̜ṟ̸̈́ R̵̞̋ù̵̺s̷̖̅ţ̸͗!̸̼͋

Rust S̵̓i̸̓n̵̉ I̴n̴f̶e̸r̵n̷a̴l mutability! Howdy, friendly Rust developer! Ever had a value get m̵̯̅ð̶͊v̴̮̾ê̴̼͘d away right under your nose just when

👑 Show in-organization ranking of GitHub activities such as review count.

gh-ranking Show in-organization ranking of GitHub activities such as review count. Installation gh extension install yukukotani/gh-ranking Usage USAG

Automatically commit all edits to a wip branch with GPT-3 commit messages

gwipt Automatic work-in-progress commits with descriptive commit messages generated by GPT-3 Codex Never again worry about the tension between "commit

Autodefault automatically makes your struct literals fill themselves with `..default()`

autodefault A library that automatically inserts ..Default::default() for you. The pitch Has this ever happened to you? #[derive(Debug, Default, Parti

A project for automatically generating and maintaining Debian repositories from a TOML spec.

Debian Repository Builder A simple utility for constructing and maintaining Debian repositories. Configuration of a repo is based on the directory hie

Automatically assess and score software repositories for supply chain risk.

Hipcheck Hipcheck scores risks for software projects; yours and your dependencies. It analyzes repositories to assess risks, review development practi

Delete useless GitHub repositories easily.

delete-unused-repo Delete useless GitHub repositories easily. Demo del-unused-repo.mp4 Usage Warning You are responsible for your own actions, this is

Retrieve all requested SBOMs from the GitHub repositories.

GitHub SBOM(s) Generator Action This GitHub Action and/or standalone CLI application generates a Software Bill of Materials (SBOM) for a given GitHub

A Discord bot focused on addressing the inherent problems with Discord, to allow a more socialist/anarchist organization of servers.

ACABot A Discord bot focused on addressing the inherent problems with Discord, to allow a more socialist/anarchist organization of servers (or "guilds

Project Masterpass is a deterministic databaseless key management algorithm, aimed to help those who cannot protect their encryption keys in storage

Project Masterpass (working title) Attention! This project is still under heavy development, and SHOULD NOT be used in practice, as the algorithms cou

An efficient pictures manager based on custom tags and file system organization.

PicturesManager An efficient pictures manager based on custom tags and file system organization. Developed with Tauri (web app) with a Rust backend an

Safer Nostr is a service that helps protect users by loading sensitive information (IP leak) and using AI to prevent inappropriate images from being uploaded.

Safer Nostr is a service that helps protect users by loading sensitive information (IP leak) and using AI to prevent inappropriate images from being uploaded. It also offers image optimization and storage options. It has configurable privacy and storage settings, as well as custom cache expiration.

Owner
Branch Autoprotector
Branch Autoprotector
Default implementation of the Wayland protocol for use with wl

Wayland An implementation of core Wayland interfaces and convenience functions for accelerating the development of Wayland clients and servers using t

AidoP 1 Jan 24, 2022
A GitHub Action to automatically build and deploy your mdbook project.

?? deploy-mdbook The deploy-mdbook action allows you to easily build and deploy your mdBook project to GitHub Pages. See action.yml for configuration

null 27 Oct 24, 2022
Autorebase automatically rebases all of your feature branches onto master.

Autorebase automatically rebases all of your feature branches onto master. If conflicts are found it will rebase to the last commit that doesn't cause conflicts. By default, branches with an upstream are excluded. You don't need to switch to any branch, the only limitation is that a branch that is checked out and not clean will not be rebased (though I may add that in future).

Tim 66 Nov 26, 2022
Adds size optimizations to any Perseus app automatically.

Perseus Size Optimization Plugin WARNING: Until Perseus #66 is fixed, this plugin can actually increase overall binary size! Once that issue is fixed

arctic_hen7 6 Aug 14, 2022
Automatically download minecraft server jars in one line

MCDL Automatically download minecraft server jars in one line (or one click) Installation Download (Windows, Linux) Install via cargo: cargo install m

Isaac Hirschfeld 1 Oct 26, 2021
A dynamic output configuration tool that automatically detects and configures connected outputs based on a set of profiles.

shikane A dynamic output configuration tool that automatically detects and configures connected outputs based on a set of profiles. Each profile speci

Hendrik Wolff 15 May 4, 2023
osu-link is a program which links osu!stable beatmaps to osu!lazer's new store format, saving you disk space.

osu-link is a program which links osu!stable beatmaps to osu!lazer's new store format, saving you disk space.

LavaDesu 2 Nov 8, 2021
A shiny new package manager written in rust explicitly for gemlock/linux and it's distributions

Gem A shiny new package manager written in rust explicitly for gemlock/linux and it's distributions. List of content How to setup Systems Ubuntu Arch

Gemlock 1 Feb 22, 2022
Insert a new named workspace between two other named workspaces

Insert a new named workspace between two other named workspaces

null 2 Mar 13, 2022
Compiler & Interpreter for the (rather new and very experimental) Y programming language.

Y Lang Why would anyone build a new (and rather experimental) language with no real world use case. Design Y (pronounced as why) is based on the idea

Louis Meyer 8 Mar 5, 2023