We have three basic branch structures that we employ, according to the requirements of each project. They are, in order of increasing complexity and overhead:
- One-branch projects
- "master" + "stable" branch projects
- "master" + "stable" + maintenance branch projects
Simple is good. For projects that can afford to use a one-branch structure, using a single "master" branch minimizes maintenance overhead and is our recommendation:
- master: active development branch
Pull requests target master and are merged in after review. We periodically cut releases from the master branch, tag them, and publish to NPM.
- liferay-frontend-projects (this repo)
These are projects which operate just like the "one-branch" projects, but with the addition of a "stable" branch that always points at the latest release.
- master: active development branch
- stable: points to the latest release; may be used as a base for preparing urgent hotfix releases.
Pull requests target master and are merged in after review. When we cut a release, we update the "stable" branch to match "master" (generally a so-called "fast-forward" merge). Further development resumes on "master".
Ideally, "master" is always in a releasable state, but if it isn't, we can apply "hotfixes" by sending a PR that targets the "stable" branch, and then cut a release from there. The same fix is also incorporated in the "master" branch via cherry-picking or merging. This is in some sense a hypothetical scenario: if we keep "master" in shape, you may never see this happen in practice.
The liferay-js-publish package can be used to perform publishes in repositories with the "master" + "stable" structure simply by running a yarn version
command.
- n/a
These operate just like "master" + "stable" branch projects, with the addition of maintenance branches that correspond to the previous major version release. For example, if we're doing our work in the 2.x series of a project on "master", but occasionally need to update and release the prior 1.x series, the branch structure would be this:
- master: active development branch
- stable: points to the latest release; may be used as a base for preparing urgent hotfix releases.
- 1.x: previous active development branch for prior major version (1.x series, less frequently updated, generally only with bug-fixes).
- 1.x-stable: points to the latest 1.x release (less frequently updated).
As before, most PRs target "master". "stable" is updated on every release.
Additionally, changes can be backported (via cherry-picking, or otherwise hand-crafting them) that target the maintenance branch ("1.x" in the example above). When we cut a "1.x" series release, we update "1.x-stable" to match.
- alloy-editor: the "2.x" series is the latest and is developed on "master" (with "stable" updating on each release); we also cut "1.x" series releases when needed, and those are developed on the "1.x" branch (with "1.x-stable" updated on each release).
Clay is an example of a repo where we should consider moving to the same structure; we're currently working on both the existing 2.x series and a new 3.x series.
- We tag our releases with an annotated tag of the form "v1.2.3" (Annotated tags are tags that include a message; in our case, we make the message the same as the tag name.)
- In a monorepo like Clay, all packages are released together and share the same version number and a single tag.
- In a monorepo like liferay-frontend-projects, the packages are released independently, so the tags have the format "package/v1.2.3" (eg. "npm-scripts/v2.2.0"). We automate this with
.yarnrc
files like this one that ensure thatyarn version
produces tags of the desired format.