Configuring a monorepo with Yarn Workspaces using shared components, GitHub actions, and Vercel deployment.
In Remate, We have two apps. One for the web made with Nuxt and the other for desktop made with Electron. So far so good but once the development is quite advanced we started to notice that there were components that had been used in both projects. This also applies to the API endpoints and more stuff.
Last week we decided to merge those two projects into a unique monorepo. That way we could have shared components YAY! But, wait a moment, is that going to work just out of the box? Short answer: No. Long answer: Yes, with Yarn Workspaces.
Yarn Workspaces
According to Yarn docs, Workspaces are
a new way to set up your package architecture that’s available by default starting from Yarn 1.0. It allows you to setup multiple packages in such a way that you only need to run yarn install once to install all of them in a single pass.
Awesome, so by default, we can have two projects and run yarn install
on the root and yarn will take care of manage both project dependencies. Yarn is smart enough to not install twice repeated dependencies among projects.
For enabling workspaces you only have to create a package.json
file in your repository root.
{
"private": true,
"workspaces": ["workspace-a", "workspace-b"]
}
Note that private: true
is required. Workspaces are not meant to be published.
The next step is creating those two folders and putting a single package.json
file inside of each workspace.
{
"name": "workspace-a",
"version": "1.0.0",
"dependencies": {
"cross-env": "5.0.5"
},
"scripts": {
"test": "node test.js"
}
}
{
"name": "workspace-b",
"version": "1.0.0",
"dependencies": {
"cross-env": "5.0.5",
"core-js": "2.6.10"
}
}
With that in place, you can now run yarn install
on the root folder (or in the root of the workspace itself 😯) and all the dependencies will be installed in a unique root node_modules
folder.
If you inspect that folder, you will see the dependencies but also two symlinks to the workspaces 🤯 which makes it extremely easy to share code between projects.
Shared workspace
Knowing that yarn workspaces creates a symlink to each subfolder, as it were a node package, we can use it as a regular npm dependency.
Let’s create a shared workspace with two files on it. The first one will be the package.json
{
"name": "shared",
"version": "1.0.0",
"main": "index.js"
}
And the actual code to import later
module.exports = {
say: thing => {
console.log(thing)
}
}
If you want to use that piece of code on, let’s say, workspace-a
you only have to require it as you always do.
const { say } = require('shared')
say('hi')
Github Actions
Okay, that’s awesome but now, our GitHub actions that previously lived on the root of each project, are not working anymore 🙂
Github expects a .github/workflows
folder on the root of the repo. So let’s create an example workflow
name: Tests
on:
pull_request:
branches: [ main ]
jobs:
Tests:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- name: Install dependencies
run: yarn
- name: Run test suite
run: yarn test
Two things are wrong with that regular
workflow:
- It’s going to be executed on each PR, even when only
workflow-b
files are included. - It’s going to be executed on the root of the repo, not in the
workflow-a
folder
Let’s fix it.
name: Tests
on:
pull_request:
branches: [ main ]
paths:
- 'workspace-a/**'
defaults:
run:
working-directory: workspace-a
jobs:
Tests:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- name: Install dependencies
run: yarn
- name: Run test suite
run: yarn test
- Adding the
on.pull_request.paths
setting we are telling Github that the action should be only executed when the PR contains, at least, one file on that specific path. - Adding the
defaults.run.working-directory
setting we are telling Github that all theruns
commands on that workflow have to be executed on that specific path.
Tests are passing, we are ready to deploy.
Vercel Deployment
We use Vercel at 64 Robots so I am going to focus on this platform for now. Similar steps should be followed on other platforms like Netlify.
The idea here is to tell Vercel that use one specific subfolder of the monorepo for the deployment.
This is quite simple on Vercel. Just pick the subfolder when you are connecting the repository. Period.
The tricky part would be not firing a deployment of the webapp when we merge stuff on the electron app, right?
This can be done on Vercel using Settings > Git > Ignored Build Steps
.
Adding this line git diff HEAD^ HEAD --quiet .
That way, if the diff doesn’t contain any file on the working directory it would cancel the deployment like this
And that's all! You now have a fully configured monorepo 🎉.
Check the code of this example out on Github.
If this post helped you or your company, consider buying me a piece of cake in Github Sponsors. Thank You 🥰.