fix(release): print the publish order and the peer edges it drops
The verify step resolved the publish order and said only that it had: the order a release actually follows, and the ordering it could not honour, stayed invisible until a publication was already running. publishOrder now returns that order together with the peer edges it dropped, verify prints both, and pack reads the order off the plan. The dropped edges are part of the result rather than a detail of forming it: the dsh family drops one (dsh-api-remotes -> dsh-api-gateway) and the vendored family drops two (cordis-plugin-include and cordis-plugin-loader, which cordis declares as peers in return), and only whoever reads the log can judge whether a newly dropped edge is expected. Because pack runs on every pull request and master push, a change to the order is now reviewable there rather than observable only at publish time. The order is also checked against the edges it exists to honour. A cycle mixing peer and dependency declarations can put a dependency on the traversal stack, where it is skipped like a peer edge, emitting a consumer before something it installs; no later step can detect that, and it would surface as an unresolvable install for a consumer of the published packages. No family has that shape today, and the new test pins the three-package case that would.
This commit is contained in:
@@ -9,7 +9,33 @@
|
||||
|
||||
import { parseArgs } from 'node:util'
|
||||
import { isEntry } from './process.ts'
|
||||
import { releaseFamily, type ReleaseFamily, type ReleaseMember } from './families.ts'
|
||||
import { releaseFamily, type PublishPlan, type ReleaseFamily, type ReleaseMember } from './families.ts'
|
||||
|
||||
/**
|
||||
* Print the publish order the release will follow, and the peer declarations it
|
||||
* leaves unordered.
|
||||
*
|
||||
* The order is the release's own plan: an interrupted publication leaves exactly
|
||||
* a prefix of it, so reading it is how anyone judges what a partial run left on
|
||||
* the registry, and printing it on every pull request is what makes a change to
|
||||
* the order reviewable rather than only observable during a publication.
|
||||
* @param family - the release family.
|
||||
* @param plan - the resolved order and its dropped edges.
|
||||
*/
|
||||
function reportPublishOrder(family: ReleaseFamily, plan: PublishPlan): void {
|
||||
console.log(`release verify: publish order for family ${family.id}, ${String(plan.order.length)} member(s):`)
|
||||
const width = String(plan.order.length).length
|
||||
for (const [index, member] of plan.order.entries()) {
|
||||
console.log(` ${String(index + 1).padStart(width, ' ')} ${member.name}@${member.version}`)
|
||||
}
|
||||
if (plan.droppedPeerEdges.length === 0) return
|
||||
console.log(
|
||||
`release verify: ${String(plan.droppedPeerEdges.length)} peer declaration(s) publish unordered,`
|
||||
+ ' because the peer cannot precede the package declaring it without contradicting a dependency edge'
|
||||
+ ' or its own cycle. npm treats an unmet peer as a warning, so this orders nothing and blocks nothing:',
|
||||
)
|
||||
for (const edge of plan.droppedPeerEdges) console.log(` ${edge.consumer} -> ${edge.peer}`)
|
||||
}
|
||||
|
||||
/**
|
||||
* Assert every member may be published: npm refuses a `private` package.
|
||||
@@ -58,12 +84,13 @@ function main(): void {
|
||||
// Resolve the publish order here, before the build: an install-edge cycle
|
||||
// makes the order unrepresentable, and that has to surface at the first gate
|
||||
// rather than when pack is already writing tarballs.
|
||||
const ordered = family.publishOrder(members)
|
||||
if (ordered.length !== members.length) {
|
||||
const plan = family.publishOrder(members)
|
||||
if (plan.order.length !== members.length) {
|
||||
throw new Error(
|
||||
`release family ${family.id}: publish order covers ${String(ordered.length)} of ${String(members.length)} members`,
|
||||
`release family ${family.id}: publish order covers ${String(plan.order.length)} of ${String(members.length)} members`,
|
||||
)
|
||||
}
|
||||
reportPublishOrder(family, plan)
|
||||
|
||||
const publishing = process.env.RELEASE_PUBLISH === 'true'
|
||||
if (publishing) {
|
||||
@@ -73,7 +100,11 @@ function main(): void {
|
||||
|
||||
const versions = [...new Set(members.map(member => member.version))]
|
||||
const summary = versions.length === 1 ? versions[0] : `${String(versions.length)} versions`
|
||||
console.log(`release verify: family ${family.id}, ${String(members.length)} member(s), ${summary}, publish order resolved${publishing ? ', publish gates passed' : ''}`)
|
||||
console.log(
|
||||
`release verify: family ${family.id}, ${String(members.length)} member(s), ${summary},`
|
||||
+ ` publish order resolved, ${String(plan.droppedPeerEdges.length)} peer declaration(s) unordered`
|
||||
+ (publishing ? ', publish gates passed' : ''),
|
||||
)
|
||||
}
|
||||
|
||||
if (isEntry(import.meta.url)) main()
|
||||
|
||||
Reference in New Issue
Block a user