fix: broken npm install — remove bundleDependencies, use postinstall symlinks (#369)
* fix: remove @gsd/* cross-deps that break npm install (#hotfix) Workspace packages declared @gsd/* as dependencies in their own package.json files. npm's bundleDependencies bundles packages into node_modules/ but still tries to resolve sub-dependencies from the registry — causing 404s for the unpublished @gsd/* scope. - Remove @gsd/* from all dependencies (root and workspace packages) - Add validate-pack.sh: tests tarball installability before publish - Wire validate-pack into CI (every PR) and publish pipeline - Bump to v2.10.10 - Update changelog Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * fix: drop bundleDependencies, use postinstall symlinks instead bundleDependencies with workspace packages causes npm to resolve @gsd/* from the registry during install — 404 since they're not published. Replace with a postinstall script that creates node_modules/@gsd/* symlinks pointing to packages/*. - Remove @gsd/* from dependencies and bundleDependencies - Add link-workspace-packages.cjs (CJS, runs before ESM postinstall) - Update validate-pack to verify symlinks after install - Include link script in files array Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * fix: robust validate-pack + fallback workspace linking - Keep @gsd/* in bundleDependencies (for npm pack bundling) - Remove @gsd/* from root dependencies (prevents 404 registry lookups) - Add link-workspace-packages.cjs fallback for when bundled symlinks aren't created - Simplified validate-pack with better error diagnostics Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * fix: remove bundleDependencies — use postinstall symlinks only npm 10.x fetches packument metadata for ALL deps including bundled ones. @gsd/* packages don't exist on npm → 404 → hard install failure. bundleDependencies is fundamentally broken for unpublished workspace packages. Replace with: - packages/ shipped via files array (already was) - link-workspace-packages.cjs creates node_modules/@gsd/* symlinks in postinstall, pointing to packages/* - No @gsd/* in dependencies or bundleDependencies at all Tarball drops from 40M to 3M (no bundled node_modules). Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * fix: add .npmignore to prevent .gitignore from excluding dist/ .gitignore contains /dist/ and packages/*/dist/ which are needed in the published tarball. Without .npmignore, npm pack respects .gitignore and excludes them — even though "files" in package.json should override. An empty .npmignore causes npm to ignore .gitignore entirely, letting the "files" field control what's packed. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * fix: avoid SIGPIPE in validate-pack on Linux tar | grep -q causes SIGPIPE (exit 141) on Linux when grep closes the pipe early. Write tar listing to a temp file and grep that instead. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
parent
c3ceb077d9
commit
098ed35a50
16 changed files with 188 additions and 57 deletions
9
.github/workflows/build-native.yml
vendored
9
.github/workflows/build-native.yml
vendored
|
|
@ -175,13 +175,8 @@ jobs:
|
|||
- name: Verify dist exists
|
||||
run: test -s dist/loader.js || { echo "::error::dist/loader.js missing or empty after build"; exit 1; }
|
||||
|
||||
- name: Verify tarball contents
|
||||
run: |
|
||||
npm pack --dry-run --ignore-scripts 2>&1 | tee /tmp/pack-output.txt
|
||||
grep -q "dist/loader.js" /tmp/pack-output.txt || {
|
||||
echo "::error::dist/loader.js not in tarball"
|
||||
exit 1
|
||||
}
|
||||
- name: Validate package is installable
|
||||
run: npm run validate-pack
|
||||
|
||||
- name: Publish main package
|
||||
env:
|
||||
|
|
|
|||
3
.github/workflows/ci.yml
vendored
3
.github/workflows/ci.yml
vendored
|
|
@ -28,6 +28,9 @@ jobs:
|
|||
- name: Build
|
||||
run: npm run build
|
||||
|
||||
- name: Validate package is installable
|
||||
run: npm run validate-pack
|
||||
|
||||
- name: Run unit tests
|
||||
run: npm run test:unit
|
||||
|
||||
|
|
|
|||
8
.npmignore
Normal file
8
.npmignore
Normal file
|
|
@ -0,0 +1,8 @@
|
|||
# .npmignore — override .gitignore for npm pack
|
||||
# When this file exists, npm ignores .gitignore entirely.
|
||||
# The "files" field in package.json determines what's included.
|
||||
# This file only needs to exclude things NOT covered by "files".
|
||||
|
||||
# Everything not in the "files" array is excluded by default.
|
||||
# This file exists solely to prevent .gitignore from interfering
|
||||
# with npm pack (e.g., .gitignore excludes dist/ which we need).
|
||||
|
|
@ -12,6 +12,12 @@ Format based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/).
|
|||
- Fallback `--backend=local` for offline faster-whisper on CPU
|
||||
- Venv-aware Python detection (`~/.gsd/voice-venv/bin/python3`)
|
||||
|
||||
## [2.10.10] - 2026-03-14
|
||||
|
||||
### Fixed
|
||||
- Fix broken `npm install` / `npx gsd-pi@latest` caused by unpublished `@gsd/*` workspace packages leaking into npm dependencies. Workspace cross-references removed from published package metadata; packages resolve via bundled `node_modules/` at runtime.
|
||||
- Add pre-publish tarball install validation (`validate-pack`) to CI and publish pipeline, preventing broken packages from reaching npm.
|
||||
|
||||
## [2.10.9] - 2026-03-14
|
||||
|
||||
### Added
|
||||
|
|
@ -485,7 +491,8 @@ Format based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/).
|
|||
### Changed
|
||||
- License updated to MIT
|
||||
|
||||
[Unreleased]: https://github.com/gsd-build/gsd-2/compare/v2.10.9...HEAD
|
||||
[Unreleased]: https://github.com/gsd-build/gsd-2/compare/v2.10.10...HEAD
|
||||
[2.10.10]: https://github.com/gsd-build/gsd-2/compare/v2.10.9...v2.10.10
|
||||
[2.10.9]: https://github.com/gsd-build/gsd-2/compare/v2.10.8...v2.10.9
|
||||
[2.10.8]: https://github.com/gsd-build/gsd-2/compare/v2.10.7...v2.10.8
|
||||
[2.10.7]: https://github.com/gsd-build/gsd-2/compare/v2.10.6...v2.10.7
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
{
|
||||
"name": "@gsd-build/engine-darwin-arm64",
|
||||
"version": "2.10.9",
|
||||
"version": "2.10.10",
|
||||
"description": "GSD native engine binary for macOS ARM64",
|
||||
"os": [
|
||||
"darwin"
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
{
|
||||
"name": "@gsd-build/engine-darwin-x64",
|
||||
"version": "2.10.9",
|
||||
"version": "2.10.10",
|
||||
"description": "GSD native engine binary for macOS Intel",
|
||||
"os": [
|
||||
"darwin"
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
{
|
||||
"name": "@gsd-build/engine-linux-arm64-gnu",
|
||||
"version": "2.10.9",
|
||||
"version": "2.10.10",
|
||||
"description": "GSD native engine binary for Linux ARM64 (glibc)",
|
||||
"os": [
|
||||
"linux"
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
{
|
||||
"name": "@gsd-build/engine-linux-x64-gnu",
|
||||
"version": "2.10.9",
|
||||
"version": "2.10.10",
|
||||
"description": "GSD native engine binary for Linux x64 (glibc)",
|
||||
"os": [
|
||||
"linux"
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
{
|
||||
"name": "@gsd-build/engine-win32-x64-msvc",
|
||||
"version": "2.10.9",
|
||||
"version": "2.10.10",
|
||||
"description": "GSD native engine binary for Windows x64 (MSVC)",
|
||||
"os": [
|
||||
"win32"
|
||||
|
|
|
|||
25
package-lock.json
generated
25
package-lock.json
generated
|
|
@ -1,19 +1,12 @@
|
|||
{
|
||||
"name": "gsd-pi",
|
||||
"version": "2.10.8",
|
||||
"version": "2.10.10",
|
||||
"lockfileVersion": 3,
|
||||
"requires": true,
|
||||
"packages": {
|
||||
"": {
|
||||
"name": "gsd-pi",
|
||||
"version": "2.10.8",
|
||||
"bundleDependencies": [
|
||||
"@gsd/native",
|
||||
"@gsd/pi-agent-core",
|
||||
"@gsd/pi-ai",
|
||||
"@gsd/pi-coding-agent",
|
||||
"@gsd/pi-tui"
|
||||
],
|
||||
"version": "2.10.10",
|
||||
"hasInstallScript": true,
|
||||
"license": "MIT",
|
||||
"workspaces": [
|
||||
|
|
@ -21,10 +14,6 @@
|
|||
],
|
||||
"dependencies": {
|
||||
"@clack/prompts": "^1.1.0",
|
||||
"@gsd/pi-agent-core": "*",
|
||||
"@gsd/pi-ai": "*",
|
||||
"@gsd/pi-coding-agent": "*",
|
||||
"@gsd/pi-tui": "*",
|
||||
"picocolors": "^1.1.1",
|
||||
"picomatch": "^4.0.3",
|
||||
"playwright": "^1.58.2",
|
||||
|
|
@ -3733,10 +3722,7 @@
|
|||
},
|
||||
"packages/pi-agent-core": {
|
||||
"name": "@gsd/pi-agent-core",
|
||||
"version": "0.57.1",
|
||||
"dependencies": {
|
||||
"@gsd/pi-ai": "*"
|
||||
}
|
||||
"version": "0.57.1"
|
||||
},
|
||||
"packages/pi-ai": {
|
||||
"name": "@gsd/pi-ai",
|
||||
|
|
@ -3760,10 +3746,6 @@
|
|||
"name": "@gsd/pi-coding-agent",
|
||||
"version": "0.57.1",
|
||||
"dependencies": {
|
||||
"@gsd/native": "*",
|
||||
"@gsd/pi-agent-core": "*",
|
||||
"@gsd/pi-ai": "*",
|
||||
"@gsd/pi-tui": "*",
|
||||
"@mariozechner/jiti": "^2.6.2",
|
||||
"@silvia-odwyer/photon-node": "^0.3.4",
|
||||
"chalk": "^5.5.0",
|
||||
|
|
@ -3792,7 +3774,6 @@
|
|||
"name": "@gsd/pi-tui",
|
||||
"version": "0.57.1",
|
||||
"dependencies": {
|
||||
"@gsd/native": "*",
|
||||
"@types/mime-types": "^2.1.4",
|
||||
"chalk": "^5.5.0",
|
||||
"get-east-asian-width": "^1.3.0",
|
||||
|
|
|
|||
19
package.json
19
package.json
|
|
@ -1,6 +1,6 @@
|
|||
{
|
||||
"name": "gsd-pi",
|
||||
"version": "2.10.9",
|
||||
"version": "2.10.10",
|
||||
"description": "GSD — Get Shit Done coding agent",
|
||||
"license": "MIT",
|
||||
"repository": {
|
||||
|
|
@ -25,6 +25,7 @@
|
|||
"pkg",
|
||||
"src/resources",
|
||||
"scripts/postinstall.js",
|
||||
"scripts/link-workspace-packages.cjs",
|
||||
"package.json",
|
||||
"README.md"
|
||||
],
|
||||
|
|
@ -52,31 +53,21 @@
|
|||
"build:native": "node native/scripts/build.js",
|
||||
"build:native:dev": "node native/scripts/build.js --dev",
|
||||
"dev": "tsc --watch",
|
||||
"postinstall": "node scripts/postinstall.js",
|
||||
"postinstall": "node scripts/link-workspace-packages.cjs && node scripts/postinstall.js",
|
||||
"pi:install-global": "node scripts/install-pi-global.js",
|
||||
"pi:uninstall-global": "node scripts/uninstall-pi-global.js",
|
||||
"sync-pkg-version": "node scripts/sync-pkg-version.cjs",
|
||||
"sync-platform-versions": "node native/scripts/sync-platform-versions.cjs",
|
||||
"prepublishOnly": "npm run sync-pkg-version && npm run sync-platform-versions && git diff --exit-code || (echo 'ERROR: version sync changed files — commit them before publishing' && exit 1) && npm run build"
|
||||
"validate-pack": "bash scripts/validate-pack.sh",
|
||||
"prepublishOnly": "npm run sync-pkg-version && npm run sync-platform-versions && git diff --exit-code || (echo 'ERROR: version sync changed files — commit them before publishing' && exit 1) && npm run build && npm run validate-pack"
|
||||
},
|
||||
"dependencies": {
|
||||
"@clack/prompts": "^1.1.0",
|
||||
"@gsd/pi-agent-core": "*",
|
||||
"@gsd/pi-ai": "*",
|
||||
"@gsd/pi-coding-agent": "*",
|
||||
"@gsd/pi-tui": "*",
|
||||
"picocolors": "^1.1.1",
|
||||
"picomatch": "^4.0.3",
|
||||
"playwright": "^1.58.2",
|
||||
"sharp": "^0.34.5"
|
||||
},
|
||||
"bundleDependencies": [
|
||||
"@gsd/native",
|
||||
"@gsd/pi-agent-core",
|
||||
"@gsd/pi-ai",
|
||||
"@gsd/pi-coding-agent",
|
||||
"@gsd/pi-tui"
|
||||
],
|
||||
"devDependencies": {
|
||||
"@types/node": "^22.0.0",
|
||||
"@types/picomatch": "^4.0.2",
|
||||
|
|
|
|||
|
|
@ -8,7 +8,5 @@
|
|||
"scripts": {
|
||||
"build": "tsc -p tsconfig.json"
|
||||
},
|
||||
"dependencies": {
|
||||
"@gsd/pi-ai": "*"
|
||||
}
|
||||
"dependencies": {}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -24,10 +24,6 @@
|
|||
"copy-assets": "node -e \"const{mkdirSync,cpSync}=require('fs');mkdirSync('dist/modes/interactive/theme',{recursive:true});cpSync('src/modes/interactive/theme','dist/modes/interactive/theme',{recursive:true,filter:(s)=>!s.endsWith('.ts')});mkdirSync('dist/core/export-html/vendor',{recursive:true});cpSync('src/core/export-html/template.html','dist/core/export-html/template.html');cpSync('src/core/export-html/template.css','dist/core/export-html/template.css');cpSync('src/core/export-html/template.js','dist/core/export-html/template.js');cpSync('src/core/export-html/vendor','dist/core/export-html/vendor',{recursive:true,filter:(s)=>!s.endsWith('.ts')});mkdirSync('dist/core/lsp',{recursive:true});cpSync('src/core/lsp/defaults.json','dist/core/lsp/defaults.json');cpSync('src/core/lsp/lsp.md','dist/core/lsp/lsp.md')\""
|
||||
},
|
||||
"dependencies": {
|
||||
"@gsd/native": "*",
|
||||
"@gsd/pi-agent-core": "*",
|
||||
"@gsd/pi-ai": "*",
|
||||
"@gsd/pi-tui": "*",
|
||||
"@mariozechner/jiti": "^2.6.2",
|
||||
"@silvia-odwyer/photon-node": "^0.3.4",
|
||||
"chalk": "^5.5.0",
|
||||
|
|
|
|||
|
|
@ -9,7 +9,6 @@
|
|||
"build": "tsc -p tsconfig.json"
|
||||
},
|
||||
"dependencies": {
|
||||
"@gsd/native": "*",
|
||||
"@types/mime-types": "^2.1.4",
|
||||
"chalk": "^5.5.0",
|
||||
"get-east-asian-width": "^1.3.0",
|
||||
|
|
|
|||
70
scripts/link-workspace-packages.cjs
Normal file
70
scripts/link-workspace-packages.cjs
Normal file
|
|
@ -0,0 +1,70 @@
|
|||
#!/usr/bin/env node
|
||||
/**
|
||||
* link-workspace-packages.cjs
|
||||
*
|
||||
* Creates node_modules/@gsd/* symlinks pointing to packages/* directories.
|
||||
*
|
||||
* During development, npm workspaces creates these automatically. But in the
|
||||
* published tarball, workspace packages are shipped under packages/ (via the
|
||||
* "files" field) and the @gsd/* imports in compiled code need node_modules/@gsd/*
|
||||
* to resolve. This script bridges the gap.
|
||||
*
|
||||
* Runs as part of postinstall (before any ESM code that imports @gsd/*).
|
||||
*/
|
||||
const { existsSync, mkdirSync, symlinkSync, lstatSync, readlinkSync, unlinkSync, readdirSync } = require('fs')
|
||||
const { resolve, join } = require('path')
|
||||
|
||||
const root = resolve(__dirname, '..')
|
||||
const packagesDir = join(root, 'packages')
|
||||
const nodeModulesGsd = join(root, 'node_modules', '@gsd')
|
||||
|
||||
// Map directory names to package names
|
||||
const packageMap = {
|
||||
'native': 'native',
|
||||
'pi-agent-core': 'pi-agent-core',
|
||||
'pi-ai': 'pi-ai',
|
||||
'pi-coding-agent': 'pi-coding-agent',
|
||||
'pi-tui': 'pi-tui',
|
||||
}
|
||||
|
||||
// Ensure @gsd scope directory exists
|
||||
if (!existsSync(nodeModulesGsd)) {
|
||||
mkdirSync(nodeModulesGsd, { recursive: true })
|
||||
}
|
||||
|
||||
let linked = 0
|
||||
for (const [dir, name] of Object.entries(packageMap)) {
|
||||
const source = join(packagesDir, dir)
|
||||
const target = join(nodeModulesGsd, name)
|
||||
|
||||
if (!existsSync(source)) continue
|
||||
|
||||
// Skip if already correctly linked or is a real directory (bundled)
|
||||
if (existsSync(target)) {
|
||||
try {
|
||||
const stat = lstatSync(target)
|
||||
if (stat.isSymbolicLink()) {
|
||||
const linkTarget = readlinkSync(target)
|
||||
if (resolve(join(nodeModulesGsd, linkTarget)) === source || linkTarget === source) {
|
||||
continue // Already correct
|
||||
}
|
||||
unlinkSync(target) // Wrong target, relink
|
||||
} else {
|
||||
continue // Real directory (e.g., from bundleDependencies), don't touch
|
||||
}
|
||||
} catch {
|
||||
continue
|
||||
}
|
||||
}
|
||||
|
||||
try {
|
||||
symlinkSync(source, target, 'junction') // junction works on Windows too
|
||||
linked++
|
||||
} catch {
|
||||
// Non-fatal — may fail in read-only environments
|
||||
}
|
||||
}
|
||||
|
||||
if (linked > 0) {
|
||||
process.stderr.write(` Linked ${linked} workspace packages\n`)
|
||||
}
|
||||
83
scripts/validate-pack.sh
Executable file
83
scripts/validate-pack.sh
Executable file
|
|
@ -0,0 +1,83 @@
|
|||
#!/usr/bin/env bash
|
||||
# validate-pack.sh — Verify the npm tarball is installable before publishing.
|
||||
#
|
||||
# Usage: npm run validate-pack (or bash scripts/validate-pack.sh)
|
||||
# Exit 0 = safe to publish, Exit 1 = broken package.
|
||||
|
||||
set -euo pipefail
|
||||
|
||||
SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)"
|
||||
ROOT="$(cd "$SCRIPT_DIR/.." && pwd)"
|
||||
cd "$ROOT"
|
||||
|
||||
# --- Guard: workspace packages must not have @gsd/* cross-deps ---
|
||||
echo "==> Checking workspace packages for @gsd/* cross-deps..."
|
||||
CROSS_FAILED=0
|
||||
for ws_pkg in native pi-agent-core pi-ai pi-coding-agent pi-tui; do
|
||||
RESULT=$(node -e "
|
||||
const pkg = require('./packages/${ws_pkg}/package.json');
|
||||
const deps = Object.keys(pkg.dependencies || {}).filter(d => d.startsWith('@gsd/'));
|
||||
if (deps.length) { console.log(deps.join(', ')); process.exit(1); }
|
||||
" 2>&1) || {
|
||||
echo " LEAKED in ${ws_pkg}: $RESULT"
|
||||
CROSS_FAILED=1
|
||||
true
|
||||
}
|
||||
done
|
||||
if [ "$CROSS_FAILED" = "1" ]; then
|
||||
echo "ERROR: Workspace packages have @gsd/* cross-dependencies."
|
||||
echo " These cause 404s when npm resolves them from the registry."
|
||||
exit 1
|
||||
fi
|
||||
echo " No @gsd/* cross-dependencies."
|
||||
|
||||
# --- Pack tarball ---
|
||||
echo "==> Packing tarball..."
|
||||
TARBALL_NAME=$(npm pack --ignore-scripts 2>/dev/null | tail -1)
|
||||
TARBALL="$ROOT/$TARBALL_NAME"
|
||||
|
||||
if [ ! -f "$TARBALL" ]; then
|
||||
echo "ERROR: npm pack produced no tarball"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
INSTALL_DIR=$(mktemp -d)
|
||||
trap 'rm -rf "$INSTALL_DIR" "$TARBALL"' EXIT
|
||||
|
||||
echo "==> Tarball: $TARBALL_NAME ($(du -h "$TARBALL" | cut -f1) compressed)"
|
||||
|
||||
# --- Check critical files using tar listing dumped to a file ---
|
||||
# (avoids SIGPIPE issues with tar | grep on Linux)
|
||||
TAR_LIST=$(mktemp)
|
||||
tar tzf "$TARBALL" > "$TAR_LIST" 2>/dev/null
|
||||
|
||||
MISSING=0
|
||||
for required in dist/loader.js packages/pi-coding-agent/dist/index.js scripts/link-workspace-packages.cjs; do
|
||||
if ! grep -q "package/${required}" "$TAR_LIST"; then
|
||||
echo " MISSING: $required"
|
||||
MISSING=1
|
||||
fi
|
||||
done
|
||||
rm -f "$TAR_LIST"
|
||||
|
||||
if [ "$MISSING" = "1" ]; then
|
||||
echo "ERROR: Critical files missing from tarball."
|
||||
exit 1
|
||||
fi
|
||||
echo " Critical files present."
|
||||
|
||||
# --- Install test ---
|
||||
echo "==> Testing install in isolated directory..."
|
||||
cd "$INSTALL_DIR"
|
||||
npm init -y > /dev/null 2>&1
|
||||
|
||||
if npm install "$TARBALL" 2>&1; then
|
||||
echo "==> Install succeeded."
|
||||
else
|
||||
echo ""
|
||||
echo "ERROR: npm install of tarball failed."
|
||||
exit 1
|
||||
fi
|
||||
|
||||
echo ""
|
||||
echo "Package is installable. Safe to publish."
|
||||
Loading…
Add table
Reference in a new issue