Skip to main content

Versioning code

Quartz is a long-lived product, people will come and go. Keeping a clean history of work is crucial to help future maintainers of Quartz understand what was done and why.

Start by reading and understanding the General version management principles, as they apply to all the assets of the system.

Contribution lifecycle

Repository owners are in charge of maintaining a good level of security and quality in their code. Using GitHub forks and pull requests is the recommended way of keeping track of every change that is pushed to the code.

Making a change to your repository requires to follow specific steps in order.

  1. Fork the repository in GitHub (when possible), and clone it locally.
  2. Create a branch when you start working on a task
  3. Commit changes to your branches.
  4. Clean your commit history when you're ready to push your changes to the main repository
  5. Push and check the CI runs successfully
  6. Create a Pull Request to review and merge your changes back into the main repository.

Fork and clone the repository

When possible, fork the repository first. Read the GitHub forking guide to learn how to do so.

Whether or not you can fork the repository, you must clone it to get a local copy on your computer. There are several ways of doing so, as explained in the GitHub cloning documentation.

Create a branch

Whether or not you can fork a repository,when you start working on a task, create a new branch from next using a consistent branch naming convention. You can do so in several ways:

  • let GitHub create the branch for you using the link provided in the Development section of the issue you're working on. Be careful to review the proposed settings and adjust the source branch as necessary.
  • use a graphical Git client to make a new branch deriving from next.
  • use the git CLI in your command prompt:
    git checkout next
    git checkout -b <type/the-new-branch-name>

Default branches

Quartz repositories come with two essential branches:

  • the main branch, in which each commit is tied to a specific production release (ex: v1.2.3)
  • the next branch, in which each commit is tied to a specific beta release (ex: 2.0.0-beta.3)

Commit changes to your branch

Your commits should follow the Conventional commits specification. Make sure to read and understand the Conventional commits section below.

In the early stages, you may fall back to using the chore: <summary> commit pattern for quick, less formal commits. Use meaningful labels anyway to help you cleaning your history later.

For example:

  • chore: update dependencies
  • chore: draft Foo parsing
  • chore: draft Foo linting
  • chore: finish Foo linting
  • chore: finish Foo parsing
  • chore: chore: lint Foo in build

Conventional commits

We use the Conventional commits specification for that purpose. Make sure you understand it. Even if you never used it before, you will quickly get used to it and see the clarity and consistency it brings to project's history.

A typical commit message goes like this:

<type>[optional scope]: <description>

[optional body]

[optional footer]

where:

  • <type> is a keyword (see table below) indicating the nature of the commit
  • <description> starts with a lowercase infinitive verb and has to be short
  • [optional scope] provides context in parenthesis
  • [optional body] is a lengthier description of the change, supporting Markdown syntax (bullet lists, emphasis, etc.)
  • [optional footer] is mostly used to describe breaking changes (see below).

Here are a few correct examples:

  • Simplest commit: fix: make tooltip visible again

  • With context: feat(button): show tooltip when label is truncated

  • With description:

    feat(button): show tooltip when label is truncated

    Tooltip is shown when:
    - button has a fixed width
    - **and** label is too long
    - **and** the `overflow` option is set to `truncate`

Breaking changes

To indicate breaking changes, add an exclamation mark (!) just before the column (:), and document the impact of the change in the footer, starting with BREAKING CHANGE:. Like this:

feat(button)!: show tooltip when label is truncated

Tooltip is shown when:
- button has a fixed width
- **and** label is too long
- **and** the `overflow` option is set to `truncate`

BREAKING CHANGE: the `overflow` option is now a string
attribute instead of a boolean, accepting the
`visible` and `truncate` options.

Commit types

The following table lists the commit types used by Quartz, their purpose, and how they impact version numbers and release notes generation:

Commit typePurposeVersion incrementIncluded in release notes
featA new featureMinor✅ Yes
fixA bug fixPatch✅ Yes
perfA code change that improves performancePatch✅ Yes
revertA previous change that was revertedMajor✅ Yes
*!Any type with BREAKING CHANGESMajor✅ Yes
buildChanges that affect the build systemIgnored🚫 No
ciChanges to CI configurationIgnored🚫 No
docsDocumentation only changesIgnored🚫 No
refactorA code change that neither fixes a bug nor adds a featureIgnored🚫 No
styleChanges that do not affect the meaning of the code (formatting, etc.)Ignored🚫 No
testAdding missing tests or correcting existing testsIgnored🚫 No
choreOther changes that don't modify source files. Don't overuse it!Ignored🚫 No

commit-msg hook

We use husky to install a commit-msg hook that will run commitlint to ensure the message follows that standard. commitlint is configured with a custom preset implementing the Quartz specific practices.

Clean commit history

We use semantic-release to handle the release process, which generates the release notes from your commit history. That's why it is crucial that you always review and clean your commit history before submitting your changes for review and release.

You can do so by using the Interactive Rebase Git feature, as described in the Rewriting History chapter from The Git book.

Be aware that using interactive rebase, you will most often have to force push your changes to the remote repository. That's why working in your branch avoids accidentally overwriting the commit history from the upstream repository.

We could clean the history from the previous example by:

  • Squashing together the draft/finish commit pairs
  • Rewording the build commit to use the proper commit type. Also, our consumer's build may fail due to the added Foo syntax linting. We indicate that by adding an exclamation mark (!) after the commit type and describing the breaking changes in the commit's description (not shown here).

Which would result in the following history:

  • chore: update dependencies
  • feat: support the Foo syntax
  • build!: add Foo syntax linting

Thanks to that clean history, when the owners release a version, semantic-release will automatically increment the major version number (because we indicated a breaking change) and generate clean release notes in the repository's CHANGELOG.md file:

# Change log

# @quartzds/my-package 2.0.0

## ⚠ BREAKING CHANGES

- The build now parses the Foo syntax

## ✨ Features

- Support the Foo syntax

Push your branch and check the CI

Create a Pull Request

Create a Pull Request when you're ready to merge your changes into the upstream repository:

The CI will first run checks on your branch, then the repository owners will review your PR.

Adding changes to your PR

While the PR is open, if the owners request changes from you, you just have to push changes to your branch. GitHub will automatically re-run the checks and notify the owners of your update.

Once the repository owners approve your PR, they will first merge it into the next branch and create a beta release. You can then collect feedback from your early testers.

Adding changes after a beta release

Once a beta version has been released, you must create a new PR and start a new beta cycle to contribute additional changes.

Releasing to production

After releasing a beta version, if your early testers are happy with your changes, request the owners to release a production version.