Skip to content
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
265 changes: 144 additions & 121 deletions docs/library-publishing.md
Original file line number Diff line number Diff line change
@@ -1,188 +1,211 @@
# Library Publishing Guide

This document explains how automated publishing works for the Wire monorepo libraries.
This guide explains how to publish Wire monorepo libraries to npm using the two-step GitHub Actions workflow.

## Overview
## Quick Links

The monorepo uses **Nx Release** to automate versioning and publishing of libraries (like `@wireapp/api-client` and `@wireapp/core`) to npm with **Trusted Publishing** (provenance).
| Workflow | Purpose | Link |
|----------|---------|------|
| **Create Library Release PR** | Bumps versions and opens a release PR | [Run workflow](https://github.com/wireapp/wire-webapp/actions/workflows/publish-libraries.yml) |
| **Publish packages to npm** | Publishes to npm when the release PR is merged | [View runs](https://github.com/wireapp/wire-webapp/actions/workflows/publish-libraries-on-merge.yml) |

---

## Which Libraries Get Published?

Only libraries tagged `npm:public` in their `project.json` are versioned and published by `nx release`. Currently that is:

| Library | npm Package | Tag |
|---------|-------------|-----|
| `libraries/api-client` | `@wireapp/api-client` | `npm:public` |

Other libraries (`core`, `config`) have `type:lib` but **not** `npm:public`, so they are linted, tested, and built by the release workflow but are **not** versioned or published.

> **To add a new library to the publish set**, add the `npm:public` tag to its `project.json` tags array and configure a trusted publisher on npmjs.com (see [NPM Trusted Publishing](#npm-trusted-publishing-setup) below).

---

## How It Works

The release process is a **two-step workflow** triggered manually and gated by a PR review:
The release process is a **two-step workflow** triggered manually and gated by a PR review.

1. **Create a release PR** — Run the `Create Library Release PR` workflow from the GitHub Actions UI. It lints, tests, and builds the libraries, bumps versions using conventional commits, and opens a PR to `dev` with the `publish-to-npm` label.
2. **Publish on merge** — When the release PR is merged to `dev`, a second workflow detects the `publish-to-npm` label and publishes the built libraries to npm with provenance.
### Step 1 — Create the Release PR

### Key Details
Trigger: **Manual** (`workflow_dispatch`) from the [Create Library Release PR](https://github.com/wireapp/wire-webapp/actions/workflows/publish-libraries.yml) action page.

- Uses **conventional commits** to determine version bumps (major/minor/patch)
- Creates GitHub releases and git tags
- Publishes with **npm provenance** for supply-chain security
- The release PR must be **merge-committed** (not squash-merged) to preserve git tags
What it does:

### Workflow Files
1. Checks out `dev` and creates a release branch (`chore/library-release-<run_id>`).
2. Installs dependencies (`yarn --immutable`).
3. Lints, tests, and builds **all** libraries (`tag:type:lib`).
4. Runs `nx release version` which:
- Reads conventional commits since the last release tag.
- Bumps versions in `package.json` for each `npm:public` library.
- Creates a git commit and tag per version bump.
5. Pushes the branch with tags and opens a PR to `dev` with the **`publish-to-npm`** label.

1. **`.github/workflows/publish-libraries.yml`** — Creates the release PR. Triggered manually via `workflow_dispatch`.
2. **`.github/workflows/publish-libraries-on-merge.yml`** — Publishes to npm when a PR with the `publish-to-npm` label is merged to `dev`.
If no version changes are detected the workflow exits without creating a PR.

### Tagged Projects
### Step 2 — Publish on Merge

Only libraries with the `npm:public` tag in their `project.json` are published:
- `libraries/core` → `@wireapp/core`
- `libraries/api-client` → `@wireapp/api-client`
Trigger: **Automatic** when a PR with the `publish-to-npm` label is merged to `dev` (or via `workflow_dispatch` from the [Publish packages to npm](https://github.com/wireapp/wire-webapp/actions/workflows/publish-libraries-on-merge.yml) action page).

### Tagged Projects
What it does:

Only libraries with the `npm:public` tag in their `project.json` are published:
- `libraries/core` → `@wireapp/core`
- `libraries/api-client` → `@wireapp/api-client`
1. Checks out the **exact merge commit** (to avoid publishing unrelated changes).
2. Installs dependencies and builds all libraries (`tag:type:lib`).
3. Runs `nx release publish` which publishes every `npm:public` library to npm with provenance.

## Configuration
---

### Nx Release Config (`nx.json`)
## Step-by-Step Guide

```json
{
"release": {
"projects": ["tag:npm:public"],
"projectsRelationship": "independent",
"version": {
"conventionalCommits": true
},
"changelog": {
"projectChangelogs": {
"createRelease": "github"
}
}
}
}
### 1. Write Your Changes with Conventional Commits

Merge your library changes to `dev` using conventional commit messages so that `nx release` can determine the correct version bump:

```bash
git commit -m "feat(api-client): add new endpoint" # → minor bump
git commit -m "fix(api-client): handle timeout" # → patch bump
git commit -m "feat(api-client)!: drop legacy auth" # → major bump
```

### Key Settings:
- **Independent versioning**: Each library has its own version
- **Conventional commits**: Automatically determines version bump from commit messages
- **GitHub releases**: Creates releases for each version

## Publishing Workflow

### Creating a Release

1. Merge your library changes to `dev` using conventional commit messages:
```bash
git commit -m "feat: add new API method" # → minor bump
git commit -m "fix: resolve auth issue" # → patch bump
git commit -m "feat!: breaking API change" # → major bump
```
2. Go to **GitHub Actions → Create Library Release PR → Run workflow**.
3. The workflow will:
- Lint, test, and build all libraries
- Run `nx release version` to bump versions based on conventional commits
- Push a release branch and open a PR to `dev` with the `publish-to-npm` label
4. Review the PR — verify version bumps and changelog entries look correct.
5. **Merge the PR using a merge commit** (do NOT squash — this preserves git tags).
6. On merge, the publish workflow automatically:
- Builds the libraries
- Publishes to npm with provenance
- Creates GitHub releases

### Local Testing (Dry Run)
### 2. Trigger the Release PR

1. Open the [**Create Library Release PR**](https://github.com/wireapp/wire-webapp/actions/workflows/publish-libraries.yml) workflow page.
2. Click **Run workflow** (branch: `dev`).
3. Wait for the workflow to complete — it will open a PR automatically.

### 3. Review the PR

- Verify the version bumps match what you expect.
- Check the generated changelog entries.

### 4. Merge the PR

> **⚠️ Important:** Use **"Merge commit"** — do **NOT** squash merge. Squash merging destroys the git tags that `nx release` created, and the publish step will fail.

### 5. Confirm the Publish

After merging, the [publish workflow](https://github.com/wireapp/wire-webapp/actions/workflows/publish-libraries-on-merge.yml) runs automatically. Check its output to confirm the packages were published successfully.

---

## Conventional Commit Reference

| Prefix | Version Bump | Example |
|--------|-------------|---------|
| `feat:` | Minor (0.**X**.0) | `feat(api-client): add cells support` |
| `fix:` | Patch (0.0.**X**) | `fix(api-client): resolve retry logic` |
| `feat!:` / `BREAKING CHANGE:` | Major (**X**.0.0) | `feat(api-client)!: remove deprecated methods` |
| `chore:`, `docs:`, `refactor:` | None | `chore: update dev dependencies` |

Only commits scoped to a published library (or unscoped commits touching its files) trigger a version bump for that library.

---

## Local Testing (Dry Run)

Preview what a release would do without publishing:

```bash
# Dry run to see what would happen
# Full dry run (version + publish simulation)
yarn release:dry-run

# Create version and changelog only (no publish)
# Version only (no publish)
yarn release:version

# Publish already versioned packages
# Publish already-versioned packages
yarn release:publish
```

## Conventional Commit Format
---

Use these prefixes to control version bumps:
## Configuration

- `feat:` → **Minor** version bump (0.X.0)
- `fix:` → **Patch** version bump (0.0.X)
- `feat!:` or `BREAKING CHANGE:` → **Major** version bump (X.0.0)
- `chore:`, `docs:`, `refactor:` → No version bump
### Nx Release Config (`nx.json`)

Examples:
```bash
git commit -m "feat(api-client): add cells support"
git commit -m "fix(core): resolve memory leak in message handling"
git commit -m "feat(api-client)!: remove deprecated auth methods"
```jsonc
{
"release": {
"projects": ["tag:npm:public"], // only npm:public libraries
"projectsRelationship": "independent", // each library versioned separately
"version": {
"conventionalCommits": true,
"fallbackCurrentVersionResolver": "disk",
"preserveLocalDependencyProtocols": false, // rewrite workspace: protocols to real versions on release
"git": {
"commit": true,
"tag": true,
"commitMessage": "chore(release): publish {projectName} {version} [WPB-22420]"
}
},
"changelog": {
"projectChangelogs": {
"createRelease": "github" // creates a GitHub release per version
}
}
}
}
```

---

## NPM Trusted Publishing Setup

The workflow uses **OIDC-based trusted publishing** for secure, keyless npm authentication — no npm tokens are stored as secrets.
The publish workflow uses **OIDC-based trusted publishing** — no long-lived npm tokens are stored as secrets.

### How It Works

GitHub Actions mints a short-lived OIDC token during the workflow run. npm verifies the token against the trusted publisher configuration on the package, confirming the publish originated from the expected repository and workflow. The `NPM_CONFIG_PROVENANCE` flag attaches a cryptographic provenance attestation to the published package.

### Requirements:
1. **GitHub Actions workflow** must have:
```yaml
permissions:
id-token: write # for OIDC token / provenance
contents: write # for tags/commits
```
2. **NPM package settings** (configured per-package on npmjs.com):
- Go to **Settings → Publishing access → Configure trusted publishers**
- Add a trusted publisher:
- **Repository**: `wireapp/wire-webapp`
- **Workflow**: `publish-libraries-on-merge.yml`

### Environment Variable:
### Required Workflow Permissions

```yaml
env:
NPM_CONFIG_PROVENANCE: true
permissions:
id-token: write # OIDC token for npm provenance
contents: write # push tags and commits
```

This generates cryptographic proof that the package was built in GitHub Actions from your repository.
### NPM Package Configuration

## Troubleshooting
For each `npm:public` package on [npmjs.com](https://www.npmjs.com/):

### Version not incrementing
- Check commit messages use conventional commit format
- Run `yarn release:dry-run` to preview changes
- Ensure commits are on the `dev` branch before triggering the workflow
1. Go to **Settings → Publishing access → Configure trusted publishers**.
2. Add a trusted publisher:
- **Repository**: `wireapp/wire-webapp`
- **Workflow**: `publish-libraries-on-merge.yml`

### Release PR not created
- The workflow skips PR creation if no version changes are detected
- Verify new conventional commits exist since the last release tag
---

### Publish fails
- Verify the merged PR has the `publish-to-npm` label
- Ensure the npm package has a trusted publisher configured for `wireapp/wire-webapp` and the `publish-libraries-on-merge.yml` workflow
- Check that the workflow has `id-token: write` permission
- Check npm package permissions
- Review workflow logs in GitHub Actions
## Troubleshooting

### Want to skip a release
- Simply don't trigger the release workflow
- Or close the release PR without merging
| Problem | What to Check |
|---------|---------------|
| **Version not incrementing** | Verify commits use conventional commit format. Run `yarn release:dry-run` locally. |
| **Release PR not created** | No new conventional commits since the last release tag — nothing to bump. |
| **Publish fails with 403** | The npm package may not have a trusted publisher configured for `wireapp/wire-webapp` + `publish-libraries-on-merge.yml`. |
| **Publish fails with missing tags** | The PR was squash-merged instead of merge-committed. Re-tag manually or re-run the release PR workflow. |
| **Want to skip a release** | Don't trigger the workflow, or close the release PR without merging. |

## Manual Release (Emergency)
### Manual Release (Emergency)

If automation fails, you can manually release:
If automation fails and you need to publish immediately:

```bash
# 1. Update version
# 1. Bump the version
cd libraries/api-client
npm version patch # or minor/major
npm version patch # or minor / major

# 2. Build
yarn nx build api-client-lib

# 3. Publish (from library directory)
# 3. Publish with provenance
npm publish --provenance --access public
```

---

## References

- [Nx Release Documentation](https://nx.dev/recipes/nx-release)
Expand Down