Resolve HEAD in ksurl to actual hash

When the image build configuration specifies kickstart URL as a HEAD of
a git repo, pungi now figures out what the actual hash of that commit is
and uses that hash instead. This might make logs clearer and should
prevent potential problems if someone pushes to that repo during
composing.

Documentation is updated to mention this.

Signed-off-by: Lubomír Sedlář <lsedlar@redhat.com>
This commit is contained in:
Lubomír Sedlář 2015-12-08 14:29:18 +01:00
parent 4a96e8e313
commit fe95221f10
4 changed files with 88 additions and 2 deletions

View File

@ -480,7 +480,8 @@ Image Build Settings
.. note::
Config can contain anything what is accepted by
koji image-build --config configfile.ini
``koji image-build --config configfile.ini``
Repo is currently the only option which is being automatically transformed
into a string.
@ -488,6 +489,10 @@ Image Build Settings
The 'format' attr is [('image_type', 'image_suffix'), ...].
productmd should ideally contain all of image types and suffixes.
If ``ksurl`` ends with ``#HEAD``, Pungi will figure out the SHA1 hash of
current HEAD and use that instead.
Example
-------
::
@ -501,6 +506,7 @@ Example
'target': 'koji-target-name',
'ksversion': 'F23', # value from pykickstart
'version': '23',
# correct SHA1 hash will be put into the URL below automatically
'ksurl': 'https://git.fedorahosted.org/git/spin-kickstarts.git?somedirectoryifany#HEAD',
'kickstart': "fedora-docker-base.ks",
'repo': ["http://someextrarepos.org/repo", "ftp://rekcod.oi/repo].

View File

@ -4,7 +4,7 @@
import os
import time
from pungi.util import get_arch_variant_data
from pungi.util import get_arch_variant_data, resolve_git_url
from pungi.phases.base import PhaseBase
from pungi.linker import Linker
from pungi.paths import translate_path
@ -34,6 +34,9 @@ class ImageBuildPhase(PhaseBase):
for variant in self.compose.get_variants(arch=arch):
image_build_data = get_arch_variant_data(self.compose.conf, self.name, arch, variant)
for image_conf in image_build_data:
# Replace possible ambiguous ref name with explicit hash.
if 'ksurl' in image_conf:
image_conf['ksurl'] = resolve_git_url(image_conf['ksurl'])
image_conf["arches"] = arch # passed to get_image_build_cmd as dict
image_conf["variant"] = variant # ^
image_conf["install_tree"] = translate_path(self.compose, self.compose.paths.compose.os_tree(arch, variant)) # ^

View File

@ -23,6 +23,7 @@ import hashlib
import errno
import pipes
import re
import urlparse
from kobo.shortcuts import run
from productmd.common import get_major_version
@ -214,6 +215,32 @@ def get_arch_variant_data(conf, var_name, arch, variant):
return result
def resolve_git_url(url):
"""Given a url to a Git repo specifying HEAD as a ref, replace that
specifier with actual SHA1 of the commit.
Otherwise, the original URL will be returned.
Raises RuntimeError if there was an error. Most likely cause is failure to
run git command.
"""
r = urlparse.urlsplit(url)
if r.fragment != 'HEAD':
return url
baseurl = urlparse.urlunsplit((r.scheme, r.netloc, r.path, '', ''))
_, output = run(['git', 'ls-remote', baseurl, r.fragment])
lines = [line for line in output.split('\n') if line]
if len(lines) != 1:
# This should never happen. HEAD can not match multiple commits in a
# single repo, and there can not be a repo without a HEAD.
raise RuntimeError('Failed to resolve %s', url)
fragment = lines[0].split()[0]
return urlparse.urlunsplit((r.scheme, r.netloc, r.path, r.query, fragment))
# fomat: {arch|*: [data]}
def get_arch_data(conf, var_name, arch):
result = []

50
tests/test_util.py Executable file
View File

@ -0,0 +1,50 @@
#!/usr/bin/env python2
# -*- coding: utf-8 -*-
import mock
import os
import sys
import unittest
sys.path.insert(0, os.path.join(os.path.dirname(__file__), ".."))
from pungi import util
class TestGitRefResolver(unittest.TestCase):
@mock.patch('pungi.util.run')
def test_successful_resolve(self, run):
run.return_value = (0, 'CAFEBABE\tHEAD\n')
url = util.resolve_git_url('https://git.example.com/repo.git?somedir#HEAD')
self.assertEqual(url, 'https://git.example.com/repo.git?somedir#CAFEBABE')
run.assert_called_once_with(['git', 'ls-remote', 'https://git.example.com/repo.git', 'HEAD'])
@mock.patch('pungi.util.run')
def test_resolve_missing_spec(self, run):
url = util.resolve_git_url('https://git.example.com/repo.git')
self.assertEqual(url, 'https://git.example.com/repo.git')
self.assertEqual(run.mock_calls, [])
@mock.patch('pungi.util.run')
def test_resolve_non_head_spec(self, run):
url = util.resolve_git_url('https://git.example.com/repo.git#some-tag')
self.assertEqual(url, 'https://git.example.com/repo.git#some-tag')
self.assertEqual(run.mock_calls, [])
@mock.patch('pungi.util.run')
def test_resolve_ambiguous(self, run):
run.return_value = (0, 'CAFEBABE\tF11\nDEADBEEF\tF10\n')
with self.assertRaises(RuntimeError):
util.resolve_git_url('https://git.example.com/repo.git?somedir#HEAD')
run.assert_called_once_with(['git', 'ls-remote', 'https://git.example.com/repo.git', 'HEAD'])
if __name__ == "__main__":
unittest.main()