Skip to content

Commit 7dc3199

Browse files
ffflorianclaude
andauthored
chore: replace lerna with multi-semantic-release and yarn workspaces (#1155)
* build: replace lerna with multi-semantic-release and yarn workspaces - Remove lerna; use `yarn workspaces foreach` for build/test orchestration - Add @qiwi/multi-semantic-release for selective per-package publishing: only packages whose files changed are released on each merge - Add semantic-release + @ffflorian/semantic-release-config; root .releaserc.json extends it - Update CI: add fetch-depth: 0, GITHUB_TOKEN, remove manual chore skip (semantic-release handles release skipping via conventional commits) - Remove NPM_TOKEN, OIDC is used for npm auth - Add AGENTS.md rule: always use pinned versions in package.json * fix: add --all flag to yarn workspaces foreach commands Yarn 4 requires --all (-A) when running foreach without a worktree or recursive scope. Also update AGENTS.md to require running build and tests after every change. * fix: exclude root workspace from workspaces foreach to prevent infinite recursion Root has 'test' and 'clean' scripts that call foreach again, causing an infinite loop when --all (-A) includes the root workspace. * build: switch to dhoulb/multi-semantic-release, use * for intra-repo dep versions Replace @qiwi/multi-semantic-release with multi-semantic-release (dhoulb). Change my-timezone's ntpclient dependency to * — multi-semantic-release replaces it with the correct version at publish time. Add --ignore-private-packages flag to release script. --------- Co-authored-by: Claude <noreply@anthropic.com>
1 parent aa42364 commit 7dc3199

7 files changed

Lines changed: 2410 additions & 2263 deletions

File tree

.github/workflows/build_test_publish.yml

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,8 @@ jobs:
1717
steps:
1818
- name: Checkout repository
1919
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd
20+
with:
21+
fetch-depth: 0
2022

2123
- name: Set up Node.js
2224
uses: actions/setup-node@53b83947a5a98c8d113130e565377fae1a50d02f
@@ -35,12 +37,11 @@ jobs:
3537
yarn test
3638
3739
- name: Release on npm
38-
if: |
39-
github.event_name != 'pull_request' &&
40-
!startsWith(github.event.head_commit.message, 'chore')
40+
if: github.event_name != 'pull_request'
4141
env:
4242
GIT_AUTHOR_NAME: Florian Imdahl
4343
GIT_AUTHOR_EMAIL: ffflorian@users.noreply.github.com
44+
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
4445
run: |
4546
export GIT_COMMITTER_NAME="${GIT_AUTHOR_NAME}"
4647
export GIT_COMMITTER_EMAIL="${GIT_AUTHOR_EMAIL}"

.releaserc.json

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
{
2+
"extends": "@ffflorian/semantic-release-config"
3+
}

AGENTS.md

Lines changed: 27 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -4,11 +4,11 @@ This file contains knowledge and conventions for AI agents working in this repos
44

55
## Project Overview
66

7-
This is a Yarn workspaces monorepo managed by [Lerna](https://lerna.js.org/), containing multiple independently published Node.js/TypeScript packages by [Florian Imdahl](https://github.com/ffflorian).
7+
This is a yarn workspaces monorepo managed by [multi-semantic-release](https://github.com/qiwi/multi-semantic-release), containing multiple independently published Node.js/TypeScript packages by [Florian Imdahl](https://github.com/ffflorian).
88

99
- **License**: GPL-3.0
1010
- **Node.js requirement**: >= 18.0 (CI uses Node.js 24.x)
11-
- **Package manager**: Yarn 4.12.0 (Berry)
11+
- **Package manager**: yarn 4.12.0 (Berry)
1212

1313
## Packages
1414

@@ -60,18 +60,24 @@ yarn workspace @ffflorian/<package-name> add -D <dep>
6060

6161
### Per-package scripts
6262

63-
Each package supports `build`, `clean`, and `test` scripts run via Lerna.
63+
Each package supports `build`, `clean`, and `test` scripts run via `yarn workspaces foreach`.
6464

6565
## Tooling
6666

6767
- **Build**: TypeScript (`tsc`) via per-package `tsconfig.build.json`
68-
- **Bundler**: Vite (used in some packages)
69-
- **Testing**: Vitest
68+
- **Bundler**: vite (used in some packages)
69+
- **Testing**: vitest
7070
- **Linting**: oxlint + ESLint with `@ffflorian/eslint-config`, run in that order
71-
- **Formatting**: Prettier with `@ffflorian/prettier-config`
72-
- **Git hooks**: Lefthook (`lefthook.yml`) — runs prettier, oxlint, and eslint with auto-fix on staged files before commit
73-
- **Versioning**: Lerna independent versioning with conventional commits
74-
- **Publishing**: Lerna publishes to npm registry; only allowed from `main` branch
71+
- **Formatting**: prettier with `@ffflorian/prettier-config`
72+
- **Git hooks**: lefthook (`lefthook.yml`) — runs prettier, oxlint, and eslint with auto-fix on staged files before commit
73+
- **Versioning**: Independent versioning via conventional commits
74+
- **Publishing**: `multi-semantic-release` (dhoulb) publishes to npm; only packages whose files changed are released. Private packages are excluded via `--ignore-private-packages`. Only allowed from `main` branch.
75+
- **Cross-repo deps**: When a package in this repo depends on another package in this repo, use `*` as the version — multi-semantic-release replaces it with the correct version at publish time.
76+
- **Release config**: Root `.releaserc.json` extends `@ffflorian/semantic-release-config`
77+
78+
## Dependencies
79+
80+
**Always use pinned (exact) versions in `package.json`.** Do not use `^`, `~`, or other range specifiers for dependencies. This is enforced by `defaultSemverRangePrefix: ''` in `.yarnrc.yml`.
7581

7682
## Commit Messages
7783

@@ -89,7 +95,8 @@ ci: CI/CD configuration changes
8995
```
9096

9197
- Do **not** include references to Claude or AI tools in commit messages or PR descriptions.
92-
- Lerna uses conventional commits to determine version bumps and generate changelogs.
98+
- `multi-semantic-release` uses conventional commits to determine version bumps and generate changelogs.
99+
- Only packages with commits touching their own directory are released — unrelated packages are never published.
93100

94101
## Branch Naming
95102

@@ -111,7 +118,8 @@ GitHub Actions workflow (`.github/workflows/build_test_publish.yml`):
111118

112119
1. Runs on pushes and PRs to `main`
113120
2. Steps: install (`yarn --immutable`), build, lint, test
114-
3. On push to `main` (non-`chore` commits): publishes changed packages to npm via `yarn release`
121+
3. On push to `main`: runs `yarn release` (`multi-semantic-release`) which publishes only changed packages to npm
122+
4. Requires `GITHUB_TOKEN` and OIDC for npm auth; checkout uses `fetch-depth: 0` for full git history
115123

116124
## Code Style
117125

@@ -124,7 +132,14 @@ GitHub Actions workflow (`.github/workflows/build_test_publish.yml`):
124132

125133
## Before Committing
126134

127-
Always run `yarn fix` before committing to auto-fix linting and formatting errors:
135+
After implementing any change, always run build and tests to verify correctness:
136+
137+
```sh
138+
yarn build:ts
139+
yarn test
140+
```
141+
142+
Then run `yarn fix` to auto-fix linting and formatting errors before committing:
128143

129144
```sh
130145
yarn fix

lerna.json

Lines changed: 0 additions & 17 deletions
This file was deleted.

package.json

Lines changed: 9 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,9 @@
66
"@evilmartians/lefthook": "2.1.4",
77
"@ffflorian/eslint-config": "0.13.7",
88
"@ffflorian/prettier-config": "0.6.7",
9+
"@ffflorian/semantic-release-config": "0.10.7",
10+
"@semantic-release/changelog": "6.0.3",
11+
"@semantic-release/git": "10.0.1",
912
"@types/node": "~24",
1013
"eslint": "10.1.0",
1114
"eslint-import-resolver-typescript": "4.4.4",
@@ -14,9 +17,10 @@
1417
"eslint-plugin-perfectionist": "5.7.0",
1518
"globals": "17.4.0",
1619
"jiti": "2.6.1",
17-
"lerna": "9.0.7",
20+
"multi-semantic-release": "3.1.0",
1821
"oxlint": "1.56.0",
1922
"prettier": "3.8.1",
23+
"semantic-release": "25.0.3",
2024
"typescript": "6.0.2",
2125
"typescript-eslint": "8.57.1",
2226
"vite": "8.0.1",
@@ -31,8 +35,8 @@
3135
"prettier": "@ffflorian/prettier-config",
3236
"private": true,
3337
"scripts": {
34-
"build:ts": "lerna run build --concurrency 4",
35-
"clean": "lerna run clean",
38+
"build:ts": "yarn workspaces foreach --all --parallel --topological --exclude node-packages run build",
39+
"clean": "yarn workspaces foreach --all --parallel --exclude node-packages run clean",
3640
"dist": "yarn clean && yarn build:ts",
3741
"fix": "yarn fix:ts && yarn fix:other",
3842
"fix:other": "yarn prettier --write",
@@ -43,9 +47,9 @@
4347
"lint:ts:eslint": "eslint packages/",
4448
"lint:ts:oxlint": "oxlint --ignore-path .gitignore packages/",
4549
"prettier": "prettier .",
46-
"release": "lerna publish",
50+
"release": "multi-semantic-release --ignore-private-packages",
4751
"test": "yarn test:all",
48-
"test:all": "lerna run test --concurrency 4"
52+
"test:all": "yarn workspaces foreach --all --parallel --exclude node-packages run test"
4953
},
5054
"workspaces": [
5155
"packages/*"

packages/my-timezone/package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
"bin": "dist/cli.js",
44
"dependencies": {
55
"commander": "14.0.3",
6-
"ntpclient": "1.16.1"
6+
"ntpclient": "*"
77
},
88
"description": "Get the exact time based on your location.",
99
"devDependencies": {

0 commit comments

Comments
 (0)