@@ -1251,10 +1251,13 @@ def _release_repos() -> list[tuple[str, Path, str, str, str]]:
12511251 return repos
12521252
12531253
1254- def _repo_release_state (path : Path , dev_branch : str , main_branch : str ) -> dict :
1254+ def _repo_release_state (path : Path , dev_branch : str , main_branch : str ,
1255+ cons : Console | None = None ) -> dict :
12551256 """Fetch and gather per-repo promotion state (non-destructive)."""
12561257 # Explicit refspecs: some clones (e.g. config) have restricted fetch
12571258 # configs that would not create refs/remotes/origin/<dev>.
1259+ if cons is not None :
1260+ cons .info (f" fetching { path .name } …" )
12581261 git_auth (path , "fetch" , "origin" , "--quiet" ,
12591262 f"+refs/heads/{ dev_branch } :refs/remotes/origin/{ dev_branch } " ,
12601263 f"+refs/heads/{ main_branch } :refs/remotes/origin/{ main_branch } " ,
@@ -1348,7 +1351,7 @@ def cmd_release_status(cons: Console) -> int:
13481351 cons .error (f"{ label :18s} repo missing at { path } " )
13491352 problems += 1
13501353 continue
1351- st = _repo_release_state (path , dev_b , main_b )
1354+ st = _repo_release_state (path , dev_b , main_b , cons )
13521355 feas = st ["feasibility" ]
13531356 if feas == "unknown" :
13541357 mark , note = "❌" , "origin refs not found (fetch failed?)"
@@ -1412,7 +1415,7 @@ def cmd_release_promote(*, assume_yes: bool, dry_run: bool, rebase: bool,
14121415 cons .error (f"{ label } : repo missing at { path } " )
14131416 ok = False
14141417 continue
1415- st = _repo_release_state (path , dev_b , main_b )
1418+ st = _repo_release_state (path , dev_b , main_b , cons )
14161419 entries .append ((label , path , dev_b , main_b , gh , st ))
14171420 if st ["origin_main" ] == "?" :
14181421 cons .error (f"{ label } : origin/{ main_b } does not exist" )
@@ -1501,10 +1504,24 @@ def cmd_release_promote(*, assume_yes: bool, dry_run: bool, rebase: bool,
15011504 stable = current [: - len ("-dev" )]
15021505 vf .write_text (stable + "\n " )
15031506 git (path , "add" , "VERSION" )
1507+ cons .info (f" devstack: committing VERSION { current } → { stable } "
1508+ f"on { main_b } …" )
1509+ cons .info (" (pre-commit hook runs the test suite — may take a "
1510+ "while)" )
15041511 out , err , code = git (path , "commit" , "-m" ,
1505- f"release: { stable } — stable branch promoted from dev" )
1512+ f"release: { stable } — stable branch promoted "
1513+ f"from dev" ,
1514+ timeout = 300 )
15061515 if code != 0 :
1507- cons .error (f" devstack: VERSION commit failed: { err .strip ()} " )
1516+ detail = err .strip () or out .strip () or "unknown error"
1517+ cons .error (f" devstack: VERSION commit failed: { detail } " )
1518+ cons .error (" State: main has the merged dev content; the "
1519+ "VERSION change is STAGED (not committed)." )
1520+ cons .error (
1521+ f" Recover: cd { path } && "
1522+ f"git commit -m 'release: { stable } — stable branch promoted "
1523+ f"from dev' && git push origin { main_b } "
1524+ )
15081525 failures .append ("devstack" )
15091526 else :
15101527 cons .info (f" devstack: VERSION { current } → { stable } (on { main_b } )" )
@@ -1521,6 +1538,7 @@ def cmd_release_promote(*, assume_yes: bool, dry_run: bool, rebase: bool,
15211538 push_args = ["push" , "origin" , main_b ]
15221539 if label in rebased :
15231540 push_args = ["push" , "--force-with-lease" , "origin" , main_b ]
1541+ cons .info (f" pushing { gh } :{ main_b } …" )
15241542 out , err , code = git_auth (path , * push_args , timeout = 180 )
15251543 if code != 0 :
15261544 cons .error (f" { label } : push failed: { err .strip () or out .strip ()} " )
@@ -1531,12 +1549,24 @@ def cmd_release_promote(*, assume_yes: bool, dry_run: bool, rebase: bool,
15311549
15321550 # ── Summary ──
15331551 cons .info ("" )
1552+ cons .info ("Result:" )
1553+ for label , path , dev_b , main_b , gh , st in entries :
1554+ if label in failures :
1555+ cons .error (f" ❌ { gh :35s} failed" )
1556+ elif label in skipped :
1557+ cons .warn (f" ⏭ { gh :35s} skipped" )
1558+ elif _repo_action (st , rebase )[0 ] == "no-op" :
1559+ cons .info (f" ✅ { gh :35s} aligned (no change)" )
1560+ else :
1561+ force = " (force)" if label in rebased else ""
1562+ cons .info (f" ✅ { gh :35s} promoted{ force } " )
15341563 if skipped :
1535- cons .warn (f"Skipped (local dirty) : { ', ' .join (skipped )} " )
1564+ cons .warn (f"Skipped: { ', ' .join (skipped )} — see notes above " )
15361565 if failures :
15371566 cons .error (f"Failed: { ', ' .join (failures )} " )
1538- cons .error ("Stable release INCOMPLETE — fix the repos above and re-run "
1539- "'lpb-config release promote' (already-promoted repos will "
1567+ cons .error ("Stable release INCOMPLETE — complete the failing repo "
1568+ "(recovery steps above), then re-run "
1569+ "'lpb-config release promote' (already-promoted repos "
15401570 "fast-forward or no-op)." )
15411571 return 1
15421572 stable_version = (version [:- len ("-dev" )] if version .endswith ("-dev" ) else version )
0 commit comments