2424from core .translations import get_translation
2525from discord .ext import tasks
2626from migrate import migrate
27- from packaging import version
27+ from packaging . version import parse
2828from pathlib import Path
2929from psycopg .errors import UndefinedTable , InFailedSqlTransaction , NotNullViolation , OperationalError
3030from psycopg .rows import dict_row
@@ -90,7 +90,6 @@ def __init__(self, name: str, config_dir: Optional[str] = 'config'):
9090 self .is_shutdown = asyncio .Event ()
9191 self .rc = 0
9292 self .dcs_branch = None
93- self .dcs_version = None
9493 self .all_nodes : dict [str , Optional [Node ]] = {self .name : self }
9594 self .suspect : dict [str , Node ] = {}
9695 self .instances : list [Instance ] = []
@@ -129,6 +128,7 @@ async def __aexit__(self, type, value, traceback):
129128 await self .close_db ()
130129
131130 async def post_init (self ):
131+ await self .get_dcs_branch_and_version ()
132132 self .pool , self .apool = await self .init_db ()
133133 try :
134134 self ._master = await self .heartbeat ()
@@ -388,7 +388,7 @@ async def _upgrade_pending_non_git(self) -> bool:
388388 current_version = re .sub ('^v' , '' , __version__ )
389389 latest_version = re .sub ('^v' , '' , result [0 ]["tag_name" ])
390390
391- if version . parse (latest_version ) > version . parse (current_version ):
391+ if parse (latest_version ) > parse (current_version ):
392392 return True
393393 except aiohttp .ClientResponseError as ex :
394394 # ignore rate limits
@@ -435,9 +435,9 @@ async def get_dcs_branch_and_version(self) -> tuple[str, str]:
435435 "Use /dcs update if you want to switch to the release branch." )
436436 return self .dcs_branch , self .dcs_version
437437
438- async def update (self , warn_times : list [int ], branch : Optional [str ] = None ) -> int :
438+ async def update (self , warn_times : list [int ], branch : Optional [str ] = None , version : Optional [ str ] = None ) -> int :
439439
440- async def do_update (branch : Optional [str ] = None ) -> int :
440+ async def do_update (branch : str , version : Optional [str ] = None ) -> int :
441441 # disable any popup on the remote machine
442442 if sys .platform == 'win32' :
443443 startupinfo = subprocess .STARTUPINFO ()
@@ -450,7 +450,9 @@ async def do_update(branch: Optional[str] = None) -> int:
450450 def run_subprocess () -> int :
451451 try :
452452 cmd = [os .path .join (self .installation , 'bin' , 'dcs_updater.exe' ), '--quiet' , 'update' ]
453- if branch :
453+ if version :
454+ cmd .append (f"{ version } @{ branch } " )
455+ else :
454456 cmd .append (f"@{ branch } " )
455457
456458 process = subprocess .run (
@@ -481,15 +483,16 @@ def run_subprocess() -> int:
481483 for callback in self .before_update .values ():
482484 await callback ()
483485 old_branch , old_version = await self .get_dcs_branch_and_version ()
484- rc = await do_update (branch )
486+ rc = await do_update (branch , version )
485487 if rc in [0 , 350 ]:
486488 self .dcs_branch = self .dcs_version = None
487489 dcs_branch , dcs_version = await self .get_dcs_branch_and_version ()
488490 # if only the updater updated itself, run the update again
489491 if old_branch == dcs_branch and old_version == dcs_version :
490492 self .log .info ("dcs_updater.exe updated to the latest version, now updating DCS World ..." )
491- rc = await do_update (branch )
493+ rc = await do_update (branch , version )
492494 self .dcs_branch = self .dcs_version = None
495+ await self .get_dcs_branch_and_version ()
493496 if rc not in [0 , 350 ]:
494497 return rc
495498 if self .locals ['DCS' ].get ('desanitize' , True ):
@@ -564,15 +567,15 @@ async def get_available_modules(self) -> list[str]:
564567 pass
565568 return list (licenses )
566569
567- async def get_latest_version (self , branch : str ) -> Optional [str ]:
568- async def _get_latest_version_no_auth () :
570+ async def get_available_dcs_versions (self , branch : str ) -> Optional [list [ str ] ]:
571+ async def _get_latest_versions_no_auth () -> Optional [ list [ str ]] :
569572 async with aiohttp .ClientSession (connector = aiohttp .TCPConnector (
570573 ssl = ssl .create_default_context (cafile = certifi .where ()))) as session :
571574 async with session .get (UPDATER_URL .format (branch )) as response :
572575 if response .status == 200 :
573- return json .loads (gzip .decompress (await response .read ()))['versions2' ][ - 1 ][ 'version' ]
576+ return [ x [ 'version' ] for x in json .loads (gzip .decompress (await response .read ()))['versions2' ]]
574577
575- async def _get_latest_version_auth () :
578+ async def _get_latest_versions_auth () -> Optional [ list [ str ]] :
576579 user = self .locals ['DCS' ].get ('user' )
577580 password = utils .get_password ('DCS' , self .config_dir )
578581 headers = {
@@ -584,17 +587,22 @@ async def _get_latest_version_auth():
584587 if r1 .status == 200 :
585588 async with await session .get (UPDATER_URL .format (branch )) as r2 :
586589 if r2 .status == 200 :
587- data = json .loads (gzip .decompress (await r2 .read ()))['versions2' ][ - 1 ][ 'version' ]
590+ data = [ x [ 'version' ] for x in json .loads (gzip .decompress (await r2 .read ()))['versions2' ]]
588591 else :
589592 data = None
590593 async with await session .get (LOGOUT_URL ):
591594 pass
592595 return data
593596
594597 if not self .locals ['DCS' ].get ('user' ):
595- return await _get_latest_version_no_auth ()
598+ return await _get_latest_versions_no_auth ()
596599 else :
597- return await _get_latest_version_auth ()
600+ return await _get_latest_versions_auth ()
601+
602+
603+ async def get_latest_version (self , branch : str ) -> Optional [str ]:
604+ versions = await self .get_available_dcs_versions (branch )
605+ return versions [- 1 ] if versions else None
598606
599607 async def register (self ):
600608 self ._public_ip = self .locals .get ('public_ip' )
@@ -679,8 +687,8 @@ def has_timeout(row: dict, timeout: int):
679687 # noinspection PyAsyncCall
680688 asyncio .create_task (self .upgrade ())
681689 return True
682- elif version . parse (cluster ['version' ]) != version . parse (__version__ ):
683- if version . parse (cluster ['version' ]) > version . parse (__version__ ):
690+ elif parse (cluster ['version' ]) != parse (__version__ ):
691+ if parse (cluster ['version' ]) > parse (__version__ ):
684692 self .log .warning (
685693 f"Bot version downgraded from { cluster ['version' ]} to { __version__ } . "
686694 f"This could lead to unexpected behavior if there have been database "
@@ -723,7 +731,7 @@ def has_timeout(row: dict, timeout: int):
723731 (self .name , self .guild_id ))
724732 return True
725733 # we have a version mismatch on the agent, a cloud sync might still be pending
726- if version . parse (__version__ ) < version . parse (cluster ['version' ]):
734+ if parse (__version__ ) < parse (cluster ['version' ]):
727735 self .log .error (f"We are running version { __version__ } where the master is on version "
728736 f"{ cluster ['version' ]} already. Trying to upgrade ..." )
729737 # TODO: we might not have bus access here yet, so be our own bus (dirty)
@@ -736,7 +744,7 @@ def has_timeout(row: dict, timeout: int):
736744 INSERT INTO intercom (guild_id, node, data) VALUES (%s, %s, %s)
737745 """ , (self .guild_id , self .name , Json (data )))
738746 return False
739- elif version . parse (__version__ ) > version . parse (cluster ['version' ]):
747+ elif parse (__version__ ) > parse (cluster ['version' ]):
740748 self .log .warning (
741749 f"This node is running on version { __version__ } where the master still runs on "
742750 f"{ cluster ['version' ]} . You need to upgrade your master node!" )
@@ -906,7 +914,7 @@ async def autoupdate(self):
906914 except aiohttp .ClientError :
907915 self .log .warning ("Update check failed, possible server outage at ED." )
908916 return
909- if new_version and old_version != new_version :
917+ if new_version and parse ( old_version ) < parse ( new_version ) :
910918 self .log .info ('A new version of DCS World is available. Auto-updating ...' )
911919 rc = await self .update ([300 , 120 , 60 ])
912920 if rc == 0 :
0 commit comments