# Build and release (/docs/operations/deployment/releases)

> How a Better IAM release is checked, packed, smoke-tested as installed tarballs, versioned in lockstep, and prepared for publication.



Better IAM ships as a set of workspace packages under `packages/*` that are always released together at one
version. A release proves three things before anything leaves the repository: the source passes every check, the
packed tarballs install and work in a fresh consumer, and their TypeScript exports resolve. This page is for
maintainers cutting a release from the monorepo.

## Release procedure [#release-procedure]

      ### Set the version [#set-the-version]

    ```sh
    node scripts/release-version.mjs 1.4.0
    ```

    `release-version.mjs` writes the version (`MAJOR.MINOR.PATCH`, optionally with a pre-release suffix) into every
    package's `package.json`. Then update the changelog and reinstall to refresh the lockfile.
  
      ### Run the checks [#run-the-checks]

    ```sh
    pnpm check
    ```

    `pnpm check` runs the type checks (the workspace build plus the client, application, Next.js, NestJS, SvelteKit,
    middleware, and React Router type suites and the console), the test suite, and package validation.
    Package validation requires every package to be publishable, Apache-2.0 licensed, public on npm, and at the same version,
    checks that every export target exists, and runs `publint`.
  
      ### Pack [#pack]

    ```sh
    pnpm pack:all
    ```

    Every package is packed into `artifacts/`. Nothing is published.
  
      ### Smoke-test the packed packages [#smoke-test-the-packed-packages]

    ```sh
    node scripts/packed-smoke.mjs   # or: pnpm test:packed
    pnpm check:packed-types
    ```

    The packed smoke test installs all tarballs into a fresh consumer, with the native builds for `argon2` and
    `better-sqlite3` allowed. It then verifies exports, native dependencies, migrations, and bootstrap: it imports the
    umbrella package, runs a SQLite migration, bootstraps a root, and runs `better-iam --help`. The packed type check
    runs `attw` (Are the Types Wrong) on each tarball with the ESM-only profile, so exports resolve under both ESM and
    bundler resolution.
  
## PostgreSQL before a release [#postgresql-before-a-release]

The default suite skips PostgreSQL-only cases. Run them against an isolated database before tagging:

```sh
BETTER_IAM_POSTGRES_URL=postgres://localhost/better_iam_test pnpm test:postgres
```

See [Database operations](/docs/operations/deployment/database#postgresql-integration-checks) for how the suite
isolates its cases.

## Publication [#publication]

All packages are Apache-2.0 licensed and publish publicly to npm. Pushing a new version to `main` publishes it:

```sh
node scripts/release-version.mjs 1.4.0
# update CHANGELOG.md, pnpm install, pnpm check, commit
git push origin main
```

The `publish` job in `.github/workflows/ci.yml` waits for every test job and the PostgreSQL job. When the package
version is not on npm yet, it runs `pnpm publish -r --provenance`, so every tarball carries an npm provenance
attestation, then tags `v1.4.0` and creates a GitHub release. Pushes that keep the version publish nothing.
Pre-release versions (`1.4.0-beta.0`) publish under the `next` dist-tag. pnpm skips packages already published, so
rerunning the job finishes a partial release.

> **Registry credentials.** 
  The workflow reads an `NPM_TOKEN` repository secret: an npm granular or automation token with publish rights on
  `better-iam` and the `@better-iam` scope. Never commit registry credentials to the repository.

## Next steps [#next-steps]

  - [Deployment](/docs/operations/deployment): How an application deploys a released version.

  - [Changelog](/docs/reference/changelog): What changed in each release.
