From ae6954a02f1cdf5feba00613c8fc00d748baa433 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E6=98=9F=E6=9C=88?= Date: Wed, 13 May 2026 17:22:50 +0800 Subject: [PATCH] fix(publish): auto-discover packages + pre-publish test gate What: Replace hardcoded PUBLISH_ORDER with auto-discovery of all non-private packages, sorted by topological dependency order (Kahn's). Add a test gate (npm test) after build, before publish. Why: The manual list was missing workflow-gateway and workflow-agent-react, causing them to never get published. Any future package additions would have the same problem. Changes: - scripts/publish.sh: Replace static PUBLISH_ORDER array with node script that reads all packages/*/package.json, filters out private, and topologically sorts by @uncaged/* internal dependencies - scripts/publish.sh: Add npm test step between build and publish, aborting on failure --- scripts/publish.sh | 108 +++++++++++++++++++++++++++++++++++++-------- 1 file changed, 90 insertions(+), 18 deletions(-) diff --git a/scripts/publish.sh b/scripts/publish.sh index a655ce9..e6c72ae 100755 --- a/scripts/publish.sh +++ b/scripts/publish.sh @@ -36,23 +36,53 @@ CURRENT=$(current_version) VERSION=$(bump_version "$CURRENT" "${1:?Usage: publish.sh }") echo "๐Ÿ“ฆ Publish: $CURRENT โ†’ $VERSION" -# โ”€โ”€โ”€ Topological publish order โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€ -PUBLISH_ORDER=( - workflow-protocol - workflow-util - workflow-cas - workflow-runtime - workflow-reactor - workflow-register - workflow-execute - cli-workflow - workflow-util-agent - workflow-agent-cursor - workflow-agent-hermes - workflow-agent-llm - workflow-template-develop - workflow-template-solve-issue -) +# โ”€โ”€โ”€ Auto-discover publishable packages (topological order) โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€ +# Finds all non-private packages and sorts by internal dependency count (fewest first) +mapfile -t PUBLISH_ORDER < <(node -e " + const fs = require('fs'); + const path = require('path'); + const pkgsDir = path.join('$REPO_ROOT', 'packages'); + const dirs = fs.readdirSync(pkgsDir).filter(d => + fs.existsSync(path.join(pkgsDir, d, 'package.json')) + ); + // Collect non-private packages + const pkgs = new Map(); + for (const d of dirs) { + const p = JSON.parse(fs.readFileSync(path.join(pkgsDir, d, 'package.json'), 'utf8')); + if (p.private) continue; + const deps = new Set(); + for (const k of ['dependencies','peerDependencies','devDependencies']) { + if (!p[k]) continue; + for (const n of Object.keys(p[k])) { + if (n.startsWith('@uncaged/')) deps.add(n.replace('@uncaged/','')); + } + } + pkgs.set(d, deps); + } + // Topological sort (Kahn's) โ€” publish dependencies before dependents + const inDeg = new Map([...pkgs.keys()].map(k => [k, 0])); + for (const [pkg, deps] of pkgs) { + for (const dep of deps) { + if (pkgs.has(dep)) inDeg.set(pkg, (inDeg.get(pkg) || 0) + 1); + } + } + const queue = [...inDeg.entries()].filter(([,d]) => d === 0).map(([k]) => k).sort(); + const order = []; + while (queue.length) { + const n = queue.shift(); + order.push(n); + for (const [pkg, deps] of pkgs) { + if (deps.has(n)) { + inDeg.set(pkg, inDeg.get(pkg) - 1); + if (inDeg.get(pkg) === 0) queue.push(pkg); + } + } + } + // Append any remaining (circular or isolated) โ€” should not happen + for (const k of pkgs.keys()) { if (!order.includes(k)) order.push(k); } + order.forEach(o => console.log(o)); +") +echo "๐Ÿ“‹ Discovered ${#PUBLISH_ORDER[@]} packages: ${PUBLISH_ORDER[*]}" # โ”€โ”€โ”€ Bump version โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€ echo "๐Ÿ”ข Bumping versions..." @@ -92,6 +122,13 @@ done echo "๐Ÿ”จ Building..." npm run build +# โ”€โ”€โ”€ Self-test โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€ +echo "๐Ÿงช Running tests..." +if ! npm test; then + echo "โŒ Tests failed โ€” aborting publish" + exit 1 +fi + # โ”€โ”€โ”€ Publish โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€ echo "๐Ÿš€ Publishing..." cat > "$REPO_ROOT/.npmrc" <" git push -[[ "$FAIL" -eq 0 ]] && echo "โœ… v${VERSION} published" || echo "โš ๏ธ v${VERSION} published with errors" +if [[ "$FAIL" -ne 0 ]]; then + echo "โš ๏ธ v${VERSION} published with errors" + exit 1 +fi + +# โ”€โ”€โ”€ Post-publish smoke test โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€ +echo "๐Ÿ” Smoke test: installing & verifying published packages..." +SMOKE_DIR=$(mktemp -d) +trap "rm -rf $SMOKE_DIR" EXIT + +cat > "$SMOKE_DIR/.npmrc" </dev/null 2>&1 && npm install $PKGS_TO_INSTALL 2>&1) || { + echo "โŒ Smoke test failed: could not install packages" + exit 1 +} + +# Try importing each package +for pkg_dir in "${PUBLISH_ORDER[@]}"; do + if ! (cd "$SMOKE_DIR" && node -e "require('@uncaged/${pkg_dir}')" 2>&1); then + echo "โŒ Smoke test failed: require('@uncaged/${pkg_dir}') threw" + exit 1 + fi + echo " โœ… @uncaged/${pkg_dir} โ€” importable" +done + +echo "โœ… v${VERSION} published & verified"