Files
web/netlify/netlify-plugin-branch-sync

netlify-plugin-branch-sync

This is a Netlify build plugin.

Its purpose is to sync a configured source branch to other configured destination branches.

The configuration is done inside of index.js instead of using environment variables so that the branch configurations are version controlled.

SYNC_SOURCE is the source branch that will be pushed to the destination branches.

SYNC_DEST is an array of destination branches that will receive the push from SYNC_SOURCE.

This plugin only executes its sync operations when the branch being built in Netlify is the SYNC_SOURCE branch and the build context is 'Branch build'. Any other branch or build context is ignored and this plugin does nothing.

How to setup

In Bitbucket, create an access token for the repository this plugin will act on, e.g. https://bitbucket.org/scandic-swap/web/admin/access-tokens.

It needs to have the following permissions:

  • repository
  • repository:write

Add the following environment variables in the Netlify dashboard.

IMPORTANT!

Be sure to set them to only be available in the "Builds" scope and only set a value for the "Branch deploys" configuration.

  • BITBUCKET_USER_EMAIL: The email for the access token (Bitbucket generates this and provides it when creating an access token)
  • BITBUCKET_ACCESS_TOKEN: The access token

How it works

It uses two build plugin events:

  • onPreBuild
  • onPostBuild

It fails the whole build if the plugin does not complete the sync to all the branches.

Since it uses git directly and we want to sync to latest always, the plugin can be rerun in case of failures and git will resume where needed.

Event: onPreBuild

  • Checks that the current branch being built is SYNC_SOURCE and the build context is a branch build, otherwise does nothing.
  • Checks for the presence of the required environment variables.
  • Uses git to configure the user.email and set it to environment variable BITBUCKET_USER_EMAIL
  • Attempts to restore the clone from the build cache and if that fails proceeds to clone it.
  • Uses git to fetches the configured branches, SYNC_SOURCE and SYNC_DEST from origin, skipping all tags.
  • Loops over the branches and uses git to sequentially push the latest ref on SYNC_SOURCE to each SYNC_DEST branch respectively. Since a single push completes fast (under 200 ms), we opt to not push in parallel to try and avoid any rate limits.
  • Reports to Netlify the completion and provides some info to the "Deploy summary" for the build.
  • If anything throws an error during execution the whole build is failed

Event: onPostBuild

  • Checks that the current branch being built is SYNC_SOURCE and the build context is a branch build, otherwise does nothing.
  • Attempts to save the clone to the build cache. If it fails by throwing an error the whole build is failed. If it fails without throwing an error the build will succeed.

Under the hood

Bare repository

Since this plugin only does git operations that do not need a working copy, it clones the repository it uses for the operations as a bare repository.

The layout of a bare repository differs from a working copy. The way git identifies a working copy with through the presence of a folder named .git. Bare repositories do not have this folder. Instead git uses different heuristics to determine if the directory is a clone, one of which is the presence of a folder named refs.

The folder named refs only contains two folders after a bare clone operation, heads and tags. These two folder are both empty themselves.

This fact poses a challenge for the build cache because it only caches files and not empty directories.

Build cache usage

This plugin uses the utilities for caching files in Netlify Build.

This utility uses cpy to copy files to and from the cache and uses move-file to move files to and from the cache.

The use of cpy is what causes the build cache to not include empty directories because cpy only copies files and skips empty directories.

So to work around the problem this plugin uses the move mode of the build cache API which instead uses move-file instead of cpy, keeping the full directory layout as git requires it.

The move mode is required for both the restore() and save() build cache operations for the workaround to work. It is done by passing move: true as options to the calls.