Refactor openstack_git_repo & add openstack_commit

It will become more and more important to have more information about
individual commits to an openstack repo.  Therefore, add the
OpenstackCommit class. Refactor OpenstackGitRepo to accomodate this.
This commit is contained in:
Craig Tracey 2014-11-15 22:06:56 -05:00
parent a0c7bde589
commit ffdccc26e9
2 changed files with 207 additions and 33 deletions

View File

@ -0,0 +1,140 @@
# vim: tabstop=4 shiftwidth=4 softtabstop=4
# Copyright 2014, Craig Tracey <craigtracey@gmail.com>
# All Rights Reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may
# not use this file except in compliance with the License. You may obtain
# a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations
import logging
import os
import re
import yaml
from giftwrap.gerrit import GerritReview
LOG = logging.getLogger(__name__)
class OpenstackCommit(object):
def __init__(self, commit, project, branch, meta_cache_dir=None):
self.commit = commit
self.project = project
self.branch = branch
self._change_id = None
self._editable_dependencies = None
self._pip_dependencies = None
self._is_merge = None
self._parent = None
self._gerrit_review = None
self._meta_cache_dir = meta_cache_dir
@property
def hexsha(self):
return self.commit.hexsha
@property
def change_id(self):
if not self._change_id:
self._change_id = str(self._get_change_id())
return self._change_id
@property
def is_merge(self):
if self._is_merge is None:
self._is_merge = (len(self.commit.parents) == 2)
return self._is_merge
@property
def parent(self):
if self.is_merge:
self._parent = OpenstackCommit(self.commit.parents[1],
self.project, self.branch)
return self._parent
@property
def gerrit_review(self):
if not self._gerrit_review:
self._gerrit_review = GerritReview(self.change_id,
self.project, self.branch)
return self._gerrit_review
def _gather_dependencies(self):
try:
deps = self.gerrit_review.build_pip_dependencies()
self._editable_dependencies = []
self._pip_dependencies = {}
for dep in deps:
if '-e' in dep:
self._editable_dependencies.append(dep)
else:
parts = dep.split('==')
self._pip_dependencies[parts[0]] = parts[1]
except Exception as e:
LOG.debug("Couldn't find dependencies for %s: %s", self.hexsha, e)
@property
def pip_dependencies(self):
if not self._pip_dependencies:
self._gather_dependencies()
return self._pip_dependencies
@property
def editable_dependencies(self):
if not self._editable_dependencies:
self._gather_dependencies()
return self._editable_dependencies
def _get_change_id(self):
commit = self.commit
if self.is_merge:
commit = self.parent.commit
match = re.search('Change-Id:\s*(I\w+)', commit.message)
if match:
return match.group(1)
def is_cached(self):
return os.path.isfile(self.cache_file)
@property
def cache_file(self):
return os.path.join(self._meta_cache_dir, self.hexsha)
def _get_from_cache(self, key):
if self.is_cached():
with open(self.cache_file, 'r') as fh:
cached_data = yaml.load(fh)
if key in cached_data:
return cached_data[key]
return None
def is_cacheable(self):
if self.pip_dependencies or self.editable_dependencies:
return True
return False
def __dict__(self):
data = {}
data['pip_dependencies'] = self.pip_dependencies
data['editable_dependencies'] = self.editable_dependencies
data['change_id'] = self.change_id
return data
def persist_to_cache(self):
if not self.is_cacheable():
LOG.debug("Not caching %s as there is no point", self.hexsha)
return
dirname = os.path.dirname(self.cache_file)
if not os.path.exists(dirname):
os.makedirs(dirname)
with open(self.cache_file, 'w') as fh:
fh.write(yaml.dump(self.__dict__()))

View File

@ -16,22 +16,27 @@
import datetime
import logging
import os
import re
import time
import urlparse
from giftwrap.openstack_commit import OpenstackCommit
from git import Repo
LOG = logging.getLogger(__name__)
class OpenstackGitRepo(object):
def __init__(self, url, ref='master'):
def __init__(self, url, project=None, branch='master',
metadata_cache_dir=None):
self.url = url
self.ref = ref
self._project = project
self.branch = branch
self._repo = None
self._head = None
self._change_id = None
self._committed_date = None
self._metadata_cache_dir = metadata_cache_dir
self._head_commit = None
@property
def cloned(self):
@ -39,50 +44,79 @@ class OpenstackGitRepo(object):
@property
def head(self):
if not self._head and self._repo:
self._head = self._repo.head.commit.hexsha
return self._head
if not self._head_commit and self._repo:
self._head_commit = OpenstackCommit(self._repo.head.commit,
self.project, self.branch,
self._cache_dir())
return self._head_commit
@property
def change_id(self):
if not self._change_id and self._repo:
for commit in self._repo.iter_commits():
match = re.search('Change-Id:\s*(I\w+)', commit.message)
if match:
self._change_id = match.group(1)
break
return self._change_id
@property
def committed_date(self):
if not self._committed_date and self._repo:
self._committed_date = self._repo.head.commit.committed_date
return self._committed_date
def _invalidate_attrs(self):
self._head = None
self._change_id = None
self._committed_date = None
def project(self):
if not self._project:
parsed_url = urlparse.urlparse(self.url)
project = os.path.splitext(parsed_url.path)[0]
self._project = re.sub(r'^/', '', project)
return self._project
def clone(self, outdir):
LOG.info("Cloning '%s' to '%s'", self.url, outdir)
self._repo = Repo.clone_from(self.url, outdir)
LOG.debug("Cloning '%s' to '%s'", self.url, outdir)
self._repo = Repo.clone_from(self.url, outdir, recursive=True)
git = self._repo.git
git.checkout(self.ref)
git.checkout(self.branch)
self._invalidate_attrs()
def checkout_branch(self, branch, update=True):
if not self._repo:
raise Exception("Cannot checkout on non-existent repo")
LOG.debug("Checking out branch: %s (update: %s)", branch, update)
self._repo.git.checkout(branch)
self._invalidate_attrs()
self.branch = branch
if update:
self._repo.git.pull('origin', branch)
@property
def branches(self):
branches = []
for ref in self._repo.remotes.origin.refs:
branches.append(re.sub('^\w*/', '', ref.name))
return branches
def __iter__(self):
if not self._repo:
raise Exception("iterator called before clone")
self._commit_iterator = self._repo.iter_commits()
return self
def next(self):
print self._cache_dir()
return OpenstackCommit(next(self._commit_iterator),
self.project, self.branch,
self._cache_dir())
def _cache_dir(self):
if self._metadata_cache_dir:
return os.path.join(self._metadata_cache_dir,
self.project, self.branch)
return None
def _invalidate_attrs(self):
self._head_commit = None
self._commit_iterator = None
def reset_to_date(self, date):
if self._repo:
commit_date_sha = None
for commit in self._repo.iter_commits():
if commit.committed_date >= date:
commit_date_sha = commit.hexsha
continue
elif commit.committed_date < date:
commit_date_sha = commit.hexsha
break
if not commit_date_sha:
raise Exception("Unable to find commit for date %s",
datetime.datetime.fromtimestamp(date))
git = self._repo.git
LOG.info("Reset repo '%s' to commit at '%s'", self.url,
time.strftime('%Y-%m-%d %H:%M:%S', time.localtime(date)))
LOG.debug("Reset repo '%s' to commit at '%s'", self.url,
time.strftime('%Y-%m-%d %H:%M:%S', time.localtime(date)))
git.checkout(commit_date_sha)