(
stablePlan: ReleasePlan,
channel: ResolvedChannel,
packages: Map<string, WorkspacePackage>,
rootDir: string,
opts: {
/**
* Display mode (release PR titles/bodies, status output): compute counters but
* skip the published-from-HEAD checks — the numbers are advisory narrative and
* the registry wins at actual publish time.
*/
forDisplay?: boolean;
} = {},
)
| 123 | * was already published from this exact commit — it is skipped rather than re-counted. |
| 124 | */ |
| 125 | export async function buildChannelReleasePlan( |
| 126 | stablePlan: ReleasePlan, |
| 127 | channel: ResolvedChannel, |
| 128 | packages: Map<string, WorkspacePackage>, |
| 129 | rootDir: string, |
| 130 | opts: { |
| 131 | /** |
| 132 | * Display mode (release PR titles/bodies, status output): compute counters but |
| 133 | * skip the published-from-HEAD checks — the numbers are advisory narrative and |
| 134 | * the registry wins at actual publish time. |
| 135 | */ |
| 136 | forDisplay?: boolean; |
| 137 | } = {}, |
| 138 | ): Promise<ChannelReleasePlanResult> { |
| 139 | const headSha = tryRunArgs(['git', 'rev-parse', 'HEAD'], { cwd: rootDir }); |
| 140 | const warnings: string[] = [...stablePlan.warnings]; |
| 141 | const alreadyPublished: Array<{ name: string; version: string }> = []; |
| 142 | const releases: PlannedRelease[] = []; |
| 143 | |
| 144 | await Promise.all( |
| 145 | stablePlan.releases.map(async (release) => { |
| 146 | const pkg = packages.get(release.name); |
| 147 | if (!pkg) return; |
| 148 | // Unpublishable packages can't participate in a registry-consumable cycle |
| 149 | if (pkg.private && !pkg.bumpy?.publishCommand) return; |
| 150 | |
| 151 | const target = release.newVersion; // stable target from the bump files |
| 152 | const state = await getPublishedPrereleaseState(pkg, target, channel.preid, rootDir); |
| 153 | |
| 154 | if (state.stablePublished) { |
| 155 | warnings.push( |
| 156 | `${release.name}@${target} is already published as a stable release — ` + |
| 157 | `merge ${target}'s release into the "${channel.branch}" branch so the cycle retargets.`, |
| 158 | ); |
| 159 | } |
| 160 | |
| 161 | if (!opts.forDisplay && state.counters.length > 0 && headSha) { |
| 162 | const latest = `${target}-${channel.preid}.${Math.max(...state.counters)}`; |
| 163 | const publishedFromHead = usesNpmRegistry(pkg) |
| 164 | ? (await fetchGitHead(pkg.name, latest, pkg.bumpy?.registry)) === headSha |
| 165 | : tryRunArgs(['git', 'rev-parse', `refs/tags/${pkg.name}@${latest}`], { cwd: rootDir }) === headSha; |
| 166 | if (publishedFromHead) { |
| 167 | alreadyPublished.push({ name: release.name, version: latest }); |
| 168 | return; |
| 169 | } |
| 170 | } |
| 171 | |
| 172 | releases.push({ |
| 173 | ...release, |
| 174 | newVersion: nextPrereleaseVersion(target, channel.preid, state.counters), |
| 175 | }); |
| 176 | }), |
| 177 | ); |
| 178 | |
| 179 | releases.sort((a, b) => a.name.localeCompare(b.name)); |
| 180 | return { |
| 181 | plan: { bumpFiles: stablePlan.bumpFiles, releases, warnings }, |
| 182 | alreadyPublished, |
no test coverage detected
searching dependent graphs…