def process(self, index: int, count: int, repo: Repo) -> Outcome: # Note: # When self.parallel is True, this task will be run in parallel with # other tasks. # # So we need to: # * capture the output of the commands we are running # * raise a DetailedCommandError instance if a command's return code # is 0 (because otherwise the output of the command is lost) # # When self.parallel is False, we don't capture the output, which means we # can't raise a DetailedCommandError. Instead, we raise a plain CommandError # instance. This means the user has to go up in the output to see the output # of the command that failed, but also that the command output are printed # in real time. full_path = self.workspace_path / repo.dest if not full_path.exists(): raise MissingRepo(repo.dest) # fmt: off self.info(ui.brown, self.workspace_path / repo.dest, " ", ui.lightgray, "$ ", ui.reset, self.description, sep="") # fmt: on full_path = self.workspace_path / repo.dest run_env = self.env_setter.get_env_for_repo(repo) run_env.update(os.environ) try: kwargs: Dict[str, Any] = { "cwd": full_path, "shell": self.shell, "env": run_env, } if self.parallel: kwargs["stdout"] = subprocess.PIPE kwargs["stderr"] = subprocess.STDOUT process = subprocess.run(self.command, **kwargs, universal_newlines=True) except OSError as e: raise CouldNotStartProcess("Error when starting process:", e) if process.returncode != 0: if self.parallel: raise DetailedCommandError( working_path=full_path, cmd=self.description, rc=process.returncode, output=process.stdout, ) else: raise CommandError() return Outcome.empty()
def process(self, index: int, count: int, repo: Repo) -> Outcome: # We just need to compute a summary here with the log between # self.from_ref and self.to_ref # # Note: make sure that when there is no diff between # self.from_ref and self.to_ref, the summary is empty, # so that the repo is not shown by OutcomeCollection.print_summary() repo_path = self.workspace_path / repo.dest if not repo_path.exists(): raise MissingRepo(repo.dest) # The main reason for the `git log` command to fail is if `self.from_ref` or # `self.to_ref` references are not found for the repo, so check for this case # explicitly rc, _ = run_git_captured(repo_path, "rev-parse", self.from_ref, check=False) if rc != 0: raise Error(f"{self.from_ref} not found") rc, _ = run_git_captured(repo_path, "rev-parse", self.to_ref, check=False) if rc != 0: raise Error(f"{self.to_ref} not found") colors = ["green", "reset", "yellow", "reset", "bold blue", "reset"] log_format = "%m {}%h{} - {}%d{} %s {}<%an>{}" log_format = log_format.format(*("%C({})".format(x) for x in colors)) cmd = [ "log", "--color=always", f"--pretty=format:{log_format}", f"{self.from_ref}...{self.to_ref}", ] rc, out = run_git_captured(repo_path, *cmd, check=True) if out: lines = [repo.dest, "-" * len(repo.dest), out] return Outcome.from_lines(lines) else: return Outcome.empty()
def process(self, index: int, count: int, item: FileSystemOperation) -> Outcome: # Note: we don't want to run this Task in parallel, just in case # the order of filesystem operations matters, so we can always # return an empty Outcome description = item.describe(self.workspace_path) self.info_count(index, count, description) try: item.perform(self.workspace_path) except OSError as e: raise Error(str(e)) return Outcome.empty()
def process(self, index: int, count: int, repo: Repo) -> Outcome: # Note: # # When self.parallel is True, the output of `git clone` and # `git reset` will be captured, so we need to compute a summary # string for the user. # # Otherwise, the output of `git clone` and # `git reset` will be shown directly to the user, so we can use # an empty summary self.info_count(index, count, "Cloning", repo.dest) self.check_shallow_with_sha1(repo) summary: str = "" summary += self.clone_repo(repo) summary += self.reset_repo(repo) return Outcome.from_summary(summary)
def process(self, index: int, count: int, repo: Repo) -> Outcome: # Note: # When self.parallel is True we need to return a string describing # all the changes, otherwise, we can just call cli_ui.info() directly summary_lines = [] for remote in repo.remotes: existing_remote = self.get_remote(repo, remote.name) if existing_remote: if existing_remote.url != remote.url: self.set_remote(repo, remote) summary_lines.append( f"{repo.dest}: remote '{remote.name}' set to '{remote.url}'" ) else: self.add_remote(repo, remote) summary_lines.append( f"{repo.dest}: added remote '{remote.name}' with url: '{remote.url}'" ) return Outcome.from_lines(summary_lines)
def process(self, index: int, count: int, repo: Repo) -> Outcome: # Note: Outcome is always empty here, because we # use self.statuses in the main `run()` function instead # of calling OutcomeCollection.print_summary() full_path = self.workspace.root_path / repo.dest self.info_count(index, count, repo.dest, end="\r") if not full_path.exists(): self.statuses[repo.dest] = MissingRepo(repo.dest) try: git_status = get_git_status(full_path) manifest_status = ManifestStatus(repo, manifest=self.manifest) manifest_status.update(git_status) status = Status(git=git_status, manifest=manifest_status) self.statuses[repo.dest] = status except Exception as e: self.statuses[repo.dest] = e if not self.parallel: erase_last_line() return Outcome.empty()
def process(self, index: int, count: int, repo: Repo) -> Outcome: """Synchronize a repo given its configuration in the manifest. Always start by running `git fetch`, then either: * try resetting the repo to the given tag or sha1 (abort if the repo is dirty) * or try merging the local branch with its upstream (abort if not on on the correct branch, or if the merge is not fast-forward). """ error = None self.info_count(index, count, "Synchronizing", repo.dest) self.fetch(repo) summary_lines = [] ref = None if repo.tag: ref = repo.tag elif repo.sha1: ref = repo.sha1 if ref: self.info_3("Resetting to", ref) self.sync_repo_to_ref(repo, ref) summary_lines += [repo.dest, "-" * len(repo.dest)] summary_lines += [f"Reset to {ref}"] else: error, current_branch = self.check_branch(repo) self.info_3("Updating branch:", current_branch) sync_summary = self.sync_repo_to_branch(repo, current_branch=current_branch) if sync_summary: title = f"{repo.dest} on {current_branch}" summary_lines += [title, "-" * len(title), sync_summary] if not repo.ignore_submodules: submodule_line = self.update_submodules(repo) if submodule_line: summary_lines.append(submodule_line) summary = "\n".join(summary_lines) return Outcome(error=error, summary=summary)
def process(self, index: int, count: int, item: str) -> Outcome: if item == "failing": raise Kaboom() return Outcome.empty()