* Try to load cached plan from `ci plan`. Returns the unpublished package names * if the cache is valid, or null to fall back to registry lookups. * * Validates that every cached package exists in the workspace with the same version, * so the cache can only filter — never fabricate — the set of
(rootDir: string, packages: Map<string, WorkspacePackage>)
| 691 | * so the cache can only filter — never fabricate — the set of packages. |
| 692 | */ |
| 693 | function loadCachedPlan(rootDir: string, packages: Map<string, WorkspacePackage>): Set<string> | null { |
| 694 | const cachePath = `${rootDir}/${CI_PLAN_CACHE_PATH}`; |
| 695 | let raw: string; |
| 696 | try { |
| 697 | raw = require('node:fs').readFileSync(cachePath, 'utf-8'); |
| 698 | // Clean up cache file after reading |
| 699 | require('node:fs').unlinkSync(cachePath); |
| 700 | } catch { |
| 701 | return null; |
| 702 | } |
| 703 | |
| 704 | try { |
| 705 | const cached = JSON.parse(raw); |
| 706 | if (cached?.mode !== 'publish' || !Array.isArray(cached.releases)) return null; |
| 707 | |
| 708 | const names = new Set<string>(); |
| 709 | for (const r of cached.releases) { |
| 710 | if (typeof r.name !== 'string' || typeof r.newVersion !== 'string') return null; |
| 711 | // Validate against workspace — reject if package doesn't exist or version doesn't match |
| 712 | const pkg = packages.get(r.name); |
| 713 | if (!pkg || pkg.version !== r.newVersion) { |
| 714 | log.dim(' ci plan cache is stale — falling back to registry lookups'); |
| 715 | return null; |
| 716 | } |
| 717 | names.add(r.name); |
| 718 | } |
| 719 | |
| 720 | log.dim(' Using cached plan from ci plan'); |
| 721 | return names; |
| 722 | } catch { |
| 723 | return null; |
| 724 | } |
| 725 | } |
| 726 | |
| 727 | /** |
| 728 | * Find unpublished packages, using the ci plan cache if available. |
no outgoing calls
no test coverage detected
searching dependent graphs…