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

View File

@ -4,7 +4,7 @@
import os import os
import time 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.phases.base import PhaseBase
from pungi.linker import Linker from pungi.linker import Linker
from pungi.paths import translate_path from pungi.paths import translate_path
@ -34,6 +34,9 @@ class ImageBuildPhase(PhaseBase):
for variant in self.compose.get_variants(arch=arch): for variant in self.compose.get_variants(arch=arch):
image_build_data = get_arch_variant_data(self.compose.conf, self.name, arch, variant) image_build_data = get_arch_variant_data(self.compose.conf, self.name, arch, variant)
for image_conf in image_build_data: 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["arches"] = arch # passed to get_image_build_cmd as dict
image_conf["variant"] = variant # ^ image_conf["variant"] = variant # ^
image_conf["install_tree"] = translate_path(self.compose, self.compose.paths.compose.os_tree(arch, 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 errno
import pipes import pipes
import re import re
import urlparse
from kobo.shortcuts import run from kobo.shortcuts import run
from productmd.common import get_major_version from productmd.common import get_major_version
@ -214,6 +215,32 @@ def get_arch_variant_data(conf, var_name, arch, variant):
return result 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]} # fomat: {arch|*: [data]}
def get_arch_data(conf, var_name, arch): def get_arch_data(conf, var_name, arch):
result = [] 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()