help was updated, git hash resolving via api

This commit is contained in:
Anton Kirilenko 2012-10-12 15:03:56 +04:00
parent 7223405c25
commit ea917ec1b0
4 changed files with 98 additions and 71 deletions

22
README Normal file
View file

@ -0,0 +1,22 @@
INTRO
Console client lets you work with ABF without web-interface. It can manage git repositories, check build-task status, create new build-task and so on.
BUILD
--arch (-a), can be set more than once.
Architecture to build with. If not set,
--save-to-repository (-s)
Repository to save built packages to. Packages will be published here.
--commit (-c), --branch (-b) and --tag (-t)
API takes git commit hash to build. So client have to resolve it.
1) If you've specified hash - it will be used "as is".
2) If you've specified branch or tag name - it will be resolved automatically using ABF API. (the hash of top commit will be used for branch)
3) If you've specified no git commit related options and you've specified a project name - this project's default branch will be used.
4) If you've specified no git commit related options and you've not specified a project name (you have to be in a git repository) - the top remote commit of your current branch will be used.

View file

@ -1,6 +1,6 @@
Name: abf-console-client Name: abf-console-client
Version: 1.1 Version: 1.1
Release: 1 Release: 2
Summary: Python API to ABF (https://abf.rosalinux.ru) Summary: Python API to ABF (https://abf.rosalinux.ru)
Group: System/Configuration/Packaging Group: System/Configuration/Packaging
License: GPLv2 License: GPLv2
@ -8,7 +8,7 @@ URL: http://wiki.rosalab.ru/en/index.php/ABF_Console_Client
Source0: %{name}-%{version}.tar.gz Source0: %{name}-%{version}.tar.gz
BuildArch: noarch BuildArch: noarch
Requires: python-abf >= 1.1-1 Requires: python-abf >= 1.1-2
Requires: python-beaker Requires: python-beaker
Requires: python-rpm Requires: python-rpm
Requires: git Requires: git

141
abf.py
View file

@ -3,7 +3,7 @@
import sys import sys
import argparse import argparse
from argparse import RawTextHelpFormatter from argparse import RawDescriptionHelpFormatter
import os import os
import shutil import shutil
from datetime import datetime from datetime import datetime
@ -55,65 +55,75 @@ default_build_platform = cfg['user']['default_build_platform']
def parse_command_line(): def parse_command_line():
global command_line global command_line
parser = argparse.ArgumentParser(description='ABF Console Client') parser = argparse.ArgumentParser(description='ABF Console Client')
parser.add_argument('-v', '--verbose', action='store_true', help='be verbose') parser.add_argument('-v', '--verbose', action='store_true', help='be verbose, display even debug messages')
parser.add_argument('-c', '--clear-cache', action='store_true', help='clear cached information about repositories, platforms, projects, etc.') parser.add_argument('-c', '--clear-cache', action='store_true', help='clear cached information about repositories, platforms, projects, etc.')
parser.add_argument('-q', '--quiet', action='store_true', help='Do not display info messages') parser.add_argument('-q', '--quiet', action='store_true', help='Do not display info messages')
subparsers = parser.add_subparsers() subparsers = parser.add_subparsers()
# help # help
parser_get = subparsers.add_parser('help', help='show a help for command') parser_get = subparsers.add_parser('help', help='show a help for command')
parser_get.add_argument('command', action='store', nargs='?', help='command to show help for') parser_get.add_argument('command', action='store', nargs='?', help='a command to show help for')
parser_get.set_defaults(func=help) parser_get.set_defaults(func=help)
# get # get
parser_get = subparsers.add_parser('get', help='clone a project from ABF') parser_get = subparsers.add_parser('get', help='clone a project from ABF')
parser_get.add_argument('project', action='store', help='project name. (can be "group/project")') parser_get.add_argument('project', action='store', help='project name. ([group/]project). If no group specified, '
'it\'s assumed to be your default group.')
parser_get.add_argument('-b', '--branch', action='store', help='branch to checkout') parser_get.add_argument('-b', '--branch', action='store', help='branch to checkout')
parser_get.set_defaults(func=get) parser_get.set_defaults(func=get)
# put # put
parser_get = subparsers.add_parser('put', help='commit changes (with -am "message") and push') parser_get = subparsers.add_parser('put', help='run "git add --all", "git commit -m <your message>", "git push"')
parser_get.add_argument('message', action='store', help='a message to commit with') parser_get.add_argument('message', action='store', help='a message to commit with')
parser_get.set_defaults(func=put) parser_get.set_defaults(func=put)
# build # build
parser_build = subparsers.add_parser('build', help='Initiate a build task on ABF', formatter_class=RawTextHelpFormatter, parser_build = subparsers.add_parser('build', help='Initiate a build task on ABF.', formatter_class=RawDescriptionHelpFormatter,
epilog="NOTES:\n1) If '--project' option was not specified and you are in a git repository\n" epilog= 'NOTES:\n'
"ndirectory, the project owner and author will be resolved from it. \n" 'API takes git commit hash to build. So client have to resolve it.\n'
"2) You can specify only one of --commit, --tag or --branch options.\n" '1) If you\'ve specified commit hash - it will be used "as is".\n'
"If no one of these options specified, the default project branch will\n" '2) If you\'ve specified branch or tag name - it will be resolved automatically\n'
"be used for remote repositories and the current commit if you are using\n" 'using ABF API. (the hash of top commit will be used for branch)\n'
"the git repository directory.") '3) If you\'ve specified no git commit related options and you\'ve\n'
parser_build.add_argument('-p', '--project', action='store', help='project name (can be "group/project")') ' specified a project name - this project\'s default branch will be used.\n'
'4) If you\'ve specified no git commit related options and you\'ve\n'
'not specified a project name (you have to be in a git repository) -\n'
'the top remote commit of your current branch will be used.\n')
parser_build.add_argument('-p', '--project', action='store', help='project name ([group/]project). If no group '
'specified, it is assumed to be your default group. If the option is not specified and you are in a git '
'repository directory - resolve a project name from it.')
parser_build.add_argument('-b', '--branch', action='store', help='branch to build.') parser_build.add_argument('-b', '--branch', action='store', help='branch to build.')
parser_build.add_argument('-t', '--tag', action='store', help='tag to build') parser_build.add_argument('-t', '--tag', action='store', help='tag to build.')
parser_build.add_argument('-c', '--commit', action='store', help='Commit hash to build') parser_build.add_argument('-c', '--commit', action='store', help='commit sha hash to build.')
parser_build.add_argument('-s', '--save-to-repository', action='store', help='repository to save results to') parser_build.add_argument('-s', '--save-to-repository', action='store', help='repository to save results to '
parser_build.add_argument('-a', '--arches', action='append', help='architectures to build, ' '([platform/]repository). If no platform part specified, it is assumed to be "<default_group>_personal". '
'can be set more than once.\nIf not set - use all the available architectures') 'If this option is not specified at all, "<default_group>_personal/main" will be used.')
parser_build.add_argument('-r', '--repository', action='append', help='repositories to build with (platform/repository).\nThey should all have the same platform.') parser_build.add_argument('-a', '--arch', action='append', help='architectures to build, '
parser_build.add_argument('--auto-publish', action='store_true', help='Enable automatic publishing') 'can be set more than once. If not set - use all the available architectures.')
parser_build.add_argument('-r', '--repository', action='append', help='repositories to build with ([platform/]repository). '
'Can be set more than once. If no platform part specified, it is assumed to be your "<default_build platform>".'
' If no repositories were specified at all, use the "main" repository from save-to platform.')
parser_build.add_argument('--auto-publish', action='store_true', help='enable automatic publishing.')
upd_types = ['security', 'bugfix', 'enhancement', 'recommended', 'newpackage'] upd_types = ['security', 'bugfix', 'enhancement', 'recommended', 'newpackage']
parser_build.add_argument('--update-type', action='store', choices=upd_types, help='Update type. Default is "%s"' % parser_build.add_argument('--update-type', action='store', choices=upd_types, help='Update type. Default is "%s".' %
(BuildList.update_types[0]) ) (BuildList.update_types[0]) )
parser_build.set_defaults(func=build) parser_build.set_defaults(func=build)
# publish # publish
parser_build = subparsers.add_parser('publish', help='Publish the task already built.') parser_build = subparsers.add_parser('publish', help='Publish the task that have already been built.')
parser_build.add_argument('task_id', action='store', help='The ID of the task to publish') parser_build.add_argument('task_ids', action='store', nargs="+", help='The IDs of the tasks to publish.')
parser_build.set_defaults(func=publish) parser_build.set_defaults(func=publish)
# backport # backport
parser_build = subparsers.add_parser('backport', help='Copy all the files from SRC_BRANCH to DST_BRANCH, or to the current brunch if not specified.') parser_build = subparsers.add_parser('backport', help='Copy all the files from SRC_BRANCH to DST_BRANCH')
parser_build.add_argument('src_branch', action='store', help='source branch') parser_build.add_argument('src_branch', action='store', help='source branch')
parser_build.add_argument('dst_branch', action='store', nargs='?', help='destination branch') parser_build.add_argument('dst_branch', action='store', nargs='?', help='destination branch. If not specified, it\'s assumed to be the current branch')
parser_build.add_argument('-p', '--pack', action='store_true', help='Create a tar.gz from the src_branch and put this archive and spec file to dst_branch') parser_build.add_argument('-p', '--pack', action='store_true', help='Create a tar.gz from the src_branch and put this archive and spec file to dst_branch')
parser_build.set_defaults(func=backport) parser_build.set_defaults(func=backport)
# buildstatus # buildstatus
parser_build = subparsers.add_parser('buildstatus', help='get a building task status') parser_build = subparsers.add_parser('buildstatus', help='get a build-task status')
parser_build.add_argument('ID', action='store', nargs='?', help='build list ID') parser_build.add_argument('ID', action='store', nargs='?', help='build list ID')
parser_build.add_argument('-l', '--logs', action='store_true', help='also download logs (not implemented)')
parser_build.set_defaults(func=buildstatus) parser_build.set_defaults(func=buildstatus)
command_line = parser.parse_args(sys.argv[1:]) command_line = parser.parse_args(sys.argv[1:])
@ -139,12 +149,14 @@ def get():
cmd = ['git', 'clone', uri] cmd = ['git', 'clone', uri]
if command_line.branch: if command_line.branch:
cmd += ['-b', command_line.branch] cmd += ['-b', command_line.branch]
#log.debug('Executing command ' + str(cmd))
execute_command(cmd, log=log, print_to_stdout=True, exit_on_error=True) execute_command(cmd, log=log, print_to_stdout=True, exit_on_error=True)
def put(): def put():
log.debug('PUT started') log.debug('PUT started')
cmd = ['git', 'commit', '-a', '-m', command_line.message] cmd = ['git', 'add', '--all']
execute_command(cmd, log=log, print_to_stdout=True, exit_on_error=True)
cmd = ['git', 'commit', '-m', command_line.message]
execute_command(cmd, log=log, print_to_stdout=True, exit_on_error=True) execute_command(cmd, log=log, print_to_stdout=True, exit_on_error=True)
log.info('Commited.') log.info('Commited.')
@ -214,10 +226,7 @@ def build():
models = Models(domain, login, password) models = Models(domain, login, password)
if not command_line.save_to_repository:
log.error("You have to specify the repository to save to (-s option)")
exit(1)
# get project # get project
if command_line.project: if command_line.project:
tmp = command_line.project.split('/') tmp = command_line.project.split('/')
@ -254,8 +263,8 @@ def build():
# get architectures # get architectures
arches = [] arches = []
all_arches = models.get_arches() all_arches = models.get_arches()
if command_line.arches: if command_line.arch:
for arch in command_line.arches: for arch in command_line.arch:
a = models.arches.get_string_key(arch) a = models.arches.get_string_key(arch)
if not a: if not a:
log.error("Invalid architecture: %s" % arch) log.error("Invalid architecture: %s" % arch)
@ -277,48 +286,42 @@ def build():
commit_hash = None commit_hash = None
if tmp == 0: if tmp == 0:
if command_line.project: if command_line.project:
log.info('Resolving hash of remote branch "%s"' % proj.default_branch)
branch_def = True
tmp = 1
command_line.branch = proj.default_branch command_line.branch = proj.default_branch
else: # we are in a git repository and it the project we are building else: # we are in a git repository and it the project we are building
commit_hash = get_current_commit_hash() command_line.branch = get_branch_name()
log.info('The git branch is assumed to be "%s"' % command_line.branch)
branch_def = True
tmp = 1
if tmp == 1: if tmp == 1:
if commit_def: if commit_def:
commit_hash = command_line.commit commit_hash = command_line.commit
else: else:
if command_line.project:
#TODO remove workaround to_resolve = command_line.branch or command_line.tag
uri = "%s/%s.git" % (cfg['user']['git_uri'], proj.owner['url'][1:] + '/' + proj.name) ref_type = (branch_def and 'commit') or (tag_def and 'tag')
tmp_dir = clone_git_repo_tmp(uri, log=log) refs = proj.get_refs_list(models)
if branch_def: for ref in refs:
commit_hash = get_remote_branch_hash(command_line.branch, cwd=tmp_dir) if ref['ref'] == to_resolve and ref['object']['type'] == ref_type:
else: commit_hash = ref['object']['sha']
commit_hash = get_tag_hash(command_line.tag, cwd=tmp_dir) if commit_hash == None:
shutil.rmtree(tmp_dir) log.error("Could not resolve hash for %s '%s'" % (ref_type, to_resolve))
else: # we are in a correct git repository
if branch_def:
commit_hash = get_remote_branch_hash(command_line.branch)
else:
commit_hash = get_tag_hash(command_line.tag)
if not commit_hash:
log.error("Could not resolve git hash for " + (command_line.branch or command_line.tag))
exit(1) exit(1)
if tmp > 1: if tmp > 1:
log.error("You should specify ONLY ONE of the following options: branch, tag or commit.") log.error("You should specify ONLY ONE of the following options: branch, tag or commit.")
exit(1) exit(1)
log.debug('Git commit hash: %s' % commit_hash) log.debug('Git commit hash: %s' % commit_hash)
# get save-to repository # get save-to repository
save_to_repository = None save_to_repository = None
build_for_platform = None build_for_platform = None
available_repos = proj.repositories available_repos = proj.repositories
items = command_line.save_to_repository.split('/') if command_line.save_to_repository:
items = command_line.save_to_repository.split('/')
else:
items = []
if len(items) == 2: if len(items) == 2:
repo_name = items[1] repo_name = items[1]
pl_name = items[0] pl_name = items[0]
@ -413,16 +416,16 @@ def build():
def publish(): def publish():
log.debug('PUBLISH started') log.debug('PUBLISH started')
try: models = Models(domain, login, password)
models = Models(domain, login, password) for task_id in command_line.task_ids:
bl = models.buildlists[command_line.task_id] try:
if bl.status != 0: bl = models.buildlists[task_id]
log.error("The status of build task %s is \"%s\", can not publish it!" % (bl.id, bl.status_by_id[bl.status])) if bl.status != 0:
exit(1) log.error("The status of build task %s is \"%s\", can not publish it!" % (bl.id, bl.status_by_id[bl.status]))
res = bl.publish(models) continue
except AbfApiException, ex: res = bl.publish(models)
log.error('You are not authorized for this action.') except AbfApiException, ex:
exit(3) log.error('Could not publish task %s: %s' %(task_id, str(ex)))
def buildstatus(): def buildstatus():

View file

@ -7,7 +7,9 @@ class Log:
@staticmethod @staticmethod
def set_verbose(): def set_verbose():
logging.getLogger("abf").propagate = 1 logging.getLogger("abf").propagate = 1
logging.getLogger("abf").handlers[0].setLevel(logging.CRITICAL)
logging.getLogger("models").propagate = 1 logging.getLogger("models").propagate = 1
logging.getLogger("models").handlers[0].setLevel(logging.CRITICAL)
@staticmethod @staticmethod
def set_quiet(): def set_quiet():