How to publish an unscoped npm package to Github Package Registry
Tags: npm, github, actions, tsdx
Published: 2021-03-16 13:25:24

## The scenario
I created a project, bootstrapped with [tsdx](https://tsdx.io/#quick-start), and deployed it to npm. I achieved this by logging into npm in my terminal and running
`npm publish` or `npm publish --access public`

## The challenge
Subsequently, I decided to publish to the [github package registry](https://github.com/features/packages). That's when I realized that to publish to the github registry, the name of my package needed to be scoped (unlike with npm where this is optional).

According to [npm](https://docs.npmjs.com/cli/v7/using-npm/scope)

>Scopes are a way of grouping related packages together, and also affect a few things about the way npm treats the package.
>
Each npm user/organization has their own scope, and only you can add packages in your scope. This means you don't have to worry about someone taking your package name ahead of you. Thus it is also a good way to signal official packages for organizations. 

In summary, scoping a package allows two packages with the same name to co-exist, as long as they are scoped differently. Typically, a scoped package would be have the `name` field in your `package.json` be something like: `@someuser/common-package-name` or `@someorganization/common-package-name` but mine was more like `common-package-name`.

Please note that your package does not have to be `@someuser` on github package registry. It could be `@anything` but in my case, it seemed like a good idea to just scope it to my username.

Naturally this would not be a problem if your npm package was already scoped like `@someuser/common-package-name` on npm. In my case it was not

## The Solution
After some googling, I found this [closed issue](https://github.com/formium/tsdx/issues/854)

Basically, [alehechka](https://github.com/formium/tsdx/issues/854#issuecomment-688474830) created a great github action to so solve this problem.

The only issue with it was, if your github username had any uppercase characters in it, it will not successfully deploy to the github package registry.

**Note**
While you could manually do all of this each time you want to deploy, using github actions would be a better and more efficient way to do this.

So, I made some slight modifications to his solution, and with just two steps, you could deploy your package to the github registry and npm.

- Step 1: add this to your package.json

```
"publishConfig": {
   "registry": "https://registry-url"
 }
```


- Step 2: create a workflow file. For instance, **deploy.yml** and paste in:

```
name: npm-publish
on:
  push:
    branches:
      - master # Change this to your default branch
jobs:
  npm-publish:
    name: npm-publish
    runs-on: ubuntu-latest

    steps:
      # Publish to Node Package Manager
      - name: Checkout Repo
        uses: actions/checkout@main

      - name: Setup Node.js (NPM)
        uses: actions/setup-node@master
        with:
          node-version: '12.x'
          registry-url: 'https://registry.npmjs.org'

      - name: Use cached node_modules
        uses: actions/cache@master
        with:
          path: node_modules
          key: nodeModules-${{ hashFiles('**/yarn.lock') }}
          restore-keys: |
            nodeModules-

      - name: Install dependencies
        run: yarn install --frozen-lockfile
        env:
          CI: true

      - name: Update Publish Config
        run: sed -i 's^registry-url^registry.npmjs.org^' package.json

      - name: Publish to NPM
        run: npm publish --access public
        env:
          CI: true
          NODE_AUTH_TOKEN: ${{ secrets.NPM_AUTH_TOKEN }}

  gpr-publish:
    name: gpr-publish
    runs-on: ubuntu-latest

    steps:
      # Publish to GitHub Package Registry
      - name: Checkout Repo
        uses: actions/checkout@main

      - name: Store lowercase actor name
        run: |
          echo 'actor_name<<EOF' >> $GITHUB_ENV
          echo ${{ github.actor }} | tr "A-Z" "a-z" >> $GITHUB_ENV
          echo 'EOF' >> $GITHUB_ENV
    
      - name: Store package name
        run: |
          echo 'package_name<<EOF' >> $GITHUB_ENV
          grep -Po '"name": *\K"[^"]*"' package.json | grep -oP '"\K[^"\047]+(?=["\047])' >> $GITHUB_ENV
          echo 'EOF' >> $GITHUB_ENV

      - name: Setup Node.js (GPR)
        uses: actions/setup-node@master
        with:
          node-version: '12.x'
          registry-url: https://npm.pkg.github.com/
          scope: '${{ env.actor_name }}'

      - name: Use cached node_modules
        uses: actions/cache@master
        with:
          path: node_modules
          key: nodeModules-${{ hashFiles('**/yarn.lock') }}
          restore-keys: |
            nodeModules-

      - name: Install dependencies
        run: yarn install --frozen-lockfile
        env:
          CI: true

      - name: Update Package Name
        run: |
          sed -i 's,"name": "${{ env.package_name }}","name": "@${{ env.actor_name }}/${{ env.package_name }}",' package.json
          cat package.json

      - name: Update Publish Config
        run: |
          sed -i 's^registry-url^npm.pkg.github.com/@${{ env.actor_name }}^' package.json
          cat package.json

      - name: Publish to GitHub Package Registry
        run: npm publish --access public
        env:
          CI: true
          NODE_AUTH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
```

That's really all you need.

The changes I made to [this](https://github.com/formium/tsdx/issues/854#issuecomment-688474830) are:

Adding the following script to convert the username to lowercase

```
 - name: Store lowercase actor name
   run: |
    echo 'actor_name<<EOF' >> $GITHUB_ENV
    echo ${{ github.actor }} | tr "A-Z" "a-z" >> $GITHUB_ENV
    echo 'EOF' >> $GITHUB_ENV
```

I added this to get the package name from the package.json and store in an environment variable.
```    
 - name: Store package name
   run: |
    echo 'package_name<<EOF' >> $GITHUB_ENV
    grep -Po '"name": *\K"[^"]*"' package.json | grep -oP '"\K[^"\047]+(?=["\047])' >> $GITHUB_ENV
    echo 'EOF' >> $GITHUB_ENV
```

I then updated to the github action to change the name of the package in the package.json, to a scoped version.
```
 - name: Update Package Name
   run: |
    sed -i 's,"name": "${{ env.package_name }}","name": "@${{ env.actor_name }}/${{ env.package_name }}",' package.json
    cat package.json
```

and finally, I switched out every use of `${{ github.actor }}` with `@${{ env.actor_name }}` which is the author's name in lowercase.