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:
parent
4a96e8e313
commit
fe95221f10
@ -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].
|
||||
|
@ -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)) # ^
|
||||
|
@ -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
50
tests/test_util.py
Executable 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()
|
Loading…
Reference in New Issue
Block a user