DEV Community

Paige Niedringhaus
Paige Niedringhaus

Posted on • Originally published at paigeniedringhaus.com on

Use GitHub Actions to Automatically Publish a Repo Subfolder as an npm Library

Person diagramming out a website workflow

Introduction

I've said it before, and I'll say it again, working as a software engineer for a startup is fun and challenging because of the sheer number of different things I get to do on a regular basis.

I work for an Internet of Things startup called Blues which specializes in getting IoT data from devices in the real world into the cloud via cellular. To help show our customers all the possibilities of what they can do with our hardware (Notecards) and our cloud (Notehub), a group of my coworkers and I have been building lots of JavaScript-based web apps to monitor and display IoT data for a variety of different use cases.

A little more about Notecards and Notehub.io

Notecards are low-power, 30mm x 35mm, prepaid cellular-connected devices designed to integrate with any IoT device, and Notecards know, out of the box, how to connect with the Notehub cloud to send as well as receive data from it.

Notehub.io is a cloud-based service for securely connecting to Notecard devices and storing data from them before sending it on to your cloud application of choice.

It quickly became apparent that rewriting the same HTTP calls in multiple projects to fetch data from the native Notehub API endpoints wasn’t easy to reuse or keep up to date as the API continues to grow and evolve.

So we created our own open source, JavaScript-based library for the Notehub API and published it on npm for anyone to use for free. Notehub JS is designed to get you connected to the Notehub API quickly, and allow you to access all of the API routes relevant to interacting with Notehub in a JavaScript-friendly way.

Figuring out how to build an open source library like this and publish it to npm was a first for me, and I learned a lot of interesting things along the way that I'd like to share with you all over the course of a few blog posts in the hope that it might help you if you decide to undertake a similar effort.

In this blog, I'll show you how to set up a GitHub Actions workflow to automatically publish a new release of a GitHub repo to npm - no muss, no fuss, no manual input required.


Notehub JS

Ok, before we get to the GitHub Actions workflow, I want to give you a little background on the Notehub JS project because it's a bit unusual.

As I mentioned above, Notehub JS is a JavaScript-based library for interacting with the native Notehub API, and it's actually generated from the Notehub project's own openapi.yaml file, which follows the OpenAPI specification standards.

OpenAPI Specification

The OpenAPI Specification (OAS) defines a standard, language-agnostic interface to HTTP APIs which allows both humans and computers to discover and understand the capabilities of the service without access to source code, documentation, or through network traffic inspection. When properly defined, a consumer can understand and interact with the remote service with a minimal amount of implementation logic.

An OpenAPI definition can then be used by documentation generation tools to display the API, code generation tools to generate servers and clients in various programming languages, testing tools, and many other use cases.

OpenAPI Generator CLI

Because the Notehub API has an openapi.yaml file that is updated regularly, I was able to use the OpenAPI Generator CLI to automatically generate a JavaScript-based library to interact with the Notehub API in just a few commands from a terminal.

The catch is, when the new folder is generated which has all the API endpoints and models, the documents, and the dist/ folder that packages everything up for consumption as an npm module, it's stored as a subfolder inside of the main Notehub JS repo.

The openapi.yaml file lives at the root level of the project along with its config.json file, a package.json file, and a few other other bits and bobs (license, contribution guidelines, code of conduct, etc.), but the real meat of the library lives inside of the src/ subfolder.

Here's a simplified view of the Notehub JS repo's folder structure:

root/
├── .github/ 
| ├── workflows/
| | ├── publish-npm.yml 
├── src/ <- this is the folder generated by the OpenAPI Generator CLI
| ├── dist/
| ├── docs/
| ├── src/ 
| | ├── api/ 
| | ├── model/
| | ├── index.js 
| openapi.yaml
| config.json
| package.json
Enter fullscreen mode Exit fullscreen mode

From the diagram above you can see the openapi.yaml file that this library is generated from lives at the root of the repo, and the src/ folder is what actually holds all the Notehub API JavaScript code that powers the Notehub JS library.

This src/ folder is what needs to be published to npm - not the root of the repo as is the case with most projects in GitHub. But not to worry, publishing just this subfolder can be done, and better yet, it can be automated.

Configure the root package.json to point to the subfolder where the Notehub JS library code lives

So now that I've covered why I only need to publish the subfolder of the Notehub JS repo to npm, let's get on to the how.

First thing: add a few extra configuration details to the package.json at the root of the project.

Update the main field to point to the subfolder's index.js file

According to the npm docs, the main field points to the primary entry point of an app, and when it's not specifically defined it defaults to index.js at the root of the project.

Since the src/dist/ folder is where the index.js file lives for Notehub JS library code that should be uploaded to npm, main needs to be updated to point to it instead.

Now the package.json's main field should read:

"main": "./src/dist/index.js"
Enter fullscreen mode Exit fullscreen mode

Update repository to include a directory field

Next, the repository field in a package.json specifies where the code lives, and it's generally helpful for people who want to contribute to the project.

It has another benefit though: if the package.json for the npm package is not in the root directory (like for us, where the auto-generated src/ folder has its own package.json), you can specify the directory in which that file lives.

Here's what the repository field should look like:

"repository": {
  "type": "git",
  "url": "https://github.com/blues/notehub-js.git",
  "directory": "src"
}
Enter fullscreen mode Exit fullscreen mode

What this translates to on the npm package page is a handy link that points back to root of the GitHub folder (see image below).

Notehub JS npm page highlighting repository link on the page

Add the publishConfig field to specify directory

Typically, the publishConfig field is used to set config values that will be used at publish time like tags, registries, or access.

Another field that can be added here is directory, which customizes the published subdirectory relative to the current package.json. This ensures npm knows the package.json it needs to read from (and display) in the npm package page is inside of the auto-generated src/ folder, not at the root of the project.

Add the following publishConfig code to the package.json like so:

"publishConfig": {
  "registry": "https://registry.npmjs.org",
  "directory": "./src"
}
Enter fullscreen mode Exit fullscreen mode

In the end, the whole package.json at the root of the Notehub JS repo looks like the code snippet below.

Note: I've omitted parts of the package.json for clarity, but if you'd like to see the whole file, you can click the file name below. It's linked to the actual file in the repo in GitHub.

package.json

{
  "name": "notehub-js",
  "description": "JavaScript library for accessing the Blues Wireless Notehub API endpoints",
  "main": "./src/dist/index.js",
  "repository": {
    "type": "git",
    "url": "https://github.com/blues/notehub-js.git",
    "directory": "src"
  },
  "publishConfig": {
    "registry": "https://registry.npmjs.org",
    "directory": "./src"
  }
}
Enter fullscreen mode Exit fullscreen mode

Great, the changes to the package.json to publish just the src/ folder to npm are complete, let's move on to the GitHub Actions workflow to make publishing a new version of the Notehub JS library to npm as automated as possible.

Create a GitHub Actions workflow to publish to npm

Need a refresher on GitHub Actions?

If you want a quick primer on what GitHub Actions are, I recommend you check out a previous article I wrote about them here.

I decided that for my use case, creating a new release made the most sense to trigger a GitHub Actions workflow to publish the Notehub JS src/ subfolder to npm.

GitHub specifically defines a release as a deployable software iteration that you can package and make available for a wider audience to download and use, which is exactly what I want.

Once I had settled on this as the trigger for my GitHub Actions workflow, it was a pretty straightforward set of steps to publish to npm.

Here's what the finished publish-npm.yml file will look like inside of the ./github/workflows/ folder - I'll break it all down afterwards.

publish-npm.yml

name: Publish notehub-js to npm

on:
  release:
    types: [created]

jobs:
  npm:
    runs-on: ubuntu-latest
    defaults:
      run:
        working-directory: ./src
    steps:
      - name: Checkout code
        uses: actions/checkout@v3

      - name: Setup .npmrc file to publish to npm
        uses: actions/setup-node@v3
        with:
          node-version: "16.x"
          registry-url: "https://registry.npmjs.org"

      - name: Install dependencies
        run: npm ci

      - name: Publish to npm
        run: npm publish
        env:
          NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }}
Enter fullscreen mode Exit fullscreen mode

Each workflow file needs a name, so I chose a descriptive one: Publish notehub-js to npm.

Next, as I already explained above, this workflow is triggered whenever a new release is created in GitHub, which is where

on:
  release: 
    types: [created]
Enter fullscreen mode Exit fullscreen mode

comes into play.

  • on is how a workflow is triggered.
  • release is the event that triggers the workflow.
  • types: [created] is the activity type for a release event that triggers the workflow. This gives us more fine-grained control of when the workflow should run.

Then the jobs section runs inside of the workflow. This particular script only has one job, npm, but if there's multiple jobs, they'll run sequentially unless otherwise specified.

The npm job defines that it runs on the latest version of Ubuntu in runs-on.

defaults.run is an important part of the script because it's where I specify that the working directory for all the following steps is the src/ folder (because I don't need any of the files from the root of the repo, just what's inside the src/ folder, I can define this at the job level).

Finally, I get to the steps.

The steps are as follows:

  1. Checkout the code so the workflow can access it using the GitHub Action actions/checkout@v3.
  2. Download a Node.js version to build the package before publishing it to npm using the GitHub Action actions/setup-node@v3 (specify the node-version and registry-url for npm here).
  3. Install the project's dependencies stored in the src/ folder's package.json file using npm ci.
  4. Publish to npm by running the command npm publish and supplying it with an NPM_TOKEN (generated in npm) to verify this workflow has permissions to publish to the specified npm package registry.

And there you have it: each time a new release is created in the Notehub JS repo, this GitHub Actions workflow will run and deploy the updated code to npm.


Conclusion

Working as an engineer for a startup company is cool because no two days are alike for me. One day I'm building web apps and updating documentation on our developer experience site, the next I'm reading up on best practices for npm packages and how to automate tasks with GitHub Actions.

Venturing into the realm of open source software and publishing my first JavaScript package to npm was quite a learning experience for me, but I'm so grateful I had the chance to push myself into this space and create something useful for my team (and hopefully our users as well!).

One thing I knew I wanted, right from the start, was to automate the tasks I knew I'd be repeating fairly regularly over time: namely, regenerating the JavaScript library as the Notehub API continued to evolve, and redeploying a new version of the library to npm with the latest updates.

With some extra configurations in the Notehub JS repo's package.json, I was able to focus in on the auto-generated subfolder that contains the useful JavaScript-based code for easily interacting with the Notehub API, and with a few GitHub Actions inside of a workflow, I was able to build the code for the package and publish it to npm with the click of a button. Not too bad for an end result.

Check back in a few weeks — I’ll be writing more about the useful things I learned while building this project in addition to other topics on JavaScript, React, IoT, or something else related to web development.

If you’d like to make sure you never miss an article I write, sign up for my newsletter here: https://paigeniedringhaus.substack.com

Thanks for reading. I hope learning how to target a specific folder inside of a project and automating deployments to npm through GitHub Actions workflows come in handy for you in the future. Enjoy!


References & Further Resources

Top comments (0)