blob: 480f639a0389c7eef6df2496835e5f8ea13e84d2 [file] [log] [blame]
#!/usr/bin/python
# Copyright (c) 2012 The Chromium OS Authors. All rights reserved.
# Use of this source code is governed by a BSD-style license that can be
# found in the LICENSE file.
import optparse
import os
import sys
from chromite.buildbot import constants
from chromite.lib import cros_build_lib
# A set of the command-line options that should not be passed along to the
# gerrit create-project command. See the GetParser docstring for more details.
LOCAL_OPTIONS = set(['dry_run'])
def GetParser():
"""Create a parser for parsing options from the command line.
This parser is a little fancier than most because it is scraped dynamically
by the main function to generate the gerrit invocation. The 'dest' option
specifies the name of the command-line option to Gerrit. If the value passed
to the 'dest' option is a string, it will be used as the value of the
command-line option.
"""
parser = optparse.OptionParser(usage='%prog project-name [options]')
parser.add_option(
'--dry-run', default=False, action='store_true', dest='dry_run',
help='Don\'t actually create the project. Just output what the '
'gerrit invocation would\'ve been.')
parser.add_option(
'--branch', default='master', action='store',
help='Set the branch that symbolic HEAD will point at; this is typically '
'master. Note the branch doesn\'t have to exist, and short form '
'needs to be used (no refs/heads).')
parser.add_option(
'--description', default=None, action='store',
help='Description of the project being created.')
parser.add_option(
'--empty-commit', default=False, action='store_true',
help='Inject an empty commit into the newly created project\'s '
'branch; this is usually not desired for third party projects.')
parser.add_option(
'--owner', default=None, action='store',
help='List of groups to grant owner/admin rights to for this project; '
'see http://goo.gl/DmVzs for docs.')
parser.add_option(
'--parent', default=None, action='store',
help='Set the project\'s parent. If none is specified, the nearest '
'permission project in the hierarchy will be used. If specified, '
'the project must exist. The parent doesn\'t have to be a '
'permission only project. This can be changed later using '
'gerrit set-project-parent.')
parser.add_option(
'--permissions-only', default=False, action='store_true',
help='If specified, this project is being created purely to attach ACLs '
'to, and have other projects inherit from.')
parser.add_option(
'--require-change-id', default=False, action='store_true',
help='If specified, a CL cannot be uploaded without a proper Change-Id: '
'git footer.')
parser.add_option(
'--submit-type', default='cherry-pick',
choices=('cherry-pick', 'merge-if-necessary', 'merge-always',
'fast-forward-only'),
help='The method used by Gerrit to merge submitted changes. Chrome '
'OS and the Chrome OS commit queue only support the cherry-pick '
'method. For docs, see http://goo.gl/Z1bXm')
parser.add_option(
'--disable-content-merge', default=True, action='store_false',
dest='use_content_merge',
help='Content merging is best known as "automatically resolve file level '
'conflicts". If disabled, then Gerrit will always flag a conflict '
'whenever two unrelated changes affect the same file.')
parser.add_option(
'--use-contributor-agreements', default=False, action='store_true',
help='Require contributor agreements before commit/merge. This is not '
'currently used in Chrome OS and may not work as expected.')
parser.add_option(
'--use-signed-off-by', default=False, action='store_true',
help='If enabled, require Signed-off-by in git footers. See '
'http://goo.gl/A1n59 for details. Typically used in kernel '
'projects.')
return parser
def FindParent(host, port, name):
"""Find the parent of a given project."""
lines = cros_build_lib.RunCommandCaptureOutput(
['ssh', host, '-p', port, 'gerrit', 'ls-projects', '--type',
'PERMISSIONS'], shell=False).output
# Normalize the output to protect ourselves against any gerrit bugs.
projects = [os.path.normpath(x) for x in lines.splitlines() if x]
# Add a '/' to the project so that we consider matches at the directory
# level, rather than intersecting 'clank' against 'clank-bot'.
scored = sorted((x for x in projects if name.startswith(x + '/')),
key=len)
if scored:
# Use the project with the longest common prefix.
return scored[-1]
# Since we only handle chromeos and chromiumos projects, we should always
# find a valid parent project. Raise an error if this isn't the case.
raise AssertionError('Missing parent project for %s' % name)
def main(argv):
"""The main function."""
parser = GetParser()
options, args = parser.parse_args(argv)
# Verify the project is there.
if len(args) < 1:
parser.error('Please specify a project')
if len(args) > 1:
parser.error('Too many arguments')
# Verify the project and calculate the hostname.
name, = args
if name.startswith('chromeos/'):
host, port = constants.GERRIT_INT_HOST, constants.GERRIT_INT_PORT
elif name.startswith('chromiumos/'):
host, port = constants.GERRIT_HOST, constants.GERRIT_PORT
else:
parser.error('Project must be a chromeos/ or chromiumos/ project.')
# If no parent was specified, search for a parent.
if options.parent is None:
options.parent = FindParent(host, port, name)
cmd = ['ssh', host, '-p', port, 'gerrit', 'create-project', '--name', name]
# --submit-type has to be in caps for gerrit to be happy; we do the
# conversion on the user's behalf; same for converting '-' to '_'.
options.submit_type = options.submit_type.upper().replace('-', '_')
for option in parser.option_list:
if option.dest and option.dest not in LOCAL_OPTIONS:
val = getattr(options, option.dest)
if val:
cmd.append('--%s' % option.dest.replace('_', '-'))
if isinstance(val, basestring):
cmd.append(val)
print 'Command is:\n %s' % ' '.join(map(repr, cmd))
if not options.dry_run:
cros_build_lib.RunCommand(cmd, shell=False)
print 'Created %s' % (name,)
return 0
if __name__ == '__main__':
sys.exit(main(sys.argv[1:]))