summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--requirements.freeze.txt1
-rw-r--r--requirements.txt1
-rwxr-xr-xybd/__main__.py6
-rw-r--r--ybd/cache.py10
-rw-r--r--ybd/config.py2
-rw-r--r--ybd/release_note.py11
-rw-r--r--ybd/repos.py200
-rw-r--r--ybd/sandbox.py8
8 files changed, 42 insertions, 197 deletions
diff --git a/requirements.freeze.txt b/requirements.freeze.txt
index e3956ac..f7dac27 100644
--- a/requirements.freeze.txt
+++ b/requirements.freeze.txt
@@ -7,3 +7,4 @@ bottle==0.12.1
cherrypy==8.1.2
riemann-client==6.3.0
fs==0.5.0
+gitmachine==0.1.5
diff --git a/requirements.txt b/requirements.txt
index 5e618c1..2b8c20e 100644
--- a/requirements.txt
+++ b/requirements.txt
@@ -7,3 +7,4 @@ bottle
cherrypy
riemann-client
fs
+gitmachine
diff --git a/ybd/__main__.py b/ybd/__main__.py
index 2aba7cd..b0c65b5 100755
--- a/ybd/__main__.py
+++ b/ybd/__main__.py
@@ -20,7 +20,7 @@
import os
import sys
import fcntl
-from ybd import app, cache, sandbox
+from ybd import app, cache, sandbox, repos
from ybd.app import cleanup, config, log, RetryException, setup, spawn, timer
from ybd.assembly import compose
from ybd.deployment import deploy
@@ -29,6 +29,7 @@ from ybd.concourse import Pipeline
from ybd.release_note import do_release_note
import sandboxlib
import yaml
+from gitmachine import GitMachine
if sys.version_info < (3,5,2):
@@ -70,6 +71,9 @@ with timer('TOTAL'):
Pipeline(target)
os._exit(0)
+ repos.gitmachine = GitMachine(config['gits'], config['tmp'],
+ config['aliases'])
+
with timer('CACHE-KEYS', 'cache-key calculations'):
cache.cache_key(target)
diff --git a/ybd/cache.py b/ybd/cache.py
index c37d790..d93b2b0 100644
--- a/ybd/cache.py
+++ b/ybd/cache.py
@@ -22,8 +22,8 @@ import os
import shutil
from subprocess import call
-from ybd import app, utils
-from ybd.repos import get_repo_url, get_tree
+from ybd import app, repos, utils
+from ybd.repos import get_tree
import tempfile
import yaml
import re
@@ -174,7 +174,8 @@ def update_manifest(dn, manifest):
if manifest.endswith('text'):
format = '%s %s %s %s %s %s\n'
m.write(format % (dn['name'], dn['cache'],
- get_repo_url(dn.get('repo', 'None')),
+ repos.gitmachine.normalize_repo_url(
+ dn.get('repo', 'None')),
dn.get('ref', 'None'),
dn.get('unpetrify-ref', 'None'),
md5(get_cache(dn))))
@@ -183,7 +184,8 @@ def update_manifest(dn, manifest):
text = {'name': dn['name'],
'summary': {'artifact': dn['cache'],
- 'repo': get_repo_url(dn.get('repo', None)),
+ 'repo': repos.gitmachine.normalize_repo_url(
+ dn.get('repo', None)),
'sha': dn.get('ref', None),
'ref': dn.get('unpetrify-ref', None),
'md5': md5(get_cache(dn))}}
diff --git a/ybd/config.py b/ybd/config.py
new file mode 100644
index 0000000..fc8f3a7
--- /dev/null
+++ b/ybd/config.py
@@ -0,0 +1,2 @@
+config = {}
+defs = {}
diff --git a/ybd/release_note.py b/ybd/release_note.py
index 68d20fd..d3485f8 100644
--- a/ybd/release_note.py
+++ b/ybd/release_note.py
@@ -17,11 +17,11 @@
import os
from subprocess import check_output
import tempfile
-from ybd import app
+from ybd import app, repos
from ybd.app import chdir, config, log
from ybd.morphs import Morphs
-from ybd.repos import explore, get_last_tag, get_repo_name
-from ybd.repos import mirror, mirror_has_ref
+from ybd.repos import explore, get_last_tag, mirror_has_ref
+from ybd.morphs import Morphs
def do_release_note(release_note):
@@ -83,9 +83,10 @@ def log_changes(dn, tmpdir, old_defs, ref):
log(dn, 'Logging git change history', tmpdir)
try:
gitdir = os.path.join(config['gits'],
- get_repo_name(dn['repo']))
+ repos.gitmachine.normalize_repo_name(
+ dn['repo']))
if not os.path.exists(gitdir):
- mirror(dn['name'], dn['repo'])
+ repos.gitmachine.mirror_repository(dn['repo'])
elif not mirror_has_ref(gitdir, ref):
update_mirror(dn['name'], dn['repo'], gitdir)
with chdir(gitdir):
diff --git a/ybd/repos.py b/ybd/repos.py
index a1235b9..4bc4b5b 100644
--- a/ybd/repos.py
+++ b/ybd/repos.py
@@ -34,30 +34,7 @@ else:
from configparser import RawConfigParser
from io import StringIO
-
-def get_repo_url(repo):
- if repo:
- for alias, url in app.config.get('aliases', {}).items():
- repo = repo.replace(alias, url)
- if repo[:4] == "http" and not repo.endswith('.git'):
- repo = repo + '.git'
- return repo
-
-
-def get_repo_name(repo):
- ''' Convert URIs to strings that only contain digits, letters, _ and %.
-
- NOTE: this naming scheme is based on what lorry uses
-
- '''
- def transl(x):
- return x if x in valid_chars else '_'
-
- valid_chars = string.digits + string.ascii_letters + '%_'
- url = get_repo_url(repo)
- if url.endswith('.git'):
- url = url[:-4]
- return ''.join([transl(x) for x in url])
+gitmachine = None
def get_version(gitdir, ref='HEAD'):
@@ -86,9 +63,23 @@ def get_last_tag(gitdir):
return None
+def mirror_has_ref(gitdir, ref):
+ with utils.chdir(gitdir), open(os.devnull, "w") as fnull:
+ out = call(['git', 'cat-file', '-t', ref], stdout=fnull, stderr=fnull)
+ return out == 0
+
+
+def checkout(dn):
+ gitmachine.arrange_into_folder(
+ dn['repo'], dn['ref'], dn.get('submodules', {}), dn['checkout'])
+
+ utils.set_mtime_recursively(dn['checkout'])
+
+
def get_tree(dn):
ref = str(dn['ref'])
- gitdir = os.path.join(app.config['gits'], get_repo_name(dn['repo']))
+ gitdir = os.path.join(app.config['gits'],
+ gitmachine.normalize_repo_name(dn['repo']))
if dn['repo'].startswith('file://') or dn['repo'].startswith('/'):
gitdir = dn['repo'].replace('file://', '')
if not os.path.isdir(gitdir):
@@ -96,7 +87,8 @@ def get_tree(dn):
if not os.path.exists(gitdir):
try:
- params = {'repo': get_repo_url(dn['repo']), 'ref': ref}
+ params = {'repo': gitmachine.normalize_repo_url(dn['repo']),
+ 'ref': ref}
r = requests.get(url=app.config['tree-server'], params=params)
return r.json()['tree']
except:
@@ -123,167 +115,11 @@ def get_tree(dn):
app.log(dn, 'No tree for ref', (ref, gitdir), exit=True)
-def mirror(name, repo):
- tempfile.tempdir = app.config['tmp']
- tmpdir = tempfile.mkdtemp()
- repo_url = get_repo_url(repo)
- try:
- tar_file = get_repo_name(repo_url) + '.tar'
- app.log(name, 'Try fetching tarball %s' % tar_file)
- # try tarball first
- with app.chdir(tmpdir), open(os.devnull, "w") as fnull:
- call(['wget', os.path.join(app.config['tar-url'], tar_file)],
- stdout=fnull, stderr=fnull)
- call(['tar', 'xf', tar_file], stderr=fnull)
- call(['git', 'config', 'gc.autodetach', 'false'], stderr=fnull)
- os.remove(tar_file)
- update_mirror(name, repo, tmpdir)
- except:
- app.log(name, 'Try git clone from', repo_url)
- with open(os.devnull, "w") as fnull:
- if call(['git', 'clone', '--mirror', '-n', repo_url, tmpdir]):
- app.log(name, 'Failed to clone', repo, exit=True)
-
- with app.chdir(tmpdir):
- if call(['git', 'rev-parse']):
- app.log(name, 'Problem mirroring git repo at', tmpdir, exit=True)
-
- gitdir = os.path.join(app.config['gits'], get_repo_name(repo))
- try:
- shutil.move(tmpdir, gitdir)
- app.log(name, 'Git repo is mirrored at', gitdir)
- except:
- pass
-
-
-def fetch(repo):
- with app.chdir(repo), open(os.devnull, "w") as fnull:
- call(['git', 'fetch', 'origin'], stdout=fnull, stderr=fnull)
-
-
-def mirror_has_ref(gitdir, ref):
- with app.chdir(gitdir), open(os.devnull, "w") as fnull:
- out = call(['git', 'cat-file', '-t', ref], stdout=fnull, stderr=fnull)
- return out == 0
-
-
-def update_mirror(name, repo, gitdir):
- with app.chdir(gitdir), open(os.devnull, "w") as fnull:
- app.log(name, 'Refreshing mirror for %s' % repo)
- repo_url = get_repo_url(repo)
- if call(['git', 'fetch', repo_url, '+refs/*:refs/*', '--prune'],
- stdout=fnull, stderr=fnull):
- app.log(name, 'Git update mirror failed', repo, exit=True)
-
-
-def checkout(dn):
- _checkout(dn['name'], dn['repo'], dn['ref'], dn['checkout'])
-
- with app.chdir(dn['checkout']):
- if os.path.exists('.gitmodules') or dn.get('submodules'):
- checkout_submodules(dn)
-
- utils.set_mtime_recursively(dn['checkout'])
-
-
-def _checkout(name, repo, ref, checkout):
- gitdir = os.path.join(app.config['gits'], get_repo_name(repo))
- if not os.path.exists(gitdir):
- mirror(name, repo)
- elif not mirror_has_ref(gitdir, ref):
- update_mirror(name, repo, gitdir)
- # checkout the required version from git
- with open(os.devnull, "w") as fnull:
- # We need to pass '--no-hardlinks' because right now there's nothing to
- # stop the build from overwriting the files in the .git directory
- # inside the sandbox. If they were hardlinks, it'd be possible for a
- # build to corrupt the repo cache. I think it would be faster if we
- # removed --no-hardlinks, though.
- if call(['git', 'clone', '--no-hardlinks', gitdir, checkout],
- stdout=fnull, stderr=fnull):
- app.log(name, 'Git clone failed for', gitdir, exit=True)
-
- with app.chdir(checkout):
- if call(['git', 'checkout', '--force', ref], stdout=fnull,
- stderr=fnull):
- app.log(name, 'Git checkout failed for', ref, exit=True)
-
- app.log(name, 'Git checkout %s in %s' % (repo, checkout))
- app.log(name, 'Upstream version %s' % get_version(checkout, ref))
-
-
def source_date_epoch(checkout):
with app.chdir(checkout):
return check_output(['git', 'log', '-1', '--pretty=%ct'])[:-1]
-def extract_commit(name, repo, ref, target_dir):
- '''Check out a single commit (or tree) from a Git repo.
- The checkout() function actually clones the entire repo, so this
- function is much quicker when you don't need to copy the whole repo into
- target_dir.
- '''
- gitdir = os.path.join(app.config['gits'], get_repo_name(repo))
- if not os.path.exists(gitdir):
- mirror(name, repo)
- elif not mirror_has_ref(gitdir, ref):
- update_mirror(name, repo, gitdir)
-
- with tempfile.NamedTemporaryFile() as git_index_file:
- git_env = os.environ.copy()
- git_env['GIT_INDEX_FILE'] = git_index_file.name
- git_env['GIT_WORK_TREE'] = target_dir
-
- app.log(name, 'Extracting commit', ref)
- if call(['git', 'read-tree', ref], env=git_env, cwd=gitdir):
- app.log(name, 'git read-tree failed for', ref, exit=True)
- app.log(name, 'Then checkout index', ref)
- if call(['git', 'checkout-index', '--all'], env=git_env, cwd=gitdir):
- app.log(name, 'Git checkout-index failed for', ref, exit=True)
- app.log(name, 'Done', ref)
-
- utils.set_mtime_recursively(target_dir)
-
-
-def checkout_submodules(dn):
- app.log(dn, 'Checking git submodules')
- with open('.gitmodules', "r") as gitfile:
- # drop indentation in sections, as RawConfigParser cannot handle it
- content = '\n'.join([l.strip() for l in gitfile.read().splitlines()])
- io = StringIO(content)
- parser = RawConfigParser()
- parser.readfp(io)
-
- for section in parser.sections():
- # validate section name against the 'submodule "foo"' pattern
- submodule = re.sub(r'submodule "(.*)"', r'\1', section)
- path = parser.get(section, 'path')
- try:
- url = dn['submodules'][path]['url']
- app.log(dn, 'Processing submodule %s from' % path, url)
- except:
- url = parser.get(section, 'url')
- app.log(dn, 'WARNING: fallback to submodule %s from' % path, url)
-
- # list objects in the parent repo tree to find the commit
- # object that corresponds to the submodule
- commit = check_output(['git', 'ls-tree', dn['ref'], path]).split()
-
- # read the commit hash from the output
- fields = list(map(lambda x: x.decode('unicode-escape'), commit))
- if len(fields) >= 2 and fields[1] == 'commit':
- submodule_commit = fields[2]
-
- # fail if the commit hash is invalid
- if len(submodule_commit) != 40:
- raise Exception
-
- fulldir = os.path.join(os.getcwd(), path)
- _checkout(dn['name'], url, submodule_commit, fulldir)
- else:
- app.log(dn, 'Skipping submodule %s, not a commit:' % path, fields)
-
-
@contextlib.contextmanager
def explore(ref):
try:
diff --git a/ybd/sandbox.py b/ybd/sandbox.py
index 8d869c2..38a49f9 100644
--- a/ybd/sandbox.py
+++ b/ybd/sandbox.py
@@ -23,10 +23,7 @@ import shutil
import stat
import tempfile
from subprocess import call, PIPE
-
-from ybd import app, cache, utils
-from ybd.repos import get_repo_url
-
+from ybd import app, cache, repos, utils
# This must be set to a sandboxlib backend before the run_sandboxed() function
# can be used.
@@ -248,7 +245,8 @@ def ccache_mounts(dn, ccache_target):
if app.config['no-ccache'] or 'repo' not in dn:
mounts = []
else:
- name = os.path.basename(get_repo_url(dn['repo']))
+ name = os.path.basename(repos.gitmachine.normalize_repo_url(
+ dn['repo']))
if name.endswith('.git'):
name = name[:-4]
ccache_dir = os.path.join(app.config['ccache_dir'], name)