Back to Flutter Inner Source homepage
inner source

Branch Protection

Branch protection is a feature provided by GitHub to add constraints to specific repository branches. For example limiting those able to push commits to the branch, or enforcing changes are reviewed before they can be merged into it.

It is an important feature because these protections ensure your contributors follow your desired workflow to produce secure and quality contributions.

Branching Strategy

A wide variety of repository branching strategy options are documented elsewhere:

Your choice of branching strategy will depend on your context and desired inner source stage. Note that for inner source:

Configuring Branch Protection

There are 3 possible ways to configure branch protection rules:

  1. Manually configure per-repository in the GitHub repository settings interface.
  2. RECOMMENDED: Declare in the capability codebases.json file and the code governor automation will set via GitHub API.
  3. Automate yourself via GitHub API.

Option (2) is recommended as it is easier to do than (3), yet can be declared across all repositories in a capability under version control unlike (1). This recommended option requires:

  1. Configuring a codebases.json file in the capability repository.
  2. Ensuring code governor is installed on your capability.

The following branch protection options can be configured in a codebases.json policy:

keyTypeDefault
value
Description
branch-name-patternstringfalseYou can define a pattern (fnmatch) to match all branches, or any branch that matches a naming pattern specified with the fnmatch syntax.

Learn more...
required-reviewsinteger1The number of required approving reviews for the pull request to be accepted.
dismisses-stale-reviewsbooleantrueNew reviewable commits pushed to a matching branch will dismiss pull request review approvals.
require-review-from-code-ownersbooleantrueRequire an approved review in pull requests including files with a designated code owner.
restricts-review-dismissalsbooleanfalse Specify users or teams allowed to dismiss pull request reviews.

ℹ️ Information
After applying this change you'll have to go to Settings -> Branches -> <branch_protection_rule> in each repository and define the list of users and/or teams.
requires-status-checksbooleantrue Activate which status checks must pass before branches can be merged into a branch that matches this rule. When enabled, commits must first be pushed to another branch, then merged or pushed directly to a branch that matches this rule after status checks have passed.

ℹ️ Information
After applying this change you'll have to go to Settings -> Branches -> <branch_protection_rule> in each repository and select the status checks that apply.
requires-strict-status-checksbooleanfalseThis ensures pull requests targeting a matching branch have been tested with the latest code. This setting will not take effect unless at least one status check is enabled.

ℹ️ Information
After applying this change you'll have to go to Settings -> Branches -> <branch_protection_rule> in each repository and select the status checks that apply.
requires-commit-signaturesbooleanfalseCommits pushed to matching branches must have verified signatures.
requires-linear-historybooleanfalsePrevent merge commits from being pushed to matching branches.
include-administratorsbooleantrueEnforce all configured restrictions above for administrators.
restrict-pushesbooleanfalseSpecify people, teams or apps allowed to push to matching branches. Required status checks will still prevent these people, teams and apps from merging if the checks fail.

ℹ️ Information
After applying this change you'll have to go to Settings -> Branches -> <branch_protection_rule> in each repository and users/teams/apps that are allowed.
allows-force-pushesbooleanfalsePermit force pushes for all users with push access.
allows-deletionsbooleanfalseAllow users with push access to delete matching branches.

Example: GitHub Flow Service

Our first example is a service repository using a simple workflow (e.g. GitHub Flow).

In this case branch protection is required on the main branch:

{
  "contributors": [{ "teams": ["all-flutter-global"] }],
  "policy": {
    "branch-protections": {
      "github-flow-service-code": {
        "parameters": {
          "branch-name-pattern": "main",
          "required-reviews-count": 2,
          "dismisses-stale-reviews": true,
          "require-review-from-codeowners": true,
          "include-administrators": true,
          "allows-force-pushes": false,
          "allows-deletions": false
        }
      }
    }
  },
  "repos": {
    "https://github.com/Flutter-Global/an-example-service": {
      "repotype": "service",
      "policy": { "branch-protection": "github-flow-service-code" }
    }
  }
}

Example: GitFlow Service

In this example critical service code is contained in the repository, and the workflow is complex (e.g. GitFlow). The protection required is similar to the previous service code example, but instead of applying protection to a single main branch, multiple branches need protection.

Example: Docs Repository

Not all repositories contain critical production service code, and for many repositories more flexible protection is appropriate. For example for a documentation repository using GitHub Flow there are several common options:

  1. No branch protection at all.
  2. Restrict pushes to main to a group of editors, allowing them to push directly, while requiring others to submit pull requests.
  3. Relaxed review requirements, allowing stale reviews and administrator override.

No Protection

No branch protection is similar to a default confluence page (editable by all) although the version history can be re-written with a force push and/or the branch deleted unless this is protected against.

This can be achieved by simply not declaring any branch protection rule for the repository in codebases.json. Note that the code governor automation will restore or re-configure any manually deleted or edited branch protection rules unless the codebases.json is updated. However it will not delete rules. This means if relaxing existing protections manual cleanup is required.

{
  "contributors": [{ "teams": ["all-flutter-global"] }],
  "repos": {
    "https://github.com/Flutter-Global/example-docs": {
      "policy": {
        "comment": "no branch protection required"
      }
    }
  }
}

Restricted Push

A branch protection rule can be configured to limit who can push to a branch. This can be used to create a group of “editors” who can push direct un-reviewed edits to main to quickly update docs. Non-editors cannot push to main so will create a new branch and raise a pull request for an editor to review & merge.

The current code governor release does not support automated creation or declaration of this type of branch protection. Therefore you should declare the repo with no protection in your codebases.json and manually configure the branch protection in the GitHub repository settings:

  1. Navigate to create/edit a branch protection rule (Settings > Branches > Add Rule).
  2. Enter the branch name pattern (e.g. main).
  3. Ensure Require pull request reviews before merging is NOT checked.
  4. Ensure Restrict who can push to matching branches is checked, and specify those who require this access.
  5. Save the rule.

Relaxed Review

For a docs repository where changes should be reviewed similar rules can be used as for production service code. However additional shortcuts and exceptions can be allowed for contributor convenience as they present no security risk:

For example:

{
  "contributors": [{ "teams": ["all-flutter-global"] }],
  "policy": {
    "branch-protections": {
      "github-flow-relaxed-review": {
        "parameters": {
          "branch-name-pattern": "main",
          "required-reviews-count": 1,
          "dismisses-stale-reviews": false,
          "require-review-from-codeowners": false,
          "include-administrators": false
        }
      }
    }
  },
  "repos": {
    "https://github.com/Flutter-Global/example-docs": {
      "policy": { "branch-protection": "github-flow-relaxed-review" }
    }
  }
}

Example: Automated Commits

Some repositories require automated commits: for example a commit to update a release version from a build script. Using a Github app for authentication is recommended but many users also use a service account. These approaches can be complicated by branch protection.

Example: Capability Protection

Product and capability repositories themselves also require branch protection: the code governor will action any codebases.json changes merged into the default branch (e.g. main or master). Since this file configures the access control and branch protection of the other capability repositories, the default branch requires protection.

The recommended setup:

CODEOWNERS:

# require maintainer review for capability configuration
codebases.json    @Flutter-Global/maintainers-cap-example

codebases.json (snippet):

{
  "contributors": [{ "teams": ["all-flutter-global"] }],
  "policy": {
    "branch-protections": {
      "codeowner-review": {
        "parameters": {
          "branch-name-pattern": "main",
          "required-reviews-count": 1,
          "dismisses-stale-reviews": true,
          "require-review-from-codeowners": true,
          "include-administrators": true
        }
      }
    }
  },
  "repos": {
    "https://github.com/Flutter-Global/cap-example": {
      "repotype": "capability",
      "policy": { "branch-protection": "codeowner-review" }
    }
  }
}

Note that the capability codebases.json specifies the branch protections of its child repositories, and not of itself. A capability is owned by a product, and the capability branch protection will be defined by the owning product. As a root repository, a product’s branch protections must be set manually unless it includes itself in its codebases.json repo list.