DEV Community

loading...
Cover image for How to publish an unscoped npm package to Github Package Registry

How to publish an unscoped npm package to Github Package Registry

akinaguda profile image Akinwunmi Aguda ・4 min read

The scenario

I created a project, bootstrapped with tsdx, 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. 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

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

Basically, alehechka 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"
 }
Enter fullscreen mode Exit fullscreen mode
  • 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 }}
Enter fullscreen mode Exit fullscreen mode

That's really all you need.

The changes I made to this 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
Enter fullscreen mode Exit fullscreen mode

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
Enter fullscreen mode Exit fullscreen mode

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
Enter fullscreen mode Exit fullscreen mode

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

Discussion (1)

pic
Editor guide
Collapse
sebmtl profile image
Sebastian Rath

Perfect timing, exactly the insights I was looking for