From: Huang Hao Date: Tue, 11 Sep 2012 10:48:47 +0000 (+0800) Subject: More changes to support profile style of config. X-Git-Tag: 0.10~26^2 X-Git-Url: http://review.tizen.org/git/?a=commitdiff_plain;h=3d059ea01bf6af54d6ee9d2798be369c15d3a148;p=tools%2Fgbs.git More changes to support profile style of config. - proflie section header should start with "proflie_", obs section header should start with "obs_", repo section header should start with "repo_". - add base_prj and target_prj config to obs section - generate profile style of config file in ~/.gbs.conf if no config file were found - clean all parsers in function reset_from_conf() - as described in issue#255, remove config file in cwd - if current config is subcommand oriented style, convert it to profile oriented style into ~/.gbs.conf.template. Leave it original config file unchange. - BrainConfigParser.set() is not compatible with super set, so change name to set_into_file() - add class RepoConf and OBSConf to repo section info and obs section info, including section name which is useful for auto new style config generation. - only update gbs.conf that we had changed and catch permission exception. - add /etc/gbs.conf back. Change-Id: I1dcee2814a7d5430ac3329a2c018e98b48734569 --- diff --git a/gitbuildsys/cmd_build.py b/gitbuildsys/cmd_build.py index b090df4..440c537 100644 --- a/gitbuildsys/cmd_build.py +++ b/gitbuildsys/cmd_build.py @@ -240,7 +240,7 @@ def do(opts, args): if opts.skip_conf_repos: repos = [] else: - repos = configmgr.get_current_profile().get_repos() + repos = [i.url for i in configmgr.get_current_profile().repos] if opts.repositories: for i in opts.repositories: diff --git a/gitbuildsys/cmd_remotebuild.py b/gitbuildsys/cmd_remotebuild.py index b87d3d3..06ef1b2 100644 --- a/gitbuildsys/cmd_remotebuild.py +++ b/gitbuildsys/cmd_remotebuild.py @@ -65,10 +65,12 @@ def do(opts, args): else: msger.error('Invalid arguments, see gbs remotebuild -h for more info') - apiurl = configmgr.get_current_profile().get_api() - if not apiurl: + obsconf = configmgr.get_current_profile().obs + if not obsconf or not obsconf.url: msger.error('no obs api found, please add it to gbs conf and try again') + apiurl = obsconf.url + if not apiurl.user: msger.error('empty user is not allowed for remotebuild, ' 'please add user/passwd to gbs conf, and try again') @@ -103,12 +105,12 @@ def do(opts, args): package = spec.name if opts.base_obsprj is None: - base_prj = configmgr.get('base_prj', 'remotebuild') + base_prj = obsconf.base or 'Tizen:Main' else: base_prj = opts.base_obsprj if opts.target_obsprj is None: - target_prj = configmgr.get('target_prj', 'remotebuild') or \ + target_prj = obsconf.target or \ "home:%s:gbs:%s" % (apiurl.user, base_prj) else: target_prj = opts.target_obsprj diff --git a/gitbuildsys/conf.py b/gitbuildsys/conf.py index 018832a..0844853 100644 --- a/gitbuildsys/conf.py +++ b/gitbuildsys/conf.py @@ -21,7 +21,6 @@ Provides classes and functions to read and write gbs.conf. from __future__ import with_statement import os -import re import ast import base64 from ConfigParser import SafeConfigParser, NoSectionError, NoOptionError, \ @@ -29,6 +28,7 @@ from ConfigParser import SafeConfigParser, NoSectionError, NoOptionError, \ from gitbuildsys import msger, errors from gitbuildsys.safe_url import SafeURL +from gitbuildsys.utils import Temp def decode_passwdx(passwdx): @@ -53,54 +53,11 @@ def split_and_evaluate_string(string, sep=None, maxsplit=-1): return [ evalute_string(i.strip()) for i in string.split(sep, maxsplit) ] -class SectionPattern(object): - '''Pattern of section that support [section "name"] and [section]. - 1. If there is white-space in section header, it must obey the format like: - section_type white_spaces section_name, - section_name could be any string. - 2. otherwise section name is the whole string in brackets - ''' - - SECTCRE = re.compile( - r'\[' # [ - r'(?P
[^] \t]+)' # section name without any white-space - r'([ \t]+' # or - r'(?P[^]]+)' # section type and section name - r')?' # this section name is optional - r'\]' # ] - ) - - class MatchObject(object): - '''Match object for SectionPattern''' - - def __init__(self, match): - self.match = match - - def group(self, _group1): - '''return a tuple(type, name) if section has a name, - otherwise return a string as section name - ''' - type_ = self.match.group('header') - name = self.match.group('name') - if not name: - return type_ - - name = evalute_string(name) - return type_, name - - def match(self, string): - '''return MatchObject if string match the pattern''' - match = self.SECTCRE.match(string) - return self.MatchObject(match) if match else match - - class BrainConfigParser(SafeConfigParser): """Standard ConfigParser derived class which can reserve most of the comments, indents, and other user customized stuff inside the ini file. """ - SECTCRE = SectionPattern() - def read_one(self, filename): """only support one input file""" return SafeConfigParser.read(self, filename) @@ -188,7 +145,7 @@ class BrainConfigParser(SafeConfigParser): else: raise NoSectionError(section) - def set(self, section, option, value, replace_opt=None): + def set_into_file(self, section, option, value, replace_opt=None): """When set new value, need to update the readin file lines, which can be saved back to file later. """ @@ -213,11 +170,10 @@ class BrainConfigParser(SafeConfigParser): if self._fpname == '': return - fptr = open(self._fpname, 'w') - for line in self._flines: - if line is not None: - fptr.write(line) - fptr.close() + with open(self._fpname, 'w') as fptr: + buf = ''.join([ line for line in self._flines if line is not None ]) + fptr.write(buf) + class ConfigMgr(object): '''Support multi-levels of gbs.conf. Use this class to get and set @@ -234,8 +190,6 @@ class ConfigMgr(object): 'build_server': 'https://api.tizen.org', 'user': '', 'passwd': '', - 'base_prj': 'Tizen:Main', - 'target_prj': '' }, 'build': { 'build_cmd': '/usr/bin/build', @@ -243,35 +197,32 @@ class ConfigMgr(object): }, } - DEFAULT_CONF_TEMPLATE = """[general] -; general settings -tmpdir = $general__tmpdir -editor = $general__editor + DEFAULT_CONF_TEMPLATE = '''[general] +profile = profile.tizen +tmpdir = /var/tmp -[remotebuild] -; settings for build subcommand -build_server = $remotebuild__build_server -user = $remotebuild__user +[profile.tizen] +; common authentication info for whole profile +#user = ; CAUTION: please use the key name "passwd" to reset plaintext password -passwdx = $remotebuild__passwdx -; Default base project -base_prj = $remotebuild__base_prj -; Default target project -target_prj = $remotebuild__target_prj - -[build] -build_cmd = $build__build_cmd -distconf = $build__distconf - -; optional, repos definitions -#repo1.url= -#repo1.user= -#repo1.passwd= -; one more repo -#repo2.url= -#repo2.user= -#repo2.passwd= -""" +#passwd = +obs = obs.tizen +; comma separated list of repositories +repos = repo.tizen_latest +distconf = /usr/share/gbs/tizen-1.0.conf + +[obs.tizen] +url = https://api.tizen.org +; optinal user/passwd, set if differ from proflie's user/passwd +#user = +#passwd = + +[repo.tizen_latest] +url = http://download.tizen.org/snapshots/trunk/common/latest/ +; optinal user/passwd, set if differ from proflie's user/passwd +#user = +#passwdx = +''' # make the manager class as singleton _instance = None @@ -296,9 +247,10 @@ distconf = $build__distconf # use the default path fpaths = self._lookfor_confs() if not fpaths: - if not self._new_conf(): - msger.error('No config file available') + self._new_conf() + fpaths = self._lookfor_confs() + self._cfgparsers = [] for fpath in fpaths: cfgparser = BrainConfigParser() try: @@ -311,70 +263,34 @@ distconf = $build__distconf @staticmethod def _lookfor_confs(): """Look for available config files following the order: - > Current git - > Cwd + > Current project > User + > System """ paths = [] - for path in (os.path.abspath('.git/gbs.conf'), - os.path.abspath('.gbs.conf'), - os.path.expanduser('~/.gbs.conf')): + for path in (os.path.abspath('.gbs.conf'), + os.path.expanduser('~/.gbs.conf'), + '/etc/gbs.conf'): if os.path.exists(path) and path not in paths: paths.append(path) return paths - def get_default_conf(self, defaults=None): - 'returns ini template string contains default values' - from string import Template - if not defaults: - defaults = self.DEFAULTS - - tmpl_keys = {} - for sec, opts in defaults.iteritems(): - for opt, val in opts.iteritems(): - tmpl_keys['%s__%s' % (sec, opt)] = val - - return Template(self.DEFAULT_CONF_TEMPLATE).safe_substitute(tmpl_keys) - - def _new_conf(self, fpath=None): - 'generate a new conf file located by fpath' - if not fpath: - fpath = os.path.expanduser('~/.gbs.conf') - - import getpass - msger.info('Creating config file %s ... ' % fpath) - # user and passwd in [build] section need user input - defaults = self.DEFAULTS.copy() - build_server = raw_input('Remote build server url (use %s by default):'\ - % defaults['remotebuild']['build_server']) - if build_server: - defaults['remotebuild']['build_server'] = build_server - - defaults['remotebuild']['user'] = \ - raw_input('Username for remote build server '\ - '(type to skip): ') - - if defaults['remotebuild']['user']: - msger.info('Your password will be encoded before saving ...') - defaults['remotebuild']['passwdx'] = \ - encode_passwd(getpass.getpass()) - else: - defaults['remotebuild']['passwdx'] = \ - encode_passwd(defaults['remotebuild']['passwd']) + def _new_conf(self): + 'generate a default conf file in home dir' + fpath = os.path.expanduser('~/.gbs.conf') with open(fpath, 'w') as wfile: - wfile.write(self.get_default_conf(defaults)) + wfile.write(self.DEFAULT_CONF_TEMPLATE) os.chmod(fpath, 0600) - msger.info('Done. Your gbs config is now located at %s' % fpath) - msger.warning("Don't forget to double-check the config manually.") - return True + msger.warning('Created a new config file %s. Please check and edit ' + 'your authentication information.' % fpath) def _check_passwd(self): 'convert passwd item to passwdx and then update origin conf files' - replaced_keys = False + dirty = set() all_sections = set() for layer in self._cfgparsers: @@ -390,16 +306,16 @@ distconf = $build__distconf if plainpass is None: # empty string password is acceptable here continue - cfgparser.set(sec, + cfgparser.set_into_file(sec, key + 'x', encode_passwd(plainpass), key) - replaced_keys = True + dirty.add(cfgparser) - if replaced_keys: + if dirty: msger.warning('plaintext password in config files will ' 'be replaced by encoded ones') - self.update() + self.update(dirty) def _get(self, opt, section='general'): 'get value from multi-levels of config file' @@ -452,45 +368,121 @@ distconf = $build__distconf else: return self._get(opt, section) - def update(self): + def update(self, cfgparsers): 'update changed values into files on disk' - for cfgparser in self._cfgparsers: - cfgparser.update() + for cfgparser in cfgparsers: + try: + cfgparser.update() + except IOError, err: + msger.warning('update config file error: %s' % err) + + +class OBSConf(object): + 'Config items related to obs section' + + def __init__(self, parent, name, url, base, target): + self.parent = parent + self.name = name + self.url = url + self.base = base + self.target = target + + def dump(self, fhandler): + 'dump ini to file object' + parser = BrainConfigParser() + parser.add_section(self.name) + + parser.set(self.name, 'url', self.url) + + if self.url.user and self.url.user != self.parent.common_user: + parser.set(self.name, 'user', self.url.user) + + if self.url.passwd and self.url.passwd != self.parent.common_password: + parser.set(self.name, 'passwdx', + encode_passwd(self.url.passwd)) + + if self.base: + parser.set(self.name, 'base_prj', self.base) + + if self.target: + parser.set(self.name, 'target_prj', self.target) + parser.write(fhandler) + + +class RepoConf(object): + 'Config items related to repo section' + + def __init__(self, parent, name, url): + self.parent = parent + self.name = name + self.url = url + + def dump(self, fhandler): + 'dump ini to file object' + parser = BrainConfigParser() + parser.add_section(self.name) + + parser.set(self.name, 'url', self.url) + + if self.url.user and self.url.user != self.parent.common_user: + parser.set(self.name, 'user', self.url.user) + + if self.url.passwd and self.url.passwd != self.parent.common_password: + parser.set(self.name, 'passwdx', + encode_passwd(self.url.passwd)) + parser.write(fhandler) class Profile(object): '''Profile which contains all config values related to same domain''' - def __init__(self, user, password): + def __init__(self, name, user, password): + self.name = name self.common_user = user self.common_password = password self.repos = [] - self.api = None + self.obs = None - def make_url(self, url, user, password): - '''make a safe url which contains auth info''' - user = user or self.common_user - password = password or self.common_password - try: - return SafeURL(url, user, password) - except ValueError, err: - raise errors.ConfigError('%s for %s' % (str(err), url)) + def _update_url(self, url): + 'update url by common auth info' + if not url.user: + url.user = self.common_user + if not url.passwd: + url.passwd = self.common_password + return url - def add_repo(self, url, user, password): + def add_repo(self, repoconf): '''add a repo to repo list of the profile''' - self.repos.append(self.make_url(url, user, password)) + self._update_url(repoconf.url) + self.repos.append(repoconf) - def set_api(self, url, user, password): + def set_obs(self, obsconf): '''set OBS api of the profile''' - self.api = self.make_url(url, user, password) + self._update_url(obsconf.url) + self.obs = obsconf + + def dump(self, fhandler): + 'dump ini to file object' + parser = BrainConfigParser() + parser.add_section(self.name) - def get_repos(self): - '''get repo list of the profile''' - return self.repos + if self.common_user: + parser.set(self.name, 'user', self.common_user) + if self.common_password: + parser.set(self.name, 'passwdx', + encode_passwd(self.common_password)) - def get_api(self): - '''get OBS api of the profile''' - return self.api + if self.obs: + parser.set(self.name, 'obs', self.obs.name) + self.obs.dump(fhandler) + + if self.repos: + names = [] + for repo in self.repos: + names.append(repo.name) + repo.dump(fhandler) + parser.set(self.name, 'repos', ', '.join(names)) + parser.write(fhandler) class BizConfigManager(ConfigMgr): @@ -506,9 +498,37 @@ class BizConfigManager(ConfigMgr): if self.is_profile_oriented(): return self._build_profile_by_name(self.get('profile')) - msger.warning('subcommand oriented style of config is deprecated, ' - 'please convert to profile oriented style.') - return self._build_profile_by_subcommand() + profile = self._build_profile_by_subcommand() + self.convert_to_new_style(profile) + return profile + + def convert_to_new_style(self, profile): + 'convert ~/.gbs.conf to new style' + def dump_general(fhandler): + 'dump options in general section' + parser = BrainConfigParser() + parser.add_section('general') + parser.set('general', 'profile', profile.name) + + for opt in self.options('general'): + val = self.get(opt) + if val != self.DEFAULTS['general'].get(opt): + parser.set('general', opt, val) + parser.write(fhandler) + + fname = '~/.gbs.conf.template' + try: + tmp = Temp() + with open(tmp.path, 'w') as fhandler: + dump_general(fhandler) + profile.dump(fhandler) + os.rename(tmp.path, os.path.expanduser(fname)) + except IOError, err: + raise errors.ConfigError(err) + + msger.warning('subcommand oriented style of config is deprecated. ' + 'Please check %s, a new profile oriented style of config which' + ' was converted from your current settings.' % fname) def get_optional_item(self, section, option, default=None): '''return default if section.option does not exist''' @@ -517,33 +537,51 @@ class BizConfigManager(ConfigMgr): except errors.ConfigError: return default - def _get_url_section(self, section_id): + def _get_url_options(self, section_id): '''get url/user/passwd from a section''' url = self.get('url', section_id) user = self.get_optional_item(section_id, 'user') password = self.get_optional_item(section_id, 'passwd') - return url, user, password + try: + return SafeURL(url, user, password) + except ValueError, err: + raise errors.ConfigError('%s for %s' % (str(err), url)) def _build_profile_by_name(self, name): '''return profile object by a given section''' - profile_id = ('profile', name) - user = self.get_optional_item(profile_id, 'user') - password = self.get_optional_item(profile_id, 'passwd') - - profile = Profile(user, password) - - conf_api = self.get_optional_item(profile_id, 'api') - if conf_api: - api = self.get('api', profile_id) - api_id = ('obs', api) - profile.set_api(*self._get_url_section(api_id)) - - conf_repos = self.get_optional_item(profile_id, 'repos') - if conf_repos: - repos = split_and_evaluate_string(conf_repos, ',') - for repo in repos: - repo_id = ('repo', repo) - profile.add_repo(*self._get_url_section(repo_id)) + if not name.startswith('profile.'): + raise msger.error('profile section name must start ' + 'with "profile.": %s' % name) + + user = self.get_optional_item(name, 'user') + password = self.get_optional_item(name, 'passwd') + + profile = Profile(name, user, password) + + obs = self.get_optional_item(name, 'obs') + if obs: + if not obs.startswith('obs.'): + msger.error('obs section name should start ' + 'with "obs.": %s' % obs) + + obsconf = OBSConf(profile, obs, + self._get_url_options(obs), + self.get_optional_item(obs, 'base_prj'), + self.get_optional_item(obs, 'target_prj')) + profile.set_obs(obsconf) + + repos = self.get_optional_item(name, 'repos') + if repos: + for repo in repos.split(','): + repo = repo.strip() + if not repo.startswith('repo.'): + msger.warning('repo section name should start ' + 'with "repo.": %s' % repo) + continue + + repoconf = RepoConf(profile, repo, + self._get_url_options(repo)) + profile.add_repo(repoconf) return profile @@ -585,19 +623,34 @@ class BizConfigManager(ConfigMgr): def _build_profile_by_subcommand(self): '''return profile object from subcommand oriented style of config''' - profile = Profile(None, None) + profile = Profile('profile.current', None, None) - section_id = 'remotebuild' - url = self.get('build_server', section_id) - user = self.get_optional_item(section_id, 'user') - password = self.get_optional_item(section_id, 'passwd') - profile.set_api(url, user, password) + sec = 'remotebuild' + addr = self.get('build_server', sec) + user = self.get_optional_item(sec, 'user') + password = self.get_optional_item(sec, 'passwd') + + try: + url = SafeURL(addr, user, password) + except ValueError, err: + raise errors.ConfigError('%s for %s' % (str(err), addr)) + + obsconf = OBSConf(profile, 'obs.%s' % sec, url, + self.get_optional_item('remotebuild', 'base_prj'), + self.get_optional_item('remotebuild', 'target_prj')) + profile.set_obs(obsconf) repos = self._parse_build_repos() for key, item in repos: if 'url' not in item: - raise errors.ConfigError("Url is not specified for %s" % key) - profile.add_repo(item['url'], item.get('user'), item.get('passwd')) + raise errors.ConfigError("URL is not specified for %s" % key) + try: + url = SafeURL(item['url'], item.get('user'), item.get('passwd')) + except ValueError, err: + raise errors.ConfigError('%s for %s' % (str(err), item['url'])) + + repoconf = RepoConf(profile, 'repo.%s' % key, url) + profile.add_repo(repoconf) return profile diff --git a/tests/test_config.py b/tests/test_config.py index 624a25e..6144198 100644 --- a/tests/test_config.py +++ b/tests/test_config.py @@ -119,33 +119,6 @@ class ConfigGettingTest(unittest.TestCase): '''value can be overwrite if name is the same''' self.assertEqual('projv1', self.get('section', 'common_key')) - @Fixture(project='project1.ini') - def test_get_named_section(self): - '''get value from named section''' - self.assertEquals('projv4', self.get(('profile', 'rsa'), 'proj_only')) - - @Fixture(home='home1.ini', project='project1.ini') - def test_inherit_named_section(self): - '''value can be inherit from named section correctly''' - self.assertEquals('homev4', self.get(('profile', 'rsa'), 'home_only')) - - @Fixture(home='home1.ini', project='project1.ini') - def test_overwrite_named_section(self): - '''value can be overwrite from named section correctly''' - self.assertEquals('projv3', self.get(('profile', 'rsa'), 'common')) - - @Fixture(project='project1.ini') - def test_no_such_named_section(self): - '''test no such section''' - self.assertRaises(ConfigError, - self.get, ('profile', 'NOT_EXISTS'), 'key') - - @Fixture(project='project1.ini') - def test_no_such_option_in_named_section(self): - '''test no such section''' - self.assertRaises(ConfigError, - self.get, ('profile', 'rsa'), 'not_exists_option') - @Fixture(home='home1.ini') def test_default_value(self): 'test get hardcode default value ' diff --git a/tests/test_passwdx.py b/tests/test_passwdx.py index 8b4474b..152f864 100644 --- a/tests/test_passwdx.py +++ b/tests/test_passwdx.py @@ -21,10 +21,12 @@ from StringIO import StringIO from mock import patch -from test_config import Fixture import gitbuildsys.conf +from gitbuildsys.conf import BrainConfigParser from gitbuildsys.errors import ConfigError +from test_config import Fixture + class FakeFile(object): 'Fake file used to get updated config file' @@ -39,6 +41,13 @@ class FakeFile(object): def close(self): 'do not close buffer, then call getvalue() to retrieve the content' + def __exit__(self, *_args): + 'mock with statement' + + def __enter__(self): + 'mock with statement' + return self + def getvalue(self): 'get content of fake file' return self.buffer.getvalue() @@ -70,8 +79,12 @@ repo1.passwdx = QlpoOTFBWSZTWYfNdxYAAAIBgAoAHAAgADDNAMNEA24u5IpwoSEPmu4s def test_two_files(self, fake_open): 'test passwdx set back to two files' confs = [FakeFile(), FakeFile()] - bak = confs[:] - fake_open.side_effect = lambda *args, **kw: bak.pop() + def side_effect(name, _mode): + 'fake open' + if name == '~/.gbs.conf': + return confs[0] + return confs[1] + fake_open.side_effect = side_effect reload(gitbuildsys.conf) @@ -105,8 +118,10 @@ repo1.passwdx = QlpoOTFBWSZTWYfNdxYAAAIBgAoAHAAgADDNAMNEA24u5IpwoSEPmu4s self.assertEquals('secret', pwd) @Fixture(home='plain_passwd.ini') - def test_get_passwd(self, _fake_open): + def test_get_passwd(self, fake_open): 'test get decode passwd' + fake_open.return_value = FakeFile() + reload(gitbuildsys.conf) pwd = gitbuildsys.conf.configmgr.get('passwd', 'remotebuild') @@ -129,5 +144,30 @@ repo1.passwdx = QlpoOTFBWSZTWYfNdxYAAAIBgAoAHAAgADDNAMNEA24u5IpwoSEPmu4s self.assertEquals('', pwd) +@patch('gitbuildsys.conf.os.chmod') +@patch('gitbuildsys.conf.open', create=True) +class AutoGenerateTest(unittest.TestCase): + 'test auto generation if no conf was found' + + @Fixture() + def test_auto_generate_conf(self, fake_open, _fake_chmod): + 'test auto generate conf should contain obs and repos' + conf = FakeFile() + fake_open.return_value = conf + + reload(gitbuildsys.conf) + + parser = BrainConfigParser() + parser.readfp(StringIO(conf.getvalue())) + + name = parser.get('general', 'profile') + obs = parser.get(name, 'obs') + repos = parser.get(name, 'repos') + + self.assertTrue(parser.has_section(obs)) + for repo in repos.split(','): + self.assertTrue(parser.has_section(repo.strip())) + + if __name__ == '__main__': unittest.main() \ No newline at end of file diff --git a/tests/test_profile.py b/tests/test_profile.py index b53cb38..4efef9a 100644 --- a/tests/test_profile.py +++ b/tests/test_profile.py @@ -18,8 +18,11 @@ """Functional tests for profile style of config""" import unittest +from mock import patch, MagicMock, Mock + import gitbuildsys.conf from test_config import Fixture +from test_passwdx import FakeFile def get_profile(): @@ -34,19 +37,19 @@ class ProfileStyleTest(unittest.TestCase): @Fixture(home='profile.ini') def test_profile_api(self): 'test get obs api' - self.assertEquals('https://api.tz/path', get_profile().get_api()) + self.assertEquals('https://api.tz/path', get_profile().obs.url) @Fixture(home='profile.ini') def test_api_inherit_auth(self): 'test api can inherit auto from parent profile section' self.assertEquals('https://Alice:secret@api.tz/path', - get_profile().get_api().full) + get_profile().obs.url.full) @Fixture(home='profile_only_has_api.ini') def test_api_auth_can_be_overwrite(self): 'test api auth can be overwrite' self.assertEquals('https://Bob:classified@api.tz/path', - get_profile().get_api().full) + get_profile().obs.url.full) @Fixture(home='profile.ini') def test_profile_repos_in_order(self): @@ -55,46 +58,58 @@ class ProfileStyleTest(unittest.TestCase): 'https://repo/ia32/non-oss', 'https://repo/ia32/base', '/local/path'], - get_profile().get_repos()) + [i.url for i in get_profile().repos]) @Fixture(home='profile.ini') def test_repo_inherit_auth(self): 'test repo can inherit auth from parent section' self.assertEquals('https://Alice:secret@repo/ia32/main', - get_profile().get_repos()[0].full) + get_profile().repos[0].url.full) @Fixture(home='profile.ini') def test_repo_overwrite_auth(self): 'test repo auth can be overwrite' self.assertEquals('https://Bob:classified@repo/ia32/base', - get_profile().get_repos()[2].full) + get_profile().repos[2].url.full) @Fixture(home='no_such_profile_section_name.ini') def test_no_such_profile(self): 'test get a empty profile when name does not exist' profile = get_profile() - self.assertEquals(None, profile.get_api()) - self.assertEquals([], profile.get_repos()) + self.assertEquals(None, profile.obs) + self.assertEquals([], profile.repos) @Fixture(home='profile.ini') def test_local_repo_need_not_auth(self): '''test local path needn't auth info''' - self.assertEquals('/local/path', get_profile().get_repos()[3].full) + self.assertEquals('/local/path', get_profile().repos[3].url.full) + + @Fixture(home='profile.ini') + def test_obs_base_project(self): + 'test read base project from conf' + self.assertEquals('base', get_profile().obs.base) + + @Fixture(home='profile.ini') + def test_obs_target_project(self): + 'test read target project from conf' + self.assertEquals('target', get_profile().obs.target) +@patch('gitbuildsys.conf.open', MagicMock(), create=True) +@patch('gitbuildsys.conf.os.rename', Mock()) class SubcommandStyleTest(unittest.TestCase): '''test for subcommand oriented config''' @Fixture(home='subcommand.ini') def test_api(self): 'test obs api' - self.assertEquals('https://api/build/server', get_profile().get_api()) + self.assertEquals('https://api/build/server', get_profile().obs.url) @Fixture(home='subcommand.ini') def test_api_auth(self): 'test api auth' self.assertEquals('https://Alice:secret@api/build/server', - get_profile().get_api().full) + get_profile().obs.url.full) @Fixture(home='subcommand.ini') def test_repos_in_order(self): @@ -102,13 +117,59 @@ class SubcommandStyleTest(unittest.TestCase): self.assertEquals(['https://repo1/path', 'https://repo2/path', '/local/path/repo'], - get_profile().get_repos()) + [i.url for i in get_profile().repos]) @Fixture(home='subcommand.ini') def test_repo_auth(self): 'test repo auth' self.assertEquals('https://Alice:secret@repo1/path', - get_profile().get_repos()[0].full) + get_profile().repos[0].url.full) + + + +@patch('gitbuildsys.conf.open', create=True) +class ConvertTest(unittest.TestCase): + 'Test convert subcommand to profile' + + @Fixture(home='subcommand.ini') + def test_convert(self, fake_open): + 'test convert' + conf = FakeFile() + fake_open.return_value = conf + + get_profile() + + self.assertEquals(conf.getvalue(), '''[general] +profile = profile.current + +[obs.remotebuild] +url = https://api/build/server +user = Alice +passwdx = QlpoOTFBWSZTWYfNdxYAAAIBgAoAHAAgADDNAMNEA24u5IpwoSEPmu4s +base_prj = Main +target_prj = Target + +[repo.repo1] +url = https://repo1/path +user = Alice +passwdx = QlpoOTFBWSZTWYfNdxYAAAIBgAoAHAAgADDNAMNEA24u5IpwoSEPmu4s + +[repo.repo2] +url = https://repo2/path +user = Alice +passwdx = QlpoOTFBWSZTWYfNdxYAAAIBgAoAHAAgADDNAMNEA24u5IpwoSEPmu4s + +[repo.repo3] +url = /local/path/repo + +[profile.current] +obs = obs.remotebuild +repos = repo.repo1, repo.repo2, repo.repo3 + +''') + + + if __name__ == '__main__': diff --git a/tests/testdata/ini/home1.ini b/tests/testdata/ini/home1.ini index 39daae6..6a996f5 100644 --- a/tests/testdata/ini/home1.ini +++ b/tests/testdata/ini/home1.ini @@ -2,6 +2,6 @@ common_key = homev1 home_only_key = homev2 -[profile "rsa"] +[profile.rsa] common = homev3 home_only = homev4 diff --git a/tests/testdata/ini/local_repo.ini b/tests/testdata/ini/local_repo.ini deleted file mode 100644 index ffed290..0000000 --- a/tests/testdata/ini/local_repo.ini +++ /dev/null @@ -1,8 +0,0 @@ -[build] -repo1.url = https://repo1/path -repo1.user = Alice -repo1.passwdx = QlpoOTFBWSZTWYfNdxYAAAIBgAoAHAAgADDNAMNEA24u5IpwoSEPmu4s - -repo2.url = https://repo2/path -repo2.user = Alice -repo2.passwdx = QlpoOTFBWSZTWYfNdxYAAAIBgAoAHAAgADDNAMNEA24u5IpwoSEPmu4s diff --git a/tests/testdata/ini/no_such_profile_section_name.ini b/tests/testdata/ini/no_such_profile_section_name.ini index a51818e..d1622ee 100644 --- a/tests/testdata/ini/no_such_profile_section_name.ini +++ b/tests/testdata/ini/no_such_profile_section_name.ini @@ -1,2 +1,2 @@ [general] -profile = test \ No newline at end of file +profile = profile.test \ No newline at end of file diff --git a/tests/testdata/ini/profile.ini b/tests/testdata/ini/profile.ini index 54b8284..a646dc5 100644 --- a/tests/testdata/ini/profile.ini +++ b/tests/testdata/ini/profile.ini @@ -1,27 +1,29 @@ [general] -profile = tz +profile = profile.tz -[profile "tz"] +[profile.tz] user = Alice #passwd = secret passwdx = QlpoOTFBWSZTWYfNdxYAAAIBgAoAHAAgADDNAMNEA24u5IpwoSEPmu4s -repos = ia32_main, ia32_non-oss, ia32_base, local -api = tz +repos = repo.ia32_main, repo.ia32_non-oss, repo.ia32_base, repo.local +obs = obs.tz -[obs "tz"] +[obs.tz] url = https://api.tz/path +base_prj = base +target_prj = target -[repo ia32_main] +[repo.ia32_main] url = https://repo/ia32/main -[repo ia32_non-oss] +[repo.ia32_non-oss] url = https://repo/ia32/non-oss -[repo ia32_base] +[repo.ia32_base] url = https://repo/ia32/base user = Bob #passwd = classified passwdx = QlpoOTFBWSZTWRwZil4AAACBgC8kCAAgADEMCCAPKGaQLT4u5IpwoSA4MxS8 -[repo local] +[repo.local] url = /local/path \ No newline at end of file diff --git a/tests/testdata/ini/profile_only_has_api.ini b/tests/testdata/ini/profile_only_has_api.ini index 89035f0..5d3f6c9 100644 --- a/tests/testdata/ini/profile_only_has_api.ini +++ b/tests/testdata/ini/profile_only_has_api.ini @@ -1,13 +1,13 @@ [general] -profile = test +profile = profile.test -[profile "test"] +[profile.test] user = Alice #passwd = secret passwdx = QlpoOTFBWSZTWYfNdxYAAAIBgAoAHAAgADDNAMNEA24u5IpwoSEPmu4s -api = test_api +obs = obs.test_api -[obs "test_api"] +[obs.test_api] url = https://api.tz/path user = Bob #passwd = classified diff --git a/tests/testdata/ini/project1.ini b/tests/testdata/ini/project1.ini index 20eeb19..aec2410 100644 --- a/tests/testdata/ini/project1.ini +++ b/tests/testdata/ini/project1.ini @@ -2,6 +2,6 @@ common_key = projv1 proj_only_key = projv2 -[profile "rsa"] +[profile.rsa] common = projv3 proj_only = projv4 diff --git a/tests/testdata/ini/subcommand.ini b/tests/testdata/ini/subcommand.ini index 974abb3..ea0a9bb 100644 --- a/tests/testdata/ini/subcommand.ini +++ b/tests/testdata/ini/subcommand.ini @@ -3,6 +3,8 @@ build_server = https://api/build/server user = Alice #passwd = secret passwdx = QlpoOTFBWSZTWYfNdxYAAAIBgAoAHAAgADDNAMNEA24u5IpwoSEPmu4s +base_prj = Main +target_prj = Target [build] repo1.url = https://repo1/path