1212import shutil
1313import subprocess
1414import sys
15+ import tempfile
1516from urllib .parse import urlparse
1617
1718try :
@@ -217,7 +218,7 @@ def get_spec(distgit_config):
217218 return specfile
218219
219220
220- def sources (args , config ):
221+ def parse_sources (args , config ):
221222 """
222223 Locate the sources, and download them from the appropriate dist-git
223224 lookaside cache.
@@ -281,7 +282,15 @@ def sources(args, config):
281282 distgit_config ["lookaside_uri_pattern" ].format (** kwargs )
282283 ])
283284
284- download_file_and_check (url_file , kwargs , distgit_config )
285+ yield url_file , kwargs , distgit_config
286+
287+
288+ def sources (args , config ):
289+ """
290+ Go through all the dist-git files, and download them.
291+ """
292+ for url , metadata , distgit_config in parse_sources (args , config ):
293+ download_file_and_check (url , metadata , distgit_config )
285294
286295
287296def handle_autospec (spec_abspath , spec_basename , args ):
@@ -308,22 +317,26 @@ def handle_autospec(spec_abspath, spec_basename, args):
308317 return result
309318
310319
320+ def _prepare_outputdir (args ):
321+ mkdir_p (args .outputdir )
322+
323+
324+ def _get_preprocessed_specfile (distgit_config , args ):
325+ specs = os .path .join (os .getcwd (), distgit_config ["specs" ])
326+ spec = get_spec (distgit_config )
327+ spec_abspath = os .path .join (specs , spec )
328+ return handle_autospec (spec_abspath , spec , args )
329+
330+
311331def srpm (args , config ):
312332 """
313333 Using the appropriate dist-git configuration, generate source RPM
314334 file. This requires running 'def sources()' first.
315335 """
316336 _ , distgit_config = get_distgit_config (config , args .forked_from )
317-
318- cwd = os .getcwd ()
319- sources_dir = os .path .join (cwd , distgit_config ["sources" ])
320- specs = os .path .join (cwd , distgit_config ["specs" ])
321- spec = get_spec (distgit_config )
322-
323- mkdir_p (args .outputdir )
324-
325- spec_abspath = os .path .join (specs , spec )
326- spec_abspath = handle_autospec (spec_abspath , spec , args )
337+ _prepare_outputdir (args )
338+ spec_abspath = _get_preprocessed_specfile (distgit_config , args )
339+ sources_dir = os .path .join (os .getcwd (), distgit_config ["sources" ])
327340
328341 if args .mock_chroot :
329342 command = [
@@ -348,6 +361,59 @@ def srpm(args, config):
348361 check_call (command )
349362
350363
364+ def _list_git_sources (subdir ):
365+ directory = os .path .normpath (subdir ) + "/"
366+ cmd = ["git" , "ls-tree" , "HEAD" , directory ]
367+ git_output = subprocess .check_output (cmd , encoding = "utf-8" )
368+ for line in git_output .splitlines ():
369+ _ , objtype , _ , name = line .split ()
370+ if objtype == "blob" :
371+ yield name
372+
373+
374+ def snapshot (args , config ):
375+ """
376+ Generate a tarball snapshot for given checkout.
377+ """
378+ _ , distgit_instance_config = get_distgit_config (config , args .forked_from )
379+ spec = _get_preprocessed_specfile (distgit_instance_config , args )
380+
381+ with tempfile .TemporaryDirectory (prefix = "copr-test-walk" ) as workdir :
382+ tar_name = os .path .basename (spec )[:- 5 ]
383+ tar_dir = os .path .join (workdir , tar_name )
384+ os .makedirs (tar_dir )
385+
386+ # We insert every file top-level, while git in general may store them in
387+ # the sources subdir. Bomb early if double should be added.
388+ basenames_added = set ()
389+ def _prepare_file (file ):
390+ basename = os .path .basename (file )
391+ if basename in basenames_added :
392+ raise RuntimeError (f"{ basename } added twice" )
393+ basenames_added .add (basename )
394+ shutil .copy (file , tar_dir )
395+
396+ _prepare_file (spec )
397+
398+ for _ , metadata , _ in parse_sources (args , config ):
399+ print (".." )
400+ _prepare_file (metadata ["filename" ])
401+
402+ for file in _list_git_sources (distgit_instance_config ["sources" ]):
403+ if os .path .basename (file ) == os .path .basename (spec ):
404+ # spec file added separately
405+ continue
406+ # Shall we filter-out files that are not related to RPM
407+ # building?
408+ _prepare_file (file )
409+
410+ tarball = os .path .join (args .outputdir , tar_name + ".tar.gz" )
411+ logging .info ("Generating tarball %s" , tarball )
412+ tar_cmd = ["tar" , "--owner=root" , "--group=root" , "--format=pax" ,
413+ "-czf" , tarball , "-C" , workdir , tar_name ]
414+ subprocess .check_call (tar_cmd )
415+
416+
351417def clone (args , config ):
352418 """
353419 Automatically clone a package from a given DistGit instance
@@ -454,6 +520,15 @@ def _get_argparser():
454520 "'@copr/copr-dev/copr-cli'." ),
455521 )
456522
523+ snapshot_parser = subparsers .add_parser (
524+ "snapshot" ,
525+ help = ("Generate a snapshot for given dist-git checkout" ),
526+ )
527+ snapshot_parser .add_argument (
528+ "--outputdir" ,
529+ default = "/tmp" ,
530+ help = "Where to store the resulting tarball" )
531+
457532 return parser
458533
459534
@@ -494,6 +569,8 @@ def main():
494569 srpm (args , config )
495570 elif args .action == "clone" :
496571 clone (args , config )
572+ elif args .action == "snapshot" :
573+ snapshot (args , config )
497574 else :
498575 sources (args , config )
499576 except RuntimeError as err :
0 commit comments