Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
18 changes: 15 additions & 3 deletions manic/checkout.py
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@
from manic.externals_description import read_externals_description_file
from manic.externals_status import check_safe_to_update_repos
from manic.sourcetree import SourceTree
from manic.utils import printlog
from manic.utils import printlog, fatal_error
from manic.global_constants import VERSION_SEPERATOR, LOG_FILE_NAME

if sys.hexversion < 0x02070000:
Expand Down Expand Up @@ -243,6 +243,10 @@ def commandline_arguments(args=None):
#
# user options
#
parser.add_argument("components", nargs="*",
help="Specific component(s) to checkout. By default"
"all required externals are checked out.")

parser.add_argument('-e', '--externals', nargs='?',
default='Externals.cfg',
help='The externals description filename. '
Expand Down Expand Up @@ -316,7 +320,12 @@ def main(args):

root_dir = os.path.abspath(os.getcwd())
external_data = read_externals_description_file(root_dir, args.externals)
external = create_externals_description(external_data)
external = create_externals_description(external_data, components=args.components)

for comp in args.components:
if comp not in external.keys():
fatal_error("No component {} found in {}".format(comp, args.externals))


source_tree = SourceTree(root_dir, external)
printlog('Checking status of externals: ', end='')
Expand Down Expand Up @@ -354,7 +363,10 @@ def main(args):
printlog(msg)
printlog('-' * 70)
else:
source_tree.checkout(args.verbose, load_all)
if not args.components:
source_tree.checkout(args.verbose, load_all)
for comp in args.components:
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I know this does the right thing but I feel adding an else before the loop would clarify that only one of these actions is intended to be taken.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actually I originally had it that way and pylint flagged it as "too many branches."

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

pylint needs to get a life 👎 (but okay to leave it as is).

source_tree.checkout(args.verbose, load_all, load_comp=comp)
printlog('')

logging.info('%s completed without exceptions.', program_name)
Expand Down
23 changes: 15 additions & 8 deletions manic/externals_description.py
Original file line number Diff line number Diff line change
Expand Up @@ -91,16 +91,16 @@ def read_externals_description_file(root_dir, file_name):
return externals_description


def create_externals_description(model_data, model_format='cfg'):
def create_externals_description(model_data, model_format='cfg', components=None):
"""Create the a externals description object from the provided data
"""
externals_description = None
if model_format == 'dict':
externals_description = ExternalsDescriptionDict(model_data, )
externals_description = ExternalsDescriptionDict(model_data, components=components)
elif model_format == 'cfg':
major, _, _ = get_cfg_schema_version(model_data)
if major == 1:
externals_description = ExternalsDescriptionConfigV1(model_data)
externals_description = ExternalsDescriptionConfigV1(model_data, components=components)
else:
msg = ('Externals description file has unsupported schema '
'version "{0}".'.format(major))
Expand Down Expand Up @@ -419,7 +419,7 @@ class ExternalsDescriptionDict(ExternalsDescription):

"""

def __init__(self, model_data):
def __init__(self, model_data, components=None):
"""Parse a native dictionary into a externals description.
"""
ExternalsDescription.__init__(self)
Expand All @@ -430,6 +430,11 @@ def __init__(self, model_data):
self._input_minor = 0
self._input_patch = 0
self._verify_schema_version()
if components:
for k in model_data.items():
if k not in components:
del model_data[k]

self.update(model_data)
self._check_user_input()

Expand All @@ -440,8 +445,8 @@ class ExternalsDescriptionConfigV1(ExternalsDescription):

"""

def __init__(self, model_data):
"""Convert the xml into a standardized dict that can be used to
def __init__(self, model_data, components=None):
"""Convert the config data into a standardized dict that can be used to
construct the source objects

"""
Expand All @@ -453,7 +458,7 @@ def __init__(self, model_data):
get_cfg_schema_version(model_data)
self._verify_schema_version()
self._remove_metadata(model_data)
self._parse_cfg(model_data)
self._parse_cfg(model_data, components=components)
self._check_user_input()

@staticmethod
Expand All @@ -465,7 +470,7 @@ def _remove_metadata(model_data):
"""
model_data.remove_section(DESCRIPTION_SECTION)

def _parse_cfg(self, cfg_data):
def _parse_cfg(self, cfg_data, components=None):
"""Parse a config_parser object into a externals description.
"""
def list_to_dict(input_list, convert_to_lower_case=True):
Expand All @@ -482,6 +487,8 @@ def list_to_dict(input_list, convert_to_lower_case=True):

for section in cfg_data.sections():
name = config_string_cleaner(section.lower().strip())
if components and name not in components:
continue
self[name] = {}
self[name].update(list_to_dict(cfg_data.items(section)))
self[name][self.REPO] = {}
Expand Down
2 changes: 1 addition & 1 deletion manic/sourcetree.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@

class _External(object):
"""
_External represents an external object in side a SourceTree
_External represents an external object inside a SourceTree
"""

# pylint: disable=R0902
Expand Down
44 changes: 44 additions & 0 deletions test/test_sys_checkout.py
Original file line number Diff line number Diff line change
Expand Up @@ -752,6 +752,18 @@ def _check_container_full_pre_checkout(self, overall, tree):
self._check_simple_opt_empty(tree)
self._check_mixed_ext_branch_required_pre_checkout(overall, tree)

def _check_container_component_post_checkout(self, overall, tree):
self.assertEqual(overall, 0)
self._check_simple_opt_ok(tree)
self._check_simple_tag_empty(tree)
self._check_simple_branch_empty(tree)

def _check_container_component_post_checkout2(self, overall, tree):
self.assertEqual(overall, 0)
self._check_simple_opt_ok(tree)
self._check_simple_tag_empty(tree)
self._check_simple_branch_ok(tree)

def _check_container_full_post_checkout(self, overall, tree):
self.assertEqual(overall, 0)
self._check_simple_tag_ok(tree)
Expand Down Expand Up @@ -1219,6 +1231,38 @@ def test_container_full(self):
self.status_args)
self._check_container_full_post_checkout(overall, tree)

def test_container_component(self):
"""Verify that optional component checkout works
"""
# create the test repository
under_test_dir = self.setup_test_repo(CONTAINER_REPO_NAME)

# create the top level externals file
self._generator.container_full(under_test_dir)

# inital checkout, first try a nonexistant component argument noref
checkout_args = ['simp_opt', 'noref']
checkout_args.extend(self.checkout_args)

with self.assertRaises(RuntimeError):
self.execute_cmd_in_dir(under_test_dir, checkout_args)

checkout_args = ['simp_opt']
checkout_args.extend(self.checkout_args)

overall, tree = self.execute_cmd_in_dir(under_test_dir,
checkout_args)

overall, tree = self.execute_cmd_in_dir(under_test_dir,
self.status_args)
self._check_container_component_post_checkout(overall, tree)
checkout_args.append('simp_branch')
overall, tree = self.execute_cmd_in_dir(under_test_dir,
checkout_args)
overall, tree = self.execute_cmd_in_dir(under_test_dir,
self.status_args)
self._check_container_component_post_checkout2(overall, tree)

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is a good test but it seems that adding tests is not part of our standard PR template. Still, adding a mention of the new test is a good idea (IMHO).

def test_mixed_simple(self):
"""Verify that a mixed use repo can serve as a 'full' container,
pulling in a set of externals and a seperate set of sub-externals.
Expand Down