@@ -186,21 +186,39 @@ def _is_latest_selector_tag(tag: str) -> bool:
186186 return tag .strip ().lower () == "latest"
187187
188188
189+ def _get_tag_release_time_in_repo (
190+ repo_dir : str | Path ,
191+ tag : str ,
192+ ) -> str :
193+ normalized_tag = tag .strip ()
194+ if not normalized_tag :
195+ return ""
196+ try :
197+ timestamp = _run_git (repo_dir , "log" , "-1" , "--format=%ct" , normalized_tag )
198+ if not timestamp :
199+ return ""
200+ return datetime .fromtimestamp (int (timestamp )).strftime ("%Y-%m-%d %H:%M:%S" )
201+ except Exception :
202+ return ""
203+
204+
189205def get_repo_version_info (repo_dir : str | Path | None = None ) -> dict [str , str ]:
190206 repository = get_repo_dir (repo_dir )
191207 describe = _run_git (repository , "describe" , "--tags" , "--always" )
192208 commit = _run_git (repository , "rev-parse" , "HEAD" )
209+ short_tag = _normalize_describe_to_version (describe )
193210 try :
194211 branch = _run_git (repository , "branch" , "--show-current" )
195212 except Exception :
196213 branch = ""
197214 return {
198215 "branch" : branch ,
199216 "describe" : describe ,
200- "short_tag" : _normalize_describe_to_version ( describe ) ,
217+ "short_tag" : short_tag ,
201218 "display_version" : _format_branch_head_version (branch , describe ),
202219 "commit" : commit ,
203220 "short_commit" : commit [:7 ],
221+ "released_at" : _get_tag_release_time_in_repo (repository , short_tag ),
204222 }
205223
206224
@@ -402,7 +420,7 @@ def _get_remote_branch_merged_tags(branch: str) -> set[str]:
402420def _get_remote_branch_head_info (branch : str ) -> dict [str , str ]:
403421 normalized_branch = branch .strip ().lower ()
404422 if _is_excluded_self_update_branch (normalized_branch ):
405- return {"describe" : "" , "short_tag" : "" , "commit" : "" }
423+ return {"describe" : "" , "short_tag" : "" , "commit" : "" , "released_at" : "" }
406424
407425 cached = _remote_branch_head_cache .get (normalized_branch )
408426 now = time .monotonic ()
@@ -425,11 +443,14 @@ def _get_remote_branch_head_info(branch: str) -> dict[str, str]:
425443 remote_ref = f"refs/remotes/origin/{ normalized_branch } "
426444 describe = _run_git (repository , "describe" , "--tags" , "--always" , remote_ref )
427445 commit = _run_git (repository , "rev-parse" , remote_ref )
446+ short_tag = _normalize_describe_to_version (describe )
447+ released_at = _get_tag_release_time_in_repo (repository , short_tag )
428448
429449 payload = {
430450 "describe" : describe ,
431- "short_tag" : _normalize_describe_to_version ( describe ) ,
451+ "short_tag" : short_tag ,
432452 "commit" : commit ,
453+ "released_at" : released_at ,
433454 }
434455 _remote_branch_head_cache [normalized_branch ] = (now , payload )
435456 return dict (payload )
@@ -464,10 +485,14 @@ def _get_local_branch_head_info(
464485 "describe" : describe ,
465486 "short_tag" : _normalize_describe_to_version (describe ),
466487 "commit" : commit ,
488+ "released_at" : _get_tag_release_time_in_repo (
489+ repository ,
490+ _normalize_describe_to_version (describe ),
491+ ),
467492 }
468493 except Exception :
469494 continue
470- return {"describe" : "" , "short_tag" : "" , "commit" : "" }
495+ return {"describe" : "" , "short_tag" : "" , "commit" : "" , "released_at" : "" }
471496
472497
473498def _get_branch_merged_tags (
@@ -555,6 +580,93 @@ def _format_branch_head_version(branch: str, describe: str) -> str:
555580 return f"{ short_tag } +{ commits_since_tag } "
556581
557582
583+ def _get_release_tag_info (
584+ branch : str ,
585+ tag : str ,
586+ * ,
587+ repo_dir : str | Path | None = None ,
588+ ) -> dict [str , Any ]:
589+ repository = get_repo_dir (repo_dir )
590+ commit = ""
591+ try :
592+ commit = _run_git (repository , "rev-parse" , f"refs/tags/{ tag } ^{{commit}}" )
593+ except Exception :
594+ commit = ""
595+ return {
596+ "branch" : branch .strip ().lower (),
597+ "supported" : True ,
598+ "describe" : tag ,
599+ "short_tag" : tag ,
600+ "display_version" : tag ,
601+ "commit" : commit ,
602+ "short_commit" : commit [:7 ] if commit else "" ,
603+ "released_at" : _get_tag_release_time_in_repo (repository , tag ),
604+ }
605+
606+
607+ def get_current_major_main_latest_info (
608+ current_version : str ,
609+ * ,
610+ repo_dir : str | Path | None = None ,
611+ ) -> dict [str , Any ]:
612+ repository = get_repo_dir (repo_dir )
613+ available_branches = set (get_available_branch_values (repo_dir = repository ))
614+ if "main" not in available_branches :
615+ return {
616+ "branch" : "main" ,
617+ "supported" : False ,
618+ "describe" : "" ,
619+ "short_tag" : "" ,
620+ "display_version" : "" ,
621+ "commit" : "" ,
622+ "short_commit" : "" ,
623+ "released_at" : "" ,
624+ }
625+
626+ current_major = _parse_major_version (current_version )
627+ if current_major is None :
628+ return get_current_branch_latest_info ("main" , repo_dir = repository )
629+
630+ tags , error = get_available_tags ("main" , repo_dir = repository )
631+ if error :
632+ return get_current_branch_latest_info ("main" , repo_dir = repository )
633+
634+ latest_same_major_tag = next (
635+ (tag for tag in tags if _parse_major_version (tag ) == current_major ),
636+ "" ,
637+ )
638+ if not latest_same_major_tag :
639+ return {
640+ "branch" : "main" ,
641+ "supported" : True ,
642+ "describe" : "" ,
643+ "short_tag" : "" ,
644+ "display_version" : "" ,
645+ "commit" : "" ,
646+ "short_commit" : "" ,
647+ "released_at" : "" ,
648+ }
649+
650+ branch_head_info = _get_branch_head_info ("main" , repo_dir = repository )
651+ head_describe = branch_head_info .get ("describe" , "" )
652+ head_short_tag = branch_head_info .get ("short_tag" , "" )
653+ _ , commits_since_tag = _split_describe_version (head_describe )
654+ if head_short_tag == latest_same_major_tag and commits_since_tag <= 0 :
655+ commit = branch_head_info .get ("commit" , "" )
656+ return {
657+ "branch" : "main" ,
658+ "supported" : True ,
659+ "describe" : head_describe ,
660+ "short_tag" : head_short_tag ,
661+ "display_version" : _format_branch_head_version ("main" , head_describe ),
662+ "commit" : commit ,
663+ "short_commit" : commit [:7 ] if commit else "" ,
664+ "released_at" : branch_head_info .get ("released_at" , "" ),
665+ }
666+
667+ return _get_release_tag_info ("main" , latest_same_major_tag , repo_dir = repository )
668+
669+
558670def get_current_branch_latest_info (
559671 current_branch : str ,
560672 * ,
@@ -572,6 +684,7 @@ def get_current_branch_latest_info(
572684 "display_version" : "" ,
573685 "commit" : "" ,
574686 "short_commit" : "" ,
687+ "released_at" : "" ,
575688 }
576689
577690 branch_head_info = _get_branch_head_info (normalized_branch , repo_dir = repository )
@@ -587,6 +700,7 @@ def get_current_branch_latest_info(
587700 ),
588701 "commit" : commit ,
589702 "short_commit" : commit [:7 ] if commit else "" ,
703+ "released_at" : branch_head_info .get ("released_at" , "" ),
590704 }
591705
592706
@@ -700,9 +814,21 @@ def get_update_info(repo_dir: str | Path | None = None) -> dict[str, Any]:
700814 repo_dir = repository ,
701815 current_version = current_version ,
702816 )
817+ if "main" in available_branch_values :
818+ _ , major_upgrade_versions , _ = get_selector_tag_options (
819+ "main" ,
820+ repo_dir = repository ,
821+ current_version = current_version ,
822+ )
823+ else :
824+ major_upgrade_versions = []
703825 return {
704826 "repo_dir" : str (repository ),
705827 "current" : version_info ,
828+ "main_branch_latest" : get_current_major_main_latest_info (
829+ current_version ,
830+ repo_dir = repository ,
831+ ),
706832 "current_branch_latest" : get_current_branch_latest_info (
707833 current_branch ,
708834 repo_dir = repository ,
@@ -714,6 +840,7 @@ def get_update_info(repo_dir: str | Path | None = None) -> dict[str, Any]:
714840 "available_tag_options" : tag_options ,
715841 "available_tags_error" : tags_error ,
716842 "available_higher_major_versions" : higher_major_versions ,
843+ "major_upgrade_versions" : major_upgrade_versions ,
717844 "paths" : {
718845 "update_file" : str (get_update_file_path ()),
719846 "status_file" : str (get_status_file_path ()),
0 commit comments