In this post, I will walk you through how to use Lerna to manage, and publish, two packages under the same monorepo. Publishing will be done to my private GitHub repository under the GitHub packages registry.
I decided to keep it as simple as possible, Lerna-only. No yarn workspaces to be found here.
Intro & Motivation For Using Lerna
Using a monolith, you have a single code base.
It is usually quite easy to share code between the different parts of the monolith, just import from the relevant file.
When it comes to microservices, however, by definition – you would have more than one microservice.
Most likely, you would have shared logic between the microservices, whether it is for everyday authentication purposes, data access, etc.
Then, one might (rightfully) suggest – let’s use a package. Where do you store that package? Yet another repo.
So far so good, but what happens when you have 35 shared packages between 18 different microservices?
You’d agree that it can be quite a hassle to manage all of these repos.
That is the part where Lerna comes in.
A tool that enables us to manage (and publish) as many npm packages as we want in a single repository.
1. Github Repository Creation
Create a new private github repository (I called mine learna but call it as you see fit).
2. Install Lerna & Setup the Project Locally
In order to set up Lerna in our project, we first need to install it globally, create a git repository locally and run lerna init:
npm install --global lerna
git init learna && cd learna
lerna init
Note: there are two modes for initializing the Lerna repo independent and fixed. We’re going to use the default one for simplicity reasons. Essentially what it means is all version numbers are tied together and managed in top-level lerna.json.
Read more about it here: https://github.com/lerna/lerna#how-it-works
Now let’s link this to our GitHub repository (replace names accordingly):
git remote add origin git@github.com:aspectom/learna.git
3. Create Lerna managed packages
Create two packages, hello-world and aloha-world (with the default options):
lerna create hello-world
lerna create aloha-world
lerna create
is Lerna’s way to help us create packages managed by a Lerna initialized repo.
Inside both of the packages, modify the corresponding js files to have them greet as we want them to:
aloha-world.js
'use strict';
module.exports = alohaWorld;
function alohaWorld() {
console.log('Aloha World');
}
hello-world.js
'use strict';
module.exports = helloWorld;
function helloWorld() {
console.log('Hello World');
}
Now we have to make a modification in our package.json to contain the GitHub username of our account / organization:
{
"name": "@aspectom/aloha-world",
"version": "0.0.0",
"description": "> TODO: description",
"author": "Tom Z <tom@aspecto.io>",
"homepage": "",
"license": "ISC",
"main": "lib/aloha-world.js",
"directories": {
"lib": "lib",
"test": "__tests__"
},
"files": [
"lib"
],
"repository": {
"type": "git",
"url": "git@github.com:aspectom/learna.git"
},
"scripts": {
"test": "echo \"Error: run tests from root\" && exit 1"
}
}
Do this for both aloha-world and hello-world, and make sure to replace my GitHub username with your own.
PS: While we’re making managing multiple repos easier, here’s how you can make running multiple microservices locally feels like a walk in the park. It’s a simple, easy-to-use hack we, at Aspecto, came up with to make this process less messy – It’s called the local router.
At this point you should have a directory structure that looks like this:
At the root of the repository, add an empty LICENSE.md.
This will be necessary later to avoid this error when publishing:
lerna WARN ENOLICENSE Packages aloha-world and hello-world are missing a license.
lerna WARN ENOLICENSE One way to fix this is to add a LICENSE.md file to the root of this repository.
lerna WARN ENOLICENSE See https://choosealicense.com for additional guidance.
Let’s make our initial commit to GitHub.
git add .
git commit -m 'Initial commit'
git push -u origin master
4. Generating a GitHub Personal Access Token
First, create a GitHub personal access token to publish and read packages:
- Go to https://github.com/settings/profile,
- Click on developer settings
- Click on personal access token
- Select write & read packages, which should also mark the repo automatically
- Add a note so that you remember what it’s about and click on generate the token.
Now, go to your .npmrc file and add the following lines (can be local .npmrc in each repo or global ~/.npmrc, but beware – better to not commit this file):
//npm.pkg.github.com/:_authToken=TOKEN
@aspectom:registry=https://npm.pkg.github.com/
Do not forget to replace TOKEN with the token you have just created, and aspectom with your own GitHub account.
5. Publishing The Packages to GPR
Now let’s publish these packages to the GitHub package registry so that we can use them in a different project:
lerna publish --registry=https://npm.pkg.github.com/
If you had the following error, you probably omitted the registry part from lerna publish:
? Are you sure you want to publish these packages? Yes
lerna info execute Skipping releases
lerna info git Pushing tags...
Enter passphrase for key '/Users/tom/.ssh/aspecto_id_rsa':
lerna info publish Publishing packages to npm...
lerna info Verifying npm credentials
lerna http fetch GET 401 https://registry.npmjs.org/-/npm/v1/user 1370ms
401 Unauthorized - GET https://registry.npmjs.org/-/npm/v1/user
Since it tries to go to npm registry instead of GitHub packages.
And if you had this error:
lerna info publish Publishing packages to npm...
lerna notice Skipping all user and access validation due to third-party registry
lerna notice Make sure you're authenticated properly ¯\_(ツ)_/¯
lerna http fetch PUT 404 https://npm.pkg.github.com/hello-world 694ms
lerna ERR! E404 404 Not Found - PUT https://npm.pkg.github.com/hello-world
You probably forgot to use @YOUR_GITHUB/package-name in one of your package.json files under the “packages” folder.
In my case – it was the hello-world package.
After resolving issues (if any) you should receive a success message, and looking at the repository you can see you have 2 packages:
Any time you want to publish, you have to make a change and commit it otherwise lerna will say that there’s no change.
You can make the change or force Lerna to publish by adding --force-publish
to the lerna publish
command, like this:
lerna publish --registry=https://npm.pkg.github.com/ --force-publish
6. Using The Packages in a Different Project
First, create a project to consume the aloha-world and hello-world packages:
mkdir use-lerna-repo
cd use-lerna-repo/
yarn init
Assuming you’ve used global .npmrc, no further steps needed to consume the packages with yarn or npm install.
If you used local npmrc in your lerna repo, copy it to the use-lerna-repo root folder.
yarn add @aspectom/aloha-world
yarn add @aspectom/hello-world
Create an index.js file:
const helloWorld = require('@aspectom/hello-world');
const alohaWorld = require('@aspectom/aloha-world');
helloWorld();
alohaWorld();
Package.json for this project:
{
"name": "use-lerna-repo",
"version": "1.0.0",
"main": "index.js",
"license": "MIT",
"scripts": {
"start": "node index.js"
},
"dependencies": {
"@aspectom/aloha-world": "^0.0.4",
"@aspectom/hello-world": "^0.0.4"
}
}
Then, run node index.js and you should get the following output:
$ node index.js
Hello World
Aloha World
And voila! We have just finished creating, publishing, and consuming our lerna-managed packages in the one monorepo.
Good luck, we at Aspecto wish you years of happy packaging and a lot of downloads!
Top comments (2)
I published code to npm but it does not push README.md. So how can I add readme.md. I also asked this question on stackoverflow. stackoverflow.com/questions/691743...
i have been tryiung to figure out how to solve this so i can finish up the package.
Hard to say your exact situation since I don't have all the context, but it seems like you put the readme in the wrong place (from your stack overflow question).
README.md file needs to be at the package folder, not at the root folder. cause every package should have its own readme.
See how it's located in the screenshot above where it shows the folders.
Hope that helps, best of luck!