class ClowderRepoTest(unittest.TestCase): """clowder_repo test subclass""" current_file_path = os.path.dirname(os.path.realpath(__file__)) cats_example_path = os.path.abspath( os.path.join(current_file_path, '..', '..', 'examples', 'cats')) def setUp(self): self.clowder_repo = ClowderRepo() self.clowder_yaml_path = os.path.join(self.cats_example_path, 'clowder.yaml') def test_member_variables(self): """Test the state of all project member variables initialized""" clowder_path = os.path.join(self.cats_example_path, '.clowder') self.assertEqual(self.clowder_repo.clowder_path, clowder_path) def test_link(self): """Test link() method""" self.clowder_repo.link() self.assertEqual( os.readlink(self.clowder_yaml_path), os.path.join(self.cats_example_path, '.clowder', 'clowder.yaml')) def test_link_version(self): """Test link() method""" self.clowder_repo.link('v0.1') version_path = os.path.join('.clowder', 'versions', 'v0.1', 'clowder.yaml') self.assertEqual(os.readlink(self.clowder_yaml_path), os.path.join(self.cats_example_path, version_path))
class ClowderRepoTest(unittest.TestCase): """clowder_repo test subclass""" def setUp(self): self.clowder_repo = ClowderRepo(CATS_EXAMPLE_PATH) self.clowder_yaml_path = os.path.join(CATS_EXAMPLE_PATH, 'clowder.yaml') def test_member_variables(self): """Test the state of all project member variables initialized""" self.assertEqual(self.clowder_repo.root_directory, CATS_EXAMPLE_PATH) clowder_path = os.path.join(CATS_EXAMPLE_PATH, '.clowder') self.assertEqual(self.clowder_repo.clowder_path, clowder_path) def test_link(self): """Test link() method""" self.clowder_repo.link() self.assertEqual(os.readlink(self.clowder_yaml_path), os.path.join(CATS_EXAMPLE_PATH, '.clowder', 'clowder.yaml')) def test_link_version(self): """Test link() method""" self.clowder_repo.link('v0.1') version_path = os.path.join('.clowder', 'versions', 'v0.1', 'clowder.yaml') self.assertEqual(os.readlink(self.clowder_yaml_path), os.path.join(CATS_EXAMPLE_PATH, version_path))
class Command(object): """Command class for parsing commandline options""" def __init__(self): self.root_directory = os.getcwd() self.clowder = None self.clowder_repo = None self.versions = None self._invalid_yaml = False self._version = '2.4.0' clowder_path = os.path.join(self.root_directory, '.clowder') # Load current clowder.yaml config if it exists if os.path.isdir(clowder_path): clowder_symlink = os.path.join(self.root_directory, 'clowder.yaml') self.clowder_repo = ClowderRepo(self.root_directory) if not os.path.islink(clowder_symlink): print() clowder_output = colored('.clowder', 'green') print(clowder_output) self.clowder_repo.link() try: self.clowder = ClowderController(self.root_directory) self.versions = self.clowder.get_saved_version_names() except (ClowderError, KeyError) as err: self._invalid_yaml = True self._error = err except (KeyboardInterrupt, SystemExit): sys.exit(1) # clowder argparse setup command_description = 'Utility for managing multiple git repositories' parser = argparse.ArgumentParser( description=command_description, formatter_class=argparse.RawDescriptionHelpFormatter) configure_argparse(parser, self.clowder, self.versions) # Argcomplete and arguments parsing argcomplete.autocomplete(parser) # Register exit handler to display trailing newline self._display_trailing_newline = True atexit.register(self._exit_handler_formatter) if not self._invalid_yaml: print() self.args = parser.parse_args() self._display_trailing_newline = False # Check for unrecognized command if self.args.clowder_command is None or not hasattr( self, self.args.clowder_command): exit_unrecognized_command(parser) # use dispatch pattern to invoke method with same name getattr(self, self.args.clowder_command)() print() def branch(self): """clowder branch command""" self._validate_clowder_yaml() if self.clowder_repo is None: exit_clowder_not_found() self.clowder_repo.print_status() if self.clowder is None: sys.exit(1) if self.args.all: self.clowder.branch(group_names=self.args.groups, project_names=self.args.projects, skip=self.args.skip, local=True, remote=True) return if self.args.remote: self.clowder.branch(group_names=self.args.groups, project_names=self.args.projects, skip=self.args.skip, remote=True) return self.clowder.branch(group_names=self.args.groups, project_names=self.args.projects, skip=self.args.skip, local=True) def clean(self): """clowder clean command""" self._validate_clowder_yaml() if self.clowder_repo is None: exit_clowder_not_found() self.clowder_repo.print_status() if self.clowder is None: sys.exit(1) if self.args.all: self.clowder.clean_all(group_names=self.args.groups, project_names=self.args.projects, skip=self.args.skip) return clean_args = '' if self.args.d: clean_args += 'd' if self.args.f: clean_args += 'f' if self.args.X: clean_args += 'X' if self.args.x: clean_args += 'x' self.clowder.clean(group_names=self.args.groups, project_names=self.args.projects, skip=self.args.skip, args=clean_args, recursive=self.args.recursive) def diff(self): """clowder diff command""" self._validate_clowder_yaml() if self.clowder_repo is None: exit_clowder_not_found() self.clowder_repo.print_status() if self.clowder is None: sys.exit(1) self.clowder.diff(group_names=self.args.groups, project_names=self.args.projects) def forall(self): """clowder forall command""" self._validate_clowder_yaml() if self.clowder_repo is None: exit_clowder_not_found() self.clowder_repo.print_status() if self.clowder is None: sys.exit(1) self.clowder.forall(self.args.command[0], self.args.ignore_errors, group_names=self.args.groups, project_names=self.args.projects, skip=self.args.skip, parallel=self.args.parallel) def herd(self): """clowder herd command""" self._validate_clowder_yaml() if self.clowder_repo is None: exit_clowder_not_found() self.clowder_repo.print_status(fetch=True) if is_offline(): print(fmt.offline_error()) sys.exit(1) if self.clowder is None: sys.exit(1) branch = None if self.args.branch is None else self.args.branch[0] tag = None if self.args.tag is None else self.args.tag[0] depth = None if self.args.depth is None else self.args.depth[0] args = { 'group_names': self.args.groups, 'project_names': self.args.projects, 'skip': self.args.skip, 'branch': branch, 'tag': tag, 'depth': depth, 'rebase': self.args.rebase } if self.args.parallel: self.clowder.herd_parallel(**args) return self.clowder.herd(**args) def init(self): """clowder init command""" if self.clowder_repo: cprint('Clowder already initialized in this directory\n', 'red') sys.exit(1) if is_offline(): print(fmt.offline_error()) sys.exit(1) url_output = colored(self.args.url, 'green') print('Create clowder repo from ' + url_output + '\n') clowder_repo = ClowderRepo(self.root_directory) if self.args.branch is None: branch = 'master' else: branch = str(self.args.branch[0]) clowder_repo.init(self.args.url, branch) def link(self): """clowder link command""" if self.clowder_repo is None: exit_clowder_not_found() self.clowder_repo.print_status() if self.args.version is None: version = None else: version = self.args.version[0] self.clowder_repo.link(version) def prune(self): """clowder prune command""" self._validate_clowder_yaml() if self.clowder_repo is None: exit_clowder_not_found() self.clowder_repo.print_status() if self.clowder is None: sys.exit(1) if self.args.all: if is_offline(): print(fmt.offline_error()) sys.exit(1) self.clowder.prune(self.args.groups, self.args.branch, project_names=self.args.projects, skip=self.args.skip, force=self.args.force, local=True, remote=True) return if self.args.remote: if is_offline(): print(fmt.offline_error()) sys.exit(1) self.clowder.prune(self.args.groups, self.args.branch, project_names=self.args.projects, skip=self.args.skip, remote=True) return self.clowder.prune(self.args.groups, self.args.branch, project_names=self.args.projects, skip=self.args.skip, force=self.args.force, local=True) def repo(self): """clowder repo command""" if self.clowder_repo is None: exit_clowder_not_found() repo_command = 'repo_' + self.args.repo_command getattr(self, repo_command)() def repo_add(self): """clowder repo add command""" if self.clowder_repo is None: exit_clowder_not_found() self.clowder_repo.print_status() self.clowder_repo.add(self.args.files) def repo_checkout(self): """clowder repo checkout command""" if self.clowder_repo is None: exit_clowder_not_found() self.clowder_repo.print_status(fetch=True) self.clowder_repo.checkout(self.args.ref[0]) def repo_clean(self): """clowder repo clean command""" if self.clowder_repo is None: exit_clowder_not_found() self.clowder_repo.print_status() self.clowder_repo.clean() def repo_commit(self): """clowder repo commit command""" if self.clowder_repo is None: exit_clowder_not_found() self.clowder_repo.print_status() self.clowder_repo.commit(self.args.message[0]) def repo_pull(self): """clowder repo pull command""" if self.clowder_repo is None: exit_clowder_not_found() if is_offline(): print(fmt.offline_error()) sys.exit(1) self.clowder_repo.print_status(fetch=True) self.clowder_repo.pull() def repo_push(self): """clowder repo push command""" if self.clowder_repo is None: exit_clowder_not_found() if is_offline(): print(fmt.offline_error()) sys.exit(1) self.clowder_repo.print_status(fetch=True) self.clowder_repo.push() def repo_run(self): """clowder repo run command""" if self.clowder_repo is None: exit_clowder_not_found() self.clowder_repo.print_status() self.clowder_repo.run_command(self.args.command[0]) def repo_status(self): """clowder repo status command""" if self.clowder_repo is None: exit_clowder_not_found() self.clowder_repo.print_status() self.clowder_repo.git_status() def reset(self): """clowder reset command""" self._validate_clowder_yaml() if self.clowder_repo is None: exit_clowder_not_found() self.clowder_repo.print_status(fetch=True) if is_offline(): print(fmt.offline_error()) sys.exit(1) if self.clowder is None: sys.exit(1) timestamp_project = None if self.args.timestamp: timestamp_project = self.args.timestamp[0] self.clowder.reset(group_names=self.args.groups, project_names=self.args.projects, skip=self.args.skip, timestamp_project=timestamp_project, parallel=self.args.parallel) def save(self): """clowder save command""" if self.clowder_repo is None: exit_clowder_not_found() if self.clowder is None: sys.exit(1) if self.args.version.lower() == 'default': print(fmt.save_default_error(self.args.version)) sys.exit(1) self.clowder_repo.print_status() self.clowder.save_version(self.args.version) def start(self): """clowder start command""" self._validate_clowder_yaml() if self.clowder_repo is None: exit_clowder_not_found() self.clowder_repo.print_status() if self.clowder is None: sys.exit(1) if self.args.tracking: if is_offline(): print(fmt.offline_error()) sys.exit(1) if self.args.projects is None: self.clowder.start_groups(self.args.groups, self.args.skip, self.args.branch, self.args.tracking) else: self.clowder.start_projects(self.args.projects, self.args.skip, self.args.branch, self.args.tracking) def stash(self): """clowder stash command""" self._validate_clowder_yaml() if self.clowder_repo is None: exit_clowder_not_found() self.clowder_repo.print_status() if self.clowder is None: sys.exit(1) self.clowder.stash(group_names=self.args.groups, project_names=self.args.projects, skip=self.args.skip) def status(self): """clowder status command""" self._validate_clowder_yaml() if self.clowder_repo is None: exit_clowder_not_found() self.clowder_repo.print_status(fetch=self.args.fetch) if self.clowder is None: sys.exit(1) if self.args.fetch: if is_offline(): print(fmt.offline_error()) sys.exit(1) print(' - Fetch upstream changes for projects\n') self.clowder.fetch(self.clowder.get_all_group_names()) all_project_paths = self.clowder.get_all_project_paths() padding = len(max(all_project_paths, key=len)) self.clowder.status(self.clowder.get_all_group_names(), padding) def sync(self): """clowder sync command""" self._validate_clowder_yaml() if self.clowder_repo is None: exit_clowder_not_found() self.clowder_repo.print_status(fetch=True) if self.clowder is None: sys.exit(1) if is_offline(): print(fmt.offline_error()) sys.exit(1) all_fork_projects = self.clowder.get_all_fork_project_names() if all_fork_projects == '': cprint(' - No forks to sync\n', 'red') sys.exit() self.clowder.sync(all_fork_projects, rebase=self.args.rebase, parallel=self.args.parallel) def version(self): """clowder version command""" print('clowder version ' + self._version + '\n') sys.exit() def yaml(self): """clowder yaml command""" if self.clowder_repo is None: exit_clowder_not_found() self.clowder_repo.print_status() if self._invalid_yaml: sys.exit(1) self.clowder.print_yaml(self.args.resolved) def _exit_handler_formatter(self): """Exit handler to display trailing newline""" if self._display_trailing_newline: print() def _validate_clowder_yaml(self): """Print invalid yaml message and exit if invalid""" if self._invalid_yaml: print(fmt.invalid_yaml_error()) print(fmt.error(self._error)) sys.exit(1)
class Command(object): """Command class for parsing commandline options""" def __init__(self): self.root_directory = os.getcwd() self.clowder = None self.clowder_repo = None self.versions = None self.group_names = '' self.project_names = '' # Load current clowder.yml config if it exists clowder_path = os.path.join(self.root_directory, '.clowder') if os.path.isdir(clowder_path): clowder_symlink = os.path.join(self.root_directory, 'clowder.yaml') self.clowder_repo = ClowderRepo(self.root_directory) if not os.path.islink(clowder_symlink): print('') clowder_output = colored('.clowder', 'green') print(clowder_output) self.clowder_repo.link() self.clowder = ClowderController(self.root_directory) self.versions = self.clowder.get_saved_version_names() if self.clowder.get_all_group_names() is not None: self.group_names = self.clowder.get_all_group_names() if self.clowder.get_all_project_names() is not None: self.project_names = self.clowder.get_all_project_names() # clowder argparse setup command_description = 'Utility for managing multiple git repositories' parser = argparse.ArgumentParser(description=command_description) parser.add_argument('--version', '-v', action='store_true', dest='clowder_version', help='Print clowder version') subparsers = parser.add_subparsers(dest='command') self._configure_subparser_clean(subparsers) self._configure_subparser_forall(subparsers) self._configure_subparser_herd(subparsers) self._configure_subparser_init(subparsers) self._configure_subparser_link(subparsers) self._configure_subparser_prune(subparsers) self._configure_subparser_repo(subparsers) self._configure_subparser_save(subparsers) self._configure_subparser_start(subparsers) self._configure_subparser_stash(subparsers) self._configure_subparser_status(subparsers) # Argcomplete and arguments parsing argcomplete.autocomplete(parser) self.args = parser.parse_args() if self.args.clowder_version: print('clowder version 1.1.2') sys.exit(0) print('') if self.args.command is None or not hasattr(self, self.args.command): exit_unrecognized_command(parser) # use dispatch pattern to invoke method with same name getattr(self, self.args.command)() print('') def clean(self): """clowder clean command""" if self.clowder_repo is not None: self.clowder_repo.print_status() print('') if self.args.projects is None: self.clowder.clean_groups(self.args.groups) else: self.clowder.clean_projects(self.args.projects) else: exit_clowder_not_found() def forall(self): """clowder forall command""" if self.clowder_repo is not None: self.clowder_repo.print_status() print('') if self.args.projects is None: if self.args.cmd is not None: self.clowder.forall_groups_command(self.args.cmd[0], self.args.groups) else: self.clowder.forall_groups_file(self.args.file[0], self.args.groups) else: if self.args.cmd is not None: self.clowder.forall_projects_command(self.args.cmd[0], self.args.projects) else: self.clowder.forall_projects_file(self.args.file[0], self.args.projects) else: exit_clowder_not_found() def herd(self): """clowder herd command""" if self.clowder_repo is not None: self.clowder_repo.print_status() print('') if self.args.branch is None: ref = None else: ref = self.args.branch[0] if self.args.depth is None: depth = None else: depth = self.args.depth[0] if self.args.projects is None: if self.args.groups is None: self.clowder.herd_groups(self.clowder.get_all_group_names(), ref, depth) else: self.clowder.herd_groups(self.args.groups, ref, depth) else: self.clowder.herd_projects(self.args.projects, ref, depth) else: exit_clowder_not_found() def init(self): """clowder init command""" if self.clowder_repo is None: url_output = colored(self.args.url, 'green') print('Create clowder repo from ' + url_output) print('') clowder_repo = ClowderRepo(self.root_directory) clowder_repo.init(self.args.url, self.args.branch) else: cprint('Clowder already initialized in this directory', 'red') print('') sys.exit() def link(self): """clowder link command""" self.clowder_repo.print_status() if self.clowder_repo is not None: if self.args.version is None: version = None else: version = self.args.version[0] self.clowder_repo.link(version) else: exit_clowder_not_found() def prune(self): """clowder prune command""" if self.clowder_repo is not None: self.clowder_repo.print_status() print('') if self.args.projects is None: self.clowder.prune_groups(self.args.groups, self.args.branch, self.args.remote) else: self.clowder.prune_projects(self.args.projects, self.args.branch, self.args.remote) else: exit_clowder_not_found() def repo(self): """clowder repo command""" if self.clowder_repo is not None: self.clowder_repo.print_status() repo_command = 'repo_' + self.args.repo_command getattr(self, repo_command)() else: exit_clowder_not_found() def repo_add(self): """clowder repo add command""" if self.clowder_repo is not None: self.clowder_repo.add(self.args.files) else: exit_clowder_not_found() def repo_checkout(self): """clowder repo checkout command""" if self.clowder_repo is not None: self.clowder_repo.checkout(self.args.ref[0]) else: exit_clowder_not_found() def repo_clean(self): """clowder repo clean command""" if self.clowder_repo is not None: self.clowder_repo.clean() else: exit_clowder_not_found() def repo_commit(self): """clowder repo commit command""" if self.clowder_repo is not None: self.clowder_repo.commit(self.args.message[0]) else: exit_clowder_not_found() def repo_pull(self): """clowder repo pull command""" if self.clowder_repo is not None: self.clowder_repo.pull() else: exit_clowder_not_found() def repo_push(self): """clowder repo push command""" if self.clowder_repo is not None: self.clowder_repo.push() else: exit_clowder_not_found() def repo_run(self): """clowder repo run command""" if self.clowder_repo is not None: self.clowder_repo.run_command(self.args.cmd[0]) else: exit_clowder_not_found() def repo_status(self): """clowder repo status command""" if self.clowder_repo is not None: self.clowder_repo.status() else: exit_clowder_not_found() def save(self): """clowder save command""" if self.clowder_repo is not None: self.clowder.save_version(self.args.version) else: exit_clowder_not_found() def start(self): """clowder start command""" if self.clowder_repo is not None: self.clowder_repo.print_status() print('') if self.args.projects is None: self.clowder.start_groups(self.args.groups, self.args.branch) else: self.clowder.start_projects(self.args.projects, self.args.branch) else: exit_clowder_not_found() def stash(self): """clowder stash command""" if self.clowder_repo is not None: self.clowder_repo.print_status() print('') if self.args.projects is None: self.clowder.stash_groups(self.args.groups) else: self.clowder.stash_projects(self.args.projects) else: exit_clowder_not_found() def status(self): """clowder status command""" if self.clowder_repo is not None: self.clowder_repo.print_status() print('') if self.args.fetch: print(' - Fetching upstream changes for projects', end="", flush=True) timer = RepeatedTimer(1, self._print_progress) if self.args.projects is None: self.clowder.fetch_groups(self.args.groups) else: self.clowder.fetch_projects(self.args.projects) timer.stop() print('\n') if self.args.projects is None: self.clowder.status_groups(self.args.groups, self.args.verbose) else: self.clowder.status_projects(self.args.projects, self.args.verbose) else: exit_clowder_not_found() # Disable errors shown by pylint for too many local variables # pylint: disable=R0201 def _configure_subparser_clean(self, subparsers): """Configure clowder clean subparser and arguments""" # clowder clean clean_help = 'Discard current changes in all projects' parser_clean = subparsers.add_parser('clean', help=clean_help) group_clean = parser_clean.add_mutually_exclusive_group() group_clean.add_argument('--groups', '-g', choices=self.group_names, default=self.group_names, nargs='+', help='Groups to clean') group_clean.add_argument('--projects', '-p', choices=self.project_names, nargs='+', help='Projects to clean') def _configure_subparser_forall(self, subparsers): """Configure clowder forall subparser and arguments""" # clowder forall forall_help = 'Run command in project directories' parser_forall = subparsers.add_parser('forall', help=forall_help) group_forall_command = parser_forall.add_mutually_exclusive_group() group_forall_command.add_argument('--cmd', '-c', nargs=1, help='Command to run in project directories') group_forall_command.add_argument('--file', '-f', nargs=1, help='Script to run') group_forall_targets = parser_forall.add_mutually_exclusive_group() group_forall_targets.add_argument('--groups', '-g', choices=self.group_names, default=self.group_names, nargs='+', help='Groups to run command for') group_forall_targets.add_argument('--projects', '-p', choices=self.project_names, nargs='+', help='Projects to run command for') def _configure_subparser_herd(self, subparsers): """Configure clowder herd subparser and arguments""" # clowder herd herd_help = 'Clone and sync latest changes for projects' parser_herd = subparsers.add_parser('herd', help=herd_help) parser_herd.add_argument('--depth', '-d', default=None, type=int, nargs=1, help='Depth to herd') parser_herd.add_argument('--branch', '-b', nargs=1, default=None, help='Branch to herd') group_herd = parser_herd.add_mutually_exclusive_group() group_herd.add_argument('--groups', '-g', choices=self.group_names, default=self.group_names, nargs='+', help='Groups to herd') group_herd.add_argument('--projects', '-p', choices=self.project_names, nargs='+', help='Projects to herd') def _configure_subparser_init(self, subparsers): """Configure clowder init subparser and arguments""" # clowder init init_help = 'Clone repository to clowder directory and create clowder.yaml symlink' parser_init = subparsers.add_parser('init', help=init_help) parser_init.add_argument('url', help='URL of repo containing clowder.yaml') parser_init.add_argument('--branch', '-b', default='master', nargs='?', help='Branch of repo containing clowder.yaml') def _configure_subparser_link(self, subparsers): """Configure clowder link subparser and arguments""" # clowder link parser_link = subparsers.add_parser('link', help='Symlink clowder.yaml version') parser_link.add_argument('--version', '-v', choices=self.versions, nargs=1, default=None, help='Version name to symlink') def _configure_subparser_prune(self, subparsers): """Configure clowder prune subparser and arguments""" # clowder prune parser_prune = subparsers.add_parser('prune', help='Prune old branch') parser_prune.add_argument('branch', help='Name of branch to remove') parser_prune.add_argument('--remote', '-r', action='store_true', help='Prune remote branches') group_prune = parser_prune.add_mutually_exclusive_group() group_prune.add_argument('--groups', '-g', choices=self.group_names, default=self.group_names, nargs='+', help='Groups to prune branch for') group_prune.add_argument('--projects', '-p', choices=self.project_names, nargs='+', help='Projects to prune branch for') def _configure_subparser_repo(self, subparsers): """Configure clowder repo subparser and arguments""" # clowder repo parser_repo = subparsers.add_parser('repo', help='Manage clowder repo') repo_subparsers = parser_repo.add_subparsers(dest='repo_command') # clowder repo add repo_add_help = 'Add files in clowder repo' parser_repo_add = repo_subparsers.add_parser('add', help=repo_add_help) parser_repo_add.add_argument('files', nargs='+', help='Files to add') # clowder repo checkout repo_checkout_help = 'Checkout ref in clowder repo' parser_repo_checkout = repo_subparsers.add_parser('checkout', help=repo_checkout_help) parser_repo_checkout.add_argument('ref', nargs=1, help='Git ref to checkout') # clowder repo clean repo_clean_help = 'Discard changes in clowder repo' repo_subparsers.add_parser('clean', help=repo_clean_help) # clowder repo commit repo_commit_help = 'Commit current changes in clowder repo yaml files' parser_repo_commit = repo_subparsers.add_parser('commit', help=repo_commit_help) parser_repo_commit.add_argument('message', nargs=1, help='Commit message') # clowder repo run repo_run_help = 'Run command in clowder repo' parser_repo_run = repo_subparsers.add_parser('run', help=repo_run_help) repo_run_command_help = 'Command to run in clowder repo directory' parser_repo_run.add_argument('cmd', nargs=1, help=repo_run_command_help) # clowder repo pull repo_pull_help = 'Pull upstream changes in clowder repo' repo_subparsers.add_parser('pull', help=repo_pull_help) # clowder repo push repo_subparsers.add_parser('push', help='Push changes in clowder repo') # clowder repo status repo_subparsers.add_parser('status', help='Print clowder repo git status') def _configure_subparser_save(self, subparsers): """Configure clowder save subparser and arguments""" # clowder save save_help = 'Create version of clowder.yaml for current repos' parser_save = subparsers.add_parser('save', help=save_help) parser_save.add_argument('version', help='Version name to save') def _configure_subparser_start(self, subparsers): """Configure clowder start subparser and arguments""" # clowder start parser_start = subparsers.add_parser('start', help='Start a new feature') parser_start.add_argument('branch', help='Name of branch to create') group_start = parser_start.add_mutually_exclusive_group() group_start.add_argument('--groups', '-g', choices=self.group_names, default=self.group_names, nargs='+', help='Groups to start feature for') group_start.add_argument('--projects', '-p', choices=self.project_names, nargs='+', help='Projects to start feature for') def _configure_subparser_stash(self, subparsers): """Configure clowder stash subparser and arguments""" # clowder stash parser_stash = subparsers.add_parser('stash', help='Stash current changes') group_stash = parser_stash.add_mutually_exclusive_group() group_stash.add_argument('--groups', '-g', choices=self.group_names, default=self.group_names, nargs='+', help='Groups to stash') group_stash.add_argument('--projects', '-p', choices=self.project_names, nargs='+', help='Projects to stash') def _configure_subparser_status(self, subparsers): """Configure clowder status subparser and arguments""" # clowder status parser_status = subparsers.add_parser('status', help='Print project status') parser_status.add_argument('--fetch', '-f', action='store_true', help='Fetch projects before printing status') parser_status.add_argument('--verbose', '-v', action='store_true', help='Print detailed diff status') group_status = parser_status.add_mutually_exclusive_group() group_status.add_argument('--groups', '-g', choices=self.group_names, default=self.group_names, nargs='+', help='Groups to print status for') group_status.add_argument('--projects', '-p', choices=self.project_names, nargs='+', help='Projects to print status for') def _print_progress(self): print('.', end="", flush=True)