How to publish packages to the GitHub Package Registry
Tags: github, registry, javascript, npm
Published: 2019-09-14 02:00:36

If you were lucky enough to get into this week's batch of beta invitations for the [GitHub Package Registry](https://github.com/features/package-registry), you might be wondering how to get started publishing your npm package there.

The [official docs](https://help.github.com/en/articles/configuring-npm-for-use-with-github-package-registry) do an OK job at explaining the basics, but aren't as straight forward as you might expect. And at some points, they are downright contradicting themselves. It is a beta, after all, and I'm sure GitHub will improve the docs as this new feature is moving closer to launch.

In this article, I'm going to explain step by step how you can publish your first package on the GitHub Package Registry (I'll call it GPR from now on.) Then, I'll give you a quick explanation how your users can install the package from the GPR, while still being able to install packages from the "normal" npmjs.org registry alongside it.

Keep in mind that the GPR is still in beta, so implementation details might change. Hopefully, I'll remember to keep this article updated, but if in doubt, better check against the official docs. And if you notice anything out-of-date, please let me know in the comments!

## Terminology

The terms used for GitHub packages are mostly the same as for the npm registry. If you're already familiar with them, you can skip straight to the [next section](#authorizing-npm)!

<dl>
  <dt>Registry</dt>
  <dd>
    To resolve packages by name and version, npm (the CLI tool) talks to a registry website. The most popular registry is hosted by npm (the company) on <a href="https://registry.npmjs.org">registry.npmjs.org</a>.
  </dd>

  <dt>GitHub Package Registry (GPR)</dt>
  <dd>
    GitHub recently announced their own registry service, which is available on <a href="https://npm.pkg.github.com">npm.pkg.github.com</a>. This service will be tightly coupled to their other offerings, so you can expect your package to integrate well with your project's home on GitHub as well as GitHub Actions.
  </dd>

  <dt>Scope</dt>
  <dd>
    Scopes are a way of grouping related packages together. Both on the npm registry and the GPR, each user and organization has their own scope. While using a scope is optional on the npm registry, every package published to the GPR must be scoped.
  </dd>
</dl>

## Authorizing npm

These steps only have to be taken once per machine and GitHub user/org. You won't have to go through them again unless you want to publish from a new device or to a new scope.

1.  Create a new [personal access token](https://github.com/settings/tokens) for the account you want to publish the package to. It should have access to the `read:packages` and `write:packages` scopes.
    If the repository you want to publish is private, the token additionally needs the `repo` permission.

2.  Create or edit the `.npmrc` file in your home directory (`~/.npmrc`) and add the following line, replacing `TOKEN` with the personal access token created above:

    ```
    //npm.pkg.github.com/:_authToken=TOKEN
    ```

## Setting up your package

Each package has to be explicitly told to publish to the GPR. Otherwise, npm will fall back to the npm registry.

1.  Create or edit the `.npmrc` file in the root of your project. Add the following line and replace `OWNER` with the username of the GitHub user or organization you want to publish to (i.e. the scope):

    ```
    @OWNER:registry=https://npm.pkg.github.com
    ```

    (The `@` is part of the config syntax and should not be replaced.)

2.  Make sure your `package.json` is properly configured:

    The package name should be prefixed with the scope. `PACKAGE` here is the actual name of your package. (Again, keep the `@`):

    ```json
    {
      "name": "@OWNER/PACKAGE"
    }
    ```

    The `repository` field should point to the GitHub repository you're publishing:

    ```json
    {
      "repository": "https://github.com/OWNER/REPO"
    }
    ```

    It's also possible to publish multiple packages per repo, by changing only the package name but keep pointing the `repository` field to the same repo.

## Publish!

All that's left to do is to create a GitHub repository for the package if it doesn't have one already, pushing the new changes, and running `npm publish`! (As far as I can tell, Yarn doesn't currently support publishing to registries other than npm.) If everything went right, you should see the first version of your package being published on https://github.com/OWNER/REPO/packages (check out [one of my own packages](https://github.com/jgierer12/hooks/packages) for an example.)

## Consuming a GPR package

Now that you published a package to the GPR, you or someone else might want to use it as a dependency in another project. To do so, you need to again add the relevant GPR URL to the project's `.npmrc`:

```
@OWNER:registry=https://npm.pkg.github.com
```

An important thing to note is that you need to repeat this process for every different GPR scope you want to use. So if you want to install the packages `@facebook/react`, `@facebook/react-dom`, `@webpack/webpack`, and `@babel/core`, the `.npmrc` should look like this:


```
@facebook:registry=https://npm.pkg.github.com
@webpack:registry=https://npm.pkg.github.com
@babel:registry=https://npm.pkg.github.com
```

These are just for demonstration purposes, at the time of writing none of these organizations have published any packages to the GPR yet. 

What's nice though is that without any further config, you can install any package from the npm registry alongside those. So if you were to `npm install lodash` with the above config, it would still be able to resolve lodash from npm's registry. Just be careful if the package you want to install is scoped under the same user as a GPR package &mdash; you can't install `@foo/bar` from the npm registry and `@foo/baz` from the GPR.

## Cross-publishing

If you'd like to start publishing to the GPR, but don't want to force users into switching registries, it's very easy to setup a 'hybrid' approach that publishes to both the GPR and the npm registry. Just setup a `postpublish` script in `package.json` like so:

```json
{
  "scripts": {
    "postpublish": "npm publish --ignore-scripts --@OWNER:registry='https://registry.npmjs.org'"
  }
}
```

Note the `--ignore-scripts` flag which prevents the `postpublish` script to call itself again.

## Closing thoughts

As you can see, it isn't exactly trivial to publish and consume packages from/to the GPR. Both GitHub and the npm/yarn developers need to put in some work to make this experience smoother, especially for the package consumers. That being said, it's great having a serious competitor to the npm registry, and the integrations into the GitHub interface and Actions are already looking really useful. I'll certainly cross-publish my future packages to GitHub, and install from there whenever possible.

<small><em>[Cover](https://unsplash.com/photos/fyaTq-fIlro) by [chuttersnap](https://unsplash.com/@chuttersnap) on [Unsplash](https://unsplash.com)</em></small>