@@ -483,12 +483,34 @@ def should_repack_archive(
483483 return True
484484
485485
486+ EXECUTABLE_SIGNATURES = set ([
487+ b"\xFE \xED \xFA \xCE " , # mach-o 32-bits big endian
488+ b"\xCE \xFA \xED \xFE " , # mach-o 32-bits little endian
489+ b"\xFE \xED \xFA \xCF " , # mach-o 64-bits big endian
490+ b"\xCF \xFA \xED \xFE " , # mach-o 64-bits little endian
491+ b"\xCA \xFE \xBA \xBE " , # mach-o FAT binary
492+ b"\x7F \x45 \x4C \x46 " , # Elf binary
493+ ])
494+
495+
486496def repack_archive (
487- orig : pathlib .Path , dest : pathlib .Path , strip_components = 0 , prefix = ""
497+ orig : pathlib .Path ,
498+ dest : pathlib .Path ,
499+ strip_components = 0 ,
500+ prefix = "" ,
501+ force_archive = False ,
488502):
489503 assert orig != dest
490504 log (f"Repacking { orig } as { dest } " )
491- orig_typ , ifh = open_stream (orig )
505+ try :
506+ orig_typ , ifh = open_stream (orig )
507+ except ArchiveTypeNotSupported :
508+ if force_archive :
509+ ifh = io .BufferedReader (orig .open (mode = "rb" ))
510+ signature = ifh .peek (4 )[:4 ]
511+ orig_typ = "exec" if signature in EXECUTABLE_SIGNATURES else None
512+ else :
513+ raise
492514 typ = archive_type (dest )
493515 if not typ :
494516 raise Exception ("Archive type not supported for %s" % dest .name )
@@ -513,7 +535,20 @@ def repack_archive(
513535
514536 with rename_after_close (dest , "wb" ) as fh :
515537 ctx = ZstdCompressor ()
516- if orig_typ == "zip" :
538+ if orig_typ in ("exec" , None ):
539+ with ctx .stream_writer (fh ) as compressor , tarfile .open (
540+ fileobj = compressor ,
541+ mode = "w:" ,
542+ ) as tar :
543+ tarinfo = tarfile .TarInfo ()
544+ tarinfo .name = filter (orig .name ) if filter else orig .name
545+ st = orig .stat ()
546+ tarinfo .size = st .st_size
547+ tarinfo .mtime = st .st_mtime
548+ tarinfo .mode = 0o0755 if orig_typ == "exec" else 0o0644
549+ tar .addfile (tarinfo , ifh )
550+
551+ elif orig_typ == "zip" :
517552 assert typ == "tar"
518553 zip = zipfile .ZipFile (ifh )
519554 # Convert the zip stream to a tar on the fly.
@@ -827,8 +862,12 @@ def command_static_url(args):
827862 if gpg_sig_url :
828863 gpg_verify_path (dl_dest , gpg_key , gpg_signature )
829864
830- if should_repack_archive (dl_dest , dest , args .strip_components , args .add_prefix ):
831- repack_archive (dl_dest , dest , args .strip_components , args .add_prefix )
865+ if args .force_archive or should_repack_archive (
866+ dl_dest , dest , args .strip_components , args .add_prefix
867+ ):
868+ repack_archive (
869+ dl_dest , dest , args .strip_components , args .add_prefix , args .force_archive
870+ )
832871 elif dl_dest != dest :
833872 log (f"Renaming { dl_dest } to { dest } " )
834873 dl_dest .rename (dest )
@@ -963,6 +1002,11 @@ def main():
9631002 dest = "headers" ,
9641003 help = "Header to send as part of the request, can be passed " "multiple times" ,
9651004 )
1005+ url .add_argument (
1006+ "--force-archive" ,
1007+ action = "store_true" ,
1008+ help = "Create an archive even when the downloaded file is not an archive" ,
1009+ )
9661010 url .add_argument ("url" , help = "URL to fetch" )
9671011 url .add_argument ("dest" , help = "Destination path" )
9681012
0 commit comments