Skip to content

Latest commit

 

History

History
329 lines (243 loc) · 13 KB

DEVELOPING.md

File metadata and controls

329 lines (243 loc) · 13 KB

Developer Guide

This document describes the workflow for contributing using the GitHub fork and pull model that consists of creating a fork, doing your work, issuing a pull request, and merging that pull request back into the original project.

Attribution

This guide is partly based on an excellent tutorial of Chase Pettit published under the MIT License.

Preparing Your Development Environment

We use pre-commit to enforce best practices in Carbyne Stack development. Before you start developing please install pre-commit using:

pip3 install pre-commit

In your Carbyne Stack project git repository with a .pre-commit-config.yaml, the git hook scripts can be installed using:

pre-commit install

git/pre-commit will run the configured hooks automatically on each commit (and potentially other git commands) from now on.

Note that there is also a handy way of automatically enabling pre-commit on repositories . This frees you from the burden of remembering to manually install the Git hooks after cloning a pre-commit-enabled repository.

Creating a Fork

Just head over to the GitHub repository and click the "Fork" button. It's just that simple. Let's assume you want to contribute to the carbynestack project in the following. Once you've done that, you can use your favorite git client to clone your repo or just head straight to the command line:

# Clone your fork to your local machine
git clone git@github.com:YOUR-USERNAME/carbynestack.git

Keeping Your Fork Up to Date

While this isn't an absolutely necessary step, if you plan on doing anything more than just a tiny quick fix, you'll want to make sure you keep your fork up to date by tracking the original "upstream" repo that you forked. To do this, you'll need to add a remote:

# Add 'upstream' repo to list of remotes
git remote add upstream https://github.com/carbynestack/carbynestack.git

# Verify the new remote named 'upstream'
git remote -v

Whenever you want to update your fork with the latest upstream changes, you'll need to first fetch the upstream repo's branches and latest commits to bring them into your repository:

# Fetch from upstream remote
git fetch upstream

# View all branches, including those from upstream
git branch -va

Now, checkout your own master branch and merge the upstream repo's master branch:

# Checkout your master branch and merge upstream
git checkout master
git merge upstream/master

If there are no unique commits on the local master branch, git will simply perform a fast-forward. However, if you have been making changes on master (in the vast majority of cases you probably shouldn't be)
see the next section, you may have to deal with conflicts. When doing so, be careful to respect the changes made upstream.

Now, your local master branch is up-to-date with everything modified upstream.

Doing Your Work

Create a Branch

Whenever you begin work on a new feature or bugfix, it's important that you create a new branch. Not only is it proper git workflow, but it also keeps your changes organized and separated from the master branch so that you can easily submit and manage multiple pull requests for every task you complete.

To create a new branch and start working on it:

# Checkout the master branch - you want your new branch to come from master
git checkout master

# Create and switch to a new branch named new-feature (give your branch its own
# simple informative name)
git checkout -b new-feature

Commit Your Changes

Now, go to town hacking away and making whatever changes you want to. Along the way, whenever you completed a self-contained piece of the high-level feature you implement, commit your changes using:

git commit -sS

The flags -s and -S make sure that your commits are cryptographically signed, and that the contribution made in that commit is signed off. See the Contributing Guide for more information on this.

Conventional Commits

We make use of Conventional Commits to create an explicit commit history that conveys the nature of a change of a commit and allows for driving automation of the release process based on Semantic Versioning. We use the commit types defined by @commitlint/config-conventional, i.e.:

Type Meaning Bumps
fix Patches a bug in the codebase patch
feat Introduces a new feature to the codebase minor
build Affect the build system or external dependencies -
chore Changes that do not relate to a fix or feature and don't modify source or test files (e.g., updating dependencies) -
ci CI configuration files and scripts -
docs Updates to documentation only -
style Changes that do not affect the meaning of the code (e.g., formatting) -
refactor Neither fixes a bug nor adds a feature -
revert Reverts a previous commit -
perf Improves performance -
test Adding new or correcting existing tests -

A breaking change, i.e., something that leads to an incompatible API change for the respective unit is indicated by adding a ! behind the commit type, e.g., feat!: .... This bumps the minor version for pre-production releases, and the major version for production (from v1.0.0 onwards) releases. While technically possible, one should not mark a change as breaking for other commit types than feat and fix.

A conventional commit message header always has the following structure:

<type>(scope #1/.../scope #n): <subject>

The available scopes are defined by the package-name fields in a file called release-please-config.json at the root of the repository.

Note that you can add multiple changes to potentially different scopes by adding multiple such lines. See here for an example.

The scopes can be completely omitted, i.e.:

<type>: <subject>

In that case all defined scopes are affected by the change.

Commit Messages

Please make sure to provide meaningful git commit messages. A great discussion of this topic is available from Chris Beams in his post How to Write a Git Commit Message. In short, please adhere to the following seven rules:

  1. Use conventional commits as described above
  2. Separate subject from body with a blank line
  3. Limit the subject line to 50 characters
  4. Do not capitalize the subject
  5. Do not end the subject line with a period
  6. Use the imperative mood in the subject line
  7. Wrap the body at 72 characters
  8. Use the body to explain what and why vs. how

An example (derived from Chris' blog post) looks like the following:

feat!(scope): summarize changes in around 50 characters or less

More detailed explanatory text, if necessary. Wrap it to about 72
characters or so. In some contexts, the first line is treated as the
subject of the commit and the rest of the text as the body. The
blank line separating the summary from the body is critical (unless
you omit the body entirely); various tools like `log`, `shortlog`
and `rebase` can get confused if you run the two together.

Explain the problem that this commit is solving. Focus on why you
are making this change as opposed to how (the code explains that).
Are there side effects or other unintuitive consequences of this
change? Here's the place to explain them.

Further paragraphs come after blank lines.

- Bullet points are okay, too

- Typically a hyphen or asterisk is used for the bullet, preceded
  by a single space, with blank lines in between, but conventions
  vary here

If you use an issue tracker, put references to them at the bottom,
like this:

Resolves: #123
See also: #456, #789

Note that all parts except the subject line are optional, i.e. should be used when needed. This does not mean that they should be omitted out of laziness.

Adhere to Quality Standards

To support meeting high-quality standards, we use several metrics and supporting tools. To get a pull request accepted by the maintainers you must make sure that:

  1. The test coverage is above 80% and does not decrease by your code changes. This metric is tracked using the Codecov tool.

  2. You do not introduce code quality issues or increase technical debt. To fulfill these requirements, we use the static code analysis tool Codacy.

  3. Your code and its (transitive) dependencies are free from security vulnerabilities. We rely on Snyk to ensure that this requirement is met.

When submitting a pull request (see next section), the respective scans are triggered automatically, and their results are made available as part of the PR conversation. Make sure that all checks succeed before requesting a review by the maintainers.

Submitting a Pull Request

Cleaning Up Your Work

Prior to submitting your pull request, you might want to do a few things to clean up your branch and make it as simple as possible for the original repo's maintainer to test, accept, and merge your work.

If any commits have been made to the upstream master branch, you should rebase your development branch so that merging it will be a simple fast-forward that won't require any conflict resolution work.

# Fetch upstream master and merge with your repo's master branch
git fetch upstream
git checkout master
git merge upstream/master

# If there were any new commits, rebase your development branch
git checkout newfeature
git rebase master

Now, it may be desirable to squash some of your smaller commits down into a few larger more cohesive commits. You can do this with an interactive rebase:

# Rebase all commits on your development branch
git checkout
git rebase -i master

This will open a text editor where you can specify which commits to squash.

Submitting

Once you've committed and pushed all of your changes to GitHub, go to the page for your fork on GitHub, select your development branch, and click the pull request button. If you need to make any adjustments to your pull request, just push the updates to GitHub. Your pull request will automatically track the changes on your development branch and update.

Accepting and Merging a Pull Request

Take note that unlike the previous sections which were written from the perspective of someone that created a fork and generated a pull request, this section is written from the perspective of the Carbyne Stack maintainers who are handling an incoming pull request. Thus, where the "forker" was referring to the original repository as upstream, we're now looking at it as the owner of that original repository and the standard origin remote.

Checking Out and Testing Pull Requests

Open up the .git/config file and add a new line under [remote "origin"]:

fetch = +refs/pull/*/head:refs/pull/origin/*

Now you can fetch and checkout any pull request so that you can test them:

# Fetch all pull request branches
git fetch origin

# Checkout out a given pull request branch based on its number
git checkout -b 999 pull/origin/999

Keep in mind that these branches will be read only, and you won't be able to push any changes.

Merging a Pull Request

If the contributor adhered to the flow of actions described above, the merge will be a simple fast-forward. You can automatically do the merge by just clicking the button on the pull request page on GitHub.