Skip to content

Commit 7dfc65f

Browse files
committed
Add a git-snapshot (tarball) generator
The src.rpm is distribution-specific format; the `rpmbuild -bs` doesn't necessarily generate the same output SRC.RPM if run on different distributions. The `snapshot` tarball is though generated in a distribution-agnostic way. Note that this tarball generator still requires specfile parsing by RPM, and it may fail easily, see #71 for more info.
1 parent 2838072 commit 7dfc65f

1 file changed

Lines changed: 89 additions & 12 deletions

File tree

dist-git-client/dist_git_client.py

Lines changed: 89 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@
1212
import shutil
1313
import subprocess
1414
import sys
15+
import tempfile
1516
from urllib.parse import urlparse
1617

1718
try:
@@ -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

287296
def 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+
311331
def 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+
351417
def 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

Comments
 (0)