import itertools import logging import re from lib.obs_revision import OBSRevision class History: """Store the history of revisions of a package in different projects. """ def __init__(self, obs, package): self.obs = obs self.package = package self.revisions = {} def __contains__(self, project): return project in self.revisions def __getitem__(self, project): return self.revisions[project] def _extract_copypac(self, comment): original_project = re.findall( r"osc copypac from project:(.*) package:", comment ) return original_project[0] if original_project else None def _fetch_revisions(self, project, **params): root = self.obs._history(project, self.package, **params) if root is not None: return [ OBSRevision(self.obs, self, project, self.package).parse(r) for r in root.findall("revision") ] def fetch_revisions(self, project, follow_copypac=False): """Get the revision history of a package""" if project in self: return revs = self._fetch_revisions(project) self.revisions[project] = revs # while ( # revs # and follow_copypac # and (copypac_project := self._extract_copypac(revs[0].comment)) # ): # # Add the history pre-copypac # # TODO: missing the old project name # revs = self._fetch_revisions(copypac_project, deleted=1) # self.revisions[project] = ( # revs + self.revisions[project] # ) def fetch_all_revisions(self, projects): """Pre-populate the history""" for project, _, api_url in projects: self.obs.change_url(api_url) self.fetch_revisions(project) def sort_all_revisions(self): """Sort revisions for all projects, from older to newer""" return sorted(itertools.chain(*self.revisions.values()), key=lambda x: x.time) def find_revision(self, project, revisionid, accepted_at): last_commited_revision = None for r in self.revisions.get(project, []): logging.debug(f"Find revision {revisionid} [{accepted_at}]: {r}") if str(r.rev) == str(revisionid) or r.srcmd5 == revisionid: if r.ignored: logging.debug( f"{r} fits but is ignored, returning {last_commited_revision}" ) return last_commited_revision else: logging.debug(f"{r} fits") return r if r.time > accepted_at: # if we can't find the right revision, we take the last # commit. Before ~2012 the data was tracked really loosely # (e.g. using different timezones and the state field was # only introduced in 2016...) logging.warning( f"Deploying workaround for missing request revision - returning {last_commited_revision}" ) return last_commited_revision if r.commit: last_commited_revision = r logging.info("No commited revision found, returning None") return None def find_last_rev_after_time(self, project, time): # revs = self.projects.get(project, []) # return next((r for r in reversed(revs) if r.time <= time), None) prev = None for rev in self.revisions.get(project, []): if rev.time > time: return prev if rev.time == time: return rev prev = rev return prev