Crypto Trends

Releasing Utilities Package to GitHub Packages: A Guide

Releasing a closed-source, reusable JavaScript/TypeScript package for internal use across frontend and backend is a common challenge, especially when you want to automate it, keep things modular, and avoid unnecessary leakage of code. Here’s how I do it, step by step, using only what’s needed for a stable, repeatable workflow.

Why GitHub Packages (and Not npmjs.org)?

Most teams reach for npmjs.org by default, but if your utilities are strictly internal – or have some private contract processing logic you’re not ready to open-source – GitHub’s own registry is more than enough:

  • Integrated with your repository: No extra accounts or keys to manage.
  • Scoped access: Control exactly who gets your code.
  • Familiar workflows: Your team’s already on GitHub; why hop away?

I’ve used this for smart contract SDKs referenced by both the frontend app and the NestJS API.

Directory Structure

I keep only my distributable code in /package, separate from internal scripts/docs, to avoid accidentally leaking dev files.

|-- .github/
|-- src/
|-- package/           # <--- Only your published files live here
    |-- package.json
    |-- dist/
    |-- index.js
|-- ...

Pro tip: npm publish runs only in /package, not at the repo root.

Manual Releases Triggered From GitHub Releases

Every package update is explicitly tagged as a release in GitHub’s UI, which helps prevent accidental releases of incomplete work.

github release package pagegithub release package page

Action Workflow File

Below is the full workflow that gets the job done.

name: Publish package on GitHub Packages

on:
  release:
    types: [created]

jobs:
  publish:
    runs-on: ubuntu-latest
    permissions:
      contents: read
      packages: write

    steps:
      - uses: actions/checkout@v3

      - uses: actions/setup-node@v3
        with:
          node-version: 18
          registry-url: "https://npm.pkg.github.com"
          scope: "@your-user-name"
          always-auth: true

      - name: Install dependencies
        run: npm ci

      - name: Build package
        run: npm run package

      - name: Install package dependencies
        working-directory: ./package
        run: npm ci

      - name: Publish package
        working-directory: ./package
        run: npm publish
        env:
          NODE_AUTH_TOKEN: ${{ secrets.GITHUB_TOKEN }}

Place it in github/workflows/publish.yml

Key Parts

  • Scopes, Not Monorepos: No workspaces, no publishing the entire repo.
  • No Source Leakage: Only files in /package are seen by consumers—no accidental pushes of TS, docs, or git history.
  • Manual Trigger: The process kicks off only when you create a GitHub Release, not on every push or PR.

Real-World Example

Let’s say you update a Smart Contracts ABI in /src, then run your internal build (maybe via a simple "package" script) to output to /package/dist.

Only that transpiled, dependency-free version ships.

Your API team can safely pull it via:

npm install @user/package-name --registry=https://npm.pkg.github.com

From both backend and frontend, with no npmjs exposure.

Related Articles

Leave a Reply

Your email address will not be published. Required fields are marked *

Back to top button