Skip to content
Merged
Show file tree
Hide file tree
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
102 changes: 102 additions & 0 deletions docs/beta-release.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,102 @@
# Beta Release Process (`@bitgo-beta/*`)

## Overview

BitGoJS publishes beta releases under the `@bitgo-beta` scope on npm. The `scripts/prepare-release.ts`
script transforms the entire monorepo — re-scoping all `@bitgo/*` packages to `@bitgo-beta/*`, computing
prerelease versions, and pinning all inter-module dependency versions. This enables publishing beta/alpha
releases without conflicting with stable releases.

## How `prepare-release.ts` Works

```
CLI: npx tsx scripts/prepare-release.ts [preid] --scope [scope] --root-dir [dir]
Defaults: preid=beta, scope=@bitgo-beta, root-dir=<repo-root>
```

### Step 1: Scope Replacement

The script walks all `.ts`, `.tsx`, `.js`, `.json` files in `modules/` and `webpack/` (skipping
`node_modules/`), performing a global regex replacement of every `@bitgo/X` reference with
`@bitgo-beta/X`. This covers:

- `package.json` dependency entries
- TypeScript/JavaScript import statements
- Any other string references to `@bitgo/` scoped packages

**Special case**: The `modules/bitgo` package is the only one published without an `@bitgo/` prefix.
Its `package.json` name is explicitly set to `@bitgo-beta/bitgo`.

Implementation: `scripts/prepareRelease/changeScopeInFile.ts`

### Step 2: Version Computation

For each module, the script:

1. Fetches dist-tags from npm (`https://registry.npmjs.org/-/package/<name>/dist-tags`)
2. Determines the previous prerelease version:
- If a beta tag exists and its base version >= latest, use the beta tag as base
- If the beta tag's base version < latest, start a new prerelease from latest
- If no beta tag exists, create one from latest
3. Computes the next version via `semver.inc(prevTag, 'prerelease', preid)`

Example: `8.2.1-beta.1009` → `8.2.1-beta.1010`

The dist-tag fetch can be cached via `BITGO_PREPARE_RELEASE_CACHE_DIST_TAGS` env var pointing to a
JSON file, avoiding repeated npm registry calls.

Implementation: `scripts/prepareRelease/distTags.ts`

### Step 3: Cross-Module Version Pinning

After each module's version is bumped, all other modules that depend on it have their dependency
version updated to the exact new version. This **removes semver ranges** (`^`, `~`), resulting in
pinned versions:

```
Before: "@bitgo/sdk-core": "^31.2.1"
After: "@bitgo-beta/sdk-core": "8.2.1-beta.1010"
```

Implementation: `scripts/prepareRelease/changePackageJson.ts`

## Side Effects for Consumers

Because all dependency versions are pinned (no ranges), consumers of `@bitgo-beta/*` packages must
**explicitly bump** to new versions when they are published. The `@bitgo/beta-tools` package provides
a canonical CLI and library for this — see its README for usage.

Key behaviors to understand:

- Each `@bitgo-beta` package has its own **independent prerelease counter** (e.g.,
`sdk-core@8.2.1-beta.788`, `statics@15.1.1-beta.791`). There is no shared suffix —
what ties a release together is the CI publish run.
- The `beta` dist-tag on npm always points to the latest published prerelease for each package.
- Fetching dist-tags individually during a multi-package publish can yield inconsistent versions
(a race condition): some packages may have the new version while others still show the old one.
Use `--versions-file` with a CI-generated manifest to avoid this.

## Helper Modules (`scripts/prepareRelease/`)

| File | Purpose |
|------|---------|
| `changeScopeInFile.ts` | Regex replacement of `@bitgo/*` → target scope in file contents |
| `changePackageJson.ts` | Updates dependency versions in `package.json` objects |
| `distTags.ts` | Fetches/caches npm dist-tags for all modules |
| `getLernaModules.ts` | Runs `lerna list --json --all` to discover all modules |
| `walk.ts` | Recursively walks directories, filtering by file extension |
| `index.ts` | Barrel export |

## Known Limitations

- `setDependencyVersion` in `changePackageJson.ts` only updates `dependencies` and `devDependencies`.
It does **not** update `peerDependencies` or `buildDependencies` (marked with a FIXME).
- Three packages are skipped during dist-tag fetch: `@bitgo-beta/express`, `@bitgo-beta/web-demo`,
`@bitgo-beta/sdk-test` (not published to npm).

## Related Scripts

| Script | Purpose |
|--------|---------|
| `scripts/prepare-release.ts` | Main transformation script (this document) |
| `@bitgo/beta-tools` (`modules/beta-tools`) | Canonical tool for consumers to bump `@bitgo-beta/*` versions |
1 change: 1 addition & 0 deletions modules/beta-tools/.eslintignore
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
dist/
3 changes: 3 additions & 0 deletions modules/beta-tools/.eslintrc.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
{
"extends": "../../.eslintrc.json"
}
4 changes: 4 additions & 0 deletions modules/beta-tools/.mocharc.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
module.exports = {
require: 'tsx',
extension: ['.js', '.ts'],
};
101 changes: 101 additions & 0 deletions modules/beta-tools/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,101 @@
# @bitgo/beta-tools

CLI and library for bumping `@bitgo-beta/*` dependency versions in consumer projects.

## Problem

BitGoJS publishes beta packages under the `@bitgo-beta` scope with pinned (non-range) inter-module
dependency versions. Consumer repos must explicitly bump these versions. This tool replaces the
divergent bespoke scripts that existed in individual consumer repos.

## How version resolution works

Strategies are tried in order — the first available one wins:

1. **Manifest file (`--versions-file`):** Reads an explicit JSON map of package→version. Most
deterministic — useful for CI pipelines that generate a manifest during publish.

2. **GitHub Actions API** (when `GITHUB_TOKEN` is set): Fetches the logs of the latest successful
"Publish @bitgo-beta" workflow run and parses the verify-release output. This gives the complete
version set (~106 packages) from a single CI run — race-free, no gaps.

3. **npm registry (default fallback):** Fetches `@bitgo-beta/bitgo` at the requested dist-tag. Its
`dependencies` field contains pinned versions for ~92 packages from the same CI publish run.
Packages not covered by the megapackage (typically 5-6 per consumer) fall back to individual
dist-tag lookups — these are racy during active publishes.

## CLI usage

```bash
# Via npx
npx @bitgo-beta/beta-tools --tag beta

# Or install as devDependency
npm install -D @bitgo-beta/beta-tools
bump-bitgo-beta --tag beta
```

### Options

| Option | Default | Description |
|---|---|---|
| `--tag <tag>` | `beta` | Dist tag to resolve |
| `--versions-file <path>` | — | JSON manifest of package→version mappings |
| `--scope <scope>` | `@bitgo-beta` | Package scope to match in `package.json` |
| `--pm <npm\|yarn\|pnpm>` | auto-detected | Package manager to use |
| `--ignore <pkg...>` | `[]` | Packages to skip |
| `--only-utxo` | `false` | Only bump UTXO-related packages |
| `--ignore-utxo` | `true` (when `--only-utxo` not set) | Skip UTXO-related packages |
| `--utxo-patterns <patterns...>` | `utxo, unspents, abstract-lightning, babylonlabs-io-btc-staking-ts` | Patterns for UTXO package detection |
| `--check-duplicates` | `true` | Check lockfile for duplicate versions after install (npm only) |
| `--check-duplicate-packages <pkg...>` | `@bitgo-beta/utxo-lib, @bitgo/wasm-utxo` | Packages to check for duplicates |
| `--dry-run` | `false` | Show what would be installed without installing |

### Examples

```bash
# Bump all non-UTXO beta deps (default behavior)
bump-bitgo-beta

# Bump only UTXO packages
bump-bitgo-beta --only-utxo

# Bump everything, skip duplicate check
bump-bitgo-beta --ignore-utxo=false --check-duplicates=false

# Use a CI-generated manifest
bump-bitgo-beta --versions-file ./beta-versions.json

# Preview without installing
bump-bitgo-beta --dry-run
```

## Library API

```typescript
import {
resolveVersions,
filterDependencies,
detectPackageManager,
createPackageManager,
checkDuplicates,
} from '@bitgo-beta/beta-tools';

const resolved = await resolveVersions({
packages: ['@bitgo-beta/sdk-core', '@bitgo-beta/statics'],
tag: 'beta',
scope: '@bitgo-beta',
});

for (const [pkg, version] of resolved.versions) {
console.log(`${pkg}@${version}`);
}
```

## Development

```bash
yarn build
yarn lint
yarn unit-test
```
43 changes: 43 additions & 0 deletions modules/beta-tools/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
{
"name": "@bitgo/beta-tools",
"version": "1.0.0",
"description": "CLI and library for bumping @bitgo-beta dependency versions",
"main": "./dist/src/index.js",
"types": "./dist/src/index.d.ts",
"bin": {
"bump-bitgo-beta": "./dist/src/cli.js"
},
"files": [
"dist/src/**/*"
],
"scripts": {
"prepare": "npm run build",
"build": "yarn tsc --build --incremental --verbose .",
"lint": "eslint --quiet .",
"unit-test": "mocha --recursive test",
"fmt": "prettier --write '{src,test}/**/*.{ts,js}'"
},
"repository": {
"type": "git",
"url": "https://github.com/BitGo/BitGoJS.git",
"directory": "modules/beta-tools"
},
"dependencies": {
"yargs": "^17.7.2"
},
"devDependencies": {
"@types/yargs": "^17.0.0",
"sinon": "^13.0.1",
"@types/sinon": "^10.0.0"
},
"lint-staged": {
"*.{js,ts}": [
"yarn prettier --write",
"yarn eslint --fix"
]
},
"publishConfig": {
"access": "public"
},
"license": "MIT"
}
Loading