new features: locate, abfcd

This commit is contained in:
Anton Kirilenko 2012-10-25 15:26:22 +04:00
parent ea917ec1b0
commit dd6727e016
8 changed files with 541 additions and 164 deletions

View file

@ -34,7 +34,9 @@ install:
mkdir -p $(DESTDIR)$(USRSHAREDIR)/bash-completion mkdir -p $(DESTDIR)$(USRSHAREDIR)/bash-completion
mkdir -p $(DESTDIR)$(ETCDIR)/bash_completion.d mkdir -p $(DESTDIR)$(ETCDIR)/bash_completion.d
mkdir -p $(DESTDIR)$(ETCDIR)/profile.d
cp "bash_autocomplete" $(DESTDIR)$(USRSHAREDIR)/bash-completion/abf cp "bash_autocomplete" $(DESTDIR)$(USRSHAREDIR)/bash-completion/abf
cp "abfcd.sh" $(DESTDIR)$(USRSHAREDIR)/profile.d/abfcd.sh

View file

@ -1,6 +1,6 @@
Name: abf-console-client Name: abf-console-client
Version: 1.1 Version: 1.3
Release: 2 Release: 0
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,10 +8,11 @@ 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-2 Requires: python-abf >= 1.2-0
Requires: python-beaker Requires: python-beaker
Requires: python-rpm Requires: python-rpm
Requires: git Requires: git
Requires: python-yaml
%description %description
Console client for ABF (https://abf.rosalinux.ru). Console client for ABF (https://abf.rosalinux.ru).
@ -35,3 +36,4 @@ ln -s %{_datadir}/bash-completion/abf %{buildroot}/%{_sysconfdir}/bash_completio
#bash_completion files #bash_completion files
%{_datadir}/bash-completion/abf %{_datadir}/bash-completion/abf
%{_sysconfdir}/bash_completion.d/abf %{_sysconfdir}/bash_completion.d/abf
%{_sysconfdir}/profile.d/abfcd.sh

304
abf.py
View file

@ -6,7 +6,6 @@ import argparse
from argparse import RawDescriptionHelpFormatter from argparse import RawDescriptionHelpFormatter
import os import os
import shutil import shutil
from datetime import datetime
import pdb import pdb
@ -14,6 +13,7 @@ import pdb
from abf.console.config import Config from abf.console.config import Config
from abf.console.log import Log from abf.console.log import Log
cfg = Config() cfg = Config()
projects_cfg = Config(conf_path='~/.abf_projects', main_conf=False)
log = Log('abf') log = Log('abf')
@ -30,11 +30,13 @@ default_build_platform = cfg['user']['default_build_platform']
#models = Models(domain, login, password) #models = Models(domain, login, password)
#r = Platform(models, init_data={'id':64, 'name': 'AAAA'})
#r = models.platforms[64] #r = models.platforms[64]
#r = models.repositories[1] #r = Platform(models, ID=64)
#r = models.projects[('abf_core', 'abf_core')] #r = Repository(models, ID=71)
#r = models.projects['akirilenko/libqb']
#r = models.projects['akirilenko/fba'] #r = Project(models, 'akirilenko/mock-urpm')
#r = BuildList(models, ID=750988)
#r = models.buildlists['715552'] #r = models.buildlists['715552']
#r = models.arches['1'] #r = models.arches['1']
@ -44,9 +46,22 @@ default_build_platform = cfg['user']['default_build_platform']
#r = models.get_arches() #r = models.get_arches()
#print r
#print r.name
#print r.owner
#print r.visibility
#print r.repositories #print r.repositories
#print r
#print r.platform.params_dict
#r = models.repositories[1]
#print 'WELL DONE'
#print r
#print r.owner
#print r.owner.email
#print r.repositories[0].platform.repositories[2].platform
#exit() #exit()
@ -61,9 +76,9 @@ def parse_command_line():
subparsers = parser.add_subparsers() subparsers = parser.add_subparsers()
# help # help
parser_get = subparsers.add_parser('help', help='show a help for command') parser_help = subparsers.add_parser('help', help='show a help for command')
parser_get.add_argument('command', action='store', nargs='?', help='a command to show help for') parser_help.add_argument('command', action='store', nargs='?', help='a command to show help for')
parser_get.set_defaults(func=help) parser_help.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')
@ -73,9 +88,27 @@ def parse_command_line():
parser_get.set_defaults(func=get) parser_get.set_defaults(func=get)
# put # put
parser_get = subparsers.add_parser('put', help='run "git add --all", "git commit -m <your message>", "git push"') parser_put = subparsers.add_parser('put', help='Execute "git add --all", "git commit -m <your message>", "git push"')
parser_get.add_argument('message', action='store', help='a message to commit with') parser_put.add_argument('message', action='store', help='a message to commit with')
parser_get.set_defaults(func=put) parser_put.set_defaults(func=put)
# show
parser_show = subparsers.add_parser('show', help='show some general information. Bash autocomplete uses it.')
show_choices = ['build-repos', 'build-platforms', 'save-to-repos', 'save-to-platforms']
parser_show.add_argument('type', action='store', choices=show_choices,help='The type of information to show')
parser_show.add_argument('-p', '--project', action='store', help='Project to show information for (if needed). Format: '
'"[group/]name". If no group specified, default group will be used.')
parser_show.set_defaults(func=show)
# locate
parser_locate = subparsers.add_parser('locate', help='tool can remember the project location and use it for some reasons.')
locate_choices = ['update', 'update-recursive']
parser_locate.add_argument('action', action='store', choices=locate_choices, nargs='?', help='The type of information to show')
parser_locate.add_argument('-p', '--project', action='store', help='Project to show information for (if needed). Format: '
'"[group/]name". If no group specified, default group will be used.')
parser_locate.add_argument('-d', '--directory', action='store', help='Directory to update locations for. It should be a '
'git repository for "update" and any directory for "update-recursive". If not specified - the current directory will be used')
parser_locate.set_defaults(func=locate)
# build # build
parser_build = subparsers.add_parser('build', help='Initiate a build task on ABF.', formatter_class=RawDescriptionHelpFormatter, parser_build = subparsers.add_parser('build', help='Initiate a build task on ABF.', formatter_class=RawDescriptionHelpFormatter,
@ -107,24 +140,31 @@ def parse_command_line():
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.add_argument('--skip-spec-check', action='store_true', help='Do not check spec file.' )
parser_build.set_defaults(func=build) parser_build.set_defaults(func=build)
# publish # publish
parser_build = subparsers.add_parser('publish', help='Publish the task that have already been built.') parser_publish = subparsers.add_parser('publish', help='Publish the task that have already been built.')
parser_build.add_argument('task_ids', action='store', nargs="+", help='The IDs of the tasks to publish.') parser_publish.add_argument('task_ids', action='store', nargs="+", help='The IDs of tasks to publish.')
parser_build.set_defaults(func=publish) parser_publish.set_defaults(func=publish)
# backport # backport
parser_build = subparsers.add_parser('backport', help='Copy all the files from SRC_BRANCH to DST_BRANCH') parser_backport = 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_backport.add_argument('src_branch', action='store', help='source 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_backport.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_backport.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_backport.set_defaults(func=backport)
# buildstatus # buildstatus
parser_build = subparsers.add_parser('buildstatus', help='get a build-task status') parser_clean = subparsers.add_parser('buildstatus', help='get a build-task status')
parser_build.add_argument('ID', action='store', nargs='?', help='build list ID') parser_clean.add_argument('ID', action='store', nargs='?', help='build list ID')
parser_build.set_defaults(func=buildstatus) parser_clean.set_defaults(func=buildstatus)
# clean
parser_clean = subparsers.add_parser('clean', help='Analyze spec file and show missing and unnecessary files from '
'the current git repository directory.')
parser_clean.add_argument('--auto-remove', action='store_true', help='automatically remove all the unnecessary files')
parser_clean.set_defaults(func=clean)
command_line = parser.parse_args(sys.argv[1:]) command_line = parser.parse_args(sys.argv[1:])
@ -135,6 +175,42 @@ def help():
sys.argv = [sys.argv[0], '-h'] sys.argv = [sys.argv[0], '-h']
parse_command_line() parse_command_line()
def get_project(models, must_exist=True, name=None):
if name:
tmp = name.split('/')
if len(tmp) > 2:
log.error('The project format is "[owner_name/]project_name"')
exit(1)
elif len(tmp) == 1:
project_name = tmp[0]
log.info("The project group is assumed to be " + default_group)
owner_name = default_group
else: # len == 2
owner_name = tmp[0]
project_name = tmp[1]
else:
owner_name, project_name = get_project_name()
if not project_name:
if must_exist:
log.error('You are not in a git repository directory. Specify the project name please!')
exit(1)
else:
return None
_update_location()
try:
proj = Project(models, '%s/%s' % (owner_name, project_name))
except PageNotFoundError:
log.error('The project %s/%s does not exist!' % (owner_name, project_name))
exit(1)
except ForbiddenError:
log.error('You do not have acces to the project %s/%s!' % (owner_name, project_name))
exit(1)
log.debug('Project: %s' % proj)
return proj
def get(): def get():
log.debug('GET started') log.debug('GET started')
proj = command_line.project proj = command_line.project
@ -142,26 +218,34 @@ def get():
if len(tmp) > 2: if len(tmp) > 2:
log.error('Specify a project name as "group_name/project_name" or just "project_name"') log.error('Specify a project name as "group_name/project_name" or just "project_name"')
exit(1) exit(1)
if len(tmp) == 1: elif len(tmp) == 1:
project_name = proj
proj = '%s/%s' % (cfg['user']['default_group'], proj) proj = '%s/%s' % (cfg['user']['default_group'], proj)
elif len(tmp) == 2:
project_name = tmp[1]
uri = "%s/%s.git" % (cfg['user']['git_uri'], proj) uri = "%s/%s.git" % (cfg['user']['git_uri'], proj)
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]
execute_command(cmd, log=log, print_to_stdout=True, exit_on_error=True) execute_command(cmd, print_to_stdout=True, exit_on_error=True)
projects_cfg[proj]['location'] = os.path.join(os.getcwd(), project_name)
def put(): def put():
log.debug('PUT started') log.debug('PUT started')
_update_location()
cmd = ['git', 'add', '--all'] cmd = ['git', 'add', '--all']
execute_command(cmd, log=log, print_to_stdout=True, exit_on_error=True) execute_command(cmd, print_to_stdout=True, exit_on_error=True)
cmd = ['git', 'commit', '-m', command_line.message] cmd = ['git', 'commit', '-m', command_line.message]
execute_command(cmd, log=log, print_to_stdout=True, exit_on_error=True) execute_command(cmd, print_to_stdout=True, exit_on_error=True)
log.info('Commited.') log.info('Commited.')
cmd = ['git', 'push'] cmd = ['git', 'push']
execute_command(cmd, log=log, print_to_stdout=True, exit_on_error=True) execute_command(cmd, print_to_stdout=True, exit_on_error=True)
log.info('Pushed') log.info('Pushed')
@ -185,22 +269,24 @@ def backport():
path = get_root_git_dir() path = get_root_git_dir()
log.debug("Repository root folder is " + path) log.debug("Repository root folder is " + path)
_update_location(path=path)
stage = 0 stage = 0
try: try:
if start_branch != dbrn: if start_branch != dbrn:
cmd = ['git', 'checkout', dbrn] cmd = ['git', 'checkout', dbrn]
execute_command(cmd, log, print_to_stdout=True, cwd=path) execute_command(cmd, print_to_stdout=True, cwd=path)
stage = 1 stage = 1
cmd = ['rm', '-rf', './*'] cmd = ['rm', '-rf', './*']
execute_command(cmd, log=log, print_to_stdout=True, cwd=path) execute_command(cmd, print_to_stdout=True, cwd=path)
stage = 2 stage = 2
cmd = ['git', 'checkout', sbrn, '*'] cmd = ['git', 'checkout', sbrn, '*']
execute_command(cmd, log=log, print_to_stdout=True, cwd=path) execute_command(cmd, print_to_stdout=True, cwd=path)
stage = 3 stage = 3
if command_line.pack: if command_line.pack:
pack_project(log, path) pack_project(path)
cmd = ['git', 'reset'] cmd = ['git', 'reset']
execute_command(cmd, log=log, print_to_stdout=True, cwd=path) execute_command(cmd, print_to_stdout=True, cwd=path)
except Exception, ex: except Exception, ex:
if type(ex) == ReturnCodeNotZero: if type(ex) == ReturnCodeNotZero:
log.error(str(ex)) log.error(str(ex))
@ -210,7 +296,7 @@ def backport():
if stage == 1 or stage == 2: if stage == 1 or stage == 2:
log.info("Checking out the initial branch (%s)" % start_branch) log.info("Checking out the initial branch (%s)" % start_branch)
cmd = ['git', 'reset', '--hard', start_branch] cmd = ['git', 'reset', '--hard', start_branch]
execute_command(cmd, log=log, print_to_stdout=True, cwd=path) execute_command(cmd, print_to_stdout=True, cwd=path)
log.info('Done') log.info('Done')
@ -228,33 +314,9 @@ def build():
# get project # get project
if command_line.project: proj = get_project(models, must_exist=True, name=command_line.project)
tmp = command_line.project.split('/') if not command_line.project and not command_line.skip_spec_check: # local git repository
if len(tmp) > 2: find_spec_problems()
log.error('The project format is "[owner_name/]project_name"')
exit(1)
elif len(tmp) == 1:
project_name = tmp[0]
log.info("The project group is assumed to be " + default_group)
owner_name = default_group
else: # len == 2
owner_name = tmp[0]
project_name = tmp[1]
else:
owner_name, project_name = get_project_name()
if not project_name:
log.error('You are not in git repository directory. Specify the project name please!')
exit(1)
try:
proj = models.projects['%s/%s' % (owner_name, project_name)]
except PageNotFoundError:
log.error('The project %s/%s does not exist!' % (owner_name, project_name))
exit(1)
except ForbiddenError:
log.error('You do not have acces to the project %s/%s!' % (owner_name, project_name))
exit(1)
log.debug('Project: %s' % proj)
if not proj.is_package: if not proj.is_package:
log.error('The project %s is not a package and can not be built.' % proj) log.error('The project %s is not a package and can not be built.' % proj)
exit(1) exit(1)
@ -262,7 +324,7 @@ def build():
# get architectures # get architectures
arches = [] arches = []
all_arches = models.get_arches() all_arches = Arch.get_arches(models)
if command_line.arch: if command_line.arch:
for arch in command_line.arch: for arch in command_line.arch:
a = models.arches.get_string_key(arch) a = models.arches.get_string_key(arch)
@ -301,6 +363,7 @@ def build():
ref_type = (branch_def and 'commit') or (tag_def and 'tag') ref_type = (branch_def and 'commit') or (tag_def and 'tag')
refs = proj.get_refs_list(models) refs = proj.get_refs_list(models)
for ref in refs: for ref in refs:
if ref['ref'] == to_resolve and ref['object']['type'] == ref_type: if ref['ref'] == to_resolve and ref['object']['type'] == ref_type:
commit_hash = ref['object']['sha'] commit_hash = ref['object']['sha']
if commit_hash == None: if commit_hash == None:
@ -359,7 +422,7 @@ def build():
# get the list of build repositories # get the list of build repositories
build_platforms = models.get_build_platforms() build_platforms = Platform.get_build_platforms(models)
build_platform_names = [x.name for x in build_platforms] build_platform_names = [x.name for x in build_platforms]
build_repositories = [] build_repositories = []
if command_line.repository: if command_line.repository:
@ -419,11 +482,11 @@ def publish():
models = Models(domain, login, password) models = Models(domain, login, password)
for task_id in command_line.task_ids: for task_id in command_line.task_ids:
try: try:
bl = models.buildlists[task_id] bl = BuildList(models, task_id)
if bl.status != 0: if bl.status != 0:
log.error("The status of build task %s is \"%s\", can not publish it!" % (bl.id, bl.status_by_id[bl.status])) log.error("The status of build task %s is \"%s\", can not publish it!" % (bl.id, bl.status_string))
continue continue
res = bl.publish(models) res = bl.publish()
except AbfApiException, ex: except AbfApiException, ex:
log.error('Could not publish task %s: %s' %(task_id, str(ex))) log.error('Could not publish task %s: %s' %(task_id, str(ex)))
@ -435,22 +498,121 @@ def buildstatus():
exit(1) exit(1)
try: try:
models = Models(domain, login, password) models = Models(domain, login, password)
bl = models.buildlists[command_line.ID] bl = BuildList(models, command_line.ID)
except AbfApiException, ex: except AbfApiException, ex:
log.error(str(ex)) log.error(str(ex))
exit(3) exit(3)
print '%-20s%s' %('Owner:', bl.owner['name']) print '%-20s%s' %('Owner:', bl.owner.name)
print '%-20s%s' %('Status:', BuildList.status_by_id[bl.status]) print '%-20s%s' %('Status:', bl.status_string)
print '%-20s%s' %('Build for platform:', bl.build_for_platform) print '%-20s%s' %('Build for platform:', bl.build_for_platform)
print '%-20s%s' %('Save to repository:', bl.save_to_repository) print '%-20s%s' %('Save to repository:', bl.save_to_repository)
print '%-20s%s' %('Build repositories:', bl.include_repos) print '%-20s%s' %('Build repositories:', bl.include_repos)
print '%-20s%s' %('Architecture:', bl.arch.name) print '%-20s%s' %('Architecture:', bl.arch.name)
print '%-20s%s' %('Created at:', datetime.fromtimestamp(float(bl.created_at))) print '%-20s%s' %('Created at:', bl.created_at)
print '%-20s%s' %('Updated at:', datetime.fromtimestamp(float(bl.updated_at))) print '%-20s%s' %('Updated at:', bl.updated_at)
def _update_location(path=None):
try:
if not path:
path = os.getcwd()
log.debug("Updating project location for %s" % path)
group, name = get_project_name(path)
if group:
proj = '%s/%s' % (group, name)
projects_cfg[proj]['location'] = path
log.info("Project %s has been located in %s" % (proj, path))
except:
pass
def _update_location_recursive(path):
items = os.listdir(path)
if '.git' in items: # it's a git directory!
_update_location(path)
return
for item in items:
item_path = os.path.join(path, item)
if not os.path.isdir(item_path) or os.path.islink(item_path):
continue
_update_location_recursive(item_path)
def locate():
log.debug('LOCATE started')
if not command_line.action: # show location
if not command_line.project:
print "To show a project location, you have to specify a project name ('-p' option)"
return
tmp = command_line.project.split('/')
if len(tmp) > 2:
log.error('error: the project format is "[owner_name/]project_name"')
exit(1)
elif len(tmp) == 1:
proj = '%s/%s' % (default_group, tmp[0])
else: # len == 2
proj = command_line.project
if proj not in projects_cfg or 'location' not in projects_cfg[proj] or not projects_cfg[proj]['location']:
print 'error: project %s can not be located' % proj
exit(1)
path = projects_cfg[proj]['location']
if not os.path.isdir(path):
print 'error: project is not located in "%s" anymore' % path
projects_cfg[proj]['location'] = ''
exit(1)
print path
return
else:
if command_line.action == 'update':
path = command_line.directory or os.getcwd()
_update_location(path)
elif command_line.action == 'update-recursive':
path = command_line.directory or os.getcwd()
_update_location_recursive(path)
def show():
log.debug('SHOW started')
Log.set_silent()
t = command_line.type
models = Models(domain, login, password)
if t in ['build-platforms', 'build-repos']:
build_platforms = Platform.get_build_platforms(models)
platform_names = []
repo_names = []
for plat in build_platforms:
if plat.repositories:
platform_names.append(plat.name)
for repo in plat.repositories:
repo_names.append(str(repo))
out = (t == 'build-platforms' and platform_names) or (t == 'build-repos' and repo_names)
if t in ['save-to-platforms', 'save-to-repos']:
proj = get_project(models, must_exist=True, name=command_line.project)
repos = proj.repositories
platform_names = []
repo_names = []
for repo in repos:
platform_names.append(repo.platform.name)
repo_names.append(str(repo))
platform_names = list(set(platform_names))
out = (t == 'save-to-platforms' and platform_names) or (t == 'save-to-repos' and repo_names)
print ' '.join(out)
def clean():
log.debug("CLEAN started")
_update_location()
find_spec_problems(auto_remove=command_line.auto_remove)
if __name__ == '__main__': if __name__ == '__main__':
parse_command_line() parse_command_line()
if command_line.verbose: if command_line.verbose:
Log.set_verbose() Log.set_verbose()
if command_line.quiet: if command_line.quiet:

View file

@ -1,6 +1,7 @@
import ConfigParser import ConfigParser
import os import os
from abf.console.misc import mkdirs, ask_user import sys
import getpass
##################################################### #####################################################
# USAGE: # USAGE:
@ -16,6 +17,36 @@ from abf.console.misc import mkdirs, ask_user
VERSION = 1 VERSION = 1
def mkdirs(path):
''' the equivalent of mkdir -p path'''
if os.path.exists(path):
return
path = os.path.normpath(path)
items = path.split('/')
p = ''
for item in items:
p += '/' + item
if not os.path.isdir(p):
os.mkdir(p)
def ask_user(prompt, can_be_empty=False, variants=None):
while True:
sys.stdout.write(prompt)
sys.stdout.flush()
res = sys.stdin.readline()
res = res.strip()
if not can_be_empty and not res:
continue
if variants:
if res in variants:
break
else:
continue
break
return res
class Section(dict): class Section(dict):
def __init__(self, config, conf_path, section): def __init__(self, config, conf_path, section):
self.section = section self.section = section
@ -55,8 +86,9 @@ class Section(dict):
class Config(dict): class Config(dict):
default_url = 'https://abf.rosalinux.ru' default_url = 'https://abf.rosalinux.ru'
default_log_path = '/var/log/abf.log' default_log_path = '/var/log/abf.log'
def __init__(self, conf_path='~/.abfcfg'): def __init__(self, conf_path='~/.abfcfg', main_conf=True):
self.conf_path = os.path.expanduser(conf_path) self.conf_path = os.path.expanduser(conf_path)
self.main_conf = main_conf
init = False init = False
if not os.path.isfile(self.conf_path): if not os.path.isfile(self.conf_path):
mkdirs(os.path.dirname(self.conf_path)) mkdirs(os.path.dirname(self.conf_path))
@ -76,11 +108,11 @@ class Config(dict):
self[section][opt] = self.config.get(section, opt) self[section][opt] = self.config.get(section, opt)
if 'config_version' not in self['main'] or int(self['main']['config_version']) != VERSION: if main_conf and ('config_version' not in self['main'] or int(self['main']['config_version']) != VERSION):
print "Sorry, but configuration schema have been changed or config file have been corrupted, so you need to reinitialize the configuration." print "Sorry, but configuration schema have been changed or config file have been corrupted, so you need to reinitialize the configuration."
init = True init = True
if init: if init and main_conf:
self.first_start() self.first_start()
@ -121,10 +153,12 @@ class Config(dict):
self['main']['domain'] = domain self['main']['domain'] = domain
user = ask_user('User: ', can_be_empty=False) user_default = getpass.getuser()
self['user']['login'] = user user = ask_user('User [%s]: ' % user_default, can_be_empty=True)
self['user']['login'] = user or user_default
password = ask_user('Password: ', can_be_empty=False) #password = ask_user('Password: ', can_be_empty=False)
password = getpass.getpass()
self['user']['password'] = password self['user']['password'] = password
git_uri = "%(protocol)s//%(user)s@%(domain)s" % \ git_uri = "%(protocol)s//%(user)s@%(domain)s" % \
@ -175,7 +209,8 @@ class Config(dict):
self['main']['config_version'] = VERSION self['main']['config_version'] = VERSION
print('Initial configuration have been completed') print('Initial configuration have been completed')
exit() print 'Now you can execute "abf locate update-recursive PATH", where PATH is your directory with ' + \
'cloned ABF projects. It will let you use "abfcd <project>" command to simply cd to project directory.\n\n'

View file

@ -18,6 +18,11 @@ class Log:
logging.getLogger("abf").handlers[0].setLevel(logging.ERROR) logging.getLogger("abf").handlers[0].setLevel(logging.ERROR)
logging.getLogger("models").handlers[0].setLevel(logging.ERROR) logging.getLogger("models").handlers[0].setLevel(logging.ERROR)
@staticmethod
def set_silent():
Log.set_verbose()
logging.getLogger("").handlers[0].setLevel(logging.CRITICAL)
def __init__(self, name=''): def __init__(self, name=''):
logging.config.fileConfig(os.path.expanduser('~/.abfcfg')) logging.config.fileConfig(os.path.expanduser('~/.abfcfg'))
self.log = logging.getLogger(name) self.log = logging.getLogger(name)

View file

@ -4,41 +4,17 @@ import time
import select import select
import subprocess import subprocess
import fcntl import fcntl
import rpm
from glob import glob from glob import glob
import shutil import shutil
import re import re
import yaml
import tempfile import tempfile
def mkdirs(path): from abf.console.log import Log
''' the equivalent of mkdir -p path''' log = Log('models')
if os.path.exists(path):
return
path = os.path.normpath(path)
items = path.split('/')
p = ''
for item in items:
p += '/' + item
if not os.path.isdir(p):
os.mkdir(p)
def ask_user(prompt, can_be_empty=False, variants=None):
while True:
sys.stdout.write(prompt)
sys.stdout.flush()
res = sys.stdin.readline()
res = res.strip()
if not can_be_empty and not res:
continue
if variants:
if res in variants:
break
else:
continue
break
return res
class CommandTimeoutExpired(Exception): class CommandTimeoutExpired(Exception):
pass pass
@ -48,9 +24,9 @@ class ReturnCodeNotZero(Exception):
super(ReturnCodeNotZero, self).__init__(message) super(ReturnCodeNotZero, self).__init__(message)
self.code = code self.code = code
def get_project_name(): def get_project_name(path=None):
try: try:
output = execute_command(['git', 'remote', 'show', 'origin', '-n']) output = execute_command(['git', 'remote', 'show', 'origin', '-n'], cwd=path)
for line in output.split('\n'): for line in output.split('\n'):
if line.startswith(' Fetch URL:'): if line.startswith(' Fetch URL:'):
@ -63,6 +39,7 @@ def get_project_name():
def get_project_name_version(spec_path): def get_project_name_version(spec_path):
try: try:
rpm = __import__('rpm') # it's initialization is too long to place it to the top of the file
ts = rpm.TransactionSet() ts = rpm.TransactionSet()
rpm_spec = ts.parseSpec(spec_path) rpm_spec = ts.parseSpec(spec_path)
name = rpm.expandMacro("%{name}") name = rpm.expandMacro("%{name}")
@ -71,6 +48,25 @@ def get_project_name_version(spec_path):
except: except:
return None return None
def get_project_data(spec_path):
rpm = __import__('rpm') # it's initialization is too long to place it to the top of the file
ts = rpm.TransactionSet()
rpm_spec = ts.parseSpec(spec_path)
name = rpm.expandMacro("%{name}")
version = rpm.expandMacro("%{version}")
sources_all = rpm_spec.sources()
sources = []
patches = []
for src in sources_all:
name, number, flag = src
if flag & 65536: # source file
sources.append((name, number))
elif flag & 131072:
patches.append((name, number))
return {'name': name, 'version': version, 'sources': sources, 'patches': patches}
def get_branch_name(): def get_branch_name():
try: try:
output = execute_command(['git', 'branch']) output = execute_command(['git', 'branch'])
@ -115,13 +111,12 @@ def get_tag_hash(tag, cwd=None):
return h return h
return None return None
def clone_git_repo_tmp(uri, log=None, depth=None): def clone_git_repo_tmp(uri, depth=None):
if log: log.info('Cloning git repository (temporary workaround)')
log.info('Cloning git repository (temporary workaround)')
tmp_dir = tempfile.mkdtemp(prefix='tmp_abf_') tmp_dir = tempfile.mkdtemp(prefix='tmp_abf_')
log.info("Temporary directory os " + tmp_dir) log.info("Temporary directory os " + tmp_dir)
cmd = ['git', 'clone', uri, tmp_dir] cmd = ['git', 'clone', uri, tmp_dir]
execute_command(cmd, log=log, print_to_stdout=True, exit_on_error=True) execute_command(cmd, print_to_stdout=True, exit_on_error=True)
return tmp_dir return tmp_dir
@ -139,15 +134,117 @@ def get_root_git_dir(path=None):
else: else:
return p return p
def pack_project(log, root_path): def get_spec_file(root_path):
# look for a spec file
specs = glob(os.path.join(root_path, '*.spec')) specs = glob(os.path.join(root_path, '*.spec'))
log.debug("Spec files found: " + str(specs)) log.debug("Spec files found: " + str(specs))
if len(specs) == 1: if len(specs) == 1:
spec = specs[0] spec = specs[0]
return spec
else: else:
log.error("Could not find single spec file") raise Excpetion("Could not find single spec file")
return
def find_spec_problems(exit_on_error=True, strict=False, auto_remove=False):
path = get_root_git_dir()
files = os.listdir(path)
files_present = []
specs_present = []
dirs_present = []
yaml_files = []
for fl in files:
if fl.startswith('.'):
continue
if os.path.isdir(fl):
dirs_present.append(fl)
continue
if fl.endswith('.spec'):
specs_present.append(fl)
continue
files_present.append(fl)
yaml_path = os.path.join(path, '.abf.yml')
if os.path.isfile(yaml_path):
with open(yaml_path, 'r') as fd:
yaml_data = yaml.load(fd)
if not 'sources' in yaml_data:
log.error("Incorrect .abf.yml file: no 'sources' key")
exit(1)
for fl in yaml_data['sources']:
yaml_files.append(fl)
if len(specs_present) == 0:
raise Exception("No spec files found!")
elif len(specs_present) > 1:
raise Exception("There are more than one found!")
spec_path = specs_present[0]
for d in dirs_present:
log.info("warning: directory '%s' was found" % d)
if auto_remove:
shutil.rmtree(os.path.join(path,d) )
res = get_project_data(spec_path)
errors = False
warnings = False
files_required = []
for fl in res['sources'] + res['patches']:
fname, n = fl
fname_base = os.path.basename(fname)
files_required.append(fname_base)
is_url = fname.startswith('http://')
presents = fname_base in files_present
in_yaml = fname_base in yaml_files
if is_url and in_yaml:
warnings = True
log.info('warning: file "%s" presents in spec (url) and in .abf.yml' % fname_base)
if is_url and not presents:
warnings = True
log.info('warning: file "%s" is listed in spec as a URL, but does not present in the current directory or in .abf.yml file' % fname_base)
if presents and in_yaml:
warnings = True
log.info('warning: file "%s" presents in the git directory and in .abf.yml' % fname_base)
if not presents and not in_yaml and not is_url:
errors = True
log.info("error: missing file %s" % fname)
remove_from_yaml = []
for fl in set(files_present + yaml_files):
if fl in files_required:
continue # file have already been processed
presents = fl in files_present
in_yaml = fl in yaml_files
if presents:
warnings = True
log.info('warning: unnecessary file "%s"' % fl)
if auto_remove:
os.remove( os.path.join(path, fl) )
if in_yaml:
warnings = True
log.info('warning: unnecessary file "%s" in .abf.yml' % fl)
remove_from_yaml.append(fl)
if auto_remove:
for fl in remove_from_yaml:
yaml_data['sources'].pop(fl)
with open(yaml_path, 'w') as fd:
yaml.dump(yaml_data, fd, default_flow_style=False)
log.info('.abf.yml file was rewritten')
if exit_on_error and (errors or (strict and warnings)):
exit(1)
def pack_project(root_path):
# look for a spec file
spec = get_spec_file(root_path)
if spec: if spec:
name, version = get_project_name_version(spec) name, version = get_project_name_version(spec)
@ -167,7 +264,7 @@ def pack_project(log, root_path):
#open(full_tarball_path, 'w').close() #open(full_tarball_path, 'w').close()
cmd = ['tar', 'czf', full_tarball_path, '--exclude-vcs', os.path.basename(root_path)] cmd = ['tar', 'czf', full_tarball_path, '--exclude-vcs', os.path.basename(root_path)]
try: try:
execute_command(cmd, log=log, cwd=os.path.dirname(root_path), exit_on_error=False) execute_command(cmd, cwd=os.path.dirname(root_path), exit_on_error=False)
except ReturnCodeNotZero, ex: except ReturnCodeNotZero, ex:
if ex.code != 1: if ex.code != 1:
raise raise
@ -188,13 +285,12 @@ def pack_project(log, root_path):
def execute_command(command, log=None, shell=False, cwd=None, timeout=0, raiseExc=True, print_to_stdout=False, exit_on_error=False): def execute_command(command, shell=False, cwd=None, timeout=0, raiseExc=True, print_to_stdout=False, exit_on_error=False):
output = "" output = ""
start = time.time() start = time.time()
try: try:
child = None child = None
if log: log.debug("Executing command: %s" % command)
log.debug("Executing command: %s" % command)
child = subprocess.Popen( child = subprocess.Popen(
command, command,
shell=shell, shell=shell,
@ -230,8 +326,7 @@ def execute_command(command, log=None, shell=False, cwd=None, timeout=0, raiseEx
if not niceExit and raiseExc: if not niceExit and raiseExc:
raise CommandTimeoutExpired("Timeout(%s) expired for command:\n # %s\n%s" % (timeout, command, output)) raise CommandTimeoutExpired("Timeout(%s) expired for command:\n # %s\n%s" % (timeout, command, output))
if log: log.debug("Child returncode was: %s" % str(child.returncode))
log.debug("Child returncode was: %s" % str(child.returncode))
if child.returncode: if child.returncode:
if exit_on_error: if exit_on_error:
exit(child.returncode) exit(child.returncode)

14
abfcd.sh Normal file
View file

@ -0,0 +1,14 @@
abfcd()
{
if [ $2 ] || [ -z $1 ] ; then
echo "Syntax: abfcd [group/]project"
return 1
fi
output=`abf locate -p $1`
if [[ $output == error* ]] || [[ -z $output ]] ; then
echo $output;
return;
fi
cd $output
}

View file

@ -2,7 +2,7 @@
# bash-completion add-on for rpmlint # bash-completion add-on for rpmlint
# http://bash-completion.alioth.debian.org/ # http://bash-completion.alioth.debian.org/
_opts() __abf_opts()
{ {
if [[ ${cur} == -* ]] ; then if [[ ${cur} == -* ]] ; then
@ -11,71 +11,133 @@ _opts()
fi fi
} }
_get() __abf_get()
{ {
_opts "--branch" __abf_opts "--branch"
} }
_put() __abf_put()
{ {
_opts "" __abf_opts ""
} }
_build() __abf_show()
{ {
_opts "--branch --tag --target-platform --arches --repository" __abf_opts "--project"
shows="build-repos build-platforms save-to-repos save-to-platforms"
if [[ ${cur} != -* ]] ; then
if [[ ${prev} == -* ]] ; then
return 0;
fi
COMPREPLY=( $(compgen -W "${shows}" -- "${cur}") )
return 0
fi
} }
_backport() __abf_locate()
{ {
_opts "--pack" __abf_opts "--project --directory"
actions="update update-recursive"
if [[ ${cur} != -* ]] ; then
if [[ ${prev} == -* ]] ; then
return 0;
fi
COMPREPLY=( $(compgen -W "${actions}" -- "${cur}") )
return 0
fi
} }
_buildstatus() __abf_build()
{ {
_opts "--logs" __abf_opts "--branch --tag --commit --target-platform --arch --repository --save-to-repository --auto-publish --update-type --skip-spec-check"
update_types="security bugfix enhancement recommended newpackage"
if [ ${prev} == -r ] || [ ${prev} == --repository ] ; then
COMPREPLY=( $(compgen -W "`abf-local show build-repos`" -- "${cur}") )
return 0
fi
if [ ${prev} == -s ] || [ ${prev} == --save-to-repository ] ; then
proj=""
next=0
for i in ${COMP_WORDS[@]}
do
if [[ $next == 1 ]] ; then
proj=$i;
next=0;
fi
if [[ "$i" == "-p" || "$i" == "--project" ]] ; then
next=1;
fi;
done
if [ -n "${proj}" ] ; then
COMPREPLY=( $(compgen -W "`abf-local show save-to-repos -p ${proj}`" -- "${cur}") )
fi
return 0
fi
if [ ${prev} == --update-type ] ; then
COMPREPLY=( $(compgen -W "${update_types}" -- "${cur}") )
return 0
fi
}
__abf_publish()
{
__abf_opts "--pack"
}
__abf_backport()
{
__abf_opts "--pack"
if [[ ${cur} != -* ]] ; then
branches=`git branch --no-color | sed 's/^..//' | xargs echo`
COMPREPLY=( $(compgen -W "${branches}" -- "${cur}") )
return 0
fi
}
__abf_buildstatus()
{
__abf_opts ""
}
__abf_help()
{
__abf_opts ""
} }
_abf()
__abf()
{ {
local opts modes local opts modes
modes="help get put build backport buildstatus" modes="help get put show build publish backport buildstatus locate"
COMPREPLY=() COMPREPLY=()
mode="${COMP_WORDS[1]}" mode="${COMP_WORDS[1]}"
prev="${COMP_WORDS[COMP_CWORD-1]}" prev="${COMP_WORDS[COMP_CWORD-1]}"
cur="${COMP_WORDS[COMP_CWORD]}" cur="${COMP_WORDS[COMP_CWORD]}"
if [ "$COMP_CWORD" == "1" ]; then if [ "$COMP_CWORD" == "1" ] || ( [ "$COMP_CWORD" == "2" ] && [ "$mode" == "help" ] ); then
COMPREPLY=( $(compgen -W "${modes}" -- ${cur}) ) COMPREPLY=( $(compgen -W "${modes}" -- ${cur}) )
return 0 return 0
fi fi
if [ "$mode" == "get" ]; then for i in ${modes}
_get do
return 0 if [[ $i == $mode ]] ; then
fi eval __abf_${i};
if [ "$mode" == "put" ]; then fi
_put done
return 0
fi
if [ "$mode" == "build" ]; then
_build
return 0
fi
if [ "$mode" == "backport" ]; then
_backport
return 0
fi
if [ "$mode" == "buildstatus" ]; then
_buildstatus
return 0
fi
} }
complete -F _abf abf complete -F __abf abf