feat(cicd): fail when fixes sit unshipped on a consumed release line - #14429
Conversation
📄 Knowledge reviewDosu skipped reviewing this PR because your organization has used its |
🎭 Playwright: ✅ 1790 passed, 0 failed · 2 flaky📊 Browser Reports
📦 Bundle: 8.69 MB gzip 🟢 -327 BDetailsSummary
Category Glance App Entry Points — 3.67 kB (baseline 3.67 kB) • ⚪ 0 BMain entry bundles and manifests
Status: 1 added / 1 removed Graph Workspace — 1.36 MB (baseline 1.36 MB) • 🟢 -78 BGraph editor runtime, canvas, workflow orchestration
Status: 1 added / 1 removed / 2 unchanged Views & Navigation — 124 kB (baseline 124 kB) • ⚪ 0 BTop-level views, pages, and routed surfaces Status: 17 unchanged Panels & Settings — 570 kB (baseline 570 kB) • ⚪ 0 BConfiguration panels, inspectors, and settings screens Status: 26 unchanged User & Accounts — 27 kB (baseline 27 kB) • ⚪ 0 BAuthentication, profile, and account management bundles Status: 10 unchanged Editors & Dialogs — 125 kB (baseline 125 kB) • ⚪ 0 BModals, dialogs, drawers, and in-app editors Status: 8 unchanged UI Components — 67.1 kB (baseline 67.1 kB) • ⚪ 0 BReusable component library chunks Status: 14 unchanged Data & Services — 3.5 MB (baseline 3.5 MB) • ⚪ 0 BStores, services, APIs, and repositories Status: 17 unchanged Utilities & Hooks — 549 kB (baseline 550 kB) • 🟢 -532 BHelpers, composables, and utility bundles
Status: 1 added / 1 removed / 37 unchanged Vendor & Third-Party — 16.3 MB (baseline 16.3 MB) • ⚪ 0 BExternal libraries and shared vendor chunks Status: 17 unchanged Other — 14 MB (baseline 14 MB) • ⚪ 0 BBundles that do not match a named category
Status: 1 added / 1 removed / 285 unchanged ⚡ Performance Report
Show regressions
All metrics
Historical variance (last 15 runs)
Trend (last 15 commits on main)
Raw data{
"timestamp": "2026-08-09T01:23:09.453Z",
"gitSha": "ccb7449b5d476660aea1ffecf46853d818fca16e",
"branch": "cb__release-stranded-guard",
"measurements": [
{
"name": "canvas-idle",
"durationMs": 2037.3289999999997,
"styleRecalcs": 9,
"styleRecalcDurationMs": 8.116999999999999,
"layouts": 0,
"layoutDurationMs": 0,
"taskDurationMs": 516.452,
"heapDeltaBytes": 5872180,
"heapUsedBytes": 70323132,
"domNodes": 18,
"jsHeapTotalBytes": 24641536,
"scriptDurationMs": 21.334,
"eventListeners": 4,
"totalBlockingTimeMs": 0,
"frameDurationMs": 16.666666666666668,
"p95FrameDurationMs": 16.800000000000182
},
{
"name": "canvas-idle",
"durationMs": 2039.0129999999544,
"styleRecalcs": 10,
"styleRecalcDurationMs": 8.527999999999999,
"layouts": 0,
"layoutDurationMs": 0,
"taskDurationMs": 473.28799999999995,
"heapDeltaBytes": 5796392,
"heapUsedBytes": 70318476,
"domNodes": 20,
"jsHeapTotalBytes": 24903680,
"scriptDurationMs": 15.010000000000003,
"eventListeners": 4,
"totalBlockingTimeMs": 0,
"frameDurationMs": 16.66333333333332,
"p95FrameDurationMs": 16.700000000000728
},
{
"name": "canvas-mouse-sweep",
"durationMs": 1988.1409999999846,
"styleRecalcs": 77,
"styleRecalcDurationMs": 42.508,
"layouts": 12,
"layoutDurationMs": 4.8549999999999995,
"taskDurationMs": 979.4590000000001,
"heapDeltaBytes": 584776,
"heapUsedBytes": 64897304,
"domNodes": 59,
"jsHeapTotalBytes": 25165824,
"scriptDurationMs": 132.602,
"eventListeners": 6,
"totalBlockingTimeMs": 0,
"frameDurationMs": 16.666666666666668,
"p95FrameDurationMs": 16.800000000000182
},
{
"name": "canvas-mouse-sweep",
"durationMs": 1822.0640000000685,
"styleRecalcs": 75,
"styleRecalcDurationMs": 35.17,
"layouts": 12,
"layoutDurationMs": 3.3229999999999995,
"taskDurationMs": 856.929,
"heapDeltaBytes": -13574088,
"heapUsedBytes": 50931108,
"domNodes": -280,
"jsHeapTotalBytes": 23568384,
"scriptDurationMs": 119.48,
"eventListeners": -153,
"totalBlockingTimeMs": 0,
"frameDurationMs": 16.66333333333332,
"p95FrameDurationMs": 16.800000000000182
},
{
"name": "canvas-zoom-sweep",
"durationMs": 1755.3859999999872,
"styleRecalcs": 32,
"styleRecalcDurationMs": 18.555000000000003,
"layouts": 6,
"layoutDurationMs": 0.685,
"taskDurationMs": 389.767,
"heapDeltaBytes": 8810548,
"heapUsedBytes": 73286140,
"domNodes": 77,
"jsHeapTotalBytes": 24379392,
"scriptDurationMs": 19.932000000000002,
"eventListeners": 19,
"totalBlockingTimeMs": 0,
"frameDurationMs": 16.666666666666668,
"p95FrameDurationMs": 16.800000000000182
},
{
"name": "canvas-zoom-sweep",
"durationMs": 1732.0700000000215,
"styleRecalcs": 31,
"styleRecalcDurationMs": 18.268,
"layouts": 6,
"layoutDurationMs": 0.551,
"taskDurationMs": 407.78900000000004,
"heapDeltaBytes": 8861400,
"heapUsedBytes": 73192232,
"domNodes": 76,
"jsHeapTotalBytes": 24379392,
"scriptDurationMs": 22.607000000000003,
"eventListeners": 19,
"totalBlockingTimeMs": 0,
"frameDurationMs": 16.66333333333335,
"p95FrameDurationMs": 16.699999999999818
},
{
"name": "dom-widget-clipping",
"durationMs": 590.0820000000522,
"styleRecalcs": 11,
"styleRecalcDurationMs": 8.559,
"layouts": 0,
"layoutDurationMs": 0,
"taskDurationMs": 377.81600000000003,
"heapDeltaBytes": -11255184,
"heapUsedBytes": 53240728,
"domNodes": 18,
"jsHeapTotalBytes": 24379392,
"scriptDurationMs": 60.923,
"eventListeners": 2,
"totalBlockingTimeMs": 0,
"frameDurationMs": 16.666666666666668,
"p95FrameDurationMs": 16.700000000000728
},
{
"name": "dom-widget-clipping",
"durationMs": 589.3980000000738,
"styleRecalcs": 12,
"styleRecalcDurationMs": 7.924,
"layouts": 0,
"layoutDurationMs": 0,
"taskDurationMs": 375.51800000000003,
"heapDeltaBytes": -11244372,
"heapUsedBytes": 53136476,
"domNodes": 20,
"jsHeapTotalBytes": 25165824,
"scriptDurationMs": 59.18599999999999,
"eventListeners": 0,
"totalBlockingTimeMs": 0,
"frameDurationMs": 16.666666666666668,
"p95FrameDurationMs": 16.700000000000728
},
{
"name": "large-graph-idle",
"durationMs": 2039.2870000000016,
"styleRecalcs": 10,
"styleRecalcDurationMs": 9.495,
"layouts": 0,
"layoutDurationMs": 0,
"taskDurationMs": 818.8570000000002,
"heapDeltaBytes": 6952908,
"heapUsedBytes": 66285120,
"domNodes": -280,
"jsHeapTotalBytes": 3510272,
"scriptDurationMs": 128.777,
"eventListeners": -151,
"totalBlockingTimeMs": 0,
"frameDurationMs": 16.66333333333332,
"p95FrameDurationMs": 16.700000000000728
},
{
"name": "large-graph-idle",
"durationMs": 2011.5749999999935,
"styleRecalcs": 8,
"styleRecalcDurationMs": 6.679000000000001,
"layouts": 0,
"layoutDurationMs": 0,
"taskDurationMs": 659.344,
"heapDeltaBytes": 6220416,
"heapUsedBytes": 65930228,
"domNodes": -284,
"jsHeapTotalBytes": 2985984,
"scriptDurationMs": 97.501,
"eventListeners": -147,
"totalBlockingTimeMs": 0,
"frameDurationMs": 16.66333333333332,
"p95FrameDurationMs": 16.700000000000728
},
{
"name": "large-graph-pan",
"durationMs": 2256.7960000000085,
"styleRecalcs": 68,
"styleRecalcDurationMs": 14.508000000000003,
"layouts": 0,
"layoutDurationMs": 0,
"taskDurationMs": 1382.942,
"heapDeltaBytes": 4751972,
"heapUsedBytes": 65610992,
"domNodes": -285,
"jsHeapTotalBytes": 3440640,
"scriptDurationMs": 461.526,
"eventListeners": -149,
"totalBlockingTimeMs": 0,
"frameDurationMs": 16.66333333333335,
"p95FrameDurationMs": 16.700000000000728
},
{
"name": "large-graph-pan",
"durationMs": 2214.7059999999783,
"styleRecalcs": 69,
"styleRecalcDurationMs": 14.286,
"layouts": 0,
"layoutDurationMs": 0,
"taskDurationMs": 1299.049,
"heapDeltaBytes": 4425420,
"heapUsedBytes": 65000380,
"domNodes": -284,
"jsHeapTotalBytes": 3178496,
"scriptDurationMs": 435.704,
"eventListeners": -151,
"totalBlockingTimeMs": 0,
"frameDurationMs": 16.670000000000012,
"p95FrameDurationMs": 16.700000000000728
},
{
"name": "large-graph-zoom",
"durationMs": 3229.8210000000154,
"styleRecalcs": 65,
"styleRecalcDurationMs": 17.841,
"layouts": 60,
"layoutDurationMs": 7.206,
"taskDurationMs": 1534.9089999999999,
"heapDeltaBytes": -1950756,
"heapUsedBytes": 60194484,
"domNodes": -286,
"jsHeapTotalBytes": 6131712,
"scriptDurationMs": 536.323,
"eventListeners": -153,
"totalBlockingTimeMs": 0,
"frameDurationMs": 16.66333333333332,
"p95FrameDurationMs": 16.700000000000728
},
{
"name": "large-graph-zoom",
"durationMs": 3214.2760000000408,
"styleRecalcs": 66,
"styleRecalcDurationMs": 15.546999999999999,
"layouts": 60,
"layoutDurationMs": 7.136,
"taskDurationMs": 1493.979,
"heapDeltaBytes": -2096296,
"heapUsedBytes": 60110004,
"domNodes": -288,
"jsHeapTotalBytes": 6131712,
"scriptDurationMs": 523.139,
"eventListeners": -153,
"totalBlockingTimeMs": 0,
"frameDurationMs": 16.666666666666668,
"p95FrameDurationMs": 16.700000000000728
},
{
"name": "minimap-idle",
"durationMs": 2027.1169999999756,
"styleRecalcs": 7,
"styleRecalcDurationMs": 6.186000000000001,
"layouts": 0,
"layoutDurationMs": 0,
"taskDurationMs": 653.7790000000001,
"heapDeltaBytes": 7195980,
"heapUsedBytes": 68245528,
"domNodes": -286,
"jsHeapTotalBytes": 3510272,
"scriptDurationMs": 99.25900000000001,
"eventListeners": -149,
"totalBlockingTimeMs": 0,
"frameDurationMs": 16.666666666666668,
"p95FrameDurationMs": 16.799999999999272
},
{
"name": "minimap-idle",
"durationMs": 2041.729000000032,
"styleRecalcs": 8,
"styleRecalcDurationMs": 8.863,
"layouts": 0,
"layoutDurationMs": 0,
"taskDurationMs": 717.649,
"heapDeltaBytes": 7110920,
"heapUsedBytes": 68260280,
"domNodes": -285,
"jsHeapTotalBytes": 1413120,
"scriptDurationMs": 118.82699999999998,
"eventListeners": -147,
"totalBlockingTimeMs": 0,
"frameDurationMs": 16.66333333333332,
"p95FrameDurationMs": 16.800000000000182
},
{
"name": "subgraph-dom-widget-clipping",
"durationMs": 597.3600000000374,
"styleRecalcs": 47,
"styleRecalcDurationMs": 10.825000000000001,
"layouts": 0,
"layoutDurationMs": 0,
"taskDurationMs": 401.466,
"heapDeltaBytes": -10715292,
"heapUsedBytes": 53885540,
"domNodes": 20,
"jsHeapTotalBytes": 26214400,
"scriptDurationMs": 119.175,
"eventListeners": 8,
"totalBlockingTimeMs": 0,
"frameDurationMs": 16.666666666666668,
"p95FrameDurationMs": 16.700000000000728
},
{
"name": "subgraph-dom-widget-clipping",
"durationMs": 578.5939999999528,
"styleRecalcs": 47,
"styleRecalcDurationMs": 11.161000000000001,
"layouts": 0,
"layoutDurationMs": 0,
"taskDurationMs": 417.84900000000005,
"heapDeltaBytes": -10632448,
"heapUsedBytes": 54154552,
"domNodes": 20,
"jsHeapTotalBytes": 25690112,
"scriptDurationMs": 123.96799999999999,
"eventListeners": 8,
"totalBlockingTimeMs": 0,
"frameDurationMs": 16.666666666666668,
"p95FrameDurationMs": 16.699999999999818
},
{
"name": "subgraph-idle",
"durationMs": 2003.877999999986,
"styleRecalcs": 9,
"styleRecalcDurationMs": 9.833,
"layouts": 0,
"layoutDurationMs": 0,
"taskDurationMs": 459.48199999999997,
"heapDeltaBytes": 5905016,
"heapUsedBytes": 70437596,
"domNodes": 18,
"jsHeapTotalBytes": 24379392,
"scriptDurationMs": 14.016,
"eventListeners": 4,
"totalBlockingTimeMs": 0,
"frameDurationMs": 16.666666666666668,
"p95FrameDurationMs": 16.800000000000182
},
{
"name": "subgraph-idle",
"durationMs": 1997.7579999999762,
"styleRecalcs": 10,
"styleRecalcDurationMs": 7.3069999999999995,
"layouts": 0,
"layoutDurationMs": 0,
"taskDurationMs": 461.318,
"heapDeltaBytes": 5859344,
"heapUsedBytes": 70479000,
"domNodes": 20,
"jsHeapTotalBytes": 24903680,
"scriptDurationMs": 12.928999999999998,
"eventListeners": 4,
"totalBlockingTimeMs": 0,
"frameDurationMs": 16.670000000000012,
"p95FrameDurationMs": 16.800000000000182
},
{
"name": "subgraph-mouse-sweep",
"durationMs": 1696.4399999999955,
"styleRecalcs": 75,
"styleRecalcDurationMs": 33.456,
"layouts": 16,
"layoutDurationMs": 3.8729999999999998,
"taskDurationMs": 811.7810000000001,
"heapDeltaBytes": -17647876,
"heapUsedBytes": 46716916,
"domNodes": -279,
"jsHeapTotalBytes": 23568384,
"scriptDurationMs": 91.269,
"eventListeners": -153,
"totalBlockingTimeMs": 0,
"frameDurationMs": 16.666666666666668,
"p95FrameDurationMs": 16.700000000000728
},
{
"name": "subgraph-mouse-sweep",
"durationMs": 1702.4940000000015,
"styleRecalcs": 77,
"styleRecalcDurationMs": 34.455,
"layouts": 16,
"layoutDurationMs": 3.797,
"taskDurationMs": 745.232,
"heapDeltaBytes": -3880260,
"heapUsedBytes": 60788040,
"domNodes": 64,
"jsHeapTotalBytes": 24379392,
"scriptDurationMs": 90.423,
"eventListeners": 4,
"totalBlockingTimeMs": 0,
"frameDurationMs": 16.666666666666668,
"p95FrameDurationMs": 16.700000000000728
},
{
"name": "subgraph-transition-enter",
"durationMs": 1417.385999999965,
"styleRecalcs": 17,
"styleRecalcDurationMs": 29.375999999999998,
"layouts": 13,
"layoutDurationMs": 12.193,
"taskDurationMs": 903.06,
"heapDeltaBytes": 31071292,
"heapUsedBytes": 101398544,
"domNodes": 13673,
"jsHeapTotalBytes": 15204352,
"scriptDurationMs": 39.399,
"eventListeners": 2375,
"totalBlockingTimeMs": 140,
"frameDurationMs": 16.66333333333332,
"p95FrameDurationMs": 16.800000000000182
},
{
"name": "viewport-pan-sweep",
"durationMs": 8315.846000000021,
"styleRecalcs": 250,
"styleRecalcDurationMs": 36.589,
"layouts": 0,
"layoutDurationMs": 0,
"taskDurationMs": 4616.339,
"heapDeltaBytes": 10290260,
"heapUsedBytes": 69808256,
"domNodes": -281,
"jsHeapTotalBytes": 6848512,
"scriptDurationMs": 1475.198,
"eventListeners": -133,
"totalBlockingTimeMs": 0,
"frameDurationMs": 16.666666666666668,
"p95FrameDurationMs": 16.80000000000109
},
{
"name": "viewport-pan-sweep",
"durationMs": 8437.779999999975,
"styleRecalcs": 250,
"styleRecalcDurationMs": 36.896,
"layouts": 0,
"layoutDurationMs": 0,
"taskDurationMs": 4523.509000000001,
"heapDeltaBytes": 11962788,
"heapUsedBytes": 71378884,
"domNodes": -282,
"jsHeapTotalBytes": 6324224,
"scriptDurationMs": 1390.27,
"eventListeners": -131,
"totalBlockingTimeMs": 0,
"frameDurationMs": 16.666666666666668,
"p95FrameDurationMs": 16.700000000000728
},
{
"name": "vue-large-graph-idle",
"durationMs": 16769.893000000025,
"styleRecalcs": 0,
"styleRecalcDurationMs": 0,
"layouts": 0,
"layoutDurationMs": 0,
"taskDurationMs": 16721.397000000004,
"heapDeltaBytes": -49599172,
"heapUsedBytes": 165642764,
"domNodes": -8316,
"jsHeapTotalBytes": -10690560,
"scriptDurationMs": 585.448,
"eventListeners": -16387,
"totalBlockingTimeMs": 0,
"frameDurationMs": 17.776666666666763,
"p95FrameDurationMs": 16.799999999999272
},
{
"name": "vue-large-graph-idle",
"durationMs": 17127.395999999975,
"styleRecalcs": 0,
"styleRecalcDurationMs": 0,
"layouts": 0,
"layoutDurationMs": 0,
"taskDurationMs": 17084.074,
"heapDeltaBytes": -61689188,
"heapUsedBytes": 167644384,
"domNodes": -8312,
"jsHeapTotalBytes": -8069120,
"scriptDurationMs": 592.667,
"eventListeners": -16387,
"totalBlockingTimeMs": 0,
"frameDurationMs": 17.779999999999927,
"p95FrameDurationMs": 16.799999999999272
},
{
"name": "vue-large-graph-pan",
"durationMs": 20699.111999999957,
"styleRecalcs": 143,
"styleRecalcDurationMs": 16.488999999999976,
"layouts": 0,
"layoutDurationMs": 0,
"taskDurationMs": 20654.85,
"heapDeltaBytes": -57417068,
"heapUsedBytes": 166245172,
"domNodes": -8312,
"jsHeapTotalBytes": -10166272,
"scriptDurationMs": 912.778,
"eventListeners": -16381,
"totalBlockingTimeMs": 175,
"frameDurationMs": 17.776666666666642,
"p95FrameDurationMs": 16.799999999999272
},
{
"name": "vue-large-graph-pan",
"durationMs": 20600.782999999978,
"styleRecalcs": 143,
"styleRecalcDurationMs": 16.568000000000026,
"layouts": 0,
"layoutDurationMs": 0,
"taskDurationMs": 20567.954999999998,
"heapDeltaBytes": -37937552,
"heapUsedBytes": 169229852,
"domNodes": -8312,
"jsHeapTotalBytes": -8822784,
"scriptDurationMs": 945.296,
"eventListeners": -16387,
"totalBlockingTimeMs": 107,
"frameDurationMs": 17.780000000000047,
"p95FrameDurationMs": 16.80000000000291
},
{
"name": "workflow-execution",
"durationMs": 445.6750000000511,
"styleRecalcs": 12,
"styleRecalcDurationMs": 17.575,
"layouts": 3,
"layoutDurationMs": 0.712,
"taskDurationMs": 105.558,
"heapDeltaBytes": 5061380,
"heapUsedBytes": 68610320,
"domNodes": 124,
"jsHeapTotalBytes": 4980736,
"scriptDurationMs": 9.198,
"eventListeners": 99,
"totalBlockingTimeMs": 0,
"frameDurationMs": 16.666666666666668,
"p95FrameDurationMs": 16.800000000000182
},
{
"name": "workflow-execution",
"durationMs": 469.8929999999564,
"styleRecalcs": 11,
"styleRecalcDurationMs": 18.288,
"layouts": 3,
"layoutDurationMs": 0.6679999999999999,
"taskDurationMs": 108.307,
"heapDeltaBytes": 5050880,
"heapUsedBytes": 68417724,
"domNodes": 121,
"jsHeapTotalBytes": 5242880,
"scriptDurationMs": 9.344,
"eventListeners": 97,
"totalBlockingTimeMs": 0,
"frameDurationMs": 16.66333333333335,
"p95FrameDurationMs": 16.699999999999818
}
]
} |
|
Warning Review limit reachedYou’ve reached a temporary PR review limit under our Fair Usage Limits Policy. Next review available in: 36 minutes Enable usage-based reviews in Billing to review now. Otherwise, wait until the next included review is available. How can I continue?After more reviews become available, a review can be triggered using the To avoid repeated limits, reduce automatic review volume by pausing incremental auto-reviews earlier, using label-based review opt-in, excluding WIP or generated PR titles, or requesting reviews manually when the PR is ready. If your team needs uninterrupted high-volume reviews, an organization admin can enable usage-based reviews. How do review limits work?CodeRabbit enforces per-developer PR review limits for each organization. Most developers receive the normal plan review availability. For paid Pro and Pro+ PR reviews, CodeRabbit uses adaptive limits for sustained high-volume activity. When a developer's recent PR review activity reaches the 95th percentile or higher among CodeRabbit users, additional reviews become available more gradually as earlier reviews age out of the rolling window. Please refer docs for additional details. Review details⚙️ Run configurationConfiguration used: Path: .coderabbit.yaml Review profile: ASSERTIVE Plan: Pro Plus Run ID: 📒 Files selected for processing (4)
📝 WalkthroughWalkthroughChangesStranded release detection
Estimated code review effort: 3 (Moderate) | ~30 minutes Sequence Diagram(s)sequenceDiagram
participant GitHubActions
participant StrandedReleaseCheck
participant GitHubRepository
participant PyPI
participant Slack
GitHubActions->>StrandedReleaseCheck: run release-commit check
StrandedReleaseCheck->>GitHubRepository: fetch release metadata and commits
StrandedReleaseCheck->>PyPI: fetch published versions
StrandedReleaseCheck-->>GitHubActions: return findings and exit status
GitHubActions->>GitHubRepository: dispatch patch release when required
GitHubActions->>Slack: post failure notification when configured
Possibly related PRs
Suggested labels: Suggested reviewers: Important Pre-merge checks failedPlease resolve all errors before merging. Addressing warnings is optional. ❌ Failed checks (1 inconclusive)
✅ Passed checks (5 passed)
✨ Finishing Touches🧪 Generate unit tests (beta)
Comment |
There was a problem hiding this comment.
Actionable comments posted: 7
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In @.github/workflows/release-stranded-check.yaml:
- Around line 70-76: Update the Slack request in the release workflow to capture
the chat.postMessage response instead of discarding it, add curl connection and
total timeouts, and parse the response with jq to require .ok == true. Preserve
continue-on-error behavior and ensure HTTP or Slack API failures cause the step
to fail.
In `@scripts/cicd/check-stranded-release-commits.test.ts`:
- Around line 1-8: Extend the imports in check-stranded-release-commits.test.ts
to include parsePinnedVersion and newestStableVersion, then add focused tests
covering valid and malformed requirements.txt pin lines plus PyPI version
selection, including unexpected or non-stable version formats. Preserve the
existing tests and verify the missing-version and relevant edge-case behavior
exposed by these pure functions.
In `@scripts/cicd/check-stranded-release-commits.ts`:
- Around line 188-194: Guard the git fetch call in the loop over linesToCheck so
a fetch failure for one branch is handled locally and skips that branch,
allowing subsequent branches to be evaluated. Preserve the existing latestTagOn
and no-tag continue behavior, and use the script’s established error-handling
pattern if available.
- Around line 118-122: Update fetchText to pass a finite timeout signal, such as
AbortSignal.timeout(...), to fetch so stalled external requests terminate
instead of hanging indefinitely. Preserve the existing HTTP status validation
and response-text behavior.
- Around line 114-158: Replace shell-interpreted command construction in git()
with execFileSync using a Git executable and argument arrays, then update
latestTagOn(), commitsPastTag(), and the git fetch call to pass arguments
separately. Preserve the existing Git operations and outputs while ensuring tag
and branch values are never interpolated into a shell command.
- Around line 160-168: Update the PyPI response parsing in main so JSON.parse
results are treated as unknown and narrowed with a type guard that verifies a
non-null object with a valid releases object before Object.keys is called.
Remove the direct `{ releases: Record<string, unknown> }` assertion, and provide
a clear diagnostic or failure when the response shape is invalid.
- Around line 221-226: Update the entry-point check around process.argv and
import.meta.url to avoid the non-null assertion and compare the intended script
path precisely, using a guard that safely handles a missing basename. Change the
main() invocation to attach an explicit catch handler that reports the failure
and exits with a nonzero status.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Path: .coderabbit.yaml
Review profile: ASSERTIVE
Plan: Pro Plus
Run ID: d73fd80a-ce39-4eea-bb8e-1dfe28630371
📒 Files selected for processing (3)
.github/workflows/release-stranded-check.yamlscripts/cicd/check-stranded-release-commits.test.tsscripts/cicd/check-stranded-release-commits.ts
Codecov Report✅ All modified and coverable lines are covered by tests. @@ Coverage Diff @@
## main #14429 +/- ##
==========================================
+ Coverage 78.72% 78.74% +0.01%
==========================================
Files 1866 1868 +2
Lines 115408 116073 +665
Branches 33539 34822 +1283
==========================================
+ Hits 90860 91398 +538
- Misses 24005 24123 +118
- Partials 543 552 +9
Flags with carried forward coverage won't be shown. Click here to find out more. 🚀 New features to boost your workflow:
|
|
Relationship to #14039, which is open and should land: that PR adds a Different windows, and 1.47.10 shows why both matter. At publish time nothing was stranded, so an at-publish assertion passes. #14065 merged onto No file overlap: this adds a new workflow and script and does not touch |
4c1e449 to
17ec699
Compare
|
Pushed a change of approach after a fair criticism: as written, this PR and #14039 only told a human that a fix was stranded. Both times this failed, the information already existed and nobody acted on it, so another alert was not the fix. Two changes. The check now dispatches the patch release for the line ComfyUI pins, rather than only failing and posting to Slack. It skips if a Release bump PR is already open against that branch, so it cannot resolve a second patch while one is in flight. Version-bump PRs join backport auto-merge. That workflow already merges backport PRs once they are approved and green; version bumps are equally bot-authored and mechanical, and leaving them for a human to click is what timed out publish-pypi on both the 1.47.10 run and the 07-27 run, which also silently skipped the ComfyUI pin PR. To be clear about what this does not do: branch protection still requires an approval and green checks on the bump PR, and GitHub re-enforces that at merge time. This removes the requirement that somebody remembers, not the requirement that somebody approves. Dropping the approval on bot-authored version bumps would close the loop entirely, but that is a branch-protection policy decision rather than a workflow change. |
There was a problem hiding this comment.
Actionable comments posted: 4
♻️ Duplicate comments (2)
scripts/cicd/check-stranded-release-commits.ts (2)
214-220: 🩺 Stability & Availability | 🟠 Major | ⚡ Quick win
git fetchfailure aborts the whole run instead of skipping the branch.
latestTagOntolerates a missing tag and continues to the next branch, but thegit('fetch', ...)call above it is not guarded. If one release line's branch is missing, renamed, or unreachable on origin,execFileSyncthrows here, and the entire scheduled check aborts before evaluating the other consumed line. This mirrors a concern raised and reportedly addressed in an earlier round of review on this PR ("The fetch is wrapped and now skips that line"), but the current code shows the fetch call unguarded.🛠️ Proposed fix
for (const branch of linesToCheck) { - git('fetch', 'origin', `${branch}:refs/remotes/origin/${branch}`, '--tags') + try { + git('fetch', 'origin', `${branch}:refs/remotes/origin/${branch}`, '--tags') + } catch { + console.error(`${branch}: could not fetch, skipping`) + continue + } const latestTag = latestTagOn(branch)🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@scripts/cicd/check-stranded-release-commits.ts` around lines 214 - 220, Guard the git fetch call inside the loop over linesToCheck so failures for a missing, renamed, or unreachable branch are caught and skipped. Update the flow around git and latestTagOn to continue to the next branch after logging the fetch failure, while preserving the existing no-tag handling for successful fetches.
191-194: 🎯 Functional Correctness | 🟠 Major | ⚡ Quick winUnnarrowed type assertion on the PyPI response.
JSON.parse(t) as { releases: Record<string, unknown> }asserts the shape without validation. If PyPI returns an error body or a different shape,pypi.releasesisundefined, andObject.keys(pypi.releases)on line 197 throws an opaqueTypeErrorinstead of a clear diagnostic. This mirrors a concern raised and reportedly addressed in an earlier round of review on this PR ("Narrowed with a guard that throws a named error instead of asserting the shape"), but the current code still shows the unguarded assertion.🐛 Proposed fix: narrow with a guard instead of an assertion
fetchText('https://pypi.org/pypi/comfyui-frontend-package/json').then( - (t) => JSON.parse(t) as { releases: Record<string, unknown> } + (t) => { + const parsed: unknown = JSON.parse(t) + if ( + typeof parsed !== 'object' || + parsed === null || + typeof (parsed as { releases?: unknown }).releases !== 'object' + ) { + throw new Error('Unexpected PyPI response shape: missing "releases"') + } + return parsed as { releases: Record<string, unknown> } + } )As per path instructions,
docs/guidance/typescript.mdrequires: "do not use any, as any, or unnecessary type assertions; use explicit types and narrowing for commit, finding, version, and input data."🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@scripts/cicd/check-stranded-release-commits.ts` around lines 191 - 194, Replace the unvalidated JSON shape assertion in the PyPI response handling with explicit parsing and a guard that verifies releases is a non-null object before use. Throw a named, descriptive error when validation fails, then pass the narrowed releases value to the existing Object.keys flow without using any or unnecessary type assertions.Source: Path instructions
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In @.github/workflows/backport-auto-merge.yaml:
- Around line 119-128: Rename the label predicate variable from is_backport to
has_supported_label to reflect both backport and Release labels. Update its
conditional usage and the related log field around the workflow’s PR filtering
logic so Release PRs are not reported as backport PRs.
- Around line 83-84: Update the scheduled sweep’s PR collection command in the
workflow to query `backport` and `Release` labels separately with OR semantics,
rather than passing both labels to one `gh pr list` invocation. Preserve the
existing result aggregation and de-duplication so pull requests matching either
label are processed once.
In `@scripts/cicd/check-stranded-release-commits.test.ts`:
- Around line 193-198: Remove the duplicate test named “does not fail merely
because a newer minor line exists” because its evaluatePin input is identical to
the existing current-pin test and cannot exercise main()’s release-line
filtering. Preserve the existing current-pin coverage, or replace this test with
distinct behavioral coverage for same-line pin lag or its failure-reporting
details using evaluatePin’s actual inputs.
In `@scripts/cicd/check-stranded-release-commits.ts`:
- Around line 214-230: Update the publishedVersion selection in main’s
linesToCheck loop to use the already-computed newestOnPinnedLine for the
pinned-line branch instead of the raw pinned value. Keep published for the
newest minor line, and ensure evaluateLine receives the actual latest
PyPI-published version corresponding to each branch.
---
Duplicate comments:
In `@scripts/cicd/check-stranded-release-commits.ts`:
- Around line 214-220: Guard the git fetch call inside the loop over
linesToCheck so failures for a missing, renamed, or unreachable branch are
caught and skipped. Update the flow around git and latestTagOn to continue to
the next branch after logging the fetch failure, while preserving the existing
no-tag handling for successful fetches.
- Around line 191-194: Replace the unvalidated JSON shape assertion in the PyPI
response handling with explicit parsing and a guard that verifies releases is a
non-null object before use. Throw a named, descriptive error when validation
fails, then pass the narrowed releases value to the existing Object.keys flow
without using any or unnecessary type assertions.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Path: .coderabbit.yaml
Review profile: ASSERTIVE
Plan: Pro Plus
Run ID: 20c71361-1bb5-4dd2-852f-c801b50727f3
📒 Files selected for processing (4)
.github/workflows/backport-auto-merge.yaml.github/workflows/release-stranded-check.yamlscripts/cicd/check-stranded-release-commits.test.tsscripts/cicd/check-stranded-release-commits.ts
There was a problem hiding this comment.
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (1)
.github/workflows/backport-auto-merge.yaml (1)
124-133:⚠️ Potential issue | 🟠 MajorUpdate the eligibility guard to use
has_supported_label.Line [124] defines
has_supported_label, but Lines [134]-[135] still expand$is_backport. Because this step runs withset -u, an open, non-draft candidate causes anis_backport: unbound variablefailure. The job exits before it can merge or skip the PR.Replace both references and update the log to include
ReleasePRs.Proposed fix
- if [ "$state" != "OPEN" ] || [ "$is_draft" != "false" ] || [ "$is_backport" != "true" ]; then - echo "Not an actionable backport PR (state=$state draft=$is_draft backport=$is_backport) — skipping." + if [ "$state" != "OPEN" ] || [ "$is_draft" != "false" ] || [ "$has_supported_label" != "true" ]; then + echo "Not an actionable backport or Release PR (state=$state draft=$is_draft has_supported_label=$has_supported_label) — skipping."🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In @.github/workflows/backport-auto-merge.yaml around lines 124 - 133, Update the eligibility guard near the open, non-draft PR checks to use the existing has_supported_label variable instead of both references to is_backport, avoiding the unset-variable failure under set -u. Also revise the associated log message to mention both backport and Release-labelled PRs.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Outside diff comments:
In @.github/workflows/backport-auto-merge.yaml:
- Around line 124-133: Update the eligibility guard near the open, non-draft PR
checks to use the existing has_supported_label variable instead of both
references to is_backport, avoiding the unset-variable failure under set -u.
Also revise the associated log message to mention both backport and
Release-labelled PRs.
ℹ️ Review info
⚙️ Run configuration
Configuration used: Path: .coderabbit.yaml
Review profile: ASSERTIVE
Plan: Pro Plus
Run ID: d9133b47-360f-4879-9849-0841e3c3c3d4
📒 Files selected for processing (3)
.github/workflows/backport-auto-merge.yamlscripts/cicd/check-stranded-release-commits.test.tsscripts/cicd/check-stranded-release-commits.ts
💤 Files with no reviewable changes (1)
- scripts/cicd/check-stranded-release-commits.test.ts
|
Two notes from landing #14039 (release-workflow hardening) alongside this: 1. The 2. Overlap with #14039. That PR's |
dante01yoon
left a comment
There was a problem hiding this comment.
Found two blocking regressions in the new release automation. Both prevent the intended automatic patch/release-PR flow from functioning.
| state=$(echo "$info" | jq -r '.state') | ||
| is_draft=$(echo "$info" | jq -r '.isDraft') | ||
| is_backport=$(echo "$info" | jq -r '[.labels[].name] | any(. == "backport")') | ||
| has_supported_label=$(echo "$info" | jq -r '[.labels[].name] | any(. == "backport" or . == "Release")') |
There was a problem hiding this comment.
issue: The rename is incomplete: the eligibility guard below still expands $is_backport twice even though this line now defines only has_supported_label. Because this step starts with set -u, every open, non-draft candidate reaches that expansion and exits with is_backport: unbound variable, so neither backport nor Release PRs can be merged. Could we update both guard/log references to has_supported_label?
There was a problem hiding this comment.
Correct, thank you. The guard and the log still expanded $is_backport after the definition was renamed, and with set -u that aborts on the first candidate, so neither backport nor Release PRs would merge. The auto-merge path was dead rather than merely mislabelled.
Worse, I told a previous reviewer this rename included the log line. It did not. Both references now use has_supported_label.
| fi | ||
|
|
||
| echo "::warning::Fix commits are stranded on ${BRANCH}; dispatching a patch release." | ||
| gh workflow run release-biweekly-comfyui.yaml \ |
There was a problem hiding this comment.
issue: This dispatch targets release-biweekly-comfyui.yaml, but that workflow no longer exists on the PR head/default branch; the current workflow is release-weekly-comfyui.yaml (with the same release_type and target_branch inputs). gh workflow run resolves workflows on the default branch, so the remediation step will fail instead of cutting the stranded patch. Could we dispatch the current workflow filename?
There was a problem hiding this comment.
Also correct. release-biweekly-comfyui.yaml was renamed to release-weekly-comfyui.yaml and gh workflow run resolves against the default branch, so the remediation step would have failed at exactly the moment it was supposed to cut the stranded patch. Now dispatches release-weekly-comfyui.yaml; I checked that release_type and target_branch are unchanged on main.
Related: #14039 still edits release-biweekly-comfyui.yaml and is 166 commits behind main, so it needs the same rename before it can land.
There was a problem hiding this comment.
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (3)
.github/workflows/release-stranded-check.yaml (2)
73-78: 🎯 Functional Correctness | 🟡 Minor | ⚡ Quick winFilter for the core version-bump PR.
version-bump-desktop-ui.yamlcan create aRelease-labelled PR against any branch, includingcore/X.Y. This query can therefore skip the patch dispatch for a non-frontend bump. Filter the result by theversion-bump-*head branch.🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In @.github/workflows/release-stranded-check.yaml around lines 73 - 78, Update the OPEN_BUMP query in the release-check workflow to filter Release-labelled PRs by a head branch matching version-bump-*. Keep the existing repository, open-state, and target-branch filters so only the core version-bump PR suppresses patch dispatch.
63-84: 🗄️ Data Integrity & Integration | 🟡 Minor | ⚡ Quick winRestrict patch dispatch to the pinned release line.
The current first-match selection can dispatch the newest line when the pinned line has a different failure and the newest line has stranded commits. Filter the
stranded-commitsfinding by the pinned branch.🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In @.github/workflows/release-stranded-check.yaml around lines 63 - 84, Update the stranded-commit selection that feeds steps.check and needs_patch_branch so it filters findings by the pinned release branch before selecting a match. Ensure the patch-release dispatch in “Cut the patch release for the stranded line” can only target that pinned line, rather than falling back to the newest release line..github/workflows/backport-auto-merge.yaml (1)
82-89: 🎯 Functional Correctness | 🟠 Major | 🏗️ Heavy liftPaginate the scheduled PR sweep.
Each
gh pr listcommand returns at most 100 PRs. If either label exceeds 100 open PRs, some eligible PRs are omitted from the sweep. Paginate both queries or detect the limit and fail or warn.🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In @.github/workflows/backport-auto-merge.yaml around lines 82 - 89, Update the scheduled PR sweep in the numbers collection to paginate both gh pr list queries, ensuring all open backport and Release PRs are included rather than only the first 100. Use gh’s supported pagination options or iterate through result pages, preserving the separate label queries and combined numbers output.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Outside diff comments:
In @.github/workflows/backport-auto-merge.yaml:
- Around line 82-89: Update the scheduled PR sweep in the numbers collection to
paginate both gh pr list queries, ensuring all open backport and Release PRs are
included rather than only the first 100. Use gh’s supported pagination options
or iterate through result pages, preserving the separate label queries and
combined numbers output.
In @.github/workflows/release-stranded-check.yaml:
- Around line 73-78: Update the OPEN_BUMP query in the release-check workflow to
filter Release-labelled PRs by a head branch matching version-bump-*. Keep the
existing repository, open-state, and target-branch filters so only the core
version-bump PR suppresses patch dispatch.
- Around line 63-84: Update the stranded-commit selection that feeds steps.check
and needs_patch_branch so it filters findings by the pinned release branch
before selecting a match. Ensure the patch-release dispatch in “Cut the patch
release for the stranded line” can only target that pinned line, rather than
falling back to the newest release line.
ℹ️ Review info
⚙️ Run configuration
Configuration used: Path: .coderabbit.yaml
Review profile: ASSERTIVE
Plan: Pro Plus
Run ID: a0aa03cf-5ad5-4f0b-8d1c-09961ebcebb8
📒 Files selected for processing (2)
.github/workflows/backport-auto-merge.yaml.github/workflows/release-stranded-check.yaml
dante01yoon
left a comment
There was a problem hiding this comment.
Re-checked the focused fixes at 4eb49e6a:
- The eligibility guard and log now consistently use
has_supported_label; aset -ushell probe accepts both supported labels and skips unsupported labels without an unbound-variable failure. - Remediation now dispatches
release-weekly-comfyui.yaml; that workflow exists onmainand still exposesrelease_typeandtarget_branch.
Required checks are green.
Encodes the 1.47.10 failure: fix commits merged onto a release branch after its tag, with PyPI and ComfyUI's pin still on the old tag, must fail loudly. Stubbed so the spec runs and fails; implementation follows.
Checks only the minors users actually consume — the one ComfyUI pins and the newest on PyPI — so dead and unreleased branches stay quiet. Fix commits past the branch's latest tag fail the job and post to #frontend-releases. Verified against live state: exits 1 and names the four stranded core/1.47 fixes, including 9a65619, the dropdown fix five users reported.
execFileSync instead of execSync: git ref names permit ; $() ` and |, so a tag
name reaching a shell is arbitrary code execution in CI.
Slack answers HTTP 200 with {ok:false} on auth and channel errors, so curl -f
alone reported a silent drop as success. Check .ok and bound the request.
Cover parsePinnedVersion and newestStableVersion, and bound the HTTP fetches.
Also close a hole the review did not reach: the guard called the line 'fully
shipped' while ComfyUI still pinned 1.47.10 against a published 1.47.11 — the
exact state where a fix exists and no user has it.
Validate PyPI's payload instead of asserting its shape, so a changed or error response gives a clear diagnostic rather than an opaque TypeError. A missing or renamed branch no longer aborts the run before the other consumed line is evaluated, and main()'s failures set the exit code explicitly rather than relying on unhandled-rejection behaviour.
Alerting was never the missing piece. Both times this failed the information existed and nobody acted, so the check now dispatches the patch release for the line ComfyUI pins, and skips if a bump PR is already open for it. Version-bump PRs also join backport auto-merge. They are bot-authored and mechanical, and leaving them for a human to click is what timed out publish-pypi on the 1.47.10 and 07-27 runs, which silently skipped the ComfyUI pin PR too. Branch protection still gates both, so this removes the requirement that someone remembers, not the requirement that someone approves.
The pinned-line branch was compared against ComfyUI's requirements.txt pin rather than the newest version actually published for that line. A tag that was never published passed silently, which is precisely the failure this check exists to catch. Repeating --label is AND, so the scheduled sweep matched neither backport nor Release PRs. Query them separately. Rename is_backport to has_supported_label, and drop a test that duplicated the assertion above it while claiming a scenario the function cannot observe.
The eligibility guard still expanded $is_backport after the variable was renamed. Under set -u that aborts on every candidate, so neither backport nor Release PRs could merge: the auto-merge path was dead, not just wrong. The remediation step dispatched release-biweekly-comfyui.yaml, which no longer exists. gh workflow run resolves on the default branch, so the step would fail rather than cut the stranded patch. Now targets release-weekly-comfyui.yaml, whose release_type and target_branch inputs are unchanged.
.pinact.yaml allowlists actions/checkout at v7; v6 has to be SHA-pinned.
bce0b29
4eb49e6 to
bce0b29
Compare
|
Pushed two commits here so this does not sit red:
Still outstanding, and I did not touch it because it needs #14039 on The fix that matches what #14039 landed for |
|
Note on the red pinact flags The check scans every workflow rather than only changed files, so it will stay red on any PR until that line is pinned. Happy to fix it here if you want it unblocked, but it belongs in its own PR since it is unrelated to this change. Thanks for the checkout bump in bce0b29 — I left it in place. |
…omfy-Org#14997) Bug in the stranded-commits check from Comfy-Org#14429, which is live and currently misfiring once a day. ## What happens The idempotency guard asked "is a Release bump PR open against this branch?". Once that PR merges but the tag is never cut, the answer is no, so the next scheduled run dispatches another patch release. Observed on `core/1.48`: ``` 2026-08-09 16:10 check finds e136c13 stranded, dispatches a patch 2026-08-09 16:13 Comfy-Org#14973 "1.48.8" opened 2026-08-10 04:58 Comfy-Org#14973 merged; draft_release fails 403, v1.48.8 never created 2026-08-10 16:20 check runs again, sees no open bump PR, dispatches 2026-08-10 16:21 Comfy-Org#14991 "1.48.9" opened ``` `core/1.48` now has `package.json` at 1.48.8 with `v1.48.7` as its newest tag. Left alone this burns one version per day for as long as the underlying failure persists. ## Fix Compare the branch's `package.json` against its newest tag. When they differ, a bump has already landed unreleased, so the release needs recovering rather than restarting, and no dispatch is requested. Stranded commits are still reported and the job still fails, so nothing goes quiet. Verified against the live state that caused the loop: ``` [FAIL] 1 fix commit(s) sit past v1.48.7 on core/1.48 and have not shipped: e136c13 [backport core/1.48] fix(billing): stop reporting "processing" ... core/1.48 is at 1.48.8 but its newest tag is v1.48.7: a bump already landed and was never released. Recover that release rather than cutting another patch; not requesting a dispatch. ``` `needs_patch_branch` is not emitted, so the dispatch step is skipped. ## Separate, and the actual blocker `draft_release` fails with `403 Resource not accessible by integration` when creating the GitHub release. No core release can be tagged until that is resolved, and this PR does not address it. Comfy-Org#14991 should probably be closed so the version stops advancing, leaving `core/1.48` at 1.48.8 ready to tag. --------- Co-authored-by: Connor Byrne <c.byrne@comfy.org>
Implements the release-done assertion from #14033 §3.
Why
v1.47.10was tagged 19 hours before the templates dropdown fix was backportedonto
core/1.47. The backport merged, every tool reported it done, ComfyUI0.29.0 pinned the older tag, and the fix reached nobody for a week while five
users reported the bug it had already fixed (#14063, #14131, #14212, #14351,
#14397).
Merged onto a release branch is not shipped, and nothing asserted the difference.
This is the second occurrence —
v1.47.9stranded 19 QA commits past its tag andforced the 1.47.10 re-cut. The lesson was written into the core-release runbook
and recurred one release later, which is the argument for a machine check.
What it does
Daily, on release publish, and on demand: for each release line users actually
consume, diff the branch against its newest tag. Fix commits sitting past the tag
fail the job and post to
#frontend-releases. Also asserts the published PyPIversion matches the branch's tag.
Scoped deliberately. Only the minor ComfyUI pins and the newest minor on PyPI
are checked. A sweep of every release branch found e.g.
core/1.48with 45stranded commits and no 1.48 on PyPI at all — dead and unreleased lines have
stranded commits by design, and alerting on them would be pure noise.
Verification
Run against live state before 1.47.11 was cut, it exited 1 and named all four
stranded
core/1.47fixes:Built test-first: the red commit ships a resolving stub with all 11 specs failing,
the green commit implements them. The fix-detection pattern initially missed
#14116 ("Fix migration of ..." — no conventional-commit colon), which was added as
a failing case before widening the pattern.
Needs before it can notify
Set the
SLACK_FRONTEND_RELEASES_CHANNEL_IDrepo variable. Without it the jobstill fails correctly and warns that the notification was skipped.
Refs #14033