Remove zuul-cloner command
With the release of zuul 3.0.0 is command is no longer needed. Jobs are setup to push the known repo state on to the remove nodes. Change-Id: I0df6e41dc05276e648d393ec62329a85f1b8c415 Signed-off-by: Paul Belanger <pabelanger@redhat.com>changes/58/560958/1
parent
f1580877cc
commit
fc120688f8
|
@ -1,16 +0,0 @@
|
|||
# vim: ft=yaml
|
||||
#
|
||||
# Example clone map for Zuul cloner
|
||||
#
|
||||
# By default it would clone projects under the directory specified by its
|
||||
# option --basepath, but you can override this behavior by definining per
|
||||
# project destinations.
|
||||
clonemap:
|
||||
|
||||
# Clone project 'mediawiki/core' directly in {basepath}
|
||||
- name: 'mediawiki/core'
|
||||
dest: '.'
|
||||
|
||||
# Clone projects below mediawiki/extensions to {basepath}/extensions/
|
||||
- name: 'mediawiki/extensions/(.*)'
|
||||
dest: 'extensions/\1'
|
|
@ -27,7 +27,6 @@ console_scripts =
|
|||
zuul-scheduler = zuul.cmd.scheduler:main
|
||||
zuul-merger = zuul.cmd.merger:main
|
||||
zuul = zuul.cmd.client:main
|
||||
zuul-cloner = zuul.cmd.cloner:main
|
||||
zuul-executor = zuul.cmd.executor:main
|
||||
zuul-bwrap = zuul.driver.bubblewrap:main
|
||||
zuul-web = zuul.cmd.web:main
|
||||
|
|
|
@ -1,79 +0,0 @@
|
|||
# Copyright 2014 Antoine "hashar" Musso
|
||||
# Copyright 2014 Wikimedia Foundation Inc.
|
||||
#
|
||||
# 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
|
||||
# under the License.
|
||||
|
||||
import testtools
|
||||
from zuul.lib.clonemapper import CloneMapper
|
||||
|
||||
|
||||
class TestCloneMapper(testtools.TestCase):
|
||||
|
||||
def test_empty_mapper(self):
|
||||
"""Given an empty map, the slashes in project names are directory
|
||||
separators"""
|
||||
cmap = CloneMapper(
|
||||
{},
|
||||
[
|
||||
'project1',
|
||||
'plugins/plugin1'
|
||||
])
|
||||
|
||||
self.assertEqual(
|
||||
{'project1': '/basepath/project1',
|
||||
'plugins/plugin1': '/basepath/plugins/plugin1'},
|
||||
cmap.expand('/basepath')
|
||||
)
|
||||
|
||||
def test_map_to_a_dot_dir(self):
|
||||
"""Verify we normalize path, hence '.' refers to the basepath"""
|
||||
cmap = CloneMapper(
|
||||
[{'name': 'mediawiki/core', 'dest': '.'}],
|
||||
['mediawiki/core'])
|
||||
self.assertEqual(
|
||||
{'mediawiki/core': '/basepath'},
|
||||
cmap.expand('/basepath'))
|
||||
|
||||
def test_map_using_regex(self):
|
||||
"""One can use regex in maps and use \\1 to forge the directory"""
|
||||
cmap = CloneMapper(
|
||||
[{'name': 'plugins/(.*)', 'dest': 'project/plugins/\\1'}],
|
||||
['plugins/PluginFirst'])
|
||||
self.assertEqual(
|
||||
{'plugins/PluginFirst': '/basepath/project/plugins/PluginFirst'},
|
||||
cmap.expand('/basepath'))
|
||||
|
||||
def test_map_discarding_regex_group(self):
|
||||
cmap = CloneMapper(
|
||||
[{'name': 'plugins/(.*)', 'dest': 'project/'}],
|
||||
['plugins/Plugin_1'])
|
||||
self.assertEqual(
|
||||
{'plugins/Plugin_1': '/basepath/project'},
|
||||
cmap.expand('/basepath'))
|
||||
|
||||
def test_cant_dupe_destinations(self):
|
||||
"""We cant clone multiple projects in the same directory"""
|
||||
cmap = CloneMapper(
|
||||
[{'name': 'plugins/(.*)', 'dest': 'catchall/'}],
|
||||
['plugins/plugin1', 'plugins/plugin2']
|
||||
)
|
||||
self.assertRaises(Exception, cmap.expand, '/basepath')
|
||||
|
||||
def test_map_with_dot_and_regex(self):
|
||||
"""Combining relative path and regex"""
|
||||
cmap = CloneMapper(
|
||||
[{'name': 'plugins/(.*)', 'dest': './\\1'}],
|
||||
['plugins/PluginInBasePath'])
|
||||
self.assertEqual(
|
||||
{'plugins/PluginInBasePath': '/basepath/PluginInBasePath'},
|
||||
cmap.expand('/basepath'))
|
|
@ -1,51 +0,0 @@
|
|||
#!/usr/bin/env python
|
||||
|
||||
# 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
|
||||
# under the License.
|
||||
|
||||
import os
|
||||
|
||||
import testtools
|
||||
import zuul.cmd.cloner
|
||||
|
||||
|
||||
class TestClonerCmdArguments(testtools.TestCase):
|
||||
|
||||
def setUp(self):
|
||||
super(TestClonerCmdArguments, self).setUp()
|
||||
self.app = zuul.cmd.cloner.Cloner()
|
||||
|
||||
def test_default_cache_dir_empty(self):
|
||||
self.app.parse_arguments(['base', 'repo'])
|
||||
self.assertIsNone(self.app.args.cache_dir)
|
||||
|
||||
def test_default_cache_dir_environ(self):
|
||||
try:
|
||||
os.environ['ZUUL_CACHE_DIR'] = 'fromenviron'
|
||||
self.app.parse_arguments(['base', 'repo'])
|
||||
self.assertEqual('fromenviron', self.app.args.cache_dir)
|
||||
finally:
|
||||
del os.environ['ZUUL_CACHE_DIR']
|
||||
|
||||
def test_default_cache_dir_override_environ(self):
|
||||
try:
|
||||
os.environ['ZUUL_CACHE_DIR'] = 'fromenviron'
|
||||
self.app.parse_arguments(['--cache-dir', 'argument',
|
||||
'base', 'repo'])
|
||||
self.assertEqual('argument', self.app.args.cache_dir)
|
||||
finally:
|
||||
del os.environ['ZUUL_CACHE_DIR']
|
||||
|
||||
def test_default_cache_dir_argument(self):
|
||||
self.app.parse_arguments(['--cache-dir', 'argument',
|
||||
'base', 'repo'])
|
||||
self.assertEqual('argument', self.app.args.cache_dir)
|
|
@ -1,167 +0,0 @@
|
|||
#!/usr/bin/env python
|
||||
#
|
||||
# Copyright 2014 Antoine "hashar" Musso
|
||||
# Copyright 2014 Wikimedia Foundation Inc.
|
||||
#
|
||||
# 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
|
||||
# under the License.
|
||||
|
||||
import argparse
|
||||
import logging
|
||||
import os
|
||||
import sys
|
||||
|
||||
import zuul.cmd
|
||||
import zuul.lib.cloner
|
||||
|
||||
ZUUL_ENV_SUFFIXES = (
|
||||
'branch',
|
||||
'ref',
|
||||
'url',
|
||||
'project',
|
||||
'newrev',
|
||||
)
|
||||
|
||||
|
||||
class Cloner(zuul.cmd.ZuulApp):
|
||||
log = logging.getLogger("zuul.Cloner")
|
||||
|
||||
def parse_arguments(self, args=sys.argv[1:]):
|
||||
"""Parse command line arguments and returns argparse structure"""
|
||||
parser = argparse.ArgumentParser(
|
||||
description='Zuul Project Gating System Cloner.')
|
||||
parser.add_argument('-m', '--map', dest='clone_map_file',
|
||||
help='specifiy clone map file')
|
||||
parser.add_argument('--workspace', dest='workspace',
|
||||
default=os.getcwd(),
|
||||
help='where to clone repositories too')
|
||||
parser.add_argument('-v', '--verbose', dest='verbose',
|
||||
action='store_true',
|
||||
help='verbose output')
|
||||
parser.add_argument('--color', dest='color', action='store_true',
|
||||
help='use color output')
|
||||
parser.add_argument('--version', dest='version', action='version',
|
||||
version=self._get_version(),
|
||||
help='show zuul version')
|
||||
parser.add_argument('--cache-dir', dest='cache_dir',
|
||||
default=os.environ.get('ZUUL_CACHE_DIR'),
|
||||
help=('a directory that holds cached copies of '
|
||||
'repos from which to make an initial clone. '
|
||||
'Can also be set via ZUUL_CACHE_DIR '
|
||||
'environment variable.'
|
||||
))
|
||||
parser.add_argument('git_base_url',
|
||||
help='reference repo to clone from')
|
||||
parser.add_argument('projects', nargs='+',
|
||||
help='list of Gerrit projects to clone')
|
||||
|
||||
project_env = parser.add_argument_group(
|
||||
'project tuning'
|
||||
)
|
||||
project_env.add_argument(
|
||||
'--branch',
|
||||
help=('branch to checkout instead of Zuul selected branch, '
|
||||
'for example to specify an alternate branch to test '
|
||||
'client library compatibility.')
|
||||
)
|
||||
project_env.add_argument(
|
||||
'--project-branch', nargs=1, action='append',
|
||||
metavar='PROJECT=BRANCH',
|
||||
help=('project-specific branch to checkout which takes precedence '
|
||||
'over --branch if it is provided; may be specified multiple '
|
||||
'times.')
|
||||
)
|
||||
|
||||
zuul_env = parser.add_argument_group(
|
||||
'zuul environment',
|
||||
'Let you override $ZUUL_* environment variables.'
|
||||
)
|
||||
for zuul_suffix in ZUUL_ENV_SUFFIXES:
|
||||
env_name = 'ZUUL_%s' % zuul_suffix.upper()
|
||||
zuul_env.add_argument(
|
||||
'--zuul-%s' % zuul_suffix, metavar='$' + env_name,
|
||||
default=os.environ.get(env_name)
|
||||
)
|
||||
|
||||
args = parser.parse_args(args)
|
||||
# Validate ZUUL_* arguments. If ref is provided then URL is required.
|
||||
zuul_args = [zuul_opt for zuul_opt, val in vars(args).items()
|
||||
if zuul_opt.startswith('zuul') and val is not None]
|
||||
if 'zuul_ref' in zuul_args and 'zuul_url' not in zuul_args:
|
||||
parser.error("Specifying a Zuul ref requires a Zuul url. "
|
||||
"Define Zuul arguments either via environment "
|
||||
"variables or using options above.")
|
||||
if 'zuul_newrev' in zuul_args and 'zuul_project' not in zuul_args:
|
||||
parser.error("ZUUL_NEWREV has been specified without "
|
||||
"ZUUL_PROJECT. Please define a ZUUL_PROJECT or do "
|
||||
"not set ZUUL_NEWREV.")
|
||||
|
||||
self.args = args
|
||||
|
||||
def setup_logging(self, color=False, verbose=False):
|
||||
"""Cloner logging does not rely on conf file"""
|
||||
if verbose:
|
||||
logging.basicConfig(level=logging.DEBUG)
|
||||
else:
|
||||
logging.basicConfig(level=logging.INFO)
|
||||
|
||||
if color:
|
||||
# Color codes http://www.tldp.org/HOWTO/Bash-Prompt-HOWTO/x329.html
|
||||
logging.addLevelName( # cyan
|
||||
logging.DEBUG, "\033[36m%s\033[0m" %
|
||||
logging.getLevelName(logging.DEBUG))
|
||||
logging.addLevelName( # green
|
||||
logging.INFO, "\033[32m%s\033[0m" %
|
||||
logging.getLevelName(logging.INFO))
|
||||
logging.addLevelName( # yellow
|
||||
logging.WARNING, "\033[33m%s\033[0m" %
|
||||
logging.getLevelName(logging.WARNING))
|
||||
logging.addLevelName( # red
|
||||
logging.ERROR, "\033[31m%s\033[0m" %
|
||||
logging.getLevelName(logging.ERROR))
|
||||
logging.addLevelName( # red background
|
||||
logging.CRITICAL, "\033[41m%s\033[0m" %
|
||||
logging.getLevelName(logging.CRITICAL))
|
||||
|
||||
def main(self):
|
||||
self.parse_arguments()
|
||||
self.setup_logging(color=self.args.color, verbose=self.args.verbose)
|
||||
project_branches = {}
|
||||
if self.args.project_branch:
|
||||
for x in self.args.project_branch:
|
||||
project, branch = x[0].split('=')
|
||||
project_branches[project] = branch
|
||||
cloner = zuul.lib.cloner.Cloner(
|
||||
git_base_url=self.args.git_base_url,
|
||||
projects=self.args.projects,
|
||||
workspace=self.args.workspace,
|
||||
zuul_branch=self.args.zuul_branch,
|
||||
zuul_ref=self.args.zuul_ref,
|
||||
zuul_url=self.args.zuul_url,
|
||||
branch=self.args.branch,
|
||||
clone_map_file=self.args.clone_map_file,
|
||||
project_branches=project_branches,
|
||||
cache_dir=self.args.cache_dir,
|
||||
zuul_newrev=self.args.zuul_newrev,
|
||||
zuul_project=self.args.zuul_project,
|
||||
)
|
||||
cloner.execute()
|
||||
|
||||
|
||||
def main():
|
||||
cloner = Cloner()
|
||||
cloner.main()
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
sys.path.insert(0, '.')
|
||||
main()
|
|
@ -1,75 +0,0 @@
|
|||
# Copyright 2014 Antoine "hashar" Musso
|
||||
# Copyright 2014 Wikimedia Foundation Inc.
|
||||
#
|
||||
# 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
|
||||
# under the License.
|
||||
|
||||
from collections import defaultdict
|
||||
from collections import OrderedDict
|
||||
import logging
|
||||
import os
|
||||
import re
|
||||
|
||||
|
||||
class CloneMapper(object):
|
||||
log = logging.getLogger("zuul.CloneMapper")
|
||||
|
||||
def __init__(self, clonemap, projects):
|
||||
self.clonemap = clonemap
|
||||
self.projects = projects
|
||||
|
||||
def expand(self, workspace):
|
||||
self.log.info("Workspace path set to: %s", workspace)
|
||||
|
||||
is_valid = True
|
||||
ret = OrderedDict()
|
||||
for project in self.projects:
|
||||
dests = []
|
||||
for mapping in self.clonemap:
|
||||
if re.match(r'^%s$' % mapping['name'],
|
||||
project):
|
||||
# Might be matched more than one time
|
||||
dests.append(
|
||||
re.sub(mapping['name'], mapping['dest'], project))
|
||||
|
||||
if len(dests) > 1:
|
||||
self.log.error("Duplicate destinations for %s: %s.",
|
||||
project, dests)
|
||||
is_valid = False
|
||||
elif len(dests) == 0:
|
||||
self.log.debug("Using %s as destination (unmatched)",
|
||||
project)
|
||||
ret[project] = [project]
|
||||
else:
|
||||
ret[project] = dests
|
||||
|
||||
if not is_valid:
|
||||
raise Exception("Expansion error. Check error messages above")
|
||||
|
||||
self.log.info("Mapping projects to workspace...")
|
||||
for project, dest in ret.items():
|
||||
dest = os.path.normpath(os.path.join(workspace, dest[0]))
|
||||
ret[project] = dest
|
||||
self.log.info(" %s -> %s", project, dest)
|
||||
|
||||
self.log.debug("Checking overlap in destination directories...")
|
||||
check = defaultdict(list)
|
||||
for project, dest in ret.items():
|
||||
check[dest].append(project)
|
||||
|
||||
dupes = dict((d, p) for (d, p) in check.items() if len(p) > 1)
|
||||
if dupes:
|
||||
raise Exception("Some projects share the same destination: %s",
|
||||
dupes)
|
||||
|
||||
self.log.info("Expansion completed.")
|
||||
return ret
|
|
@ -1,247 +0,0 @@
|
|||
# Copyright 2014 Antoine "hashar" Musso
|
||||
# Copyright 2014 Wikimedia Foundation Inc.
|
||||
#
|
||||
# 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
|
||||
# under the License.
|
||||
|
||||
import git
|
||||
import logging
|
||||
import os
|
||||
import re
|
||||
|
||||
from git import GitCommandError
|
||||
from zuul import exceptions
|
||||
from zuul.lib.clonemapper import CloneMapper
|
||||
from zuul.lib import yamlutil as yaml
|
||||
from zuul.merger.merger import Repo
|
||||
|
||||
|
||||
class Cloner(object):
|
||||
log = logging.getLogger("zuul.Cloner")
|
||||
|
||||
def __init__(self, git_base_url, projects, workspace, zuul_branch,
|
||||
zuul_ref, zuul_url, branch=None, clone_map_file=None,
|
||||
project_branches=None, cache_dir=None, zuul_newrev=None,
|
||||
zuul_project=None):
|
||||
|
||||
self.clone_map = []
|
||||
self.dests = None
|
||||
|
||||
self.branch = branch
|
||||
self.git_url = git_base_url
|
||||
self.cache_dir = cache_dir
|
||||
self.projects = projects
|
||||
self.workspace = workspace
|
||||
self.zuul_branch = zuul_branch or ''
|
||||
self.zuul_ref = zuul_ref or ''
|
||||
self.zuul_url = zuul_url
|
||||
self.zuul_project = zuul_project
|
||||
|
||||
self.project_branches = project_branches or {}
|
||||
self.project_revisions = {}
|
||||
|
||||
if zuul_newrev and zuul_project:
|
||||
self.project_revisions[zuul_project] = zuul_newrev
|
||||
|
||||
if clone_map_file:
|
||||
self.readCloneMap(clone_map_file)
|
||||
|
||||
def readCloneMap(self, clone_map_file):
|
||||
clone_map_file = os.path.expanduser(clone_map_file)
|
||||
if not os.path.exists(clone_map_file):
|
||||
raise Exception("Unable to read clone map file at %s." %
|
||||
clone_map_file)
|
||||
clone_map_file = open(clone_map_file)
|
||||
self.clone_map = yaml.safe_load(clone_map_file).get('clonemap')
|
||||
self.log.info("Loaded map containing %s rules", len(self.clone_map))
|
||||
return self.clone_map
|
||||
|
||||
def execute(self):
|
||||
mapper = CloneMapper(self.clone_map, self.projects)
|
||||
dests = mapper.expand(workspace=self.workspace)
|
||||
|
||||
self.log.info("Preparing %s repositories", len(dests))
|
||||
for project, dest in dests.items():
|
||||
self.prepareRepo(project, dest)
|
||||
self.log.info("Prepared all repositories")
|
||||
|
||||
def cloneUpstream(self, project, dest):
|
||||
# Check for a cached git repo first
|
||||
git_cache = '%s/%s' % (self.cache_dir, project)
|
||||
|
||||
# Then, if we are cloning the repo for the zuul_project, then
|
||||
# set its origin to be the zuul merger, as it is guaranteed to
|
||||
# be correct and up to date even if mirrors haven't updated
|
||||
# yet. Otherwise, we can not be sure about the state of the
|
||||
# project, so our best chance to get the most current state is
|
||||
# by setting origin to the git_url.
|
||||
if (self.zuul_url and project == self.zuul_project):
|
||||
git_upstream = '%s/%s' % (self.zuul_url, project)
|
||||
else:
|
||||
git_upstream = '%s/%s' % (self.git_url, project)
|
||||
|
||||
repo_is_cloned = os.path.exists(os.path.join(dest, '.git'))
|
||||
if (self.cache_dir and
|
||||
os.path.exists(git_cache) and
|
||||
not repo_is_cloned):
|
||||
# file:// tells git not to hard-link across repos
|
||||
git_cache = 'file://%s' % git_cache
|
||||
self.log.info("Creating repo %s from cache %s",
|
||||
project, git_cache)
|
||||
new_repo = git.Repo.clone_from(git_cache, dest)
|
||||
self.log.info("Updating origin remote in repo %s to %s",
|
||||
project, git_upstream)
|
||||
new_repo.remotes.origin.config_writer.set('url',
|
||||
git_upstream).release()
|
||||
else:
|
||||
self.log.info("Creating repo %s from upstream %s",
|
||||
project, git_upstream)
|
||||
repo = Repo(
|
||||
remote=git_upstream,
|
||||
local=dest,
|
||||
email=None,
|
||||
username=None)
|
||||
|
||||
if not repo.isInitialized():
|
||||
raise Exception("Error cloning %s to %s" % (git_upstream, dest))
|
||||
|
||||
return repo
|
||||
|
||||
def fetchRef(self, repo, project, ref):
|
||||
# If we are fetching a zuul ref, the only place to get it is
|
||||
# from the zuul merger (and it is guaranteed to be correct).
|
||||
# Otherwise, the only way we can be certain that the ref
|
||||
# (which, since it is not a zuul ref, is a branch or tag) is
|
||||
# correct is in the case that it matches zuul_project. If
|
||||
# neither of those two conditions are met, we are most likely
|
||||
# to get the correct state from the git_url.
|
||||
if (ref.startswith('refs/zuul') or
|
||||
project == self.zuul_project):
|
||||
|
||||
remote = '%s/%s' % (self.zuul_url, project)
|
||||
else:
|
||||
remote = '%s/%s' % (self.git_url, project)
|
||||
|
||||
try:
|
||||
repo.fetchFrom(remote, ref)
|
||||
self.log.debug("Fetched ref %s from %s", ref, remote)
|
||||
return True
|
||||
except ValueError:
|
||||
self.log.debug("Repo %s does not have ref %s",
|
||||
remote, ref)
|
||||
return False
|
||||
except GitCommandError as error:
|
||||
# Bail out if fetch fails due to infrastructure reasons
|
||||
if error.stderr.startswith('fatal: unable to access'):
|
||||
raise
|
||||
self.log.debug("Repo %s does not have ref %s",
|
||||
remote, ref)
|
||||
return False
|
||||
|
||||
def prepareRepo(self, project, dest):
|
||||
"""Clone a repository for project at dest and apply a reference
|
||||
suitable for testing. The reference lookup is attempted in this order:
|
||||
|
||||
1) The indicated revision for specific project
|
||||
2) Zuul reference for the indicated branch
|
||||
3) Zuul reference for the master branch
|
||||
4) The tip of the indicated branch
|
||||
5) The tip of the master branch
|
||||
|
||||
If an "indicated revision" is specified for this project, and we are
|
||||
unable to meet this requirement, we stop attempting to check this
|
||||
repo out and raise a zuul.exceptions.RevNotFound exception.
|
||||
|
||||
The "indicated branch" is one of the following:
|
||||
|
||||
A) The project-specific override branch (from project_branches arg)
|
||||
B) The user specified branch (from the branch arg)
|
||||
C) ZUUL_BRANCH (from the zuul_branch arg)
|
||||
"""
|
||||
|
||||
repo = self.cloneUpstream(project, dest)
|
||||
|
||||
# Ensure that we don't have stale remotes around
|
||||
repo.prune()
|
||||
# We must reset after pruning because reseting sets HEAD to point
|
||||
# at refs/remotes/origin/master, but `git branch` which prune runs
|
||||
# explodes if HEAD does not point at something in refs/heads.
|
||||
# Later with repo.checkout() we set HEAD to something that
|
||||
# `git branch` is happy with.
|
||||
repo.reset()
|
||||
|
||||
indicated_revision = None
|
||||
if project in self.project_revisions:
|
||||
indicated_revision = self.project_revisions[project]
|
||||
|
||||
indicated_branch = self.branch or self.zuul_branch
|
||||
if project in self.project_branches:
|
||||
indicated_branch = self.project_branches[project]
|
||||
|
||||
if indicated_branch:
|
||||
override_zuul_ref = re.sub(self.zuul_branch, indicated_branch,
|
||||
self.zuul_ref)
|
||||
else:
|
||||
override_zuul_ref = None
|
||||
|
||||
if indicated_branch and repo.hasBranch(indicated_branch):
|
||||
self.log.info("upstream repo has branch %s", indicated_branch)
|
||||
fallback_branch = indicated_branch
|
||||
else:
|
||||
if indicated_branch:
|
||||
self.log.info("upstream repo is missing branch %s",
|
||||
indicated_branch)
|
||||
# FIXME should be origin HEAD branch which might not be 'master'
|
||||
fallback_branch = 'master'
|
||||
|
||||
if self.zuul_branch:
|
||||
fallback_zuul_ref = re.sub(self.zuul_branch, fallback_branch,
|
||||
self.zuul_ref)
|
||||
else:
|
||||
fallback_zuul_ref = None
|
||||
|
||||
# If the user has requested an explicit revision to be checked out,
|
||||
# we use it above all else, and if we cannot satisfy this requirement
|
||||
# we raise an error and do not attempt to continue.
|
||||
if indicated_revision:
|
||||
self.log.info("Attempting to check out revision %s for "
|
||||
"project %s", indicated_revision, project)
|
||||
try:
|
||||
self.fetchRef(repo, project, self.zuul_ref)
|
||||
commit = repo.checkout(indicated_revision)
|
||||
except (ValueError, GitCommandError):
|
||||
raise exceptions.RevNotFound(project, indicated_revision)
|
||||
self.log.info("Prepared '%s' repo at revision '%s'", project,
|
||||
indicated_revision)
|
||||
# If we have a non empty zuul_ref to use, use it. Otherwise we fall
|
||||
# back to checking out the branch.
|
||||
elif ((override_zuul_ref and
|
||||
self.fetchRef(repo, project, override_zuul_ref)) or
|
||||
(fallback_zuul_ref and
|
||||
fallback_zuul_ref != override_zuul_ref and
|
||||
self.fetchRef(repo, project, fallback_zuul_ref))):
|
||||
# Work around a bug in GitPython which can not parse FETCH_HEAD
|
||||
gitcmd = git.Git(dest)
|
||||
fetch_head = gitcmd.rev_parse('FETCH_HEAD')
|
||||
repo.checkout(fetch_head)
|
||||
self.log.info("Prepared %s repo with commit %s",
|
||||
project, fetch_head)
|
||||
else:
|
||||
# Checkout branch
|
||||
self.log.info("Falling back to branch %s", fallback_branch)
|
||||
try:
|
||||
commit = repo.checkout('remotes/origin/%s' % fallback_branch)
|
||||
except (ValueError, GitCommandError):
|
||||
self.log.exception("Fallback branch not found: %s",
|
||||
fallback_branch)
|
||||
self.log.info("Prepared %s repo with branch %s at commit %s",
|
||||
project, fallback_branch, commit)
|
Loading…
Reference in New Issue