DEV Community

Ryosuke Iwanaga
Ryosuke Iwanaga

Posted on

Using GitHub Releases for patching Monorepo npm package

When you find a bug in a npm package, you'll send a Pull Request (PR) to the original package. It would be great if your PR are merged quickly and released in the new version in npm registry. However, sometime it takes time but you want to use your bug fix right away. How can we do that?

It is simple if the package is the root of GitHub repository i.e. not Monorepo because package.json supports GitHub URL in the dependencies. Since you have already sumitted the PR, you should have your own fork with the bug fix. Therefore, you can simply specify your branch in your package.json and npm command handles the rest including running prepare.

Things are not so easy if the package is managed by a repository having multiple packages in it a.k.a. Monorepo. Unfortunately, there is no universal support by package.json because each monorepo is managed differently. Therefore, you need to build the package on your forked branch and use it from your application.

There are a bunch of solutions here, but I'd like to introduce a simple solution using GitHub Releases. In this solution, you just need a few manual steps:

  1. Build the monorepo e.g. lerna run build
  2. Create .tgz package for your patched version e.g. cd packages/package/dis && npm pack
  3. Publish GitHub Releases i.e. gh release create ... package.tgz
  4. Use the artifact URL in your application's dependencies

You need to run 1-3 whenever you update the branch, and probably need to reinstall packages in your application. You can automate these steps by GitHub Actions but you need to add action.yaml to your branch which is unrelated to your PR. Thus, I think this solution will work better with a repository getting less frequent update in upstream.

Steps

Let me give you a concrete example I did. I needed this fix in my application right away. The repository is monorepo and managed by lerna. I've already checked the forked repository out and had a branch named fix-classname on my laptop.

Build the monorepo

Simply built the monorepo:

➜  use-twind-with git:(fix-classname) npx lerna run build
Enter fullscreen mode Exit fullscreen mode

This created dist directory under the package and it contains JavaScript files transpiled from TypeScript:

➜  use-twind-with git:(fix-classname) cd packages/preact
➜  preact git:(fix-classname) ls ./dist
CHANGELOG.md         package.json         preact.d.ts          preact.esnext.js.map preact.mjs
LICENSE              preact.cjs           preact.d.ts.map      preact.js            preact.umd.js
README.md            preact.cjs.map       preact.esnext.js     preact.js.map        preact.umd.js.map
Enter fullscreen mode Exit fullscreen mode

Create .tgz package

I just needed dist directory only, so I ran npm pack there:

➜  preact git:(fix-classname) cd dist
➜  dist git:(fix-classname) npm pack
npm notice
npm notice 📦  @twind/preact@1.0.5
npm notice === Tarball Contents ===
npm notice 1.4kB CHANGELOG.md
npm notice 1.1kB LICENSE
npm notice 2.5kB README.md
npm notice 1.3kB package.json
npm notice 3.9kB preact.cjs
npm notice 3.7kB preact.cjs.map
npm notice 751B  preact.d.ts
npm notice 92B   preact.d.ts.map
npm notice 1.6kB preact.esnext.js
npm notice 3.4kB preact.esnext.js.map
npm notice 1.7kB preact.js
npm notice 3.4kB preact.js.map
npm notice 108B  preact.mjs
npm notice 1.5kB preact.umd.js
npm notice 3.3kB preact.umd.js.map
npm notice === Tarball Details ===
npm notice name:          @twind/preact
npm notice version:       1.0.5
npm notice filename:      @twind/preact-1.0.5.tgz
npm notice package size:  6.9 kB
npm notice unpacked size: 29.8 kB
npm notice shasum:        b258372501fb0c0f392274e237f230575c56342f
npm notice integrity:     sha512-y+zDKMLhQNSUu[...]UpgzvPXvPto7w==
npm notice total files:   15
npm notice
twind-preact-1.0.5.tgz
Enter fullscreen mode Exit fullscreen mode

Now, I got twind-preaxt-1.0.5.tgz file.

Note: If you run npm pack in the package root, the .tgz file points TypeScript file as main and won't work with npm install.

Publish GitHub Releases

To release, I needed a Git tag. I used a convention like <original-tag>-<branch-name>. In this case, original tag was @twind/preact@1.0.5 and the branch name was fix-classname. I needed to create and push the tag first. Then, published the tag with the packed .tgz file:

➜  dist git:(fix-classname) git tag @twind/preact@1.0.5-fix-classname
➜  dist git:(fix-classname) git push --tags
➜  dist git:(fix-classname) gh release create \
    --target fix-classname \
    --title @twind/preact@1.0.5-fix-classname \
    --notes '' \
    @twind/preact@1.0.5-fix-classname twind-preact-1.0.5.tgz
Enter fullscreen mode Exit fullscreen mode

This is the actual release page: https://github.com/riywo/use-twind-with/releases/tag/%40twind%2Fpreact%401.0.5-fix-classname

Note: gh command can be installed by https://github.com/cli/cli#installation

Use the artifact URL

Now, I got the .tgz public URL https://github.com/riywo/use-twind-with/releases/download/%40twind%2Fpreact%401.0.5-fix-classname/twind-preact-1.0.5.tgz. The last step was simply adding this to my application's package.json:

  "dependencies": {
    "@twind/preact": "https://github.com/riywo/use-twind-with/releases/download/%40twind%2Fpreact%401.0.5-fix-classname/twind-preact-1.0.5.tgz",
    ...
  },
Enter fullscreen mode Exit fullscreen mode

Then, ran npm install. That's it! I compared the files under node_modules/@twind/preact and they were at the patched version:

➜  app git:(main)ls node_modules/@twind/preact
CHANGELOG.md         package.json         preact.d.ts          preact.esnext.js.map preact.mjs
LICENSE              preact.cjs           preact.d.ts.map      preact.js            preact.umd.js
README.md            preact.cjs.map       preact.esnext.js     preact.js.map        preact.umd.js.map
Enter fullscreen mode Exit fullscreen mode

Alternatives

You can do the similar thing without the public artifact, for example with Git Submodule/Subtree + custom npm scripts. This would be great because you don't have to manage GitHub Releases artifacts. However, the build process of your application might become complicated and take more time.

Conclusion

I explained my solution to use a forked branch of monorepo packages via GitHub Releases. It requires a few manual steps but is relatively simple and easy to maintain.

Top comments (0)