1010from my_unicorn .download import IconAsset
1111
1212from ..logger import get_logger
13+ from ..utils import extract_and_validate_version
1314from .install_url import InstallationError , InstallStrategy , ValidationError
1415
1516logger = get_logger (__name__ )
@@ -193,9 +194,11 @@ async def _install_single_app(
193194
194195 # Verify download if requested
195196 if kwargs .get ("verify_downloads" , True ):
196- await self ._perform_verification (
197+ verification_result = await self ._perform_verification (
197198 download_path , appimage_asset , app_config , release_data
198199 )
200+ else :
201+ verification_result = None
199202
200203 # Move to install directory
201204 final_path = self .storage_service .move_to_install_dir (download_path )
@@ -220,9 +223,15 @@ async def _install_single_app(
220223 if app_config .get ("icon" ):
221224 icon_path = await self ._setup_catalog_icon (app_config , app_name , icon_dir )
222225
223- # Create application configuration
226+ # Create application configuration (pass verification result for config updates)
224227 self ._create_app_config (
225- app_name , final_path , app_config , release_data , icon_dir , appimage_asset
228+ app_name ,
229+ final_path ,
230+ app_config ,
231+ release_data ,
232+ icon_dir ,
233+ appimage_asset ,
234+ verification_result ,
226235 )
227236
228237 # Create desktop entry to reflect any changes (icon, paths, etc.)
@@ -252,7 +261,7 @@ async def _install_single_app(
252261 "path" : str (final_path ),
253262 "name" : final_path .name ,
254263 "source" : "catalog" ,
255- "version" : release_data .get ("tag_name" ),
264+ "version" : extract_and_validate_version ( release_data .get ("tag_name" , "" ) ),
256265 "icon_path" : str (icon_path ) if icon_path else None ,
257266 }
258267
@@ -363,7 +372,7 @@ async def _perform_verification(
363372 asset : dict [str , Any ],
364373 app_config : dict [str , Any ],
365374 release_data : dict [str , Any ],
366- ) -> None :
375+ ) -> dict [ str , Any ] :
367376 """Perform download verification based on catalog configuration.
368377
369378 Args:
@@ -372,6 +381,9 @@ async def _perform_verification(
372381 app_config: Application configuration from catalog
373382 release_data: GitHub release data
374383
384+ Returns:
385+ Dictionary containing successful verification methods and updated config
386+
375387 Raises:
376388 InstallationError: If verification fails
377389
@@ -381,28 +393,53 @@ async def _perform_verification(
381393 # Get verification configuration from catalog
382394 verification_config = app_config .get ("verification" , {})
383395
384- # Skip verification if configured
385- if verification_config .get ("skip" , False ):
386- logger .debug ("⏭️ Verification skipped (configured in catalog)" )
387- return
388-
389396 logger .debug (f"🔍 Starting verification for { path .name } " )
390397 verifier = Verifier (path )
391398 verification_passed = False
399+ successful_methods = {}
400+ updated_verification_config = verification_config .copy ()
401+
402+ # Detect available verification methods
403+ has_digest = bool (asset .get ("digest" ))
404+ has_checksum_file = bool (verification_config .get ("checksum_file" ))
405+ catalog_skip = verification_config .get ("skip" , False )
406+
407+ logger .debug (
408+ f" Available methods: digest={ has_digest } , checksum_file={ has_checksum_file } "
409+ )
410+ logger .debug (f" Catalog skip setting: { catalog_skip } " )
392411
393- # Try digest verification first if enabled and available
394- if verification_config .get ("digest" , False ) and asset .get ("digest" ):
412+ # Only skip if configured AND no strong verification methods available
413+ if catalog_skip and not has_digest and not has_checksum_file :
414+ logger .debug (
415+ "⏭️ Verification skipped (configured in catalog, no strong methods available)"
416+ )
417+ return {"successful_methods" : {}, "updated_config" : updated_verification_config }
418+ elif catalog_skip and (has_digest or has_checksum_file ):
419+ logger .debug (
420+ "🔄 Overriding catalog skip setting - strong verification methods available"
421+ )
422+ # Update config to reflect that we're now using verification
423+ updated_verification_config ["skip" ] = False
424+
425+ # Try digest verification first if available (prioritize over catalog setting)
426+ if has_digest :
395427 try :
396- logger .debug ("🔐 Attempting digest verification" )
428+ logger .debug ("🔐 Attempting digest verification (from GitHub API)" )
429+ if catalog_skip :
430+ logger .debug (" Note: Using digest despite catalog skip=true setting" )
397431 verifier .verify_digest (asset ["digest" ])
398432 logger .debug ("✅ Digest verification passed" )
399433 verification_passed = True
434+ successful_methods ["digest" ] = asset ["digest" ]
435+ # Enable digest verification in config for future use
436+ updated_verification_config ["digest" ] = True
400437 except Exception as e :
401438 logger .error (f"❌ Digest verification failed: { e } " )
402439 # Continue to try other verification methods
403440
404441 # Try checksum file verification if configured and digest didn't pass
405- if not verification_passed and verification_config . get ( "checksum_file" ) :
442+ if not verification_passed and has_checksum_file :
406443 checksum_file = verification_config ["checksum_file" ]
407444 hash_type = verification_config .get ("checksum_hash_type" , "sha256" )
408445
@@ -421,6 +458,11 @@ async def _perform_verification(
421458 )
422459 logger .debug ("✅ Checksum file verification passed" )
423460 verification_passed = True
461+ successful_methods ["checksum_file" ] = {
462+ "url" : checksum_url ,
463+ "hash_type" : hash_type ,
464+ "file" : checksum_file ,
465+ }
424466 except Exception as e :
425467 logger .error (f"❌ Checksum file verification failed: { e } " )
426468 # Continue to basic file size check
@@ -439,15 +481,24 @@ async def _perform_verification(
439481 if not verification_passed :
440482 raise InstallationError ("File verification failed" )
441483
442- # If we have verification methods configured but none passed, fail
443- if (
444- verification_config .get ("digest" , False )
445- or verification_config .get ("checksum_file" )
446- ) and not verification_passed :
447- raise InstallationError ("Configured verification methods failed" )
484+ # If we have strong verification methods available but none passed, fail
485+ if (has_digest or has_checksum_file ) and not verification_passed :
486+ available_methods = []
487+ if has_digest :
488+ available_methods .append ("digest" )
489+ if has_checksum_file :
490+ available_methods .append ("checksum_file" )
491+ raise InstallationError (
492+ f"Available verification methods failed: { ', ' .join (available_methods )} "
493+ )
448494
449495 logger .debug ("✅ Verification completed" )
450496
497+ return {
498+ "successful_methods" : successful_methods ,
499+ "updated_config" : updated_verification_config ,
500+ }
501+
451502 async def _setup_catalog_icon (
452503 self , app_config : dict [str , Any ], app_name : str , icon_dir : Path
453504 ) -> Path | None :
@@ -497,6 +548,7 @@ def _create_app_config(
497548 release_data : dict [str , Any ],
498549 icon_dir : Path ,
499550 appimage_asset : dict [str , Any ] | None = None ,
551+ verification_result : dict [str , Any ] | None = None ,
500552 ) -> None :
501553 """Create application configuration after successful installation.
502554
@@ -507,6 +559,7 @@ def _create_app_config(
507559 release_data: GitHub release data
508560 icon_dir: Directory where icons are stored
509561 appimage_asset: AppImage asset info for digest
562+ verification_result: Result from verification with updated config
510563
511564 """
512565 # Extract appimage config from catalog config
@@ -516,7 +569,8 @@ def _create_app_config(
516569 "config_version" : "1.0.0" ,
517570 "source" : "catalog" ,
518571 "appimage" : {
519- "version" : release_data .get ("tag_name" , "unknown" ),
572+ "version" : extract_and_validate_version (release_data .get ("tag_name" , "" ))
573+ or "unknown" ,
520574 "name" : app_path .name ,
521575 "rename" : appimage_config .get ("rename" , app_name ),
522576 "name_template" : appimage_config .get ("name_template" , "" ),
@@ -527,14 +581,8 @@ def _create_app_config(
527581 "owner" : catalog_config .get ("owner" , "" ),
528582 "repo" : catalog_config .get ("repo" , "" ),
529583 "github" : catalog_config .get ("github" , {"repo" : True , "prerelease" : False }),
530- "verification" : catalog_config .get (
531- "verification" ,
532- {
533- "digest" : True ,
534- "skip" : False ,
535- "checksum_file" : "" ,
536- "checksum_hash_type" : "sha256" ,
537- },
584+ "verification" : self ._get_updated_verification_config (
585+ catalog_config , verification_result
538586 ),
539587 "icon" : catalog_config .get ("icon" , {}),
540588 }
@@ -553,7 +601,53 @@ def _create_app_config(
553601 config ["icon" ]["path" ] = str (icon_path )
554602
555603 self .catalog_manager .save_app_config (app_name , config )
556- logger .debug (f"📝 Saved configuration for { app_name } " )
604+
605+ # Log verification config updates if any
606+ if verification_result and verification_result .get ("successful_methods" ):
607+ methods = list (verification_result ["successful_methods" ].keys ())
608+ logger .debug (
609+ f"📝 Saved configuration for { app_name } with updated verification: { ', ' .join (methods )} "
610+ )
611+ else :
612+ logger .debug (f"📝 Saved configuration for { app_name } " )
613+
614+ def _get_updated_verification_config (
615+ self , catalog_config : dict [str , Any ], verification_result : dict [str , Any ] | None
616+ ) -> dict [str , Any ]:
617+ """Get updated verification configuration based on successful verification methods.
618+
619+ Args:
620+ catalog_config: Original catalog configuration
621+ verification_result: Result from verification process
622+
623+ Returns:
624+ Updated verification configuration
625+
626+ """
627+ # Start with catalog default or fallback
628+ verification_config = catalog_config .get (
629+ "verification" ,
630+ {
631+ "digest" : True ,
632+ "skip" : False ,
633+ "checksum_file" : "" ,
634+ "checksum_hash_type" : "sha256" ,
635+ },
636+ )
637+
638+ # Update based on verification results if available
639+ if verification_result and verification_result .get ("updated_config" ):
640+ updated_config = verification_result ["updated_config" ]
641+ verification_config .update (updated_config )
642+
643+ # Log what was updated
644+ if verification_result .get ("successful_methods" ):
645+ methods = list (verification_result ["successful_methods" ].keys ())
646+ logger .debug (
647+ f"🔧 Updated verification config based on successful methods: { ', ' .join (methods )} "
648+ )
649+
650+ return verification_config
557651
558652 def _get_current_timestamp (self ) -> str :
559653 """Get current timestamp as ISO string.
0 commit comments