Skip to main content

Publishing to npm

This guide will help you publish the lambda-toolkit package to npm.

Prerequisites​

  1. All tests passing locally and in CI:

    pnpm test
  2. An npm account with publish rights to the @leighton-digital/* scope.

    npm login
  3. Access to the repo’s NPM_TOKEN secret (must be an Automation token if org requires 2FA).

  4. package.json includes:

    {
    "name": "@leighton-digital/<your-package>",
    "version": "0.0.0-development",
    "private": false,
    "packageManager": "pnpm@9",
    "publishConfig": { "access": "public" }
    }

    version is managed by semantic-release at publish time. For scoped public packages, publishConfig.access must be "public".


Build​

Build locally exactly as CI does:

pnpm install --frozen-lockfile
pnpm build

Your build should output distributable files (e.g., dist/) referenced by package.json fields like main, module, types, exports, etc.


Versioning & Changelog (Automated)​

We use Conventional Commits + semantic-release:

  • feat: … → minor
  • fix: … → patch
  • perf: / refactor: may map to patch (configurable)
  • BREAKING CHANGE: in the commit footer (or feat!: / fix!:) → major
  • Changelog is generated automatically into CHANGELOG.md.

Commit examples

feat(parser): add YAML support
fix(cli): handle empty config without crashing
feat(api)!: remove deprecated v1 endpoints
BREAKING CHANGE: The v1 /users endpoint was removed in favor of /v2/users.

How Publishing Works (GitHub Actions)​

Releases are created automatically on pushes to main that include qualifying Conventional Commits.

1) Workflow file​

The release is automatically performed through the GitHub Release Workflow

2) semantic-release config​

The releaserc.json provides the release configuration.

3) Dependencies​

The following packages have been installed to release the package:

semantic-release @semantic-release/{npm,git,github,changelog,commit-analyzer,release-notes-generator}

Tip: To validate locally without publishing, run:

pnpm dlx semantic-release --dry-run

Triggering a Release​

  • Merge/push Conventional Commit(s) to main.
  • The workflow computes the next version, updates CHANGELOG.md, creates a Git tag & GitHub Release, and publishes to npm.

You don’t manually bump versions or run pnpm publish. CI handles it.


Manual/Emergency Publishing (avoid if possible)​

If CI is unavailable and you must publish:

  1. Ensure working tree is clean and built:

    pnpm install --frozen-lockfile
    pnpm build
  2. Strongly preferred: run a local dry run first:

    pnpm dlx semantic-release --dry-run
  3. If absolutely necessary to publish without semantic-release, you can:

    pnpm version patch|minor|major
    pnpm publish

    Caveat: This bypasses automation and will desync the next SR calculation. Afterward, reset version to 0.0.0-development and push a corrective commit. Use only as a last resort.


Biome & Lefthook Integration​

To ensure code quality and consistency, Biome and Lefthook run locally and in CI.

Local (via Lefthook)​

  • pre-commit

    • biome format --write on staged files
    • biome lint on staged files
  • pre-push

    • pnpm typecheck and pnpm test
  • commit-msg

    • non-empty: run: test -s {1} || (echo "✖ Commit message is empty" && exit 1)
    • commitlint: run: pnpm commitlint --edit {1}

CI (via GitHub Actions)​

Every PR and push to main should run:

pnpm format:check   # biome check --formatter
pnpm lint # biome check --linter
pnpm typecheck
pnpm build
pnpm test

The release job runs these before publishing.


Troubleshooting​

“No release published”​

  • Cause: No Conventional Commits since last release (no feat, fix, or BREAKING CHANGE).
  • Fix: Use proper commit types; push again.

“Semantic-release can’t find previous tag”​

  • Cause: Shallow clone.
  • Fix: Ensure actions/checkout uses fetch-depth: 0.

403 from npm / publish failed​

  • Cause: Token lacks permission or isn’t an Automation token (when 2FA enforced).
  • Fix: Regenerate NPM_TOKEN with Automation type; confirm org permissions.

“Package is private”​

  • Cause: Missing public access for scoped package.
  • Fix: Ensure publishConfig.access: "public" in package.json.

Wrong branch​

  • Cause: Releasing from a non-configured branch.
  • Fix: Update .releaserc.json#branches or use main.

Changelog not updated​

  • Cause: Missing @semantic-release/changelog or @semantic-release/git.
  • Fix: Ensure both plugins are present in config and installed.

Checking Published Packages​

View the package on npm:

npm view @leighton-digital/api-gateway-cloudfront-distribution

Check available versions:

npm view @leighton-digital/api-gateway-cloudfront-distribution versions

Best Practices​

  • Before opening a PR:

    pnpm format:check
    pnpm lint
    pnpm typecheck
    pnpm test
  • Use npm pack locally to preview the publish contents:

    pnpm pack
  • Prefer CI-driven releases. Avoid manual pnpm publish except in emergencies.

  • Adopt Conventional Commits consistently across all changes.