Skip to content

Commit 0fbb142

Browse files
committed
chore: an attempt at getting semantic release to publish properly with pnpm
1 parent 6f2ce28 commit 0fbb142

File tree

9 files changed

+159
-2
lines changed

9 files changed

+159
-2
lines changed

.github/workflows/release.yml

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -56,6 +56,9 @@ jobs:
5656
- name: Debug - Run verify-release-config
5757
run: pnpm verify-release-config
5858

59+
- name: Make scripts executable
60+
run: chmod +x scripts/*.js
61+
5962
- name: Configure Git
6063
run: |
6164
git config --global user.email "neuralsoft@gmail.com"

.npmrc

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
save-workspace-protocol=false

CONTRIBUTING.md

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -208,4 +208,25 @@ This project and everyone participating in it is governed by our Code of Conduct
208208
- `perf:` commits trigger a patch version bump
209209
- Commits with `BREAKING CHANGE:` in the footer trigger a major version bump
210210

211+
### Workspace Dependencies in Published Packages
212+
213+
The monorepo uses pnpm workspaces, which means dependencies between packages are specified as `"workspace:*"` in package.json files. However, when publishing to npm, these references need to be transformed into actual version numbers.
214+
215+
We have implemented several safeguards to ensure this happens correctly:
216+
217+
1. A `prepare-publish.js` script that transforms workspace dependencies before publishing
218+
2. A `.npmrc` file with `save-workspace-protocol=false` setting
219+
3. `prepublishOnly` scripts in each package that ensure dependencies are correctly resolved
220+
221+
If you need to manually publish a package, you should:
222+
223+
```bash
224+
# From the root directory
225+
pnpm prepare-publish
226+
227+
# Then, from the package directory
228+
cd packages/your-package
229+
pnpm publish
230+
```
231+
211232
Thank you for contributing to MyCoder! 👍

eslint.config.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -75,7 +75,7 @@ export default ts.config(
7575
'**/.output',
7676
'**/pnpm-lock.yaml',
7777
'**/routeTree.gen.ts',
78-
'scripts/verify-release-config.js',
78+
'scripts/**/*.js',
7979
],
8080
},
8181
);

package.json

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,8 @@
2222
"cli": "cd packages/cli && node --no-deprecation bin/cli.js",
2323
"prepare": "husky",
2424
"verify-release-config": "node scripts/verify-release-config.js",
25-
"release": "pnpm verify-release-config && pnpm -r --workspace-concurrency=1 exec -- pnpm exec semantic-release -e semantic-release-monorepo"
25+
"prepare-publish": "node scripts/prepare-publish.js",
26+
"release": "pnpm verify-release-config && pnpm prepare-publish && pnpm -r --workspace-concurrency=1 exec -- pnpm exec semantic-release -e semantic-release-monorepo"
2627
},
2728
"lint-staged": {
2829
"*.{js,jsx,ts,tsx}": [

packages/agent/package.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@
2929
"typecheck": "tsc --noEmit",
3030
"clean": "rimraf dist",
3131
"clean:all": "rimraf node_modules dist",
32+
"prepublishOnly": "node ../../scripts/prepare-publish.js",
3233
"semantic-release": "pnpm exec semantic-release -e semantic-release-monorepo"
3334
},
3435
"keywords": [

packages/cli/package.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@
2727
"test": "vitest run",
2828
"test:watch": "vitest",
2929
"test:ci": "vitest --run --coverage",
30+
"prepublishOnly": "node ../../scripts/prepare-publish.js",
3031
"semantic-release": "pnpm exec semantic-release -e semantic-release-monorepo"
3132
},
3233
"keywords": [

scripts/README.md

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
# Scripts
2+
3+
This directory contains utility scripts for the project.
4+
5+
## prepare-publish.js
6+
7+
This script transforms workspace dependencies (e.g., `"mycoder-agent": "workspace:*"`) into actual version numbers before publishing to npm.
8+
9+
### Why is this needed?
10+
11+
When using pnpm workspaces, dependencies between packages in the same workspace are specified using the `workspace:*` protocol. This is great for local development, but when publishing to npm, these references need to be transformed into actual version numbers.
12+
13+
Normally, pnpm handles this transformation automatically when using `pnpm publish`. However, when using `semantic-release-monorepo` for automated releases, this transformation doesn't happen because semantic-release bypasses pnpm's publishing mechanism.
14+
15+
### How it works
16+
17+
The script:
18+
1. Reads all package.json files in the workspace
19+
2. Builds a map of package names to their current versions
20+
3. Replaces all `workspace:*` dependencies with the actual version number
21+
4. Writes the updated package.json files
22+
23+
### Usage
24+
25+
This script is run automatically as part of the release process via:
26+
- The `prepare-publish` npm script in the root package.json
27+
- The `prepublishOnly` npm script in each package
28+
29+
### Alternatives
30+
31+
An alternative approach is to set `save-workspace-protocol=false` in `.npmrc`, which we've also included as an additional safeguard.

scripts/prepare-publish.js

Lines changed: 98 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,98 @@
1+
#!/usr/bin/env node
2+
3+
import fs from 'fs';
4+
import path from 'path';
5+
6+
const ROOT_DIR = path.resolve('.');
7+
const PACKAGES_DIR = path.join(ROOT_DIR, 'packages');
8+
9+
console.log('Starting prepare-publish script...');
10+
11+
// Get all packages
12+
const packages = fs
13+
.readdirSync(PACKAGES_DIR)
14+
.filter((dir) => fs.statSync(path.join(PACKAGES_DIR, dir)).isDirectory());
15+
16+
console.log(`Found packages: ${packages.join(', ')}`);
17+
18+
// First, read all package versions
19+
const packageVersions = {};
20+
21+
for (const pkg of packages) {
22+
const packageDir = path.join(PACKAGES_DIR, pkg);
23+
const packageJsonPath = path.join(packageDir, 'package.json');
24+
25+
if (fs.existsSync(packageJsonPath)) {
26+
const packageJson = JSON.parse(fs.readFileSync(packageJsonPath, 'utf8'));
27+
packageVersions[packageJson.name] = packageJson.version;
28+
console.log(
29+
`Package ${packageJson.name} has version ${packageJson.version}`,
30+
);
31+
}
32+
}
33+
34+
// Then, update all workspace dependencies
35+
for (const pkg of packages) {
36+
const packageDir = path.join(PACKAGES_DIR, pkg);
37+
const packageJsonPath = path.join(packageDir, 'package.json');
38+
39+
if (fs.existsSync(packageJsonPath)) {
40+
const packageJson = JSON.parse(fs.readFileSync(packageJsonPath, 'utf8'));
41+
let modified = false;
42+
43+
// Check dependencies
44+
if (packageJson.dependencies) {
45+
for (const [depName, depVersion] of Object.entries(
46+
packageJson.dependencies,
47+
)) {
48+
if (depVersion.startsWith('workspace:')) {
49+
if (packageVersions[depName]) {
50+
console.log(
51+
`Transforming dependency in ${packageJson.name}: ${depName}@${depVersion} -> ${depName}@${packageVersions[depName]}`,
52+
);
53+
packageJson.dependencies[depName] = packageVersions[depName];
54+
modified = true;
55+
} else {
56+
console.warn(
57+
`Warning: Could not find version for workspace dependency ${depName} in ${packageJson.name}`,
58+
);
59+
}
60+
}
61+
}
62+
}
63+
64+
// Check devDependencies
65+
if (packageJson.devDependencies) {
66+
for (const [depName, depVersion] of Object.entries(
67+
packageJson.devDependencies,
68+
)) {
69+
if (depVersion.startsWith('workspace:')) {
70+
if (packageVersions[depName]) {
71+
console.log(
72+
`Transforming devDependency in ${packageJson.name}: ${depName}@${depVersion} -> ${depName}@${packageVersions[depName]}`,
73+
);
74+
packageJson.devDependencies[depName] = packageVersions[depName];
75+
modified = true;
76+
} else {
77+
console.warn(
78+
`Warning: Could not find version for workspace devDependency ${depName} in ${packageJson.name}`,
79+
);
80+
}
81+
}
82+
}
83+
}
84+
85+
// Save changes if modified
86+
if (modified) {
87+
fs.writeFileSync(
88+
packageJsonPath,
89+
JSON.stringify(packageJson, null, 2) + '\n',
90+
);
91+
console.log(`Updated package.json for ${packageJson.name}`);
92+
}
93+
}
94+
}
95+
96+
console.log(
97+
'✅ All workspace dependencies have been transformed to actual versions!',
98+
);

0 commit comments

Comments
 (0)