From 6f270d1ef4c6f13d320c72201f6ee6f0afe179a1 Mon Sep 17 00:00:00 2001 From: jithinraj <7850727+jithinraj@users.noreply.github.com> Date: Mon, 9 Mar 2026 20:04:35 +0530 Subject: [PATCH] test: expand post-publish registry smoke to full publish manifest --- .../release/pack-install-smoke-registry.sh | 184 ++++++++++++++++-- 1 file changed, 163 insertions(+), 21 deletions(-) diff --git a/scripts/release/pack-install-smoke-registry.sh b/scripts/release/pack-install-smoke-registry.sh index 9c5957f6..f4b5ed20 100755 --- a/scripts/release/pack-install-smoke-registry.sh +++ b/scripts/release/pack-install-smoke-registry.sh @@ -3,9 +3,12 @@ set -euo pipefail # Pack-Install Smoke Test: Post-Publish (Registry) # -# Installs representative packages from the npm registry and verifies: +# Installs packages from the npm registry and verifies: # ESM import, CJS require, TypeScript types resolution, and CLI bin execution. # +# By default, reads the full package list from scripts/publish-manifest.json. +# Use --representative for a fast 5-package layer-spanning subset. +# # This script should be run AFTER publishing to verify that packages resolve # correctly from the public registry with all cross-dependencies satisfied. # For pre-publish local tarball verification, see pack-install-smoke.sh. @@ -13,53 +16,78 @@ set -euo pipefail # Usage: # bash scripts/release/pack-install-smoke-registry.sh # bash scripts/release/pack-install-smoke-registry.sh --dist-tag next +# bash scripts/release/pack-install-smoke-registry.sh --representative # # Flags: -# --dist-tag Install from a specific dist-tag (default: latest) +# --dist-tag Install from a specific dist-tag (default: latest) +# --representative Test only 5 representative packages (fast path) SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" REPO_ROOT="$(cd "$SCRIPT_DIR/../.." && pwd)" cd "$REPO_ROOT" DIST_TAG="latest" +REPRESENTATIVE=false while [[ $# -gt 0 ]]; do case "$1" in --dist-tag) DIST_TAG="$2" shift 2 ;; + --representative) + REPRESENTATIVE=true + shift + ;; *) echo "Unknown option: $1" >&2 - echo "Usage: $0 [--dist-tag ]" >&2 + echo "Usage: $0 [--dist-tag ] [--representative]" >&2 exit 1 ;; esac done VERSION=$(node -p "require('./package.json').version") +MANIFEST="$REPO_ROOT/scripts/publish-manifest.json" CONSUMER_DIR=$(mktemp -d) FAILED=0 +TOTAL_TESTED=0 +TOTAL_BINS=0 +WORKSPACE_CLEAN=true cleanup() { rm -rf "$CONSUMER_DIR" } trap cleanup EXIT +# --- Build package list --- +SMOKE_PACKAGES=() +if $REPRESENTATIVE; then + # Layer-spanning subset: L0 kernel, L1 schema, L2 crypto, L3 protocol, L5 mcp-server. + # All are in the publish manifest and available on npm. + SMOKE_PACKAGES=( + "@peac/kernel" + "@peac/schema" + "@peac/crypto" + "@peac/protocol" + "@peac/mcp-server" + ) + MODE="representative (${#SMOKE_PACKAGES[@]} packages)" +else + while IFS= read -r line; do + SMOKE_PACKAGES+=("$line") + done < <(node -e " + const m = JSON.parse(require('fs').readFileSync('$MANIFEST', 'utf-8')); + m.packages.forEach(p => console.log(p)); + ") + MODE="full manifest (${#SMOKE_PACKAGES[@]} packages)" +fi + echo "=== Pack-Install Smoke Test (Post-Publish: Registry) ===" echo " Version: $VERSION" echo " Dist-tag: $DIST_TAG" +echo " Mode: $MODE" echo "" -# Representative packages across layers. -SMOKE_PACKAGES=( - "@peac/kernel" - "@peac/schema" - "@peac/crypto" - "@peac/protocol" - "@peac/mcp-server" - "@peac/adapter-eat" -) - # --- Install all packages from registry --- cd "$CONSUMER_DIR" npm init -y > /dev/null 2>&1 @@ -79,14 +107,90 @@ else exit 1 fi +# --- Check for unresolved workspace dependencies --- +echo "" +echo "--- Workspace dependency check ---" +if grep -r "workspace:" node_modules/@peac/*/package.json 2>/dev/null; then + echo " FAIL: Found unresolved workspace:* dependencies" + FAILED=$((FAILED + 1)) + WORKSPACE_CLEAN=false +else + echo " OK: No unresolved workspace dependencies" +fi + +# --- Resolve bin names for packages that have them --- +resolve_bin_name() { + local pkg_name="$1" + local pkg_json="node_modules/$pkg_name/package.json" + if [ ! -f "$pkg_json" ]; then + echo "" + return + fi + node -e ' + const p = require("./'$pkg_json'"); + const b = p.bin; + if (b == null) process.exit(0); + if (typeof b === "string") { process.stdout.write(p.name.split("/").pop()); process.exit(0); } + const k = Object.keys(b)[0]; + if (k) process.stdout.write(k); + ' 2>/dev/null || echo "" +} + # --- Verify each package --- echo "" echo "--- Verify ---" for pkg_name in "${SMOKE_PACKAGES[@]}"; do echo -n " [$pkg_name] " - # Verify installed version - installed_version=$(node -p "require('$pkg_name/package.json').version" 2>/dev/null || echo "unknown") + TOTAL_TESTED=$((TOTAL_TESTED + 1)) + + # Verify installed version (handles strict exports maps that block /package.json) + installed_version=$(node -e ' + try { console.log(require("'$pkg_name'/package.json").version); } + catch { try { const fs = require("fs"); const p = JSON.parse(fs.readFileSync("node_modules/'$pkg_name'/package.json","utf-8")); console.log(p.version); } + catch { console.log("unknown"); } } + ' 2>/dev/null || echo "unknown") + if [ "$installed_version" = "unknown" ]; then + echo "FAIL (not installed)" + FAILED=$((FAILED + 1)) + continue + fi + + # Detect if this is a CLI-only package (has bin, no library exports) + has_bin=false + bin_ok=true + bin_name=$(resolve_bin_name "$pkg_name") + if [ -n "$bin_name" ]; then + has_bin=true + TOTAL_BINS=$((TOTAL_BINS + 1)) + ./node_modules/.bin/"$bin_name" --help > /dev/null 2>&1 || bin_ok=false + fi + + # Check if package exports any library surface (some packages are CLI-only) + has_exports=true + node --input-type=module -e " + const m = await import('$pkg_name'); + if (Object.keys(m).length === 0) process.exit(1); + " > /dev/null 2>&1 || { + has_exports=false + } + + # For CLI-only packages: pass if bin works and types exist + if $has_bin && ! $has_exports; then + types_ok=true + if ! find "node_modules/$pkg_name" -name '*.d.ts' -print -quit 2>/dev/null | grep -q .; then + types_ok=false + fi + if $bin_ok; then + types_label="" + $types_ok && types_label=" +types" + echo "PASS (v${installed_version}, cli-only +bin${types_label})" + else + echo "FAIL (v${installed_version}, cli-only -bin)" + FAILED=$((FAILED + 1)) + fi + continue + fi # Test ESM import esm_ok=true @@ -102,13 +206,26 @@ for pkg_name in "${SMOKE_PACKAGES[@]}"; do } } - # Test CJS require + # Test CJS require (skip for ESM-only packages) cjs_ok=true + esm_only=false node -e " const m = require('$pkg_name'); if (typeof m === 'undefined' || (typeof m === 'object' && Object.keys(m).length === 0)) throw new Error('empty'); " > /dev/null 2>&1 || { - cjs_ok=false + # Check if the package is ESM-only (no require condition in exports) + node -e ' + const fs = require("fs"); + const p = JSON.parse(fs.readFileSync("node_modules/'$pkg_name'/package.json","utf-8")); + const e = p.exports && p.exports["."]; + if (e && typeof e === "object" && e.import && (e.require == null)) process.exit(0); + process.exit(1); + ' > /dev/null 2>&1 && { + esm_only=true + cjs_ok=true + } || { + cjs_ok=false + } } # Test types existence @@ -118,12 +235,30 @@ for pkg_name in "${SMOKE_PACKAGES[@]}"; do fi if $esm_ok && $cjs_ok && $types_ok; then - echo "PASS (v${installed_version}, esm +cjs +types)" + format_label="esm" + if $esm_only; then + format_label="esm-only" + else + format_label="esm +cjs" + fi + extras="" + if $has_bin && $bin_ok; then + extras=" +bin" + elif $has_bin && ! $bin_ok; then + extras=" -bin" + FAILED=$((FAILED + 1)) + fi + echo "PASS (v${installed_version}, ${format_label} +types${extras})" else failures="" $esm_ok || failures="${failures} esm" - $cjs_ok || failures="${failures} cjs" + if ! $esm_only; then + $cjs_ok || failures="${failures} cjs" + fi $types_ok || failures="${failures} types" + if $has_bin && ! $bin_ok; then + failures="${failures} bin" + fi echo "FAIL (v${installed_version}, ${failures# })" FAILED=$((FAILED + 1)) fi @@ -131,11 +266,18 @@ done cd "$REPO_ROOT" +# --- Summary --- +echo "" +echo "--- Summary ---" +echo " Packages tested: $TOTAL_TESTED" +echo " Packages with bins: $TOTAL_BINS" +echo " Workspace deps clean: $WORKSPACE_CLEAN" +echo " Failures: $FAILED" echo "" if [ "$FAILED" -eq 0 ]; then - echo "All registry smoke tests passed." + echo "All registry smoke tests passed ($MODE)." exit 0 else - echo "$FAILED package(s) failed registry smoke test." + echo "$FAILED check(s) failed registry smoke test ($MODE)." exit 1 fi