How to open-source a project in 4 steps: Part 1

Ori Neuman
skai engineering blog
8 min readMay 13, 2021

Authors: Ori Neuman, Liat Gofshtein

Let’s start at the end - how did we get here?

Our story begins when we were developing a component at Kenshoo for displaying and managing a hierarchical structure of items - React Tree.

This component was developed in our private repository, but we realized it might be a great open source contribution for the community.

When we started searching for information, we discovered it was scattered across many different pieces. We felt it would be useful to have a centralized guide with all the relevant information in it.

So we decided to create this guide for you!

The two posts in this blog series provide a hands-on guide for open-sourcing a GitHub project, based on our experience with React Tree.

This guide is designed specifically for Node/JavaScript, and is intended for people who are familiar with Yarn and npm.

In this series we’ll cover the open source creation flow step by step, with explanations, examples and relevant links.

In this post, Part 1, we’ll go over the following steps:

  1. Project creation
  2. CI\CD

Part 2 will cover:

  1. Publishing
  2. Documentation

By the time you’re done reading these posts, you too can have an open source project!

Prerequisites

Before diving into open source project creation, make sure you have Node v10+ and Yarn installed, as well as an npm account.

Now let’s get started!

Step 1: Project creation

Start by creating a repository. This is where you’ll be storing your code and letting other developers contribute and provide feedback.

We use GitHub as our source control, and we recommend that you do the same.

Start by installing git from here. Next, create your repository in GitHub by either clicking here (you need to be logged in to GitHub for this link to work), or by clicking New + > New repository.

Keep Public selected so anyone on the internet can see your repository:

If you’ve already created a repository, update your repository’s visibility as described in this guide.

In addition, you’ll want your main branch to be protected, so that no one can push directly to the master branch or any other branch you’ve decided to make protected.

After creating your repository, go to Settings > Branches and add your main branch to the list. Note that Settings is available for admins only, and Branches will become available after your first commit.

Now that you have a repository, you’re ready to create your first commit! In this commit, you’ll be defining the structure of your project.

Project structure

We recommend building your project as a monorepo — one repository that stores many packages.

Why?

  • The ability to add more packages to your repository in the future will always be available.
  • It simplifies versioning. You can decide whether you want to have one version for all the packages, or a specific version for each package.
  • It simplifies both internal and third-party dependency management — all exist in one code base.
  • Switching to a monorepo configuration later on is harder.

In our React Tree, our initial project included a core component (based on @emotion/core) with a simple style.

We later decided to publish material-tree (using Material-UI components), which depends on the core component. Doing this in a monorepo was super-easy — we just added another package.

So today we support two optional themes:

  • Basic theme
  • Material theme

Each theme is stored as a separate package, with its own separate package.json. This allows us to publish each package independently, and our consumers can just download and use the theme they need.

Our monorepo is managed by Lerna. Lerna is a tool that allows us to easily manage multiple packages simultaneously, with features such as linking between packages, publishing many packages at once, updating package versions, etc.

Lerna supports two modes:

  • Fixed/Locked: This mode ties all the package versions together. There is only one version, kept in the root lerna.json file, which represents all the packages. This is the default mode.
    Use this mode if your packages are related or interdependent.
  • Independent: In this mode, package versions are updated independently of each other. Use this mode if your packages are independent of each other.

How to create a monorepo

  1. Install Lerna CLI globally:
npm install --global lerna

2. Under the root folder, run lerna init:

lerna init

This automatically creates the following structure:

Where:

  • The following fields are in the package.json file:

The following fields are in the lerna.json file:

3. Update the fields in lerna.json:

useWorkspaces enables the integration with Yarn Workspaces.
The packages field is no longer needed in lerna.json, because when useWorkspaces is set to true, your packages are taken from the root package.json/workspaces.

4. Add the workspaces field to package.json:

To view an example, see the React Tree’s main package.json and lerna.json.

This is a good checkpoint for pushing your initial commit. Next, we’ll add a new package.

Add your package!

Now that you’ve created the main structure of your repository, the next step is to create the package that will be published. You do this by adding a new package under the packages folder:

From the packages folder, run the lerna create command: lerna create <package_name>.

This creates a new package with a new package.json file.

Here are some main fields that you should consider adding to your package.json:

At this point, your project should be looking like this:

You’ll find your published packages in the folders package_1.

For example:

We have 2 published packages in React Tree:

Each of these packages has a separate package.json. They are published separately, and their versions are independent of each other.

Now that you have a package to publish, you can configure your project to work with CI\CD.

Step 2: CI/CD with GitHub Actions

CI/CD refers to Continuous Integration & Continuous Deployment/Delivery practice. With GitHub Actions, it’s very easy to do!

GitHub Actions are CI/CD pipelines within GitHub. You’re going to use them to automatically publish your packages whenever you merge code to your main branch.

GitHub Actions are free for open source projects, and we found it easy to manage builds within the same platform. However, there are some great alternative pipelines available, such as Travis or CircleCI.

GitHub Actions helps us manage our development lifecycle at Kenshoo with ease. All its jobs and tasks are located in one yaml file.

The file must be located under the path .github/workflow, but you can give it any name you want. It includes a series of jobs being executed one after the other immediately after an event has occurred.

But what is an event? Here are some useful definitions for this and other terms:

  • event: An activity that triggers a workflow, for example, when someone pushes a commit or an issue or pull request is created. The main events we use at Kenshoo are pull_request, push, and release. You can see all defined events here.
  • workflow: One or more jobs, scheduled or triggered by an event.
  • job: A series of steps.
  • step: An isolated, individual task.
  • action: A predefined command provided by GitHub or by the community, or developed by you.

Let’s move on to understanding the file structure.

Yaml file structure:

Here is an example of a yaml file:

Where:

  • The top of the file declares its name.
  • The on section defines the event that triggers this action.

In this example, we declare steps to run when a push is made to our repository, in the branch name master. If we have more than one branch we can specify an array, or we can specify branches-ignore if there’s a branch we want to exclude.

Your next step is to declare the jobs — the series of steps that are going to run!

Inside this block you can have several jobs that can run either in sequence or in parallel (default). Each job has a unique name, which should be clear and descriptive.

For each job, you define the host in which it runs and the steps that it needs to perform.

You do this by declaring the following:

  • runs-on: The host’s virtual environment (mostly OS flavor)
  • steps: Each step with its own unique name, followed by a uses or a run action

​ ​ ​​​ ​ ​​​ ​ ​​​ ​ ​​​ ​ ​​uses: The use of predefined actions created by the community

​ ​ ​​​ ​ ​​​ ​ ​​​ ​ ​​​ ​ ​​run: Shell commands

In our example above, the first two steps use predefined GitHub actions. The third step just runs a shell command. Since some GitHub actions need a parameter, we can use the key with to pass it. As you can see above, the Setup Node.js step requires a node version.

You can copy the below yaml file to your repository to install, build, and release your commit to your main branch:

* To get the tokens under the Create Release step, go to the secrets tab in your GitHub repository’s settings page:

* We’ll cover the auto shipit command in our next post!

Recap

In this post we covered the first two steps for open-sourcing a project:

  1. Project creation
  2. CI\CD

Stay tuned for part 2, in which we’ll cover the final steps in your open-source journey:

  1. Publishing
  2. Documentation

--

--