def setUp(self): self.name = 'test_fork' self.remote_name = 'origin' self.fork_yaml = {'name': self.name, 'remote': self.remote_name} self.source = Source(__github_source_yaml__) self.path = 'fork/path' self.fork = Fork(self.fork_yaml, self.path, self.source)
def setUp(self): self.name = 'test_fork' self.remote_name = 'origin' self.fork_yaml = {'name': self.name, 'remote': self.remote_name} self.source = Source(__github_ssh_source_yaml__) self.root_directory = self.cats_example_path self.path = 'fork/path' self.fork = Fork(self.fork_yaml, self.root_directory, self.path, self.source)
class ForkTest(unittest.TestCase): """fork 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.name = 'test_fork' self.remote_name = 'origin' self.fork_yaml = {'name': self.name, 'remote': self.remote_name} self.source = Source(__github_ssh_source_yaml__) self.root_directory = self.cats_example_path self.path = 'fork/path' self.fork = Fork(self.fork_yaml, self.root_directory, self.path, self.source) def test_get_yaml(self): """Test get_yaml() method""" self.assertEqual(self.fork.get_yaml(), self.fork_yaml) def test_member_variables(self): """Test the state of all project member variables initialized""" self.assertEqual(self.fork.root_directory, self.root_directory) self.assertEqual(self.fork.path, self.path) self.assertEqual(self.fork.name, self.name) self.assertEqual(self.fork.remote_name, self.remote_name) self.assertEqual(self.fork.url, self.source.get_url_prefix() + self.name + ".git")
def __init__(self, project, group, defaults, sources): """Project __init__ :param dict project: Parsed YAML python object for project :param dict group: Parsed YAML python object for group :param Defaults defaults: Defaults instance :param list[Source] sources: List of Source instances :raise ClowderYAMLError: """ self.name = project['name'] self.path = project['path'] self.ref = project.get('ref', group.get('ref', defaults.ref)) self.remote = project.get('remote', group.get('remote', defaults.remote)) self.depth = project.get('depth', group.get('depth', defaults.depth)) self.recursive = project.get( 'recursive', group.get('recursive', defaults.recursive)) self._protocol = defaults.protocol self._timestamp_author = project.get( 'timestamp_author', group.get('timestamp_author', defaults.timestamp_author)) self._print_output = True self.source = None source_name = project.get('source', group.get('source', defaults.source)) for source in sources: if source.name == source_name: self.source = source self.fork = None if 'fork' in project: fork = project['fork'] if fork['remote'] == self.remote: raise ClowderYAMLError( fmt.remote_name_error(fork['name'], self.name, self.remote)) self.fork = Fork(fork, self.path, self.source, self._protocol)
def __init__(self, root_directory, project, group, defaults, sources): self.name = project['name'] self.path = project['path'] self._root_directory = root_directory self._ref = project.get('ref', group.get('ref', defaults['ref'])) self._remote = project.get('remote', group.get('remote', defaults['remote'])) self._depth = project.get('depth', group.get('depth', defaults['depth'])) self._recursive = project.get( 'recursive', group.get('recursive', defaults.get('recursive', False))) self._timestamp_author = project.get( 'timestamp_author', group.get('timestamp_author', defaults.get('timestamp_author', None))) self._print_output = True self._source = None source_name = project.get('source', group.get('source', defaults['source'])) for source in sources: if source.name == source_name: self._source = source self._url = self._source.get_url_prefix() + self.name + ".git" self.fork = None if 'fork' in project: fork = project['fork'] if fork['remote'] == self._remote: error = fmt.remote_name_error(fork['name'], self.name, self._remote) print(fmt.invalid_yaml_error()) print(error + '\n') sys.exit(1) self.fork = Fork(fork, self._root_directory, self.path, self._source)
class Project(object): """clowder.yaml Project model class :ivar str name: Project name :ivar str path: Project relative path :ivar str ref: Default git ref :ivar str remote: Default remote name :ivar int depth: Depth to clone project repo :ivar bool recursive: Whether to recursively clone submodules :ivar Source source: Default source :ivar Fork fork: Project's associated Fork """ def __init__(self, project, group, defaults, sources): """Project __init__ :param dict project: Parsed YAML python object for project :param dict group: Parsed YAML python object for group :param Defaults defaults: Defaults instance :param list[Source] sources: List of Source instances :raise ClowderYAMLError: """ self.name = project['name'] self.path = project['path'] self.ref = project.get('ref', group.get('ref', defaults.ref)) self.remote = project.get('remote', group.get('remote', defaults.remote)) self.depth = project.get('depth', group.get('depth', defaults.depth)) self.recursive = project.get( 'recursive', group.get('recursive', defaults.recursive)) self._protocol = defaults.protocol self._timestamp_author = project.get( 'timestamp_author', group.get('timestamp_author', defaults.timestamp_author)) self._print_output = True self.source = None source_name = project.get('source', group.get('source', defaults.source)) for source in sources: if source.name == source_name: self.source = source self.fork = None if 'fork' in project: fork = project['fork'] if fork['remote'] == self.remote: raise ClowderYAMLError( fmt.remote_name_error(fork['name'], self.name, self.remote)) self.fork = Fork(fork, self.path, self.source, self._protocol) @project_repo_exists def branch(self, local=False, remote=False): """Print branches for project .. py:function:: branch(local=False, remote=False) :param Optional[bool] local: Print local branches :param Optional[bool] remote: Print remote branches """ repo = ProjectRepo(self.full_path(), self.remote, self.ref) if not is_offline() and remote: if self.fork is None: repo.fetch(self.remote, depth=self.depth) else: repo.fetch(self.fork.remote_name) repo.fetch(self.remote) repo.print_branches(local=local, remote=remote) @project_repo_exists def checkout(self, branch): """Checkout branch :param str branch: Branch to check out """ self._repo(self.full_path(), self.remote, self.ref, self.recursive).checkout(branch, allow_failure=True) @project_repo_exists def clean(self, args='', recursive=False): """Discard changes for project .. py:function:: clean(args='', recursive=False) :param Optional[str] args: Git clean options - ``d`` Remove untracked directories in addition to untracked files - ``f`` Delete directories with .git sub directory or file - ``X`` Remove only files ignored by git - ``x`` Remove all untracked files :param Optional[bool] recursive: Clean submodules recursively """ self._repo(self.full_path(), self.remote, self.ref, self.recursive and recursive).clean(args=args) @project_repo_exists def clean_all(self): """Discard all changes for project Equivalent to: ``git clean -ffdx; git reset --hard; git rebase --abort`` ``git submodule foreach --recursive git clean -ffdx`` ``git submodule foreach --recursive git reset --hard`` ``git submodule update --checkout --recursive --force`` """ self._repo(self.full_path(), self.remote, self.ref, self.recursive).clean(args='fdx') @project_repo_exists def diff(self): """Show git diff for project Equivalent to: ``git status -vv`` """ ProjectRepo(self.full_path(), self.remote, self.ref).status_verbose() def existing_branch(self, branch, is_remote): """Check if branch exists :param str branch: Branch to check for :param bool is_remote: Check for remote branch :return: True, if branch exists :rtype: bool """ repo = ProjectRepo(self.full_path(), self.remote, self.ref) if not is_remote: return repo.existing_local_branch(branch) remote = self.remote if self.fork is None else self.fork.remote_name return repo.existing_remote_branch(branch, remote) @project_repo_exists def fetch_all(self): """Fetch upstream changes if project exists on disk""" repo = ProjectRepo(self.full_path(), self.remote, self.ref) if self.fork is None: repo.fetch(self.remote, depth=self.depth) return repo.fetch(self.fork.remote_name) repo.fetch(self.remote) def formatted_project_path(self): """Return formatted project path :return: Formatted string of full file path :rtype: str """ repo = ProjectRepo(self.full_path(), self.remote, self.ref) return format_project_string(repo, self.path) def full_path(self): """Return full path to project :return: Project's full file path :rtype: str """ return os.path.join(ROOT_DIR, self.path) def get_current_timestamp(self): """Return timestamp of current HEAD commit :return: HEAD commit timestamp :rtype: str """ repo = ProjectRepo(self.full_path(), self.remote, self.ref) return repo.get_current_timestamp() def get_yaml(self, resolved=False): """Return python object representation for saving yaml .. py:function:: get_yaml(resolved=False) :param Optional[bool] resolved: Return default ref rather than current commit sha :return: YAML python object :rtype: dict """ if resolved: ref = self.ref else: repo = ProjectRepo(self.full_path(), self.remote, self.ref) ref = repo.sha() project = { 'name': self.name, 'path': self.path, 'depth': self.depth, 'recursive': self.recursive, 'ref': ref, 'remote': self.remote, 'source': self.source.name } if self.fork: fork_yaml = self.fork.get_yaml() project['fork'] = fork_yaml if self._timestamp_author: project['timestamp_author'] = self._timestamp_author return project def herd(self, **kwargs): """Clone project or update latest from upstream .. py:function:: herd(branch=None, tag=None, depth=0, rebase=False, parallel=False) Keyword Args: branch (str): Branch to attempt to herd tag (str): Tag to attempt to herd depth (int): Git clone depth. 0 indicates full clone, otherwise must be a positive integer rebase (bool): Whether to use rebase instead of pulling latest changes parallel (bool): Whether command is being run in parallel, affects output protocol (str): Git protocol ('ssh' or 'https') """ branch = kwargs.get('branch', None) tag = kwargs.get('tag', None) depth = kwargs.get('depth', None) rebase = kwargs.get('rebase', False) parallel = kwargs.get('parallel', False) protocol = kwargs.get('protocol', self._protocol) self._print_output = not parallel herd_depth = self.depth if depth is None else depth repo = self._repo(self.full_path(), self.remote, self.ref, self.recursive, parallel=parallel) if branch: fork_remote = None if self.fork is None else self.fork.remote_name self._run_herd_command('herd_branch', repo, protocol, branch, depth=herd_depth, rebase=rebase, fork_remote=fork_remote) return if tag: self._run_herd_command('herd_tag', repo, protocol, tag, depth=herd_depth, rebase=rebase) return self._run_herd_command('herd', repo, protocol, depth=herd_depth, rebase=rebase) def is_dirty(self): """Check if project is dirty :return: True, if dirty :rtype: bool """ return not self._repo(self.full_path(), self.remote, self.ref, self.recursive).validate_repo() def is_valid(self): """Validate status of project :return: True, if not dirty or if the project doesn't exist on disk :rtype: bool """ return ProjectRepo(self.full_path(), self.remote, self.ref).validate_repo() def print_validation(self): """Print validation message for project""" if not self.is_valid(): print(self.status()) repo = ProjectRepo(self.full_path(), self.remote, self.ref) print_validation(repo) @project_repo_exists def prune(self, branch, force=False, local=False, remote=False): """Prune branch .. py:function:: prune(branch, force=False, local=False, remote=False) :param str branch: Branch to prune :param Optional[bool] force: Force delete branch :param Optional[bool] local: Delete local branch :param Optional[bool] remote: Delete remote branch """ repo = ProjectRepo(self.full_path(), self.remote, self.ref) if local and repo.existing_local_branch(branch): repo.prune_branch_local(branch, force) if remote: git_remote = self.remote if self.fork is None else self.fork.remote_name if repo.existing_remote_branch(branch, git_remote): repo.prune_branch_remote(branch, git_remote) def reset(self, timestamp=None, parallel=False): """Reset project branch to upstream or checkout tag/sha as detached HEAD .. py:function:: reset(timestamp=None, parallel=False) :param Optional[str] timestamp: Reset to commit at timestamp, or closest previous commit :param Optional[bool] parallel: Whether command is being run in parallel, affects output """ self._print_output = not parallel repo = self._repo(self.full_path(), self.remote, self.ref, self.recursive, parallel=parallel) if self.fork is None: if timestamp: repo.reset_timestamp(timestamp, self._timestamp_author, self.ref) return repo.reset(depth=self.depth) return self._print(self.fork.status()) repo.configure_remotes(self.remote, self._url(), self.fork.remote_name, self.fork.url(self._protocol)) self._print(fmt.fork_string(self.name)) if timestamp: repo.reset_timestamp(timestamp, self._timestamp_author, self.ref) return repo.reset() def run(self, commands, ignore_errors, parallel=False): """Run commands or script in project directory .. py:function:: run(commands, ignore_errors, parallel=False) :param list[str] commands: Commands to run :param bool ignore_errors: Whether to exit if command returns a non-zero exit code :param Optional[bool] parallel: Whether commands are being run in parallel, affects output """ if not parallel and not existing_git_repository(self.full_path()): print(colored(" - Project is missing\n", 'red')) return self._print_output = not parallel forall_env = { 'CLOWDER_PATH': ROOT_DIR, 'PROJECT_PATH': self.full_path(), 'PROJECT_NAME': self.name, 'PROJECT_REMOTE': self.remote, 'PROJECT_REF': self.ref } if self.fork: forall_env['FORK_REMOTE'] = self.fork.remote_name for cmd in commands: self._run_forall_command(cmd, forall_env, ignore_errors, parallel) @project_repo_exists def start(self, branch, tracking): """Start a new feature branch :param str branch: Local branch name to create :param bool tracking: Whether to create a remote branch with tracking relationship """ remote = self.remote if self.fork is None else self.fork.remote_name depth = self.depth if self.fork is None else 0 repo = ProjectRepo(self.full_path(), self.remote, self.ref) repo.start(remote, branch, depth, tracking) def status(self, padding=None): """Return formatted status for project :param Optional[int] padding: Amount of padding to use for printing project on left and current ref on right :return: Formatting project name and status :rtype: str """ if not existing_git_repository(self.full_path()): return colored(self.name, 'green') repo = ProjectRepo(self.full_path(), self.remote, self.ref) project_output = format_project_string(repo, self.path) current_ref_output = format_project_ref_string(repo) if padding: project_output = project_output.ljust(padding) return project_output + ' ' + current_ref_output @project_repo_exists def stash(self): """Stash changes for project if dirty""" if self.is_dirty(): repo = ProjectRepo(self.full_path(), self.remote, self.ref) repo.stash() def sync(self, protocol, rebase=False, parallel=False): """Sync fork project with upstream remote .. py:function:: sync(rebase=False, parallel=False) :param Optional[bool] rebase: Whether to use rebase instead of pulling latest changes :param Optional[bool] parallel: Whether command is being run in parallel, affects output """ self._print_output = not parallel if protocol is None: protocol = self._protocol repo = self._repo(self.full_path(), self.remote, self.ref, self.recursive, parallel=parallel) self._run_herd_command('herd', repo, protocol, rebase=rebase) self._print(self.fork.status()) repo.sync(self.fork.remote_name, rebase=rebase) def _print(self, val): """Print output if self._print_output is True :param str val: String to print """ if self._print_output: print(val) @staticmethod def _repo(path, remote, ref, recursive, **kwargs): """Return ProjectRepo or ProjectRepoRecursive instance :param str path: Repo path :param str remote: Default repo remote :param str ref: Default repo ref :param bool recursive: Whether to handle submodules Keyword Args: parallel (bool): Whether command is being run in parallel print_output (bool): Whether to print output """ if recursive: return ProjectRepoRecursive(path, remote, ref, **kwargs) return ProjectRepo(path, remote, ref, **kwargs) def _run_forall_command(self, command, env, ignore_errors, parallel): """Run command or script in project directory :param str command: Command to run :param dict env: Environment variables :param bool ignore_errors: Whether to exit if command returns a non-zero exit code :param bool parallel: Whether command is being run in parallel, affects output Raises: ClowderError ClowderExit """ self._print(fmt.command(command)) try: execute_forall_command(command, self.full_path(), env, self._print_output) except ClowderError: if not ignore_errors: err = fmt.command_failed_error(command) self._print(err) if parallel: raise ClowderError(err) raise ClowderExit(1) def _run_herd_command(self, command, repo, protocol, *args, **kwargs): """Run herd command :param str command: Repo path :param ProjectRepo repo: ProjectRepo or ProjectRepoRecursive instance :param str protocol: Git protocol ('ssh' or 'https') Other Parameters: branch (str): Branch to attempt to herd tag (str): Tag to attempt to herd Keyword Args: depth (int): Git clone depth. 0 indicates full clone, otherwise must be a positive integer rebase (bool): Whether to use rebase instead of pulling latest changes fork_remote (str): Fork remote name """ if self.fork is None: getattr(repo, command)(self._url(protocol), *args, **kwargs) return self._print(self.fork.status()) repo.configure_remotes(self.remote, self._url(protocol), self.fork.remote_name, self.fork.url(protocol)) self._print(fmt.fork_string(self.name)) kwargs['depth'] = 0 getattr(repo, command)(self._url(protocol), *args, **kwargs) self._print(fmt.fork_string(self.fork.name)) frame = inspect.currentframe() vals = inspect.getargvalues(frame) branch_arg = [ a for a in vals.args if vals.locals[a] if vals.locals[a] == 'branch' ] branch = branch_arg[0] if branch_arg else None repo.herd_remote(self.fork.url(protocol), self.fork.remote_name, branch=branch) def _url(self, protocol=None): """Return project url :param Optional[str] protocol: Git protocol ('ssh' or 'https') """ if protocol: return git_url(protocol, self.source.url, self.name) return git_url(self._protocol, self.source.url, self.name)
class Project(object): """clowder.yaml project class""" def __init__(self, root_directory, project, group, defaults, sources): self.name = project['name'] self.path = project['path'] self._root_directory = root_directory self._ref = project.get('ref', group.get('ref', defaults['ref'])) self._remote = project.get('remote', group.get('remote', defaults['remote'])) self._depth = project.get('depth', group.get('depth', defaults['depth'])) self._recursive = project.get( 'recursive', group.get('recursive', defaults.get('recursive', False))) self._timestamp_author = project.get( 'timestamp_author', group.get('timestamp_author', defaults.get('timestamp_author', None))) self._print_output = True self._source = None source_name = project.get('source', group.get('source', defaults['source'])) for source in sources: if source.name == source_name: self._source = source self._url = self._source.get_url_prefix() + self.name + ".git" self.fork = None if 'fork' in project: fork = project['fork'] if fork['remote'] == self._remote: error = fmt.remote_name_error(fork['name'], self.name, self._remote) print(fmt.invalid_yaml_error()) print(error + '\n') sys.exit(1) self.fork = Fork(fork, self._root_directory, self.path, self._source) def branch(self, local=False, remote=False): """Print branches for project""" if not os.path.isdir(self.full_path()): print(colored(" - Project is missing\n", 'red')) return repo = ProjectRepo(self.full_path(), self._remote, self._ref) if not is_offline(): if remote: if self.fork is None: repo.fetch(self._remote, depth=self._depth) else: repo.fetch(self.fork.remote_name) repo.fetch(self._remote) repo.print_branches(local=local, remote=remote) def clean(self, args='', recursive=False): """Discard changes for project""" if not os.path.isdir(self.full_path()): print(colored(" - Project is missing\n", 'red')) return repo = self._repo(self.full_path(), self._remote, self._ref, self._recursive and recursive) repo.clean(args=args) def clean_all(self): """Discard all changes for project""" if not os.path.isdir(self.full_path()): print(colored(" - Project is missing\n", 'red')) return repo = self._repo(self.full_path(), self._remote, self._ref, self._recursive) repo.clean(args='fdx') def diff(self): """Show git diff for project""" if not os.path.isdir(self.full_path()): print(colored(" - Project is missing\n", 'red')) return repo = ProjectRepo(self.full_path(), self._remote, self._ref) repo.status_verbose() def exists(self): """Check if project exists on disk""" path = os.path.join(self.full_path()) return os.path.isdir(path) def existing_branch(self, branch, is_remote): """Check if branch exists""" repo = ProjectRepo(self.full_path(), self._remote, self._ref) if not is_remote: return repo.existing_local_branch(branch) rem = self._remote if self.fork is None else self.fork.remote_name return repo.existing_remote_branch(branch, rem) def fetch_all(self): """Fetch upstream changes if project exists on disk""" if not self.exists(): self.print_exists() return repo = ProjectRepo(self.full_path(), self._remote, self._ref) if self.fork is None: repo.fetch(self._remote, depth=self._depth) return repo.fetch(self.fork.remote_name) repo.fetch(self._remote) def formatted_project_path(self): """Return formatted project path""" repo_path = os.path.join(self._root_directory, self.path) return ProjectRepo.format_project_string(repo_path, self.path) def full_path(self): """Return full path to project""" return os.path.join(self._root_directory, self.path) def get_current_timestamp(self): """Clone project or update latest from upstream""" repo = ProjectRepo(self.full_path(), self._remote, self._ref) return repo.get_current_timestamp() def get_yaml(self, resolved=False): """Return python object representation for saving yaml""" if resolved: ref = self._ref else: repo = ProjectRepo(self.full_path(), self._remote, self._ref) ref = repo.sha() project = { 'name': self.name, 'path': self.path, 'depth': self._depth, 'recursive': self._recursive, 'ref': ref, 'remote': self._remote, 'source': self._source.name } if self.fork: fork_yaml = self.fork.get_yaml() project['fork'] = fork_yaml if self._timestamp_author: project['timestamp_author'] = self._timestamp_author return project def herd(self, branch=None, tag=None, depth=None, rebase=False, parallel=False): """Clone project or update latest from upstream""" self._print_output = not parallel herd_depth = depth if depth is not None else self._depth repo = self._repo(self.full_path(), self._remote, self._ref, self._recursive, parallel=parallel, print_output=self._print_output) if branch: self._herd_branch(repo, branch, herd_depth, rebase) return if tag: self._herd_tag(repo, tag, herd_depth, rebase) return self._herd_ref(repo, herd_depth, rebase) def is_dirty(self): """Check if project is dirty""" repo = self._repo(self.full_path(), self._remote, self._ref, self._recursive) return not repo.validate_repo() def is_valid(self): """Validate status of project""" repo = ProjectRepo(self.full_path(), self._remote, self._ref) return repo.validate_repo() def print_exists(self): """Print existence validation message for project""" if not self.exists(): print(self.status()) ProjectRepo.exists(self.full_path()) def print_validation(self): """Print validation message for project""" if not self.is_valid(): print(self.status()) ProjectRepo.validation(self.full_path()) def prune(self, branch, force=False, local=False, remote=False): """Prune branch""" if not ProjectRepo.existing_git_repository(self.full_path()): return if local and remote: self._prune_local(branch, force) self._prune_remote(branch) elif local: self._prune_local(branch, force) elif remote: self._prune_remote(branch) def reset(self, timestamp=None, parallel=False): """Reset project branches to upstream or checkout tag/sha as detached HEAD""" self._print_output = not parallel repo = self._repo(self.full_path(), self._remote, self._ref, self._recursive, parallel=parallel, print_output=self._print_output) self._reset(repo, timestamp=timestamp) def run(self, command, ignore_errors, parallel=False): """Run command or script in project directory""" if not parallel: if not os.path.isdir(self.full_path()): print(colored(" - Project is missing\n", 'red')) return self._print_output = not parallel self._print(fmt.command(command)) forall_env = { 'CLOWDER_PATH': self._root_directory, 'PROJECT_PATH': self.full_path(), 'PROJECT_NAME': self.name, 'PROJECT_REMOTE': self._remote, 'PROJECT_REF': self._ref } if self.fork: forall_env['FORK_REMOTE'] = self.fork.remote_name return_code = execute_forall_command(command.split(), self.full_path(), forall_env, self._print_output) if not ignore_errors: err = fmt.command_failed_error(command) if return_code != 0: self._print(err) self._exit(err, return_code=return_code, parallel=parallel) def start(self, branch, tracking): """Start a new feature branch""" if not ProjectRepo.existing_git_repository(self.full_path()): print(colored(" - Directory doesn't exist", 'red')) return remote = self._remote if self.fork is None else self.fork.remote_name depth = self._depth if self.fork is None else 0 repo = ProjectRepo(self.full_path(), self._remote, self._ref) repo.start(remote, branch, depth, tracking) def status(self, padding=None): """Return formatted status for project""" if not ProjectRepo.existing_git_repository(self.full_path()): print(colored(self.name, 'green')) return project_output = ProjectRepo.format_project_string( self.full_path(), self.path) current_ref_output = ProjectRepo.format_project_ref_string( self.full_path()) if padding: project_output = project_output.ljust(padding) return project_output + ' ' + current_ref_output def stash(self): """Stash changes for project if dirty""" if self.is_dirty(): repo = ProjectRepo(self.full_path(), self._remote, self._ref) repo.stash() def sync(self, rebase=False, parallel=False): """Sync fork project with upstream""" self._print_output = not parallel repo = self._repo(self.full_path(), self._remote, self._ref, self._recursive, parallel=parallel, print_output=self._print_output) self._sync(repo, rebase) @staticmethod def _exit(message, parallel=False, return_code=1): """Exit based on serial or parallel job""" if parallel: raise ClowderError(message) sys.exit(return_code) def _herd_branch(self, repo, branch, depth, rebase): """Clone project or update latest from upstream""" if self.fork is None: repo.herd_branch(self._url, branch, depth=depth, rebase=rebase) return self._print(self.fork.status()) repo.configure_remotes(self._remote, self._url, self.fork.remote_name, self.fork.url) self._print(fmt.fork_string(self.name)) repo.herd_branch(self._url, branch, rebase=rebase, fork_remote=self.fork.remote_name) self._print(fmt.fork_string(self.fork.name)) repo.herd_remote(self.fork.url, self.fork.remote_name, branch=branch) def _herd_ref(self, repo, depth, rebase): """Clone project or update latest from upstream""" if self.fork is None: repo.herd(self._url, depth=depth, rebase=rebase) return self._print(self.fork.status()) repo.configure_remotes(self._remote, self._url, self.fork.remote_name, self.fork.url) self._print(fmt.fork_string(self.name)) repo.herd(self._url, rebase=rebase) self._print(fmt.fork_string(self.fork.name)) repo.herd_remote(self.fork.url, self.fork.remote_name) def _herd_tag(self, repo, tag, depth, rebase): """Clone project or update latest from upstream""" if self.fork is None: repo.herd_tag(self._url, tag, depth=depth, rebase=rebase) return self._print(self.fork.status()) repo.configure_remotes(self._remote, self._url, self.fork.remote_name, self.fork.url) self._print(fmt.fork_string(self.name)) repo.herd_tag(self._url, tag, rebase=rebase) self._print(fmt.fork_string(self.fork.name)) repo.herd_remote(self.fork.url, self.fork.remote_name) def _print(self, val): """Print output if self._print_output is True""" if self._print_output: print(val) def _prune_local(self, branch, force): """Prune local branch""" repo = ProjectRepo(self.full_path(), self._remote, self._ref) if repo.existing_local_branch(branch): repo.prune_branch_local(branch, force) def _prune_remote(self, branch): """Prune remote branch""" remote = self._remote if self.fork is None else self.fork.remote_name repo = ProjectRepo(self.full_path(), remote, self._ref) if repo.existing_remote_branch(branch, remote): repo.prune_branch_remote(branch, remote) @staticmethod def _repo(path, remote, ref, recursive, **kwargs): """Clone project or update latest from upstream""" if recursive: return ProjectRepoRecursive(path, remote, ref, **kwargs) return ProjectRepo(path, remote, ref, **kwargs) def _reset(self, repo, timestamp=None): """Clone project or update latest from upstream""" if self.fork is None: if timestamp: repo.reset_timestamp(timestamp, self._timestamp_author, self._ref) return repo.reset(depth=self._depth) return self._print(self.fork.status()) repo.configure_remotes(self._remote, self._url, self.fork.remote_name, self.fork.url) self._print(fmt.fork_string(self.name)) if timestamp: repo.reset_timestamp(timestamp, self._timestamp_author, self._ref) return repo.reset() def _sync(self, repo, rebase): """Sync fork project with upstream""" self._print(self.fork.status()) repo.configure_remotes(self._remote, self._url, self.fork.remote_name, self.fork.url) self._print(fmt.fork_string(self.name)) repo.herd(self._url, self._remote, rebase=rebase) self._print(fmt.fork_string(self.fork.name)) repo.herd_remote(self.fork.url, self.fork.remote_name) self._print(self.fork.status()) repo.sync(self.fork.remote_name, rebase=rebase)