@@ -24,18 +24,26 @@ jobs:
2424
2525 - uses : oven-sh/setup-bun@v2
2626
27- - name : Configure git
28- run : |
29- git config user.name "github-actions[bot]"
30- git config user.email "github-actions[bot]@users.noreply.github.com"
31-
32- - name : Publish changed non-private packages
27+ # Install the whole workspace once with bun (the repo's package manager).
28+ # bun understands the "workspace:*" protocol and links local packages, so
29+ # every package gets its devDependencies (e.g. vite) and can be built.
30+ # Plain `npm install` cannot do this inside a bun workspace β it dies on
31+ # sibling "workspace:*" deps (EUNSUPPORTEDPROTOCOL) and peer conflicts,
32+ # which left devDependencies uninstalled and builds failing with
33+ # "vite: not found".
34+ - name : Install workspace dependencies
35+ run : bun install
36+
37+ - name : Publish non-private packages with a new version
3338 env :
3439 NODE_AUTH_TOKEN : ${{ secrets.NPM_TOKEN }}
3540 run : |
36- set -euo pipefail
41+ # Note: intentionally no `-e`. Per-package outcomes are handled
42+ # explicitly so one package's failure can't abort the whole run.
43+ set -uo pipefail
3744
3845 failed_packages=""
46+ skipped_builds=""
3947
4048 for dir in packages/*/; do
4149 pkg="${dir}package.json"
@@ -47,66 +55,95 @@ jobs:
4755
4856 is_private=$(node -e "const p=require('./$pkg'); console.log(!!p.private)")
4957 name=$(node -e "const p=require('./$pkg'); console.log(p.name)")
50- version=$(node -e "const p=require('./$pkg'); console.log(p.version)")
5158
5259 if [ "$is_private" = "true" ]; then
5360 echo "β Skipping $name (private)"
5461 continue
5562 fi
5663
57- if git diff --quiet HEAD^ HEAD -- "$dir"; then
58- echo "β Skipping $name (no changes)"
59- continue
60- fi
61-
62- echo "π¦ Releasing $name from $dir"
64+ echo "π¦ Evaluating $name from $dir"
6365
66+ # Each package is handled in a subshell with its own `set -e` so a
67+ # failing step propagates to $rc without killing the outer loop.
6468 (
69+ set -e
6570 cd "$dir"
6671
67- remote_version =$(npm view "$name" version 2>/dev/null || echo " ")
72+ version =$(node -e "const p=require('./package.json'); console.log(p.version) ")
6873
69- if [ "$remote_version" = "$version" ]; then
70- echo "πΌ Bumping $name because $version already exists on npm"
71- npm version patch --no-git-tag-version
72- version=$(node -e "const p=require('./package.json'); console.log(p.version)")
74+ # Publish based on the version, not on which files the triggering
75+ # commit touched: if this exact version is already on npm there is
76+ # nothing new to release; otherwise a version bump signals changes
77+ # since the last published release, so publish it.
78+ already=$(npm view "$name@$version" version 2>/dev/null || echo "")
79+ if [ -n "$already" ]; then
80+ echo "β $name@$version already on npm β nothing new to release"
81+ exit 0
7382 fi
7483
75- # Strip workspace:* dependencies before install and build
84+ # npm keeps the literal "workspace:*" protocol in the tarball, which
85+ # consumers cannot resolve. Replace each "workspace:*" with a real
86+ # semver range ("^<version>") taken from the referenced local
87+ # package so published packages keep depending on their siblings.
88+ # A local package that isn't published is dropped instead.
89+ # (This runs on the ephemeral CI checkout and is never committed.)
7690 node -e "
7791 const fs = require('fs');
78- const pkg = JSON.parse(fs.readFileSync('package.json', 'utf8'));
79- for (const section of ['dependencies', 'devDependencies', 'peerDependencies']) {
80- if (pkg[section]) {
81- for (const [key, value] of Object.entries(pkg[section])) {
82- if (typeof value === 'string' && value.startsWith('workspace:')) {
83- delete pkg[section][key];
84- console.log('Removed workspace dependency:', key);
85- }
92+ const path = require('path');
93+ // Map local package name -> version across the workspace.
94+ const localVersions = {};
95+ for (const d of fs.readdirSync('..')) {
96+ const sib = path.join('..', d, 'package.json');
97+ if (!fs.existsSync(sib)) continue;
98+ try {
99+ const sp = JSON.parse(fs.readFileSync(sib, 'utf8'));
100+ if (sp.name && sp.version) localVersions[sp.name] = sp.version;
101+ } catch {}
102+ }
103+ const p = JSON.parse(fs.readFileSync('package.json', 'utf8'));
104+ for (const s of ['dependencies', 'devDependencies', 'peerDependencies']) {
105+ if (!p[s]) continue;
106+ for (const [k, v] of Object.entries(p[s])) {
107+ if (typeof v !== 'string' || !v.startsWith('workspace:')) continue;
108+ if (localVersions[k]) {
109+ p[s][k] = '^' + localVersions[k];
110+ console.log('Pinned workspace dependency', k, '->', p[s][k]);
111+ } else {
112+ delete p[s][k];
113+ console.log('Removed unresolved workspace dependency:', k);
86114 }
87115 }
88116 }
89- fs.writeFileSync('package.json', JSON.stringify(pkg , null, 2));
117+ fs.writeFileSync('package.json', JSON.stringify(p , null, 2));
90118 "
91119
92120 npm install --workspaces=false --ignore-scripts --legacy-peer-deps --include=dev
93121
94122 has_build=$(node -e "const p=require('./package.json'); console.log(!!(p.scripts && p.scripts.build))")
123+ build_ok=true
95124 if [ "$has_build" = "true" ]; then
96- npm run build
125+ npm run build || build_ok=false
126+ fi
127+
128+ if [ "$build_ok" != "true" ]; then
129+ echo "β Build failed for $name β not publishing (won't ship a broken tarball)"
130+ exit 42
97131 fi
98132
99- echo "Publishing $(node -e "const p=require('./package.json'); console.log(p. name+'@'+p. version)") "
133+ echo "Publishing $name@$ version"
100134 npm publish --provenance --access public
101- ) || {
102- echo "β Failed to publish $name"
135+ )
136+ rc=$?
137+
138+ if [ "$rc" = "42" ]; then
139+ skipped_builds="$skipped_builds $name"
140+ elif [ "$rc" != "0" ]; then
103141 failed_packages="$failed_packages $name"
104- }
142+ fi
105143 done
106144
107- if [ -n "$failed_packages" ]; then
108- echo "β Failed packages:$failed_packages"
109- exit 1
145+ if [ -n "$skipped_builds" ]; then
146+ echo "::warning::Skipped (build failed, not published):$skipped_builds"
110147 fi
111148
112149 - name : Commit version bumps
@@ -117,4 +154,4 @@ jobs:
117154 git add "${files[@]}"
118155 git diff --staged --quiet && echo "No version changes to commit" && exit 0
119156 git commit -m "chore: bump published package versions [skip ci]"
120- git push
157+ git push
0 commit comments