( rootDir: string, opts: PublishCommandOptions, )
| 70 | * transiently into the working tree, published to the channel's dist-tag, and restored. |
| 71 | */ |
| 72 | export async function publishCommand( |
| 73 | rootDir: string, |
| 74 | opts: PublishCommandOptions, |
| 75 | ): Promise<SnapshotPublishOutcome | null | void> { |
| 76 | const config = await loadConfig(rootDir); |
| 77 | const { packages, catalogs } = await discoverWorkspace(rootDir, config); |
| 78 | const { packageManager: detectedPm } = await detectWorkspaces(rootDir); |
| 79 | const depGraph = new DependencyGraph(packages); |
| 80 | |
| 81 | if (!opts.dryRun && hasUncommittedChanges({ cwd: rootDir })) { |
| 82 | log.warn('You have uncommitted changes. Commit or stash them before publishing.'); |
| 83 | process.exit(1); |
| 84 | } |
| 85 | |
| 86 | // Snapshots are a distinct, transient release model — never mixed with the channel flow. |
| 87 | if (opts.snapshot !== undefined) { |
| 88 | if (opts.channel !== undefined) { |
| 89 | log.error('--snapshot and --channel cannot be used together — they are distinct release models.'); |
| 90 | process.exit(1); |
| 91 | } |
| 92 | return await publishSnapshot(rootDir, config, packages, catalogs, detectedPm, depGraph, opts); |
| 93 | } |
| 94 | |
| 95 | const channel = resolveActiveChannel(rootDir, config, opts.channel); |
| 96 | if (channel) { |
| 97 | await publishChannel(rootDir, config, packages, catalogs, detectedPm, depGraph, channel, opts); |
| 98 | return; |
| 99 | } |
| 100 | |
| 101 | // Find packages that need publishing — use cached plan from `ci plan` if available, |
| 102 | // otherwise query the registry |
| 103 | let toPublish = await findUnpublishedWithCache(rootDir, packages, config); |
| 104 | |
| 105 | // When channels are configured, prerelease versions must never reach the stable |
| 106 | // flow (they'd land on @latest). With the no-commit model this can't normally |
| 107 | // happen — committed versions are always stable — so a suffixed version here |
| 108 | // means something went wrong. Refuse loudly rather than publish it. |
| 109 | if (Object.keys(config.channels || {}).length > 0) { |
| 110 | const prereleases = toPublish.filter((r) => semver.prerelease(r.newVersion) !== null); |
| 111 | if (prereleases.length > 0) { |
| 112 | log.error('Refusing to publish prerelease versions outside a channel:'); |
| 113 | for (const r of prereleases) log.error(` • ${r.name}@${r.newVersion}`); |
| 114 | log.error('Prerelease versions should never be committed — see https://bumpy.varlock.dev/docs/prereleases'); |
| 115 | process.exit(1); |
| 116 | } |
| 117 | } |
| 118 | |
| 119 | // Exclude packages with pending non-none bumps (they'll be superseded by the next version PR) |
| 120 | if (opts.excludePackages && opts.excludePackages.size > 0) { |
| 121 | const excluded = toPublish.filter((r) => opts.excludePackages!.has(r.name)); |
| 122 | if (excluded.length > 0) { |
| 123 | for (const r of excluded) { |
| 124 | log.dim(` Skipping ${r.name}@${r.newVersion} — pending bump will supersede this version`); |
| 125 | } |
| 126 | toPublish = toPublish.filter((r) => !opts.excludePackages!.has(r.name)); |
| 127 | } |
| 128 | } |
| 129 |
no test coverage detected
searching dependent graphs…