Skip to content
GitHub settings as code
Dispatch

GitHub settings as code

How/why you should apply code policies to your GitHub repo's settings

Go back

Working on a repository one of the common requirements is to have things like branch protection. This is really easy to setup but requires an admin to configure it. This gets cumbersome when you want to restrict who has this access (especially useful to avoid making mistakes as an admin). On some other projects, for example for a company’s open source repositories needed to be spruced up by improving the description, tags etc.

Usually to make that happen, we give out admin rights and then resind them. 🤮. I’m not a fan of this strategy since:

Thankfully Probot has a better solution to this dilemma. We update a file stored in the repo (.github/settings.yml). After committing and pushing the file, the Probot Settings app kicks in and makes sure that the repository settings match what the file says.

This has a few benefits:

Steps to get it running

  1. Install the Probot Settings app

  2. Create a .github/settings.yml file in your repository

    repository:
    # See https://developer.github.com/v3/repos/#edit for all available settings.
    
    # The name of the repository. Changing this will rename the repository
    name: repo-name
    
    # A short description of the repository that will show up on GitHub
    description: description of repo
    
    # A URL with more information about the repository
    homepage: https://example.github.io/
    
    # Either `true` to make the repository private, or `false` to make it public.
    private: false
    
    # Either `true` to enable issues for this repository, `false` to disable them.
    has_issues: true
    
    # Either `true` to enable the wiki for this repository, `false` to disable it.
    has_wiki: true
    
    # Either `true` to enable downloads for this repository, `false` to disable them.
    has_downloads: true
    
    # Updates the default branch for this repository.
    default_branch: main
    
    # Either `true` to allow squash-merging pull requests, or `false` to prevent
    # squash-merging.
    allow_squash_merge: true
    
    # Either `true` to allow merging pull requests with a merge commit, or `false`
    # to prevent merging pull requests with merge commits.
    allow_merge_commit: true
    
    # Either `true` to allow rebase-merging pull requests, or `false` to prevent
    # rebase-merging.
    allow_rebase_merge: true
    
    # Labels: define labels for Issues and Pull Requests
    labels:
      - name: bug
        color: CC0000
      - name: feature
        color: 336699
      - name: first-timers-only
        # include the old name to rename and existing label
        oldname: Help Wanted
    
    # Collaborators: give specific users access to this repository.
    collaborators:
      - username: bkeepers
        # Note: Only valid on organization-owned repositories.
        # The permission to grant the collaborator. Can be one of:
        # * `pull` - can pull, but not push to or administer this repository.
        # * `push` - can pull and push, but not administer this repository.
        # * `admin` - can pull, push and administer this repository.
        permission: push
    
      - username: hubot
        permission: pull
    
      - username:
        permission: pull

Wait a minute

… did I say maintainers can change settings that only Admins have access to? Yes.

Is this an issue? Depending on your circumstances this may or may not be an issue - the consultant answer applies here.

It depends.

Every consultant ever

OK, let’s assume that this is a a hole that needs to be plugged.

Thankfully we can use the CODEOWNERS file to automatically request specific reviewers when people open pull requests that make changes to files. In this case, we could put in a pattern that like:

.github/settings.yml @org/team-leads

That way when someone modifies the settings file and opens a PR, the ‘team-leads’ team will be marked as a reviewer. This should help prevent unwanted changes being made to the repository. Although it does involve setting up branch protection properly.

So we’ll go back to the settings yaml and put a little snippet in there to complete the loop.

branches:
  - name: main
    # https://docs.github.com/en/rest/reference/repos#update-branch-protection
    # Branch Protection settings. Set to null to disable
    protection:
      # Required. Require at least one approving review on a pull request, before merging. Set to null to disable.
      required_pull_request_reviews:
        # The number of approvals required. (1-6)
        required_approving_review_count: 1
        # Dismiss approved reviews automatically when a new commit is pushed.
        dismiss_stale_reviews: true
        # Blocks merge until code owners have reviewed.
        require_code_owner_reviews: true
        # Specify which users and teams can dismiss pull request reviews. Pass an empty dismissal_restrictions object to disable. User and team dismissal_restrictions are only available for organization-owned repositories. Omit this parameter for personal repositories.
        dismissal_restrictions:
          users: []
          teams: []
      # Required. Require status checks to pass before merging. Set to null to disable
      required_status_checks:
        # Required. Require branches to be up to date before merging.
        strict: true
        # Required. The list of status checks to require in order to merge into this branch
        contexts: []
      # Required. Enforce all configured restrictions for administrators. Set to true to enforce required status checks for repository administrators. Set to null to disable.
      enforce_admins: true
      # Prevent merge commits from being pushed to matching branches
      required_linear_history: true

Branch protection as code is awesome. Given the scenario where I’ve just added a new pipeline, I can modify the branch protection as well so that future PRs will require that workflow to pass. To me this takes the power of pipelines as code that little step further. 🎉

OK, so now we have a setup where:

BONUS 1: YAML is hard

There’s a lot of YAML being covered off here, so what happens when someone (me) makes a mistake and commits/merges invalid yaml (bad spacing).

Well we can add another workflow to look for changes to .github/settings.yml and run some custom actions to trigger the probot settings app. Essentially we can have a workflow that:

These actions are pretty quick, so there’s maybe 2 seconds or so where the repo settings may be out of whack. This is a file that would normally have infrequent churn so hopefully being able to pickup bad yaml outweighs this risk.

I’m running it on my blog or you can have a look at a more focused example at https://github.com/GuacamoleResearch/MySettings.

Edit page
Share this post on:
Discussion
Continue Reading
Previous
The downfalls of environment branching patterns
Next
Git and diffs