def create_release_vcs(path, vcs_name=None): """Return a new release VCS that can release from this source path.""" from rez.plugin_managers import plugin_manager vcs_types = get_release_vcs_types() if vcs_name: if vcs_name not in vcs_types: raise ReleaseVCSError("Unknown version control system: %r" % vcs_name) cls = plugin_manager.get_plugin_class('release_vcs', vcs_name) return cls(path) clss = [] for vcs_name in vcs_types: cls = plugin_manager.get_plugin_class('release_vcs', vcs_name) if cls.is_valid_root(path): clss.append(cls) if len(clss) > 1: clss_str = ", ".join(x.name() for x in clss) raise ReleaseVCSError("Several version control systems are associated " "with the path %s: %s. Use rez-release --vcs to " "choose." % (path, clss_str)) elif not clss: raise ReleaseVCSError("No version control system for package " "releasing is associated with the path %s" % path) else: return clss[0](path)
def get_last_changed_revision(client, url): """ util func, get last revision of url """ try: svn_entries = client.info2(url, pysvn.Revision(pysvn.opt_revision_kind.head), recurse=False) if not svn_entries: raise ReleaseVCSError("svn.info2() returned no results on url %s" % url) return svn_entries[0][1].last_changed_rev except pysvn.ClientError, ce: raise ReleaseVCSError("svn.info2() raised ClientError: %s" % ce)
def validate_repostate(self): b = self.git("rev-parse", "--is-bare-repository") if b == "true": raise ReleaseVCSError("Could not release: bare git repository") remote, remote_branch = self.get_tracking_branch() # check for upstream branch if remote is None and not self.settings.allow_no_upstream: raise ReleaseVCSError( "Release cancelled: there is no upstream branch (git cannot see " "a remote repo - you should probably FIX THIS FIRST!). To allow " "the release, set the config entry " "'plugins.release_vcs.git.allow_no_upstream' to true.") # check we are releasing from a valid branch releasable_branches = self.type_settings.releasable_branches if releasable_branches: releasable = False current_branch_name = self.get_local_branch() for releasable_branch in releasable_branches: if re.search(releasable_branch, current_branch_name): releasable = True break if not releasable: raise ReleaseVCSError( "Could not release: current branch is %s, must match " "one of: %s" % (current_branch_name, ', '.join(releasable_branches))) # check for uncommitted changes try: self.git("diff-index", "--quiet", "HEAD") except ReleaseVCSError: msg = "Could not release: there are uncommitted changes:\n" statmsg = self.git("diff-index", "--stat", "HEAD") msg += '\n'.join(statmsg) raise ReleaseVCSError(msg) # check if we are behind/ahead of remote if remote: self.git("remote", "update") n = self.get_relative_to_remote() if n: s = "ahead of" if n > 0 else "behind" remote_uri = '/'.join((remote, remote_branch)) raise ReleaseVCSError( "Could not release: %d commits %s %s." % (abs(n), s, remote_uri))
def find_executable(cls, name): exe = which(name) if not exe: raise ReleaseVCSError( "Couldn't find executable '%s' for VCS '%s'" % (name, cls.name())) return exe
def _cmd(self, *nargs): """Convenience function for executing a program such as 'git' etc.""" cmd_str = ' '.join(map(quote, nargs)) if self.package.config.debug("package_release"): print_debug("Running command: %s" % cmd_str) p = Popen(nargs, stdout=subprocess.PIPE, stderr=subprocess.PIPE, cwd=self.pkg_root, text=True) out, err = p.communicate() if p.returncode: print_debug("command stdout:") print_debug(out) print_debug("command stderr:") print_debug(err) raise ReleaseVCSError("command failed: %s\n%s" % (cmd_str, err)) out = out.strip() if out: return [x.rstrip() for x in out.split('\n')] else: return []
def create_release_vcs(path, vcs_name=None): """Return a new release VCS that can release from this source path.""" from rez.plugin_managers import plugin_manager vcs_types = get_release_vcs_types() if vcs_name: if vcs_name not in vcs_types: raise ReleaseVCSError("Unknown version control system: %r" % vcs_name) cls = plugin_manager.get_plugin_class('release_vcs', vcs_name) return cls(path) classes_by_level = {} for vcs_name in vcs_types: cls = plugin_manager.get_plugin_class('release_vcs', vcs_name) result = cls.find_vcs_root(path) if not result: continue vcs_path, levels_up = result classes_by_level.setdefault(levels_up, []).append((cls, vcs_path)) if not classes_by_level: raise ReleaseVCSError("No version control system for package " "releasing is associated with the path %s" % path) # it's ok to have multiple results, as long as there is only one at the # "closest" directory up from this dir - ie, if we start at: # /blah/foo/pkg_root # and these dirs exist: # /blah/.hg # /blah/foo/.git # ...then this is ok, because /blah/foo/.git is "closer" to the original # dir, and will be picked. However, if these two directories exist: # /blah/foo/.git # /blah/foo/.hg # ...then we error, because we can't decide which to use lowest_level = sorted(classes_by_level)[0] clss = classes_by_level[lowest_level] if len(clss) > 1: clss_str = ", ".join(x[0].name() for x in clss) raise ReleaseVCSError("Several version control systems are associated " "with the path %s: %s. Use rez-release --vcs to " "choose." % (path, clss_str)) else: cls, vcs_root = clss[0] return cls(pkg_root=path, vcs_root=vcs_root)
def _url(op): origin = doc["tracking_branch"].split('/')[0] lines = self.git("remote", "-v") lines = [x for x in lines if origin in x.split()] lines = [x for x in lines if ("(%s)" % op) in x.split()] try: return lines[0].split()[1] except: raise ReleaseVCSError("failed to parse %s url from:\n%s" % (op, '\n'.join(lines)))
def validate_repostate(self): status_list = self.svnc.status(self.pkg_root, get_all=False, update=True) status_list_known = [] for status in status_list: if status.entry: status_list_known.append(status) if status_list_known: raise ReleaseVCSError( "'%s' is not in a state to release - you may need to svn-checkin " "and/or svn-update: %s" % (self.pkg_root, str(status_list_known)))
def get_tag_url(self, tag_name=None): # find the base path, ie where 'trunk', 'branches', 'tags' should be pos_tr = self.this_url.find("/trunk") pos_br = self.this_url.find("/branches") pos = max(pos_tr, pos_br) if (pos == -1): raise ReleaseVCSError("%s is not in a branch or trunk" % self.pkg_root) base_url = self.this_url[:pos] tag_url = base_url + "/tags" if tag_name: tag_url += '/' + tag_name return tag_url
def __init__(self, pkg_root, vcs_root=None): if vcs_root is None: result = self.find_vcs_root(pkg_root) if not result: raise ReleaseVCSError("Could not find %s repository for the " "path %s" % (self.name(), pkg_root)) vcs_root = result[0] else: assert (self.is_valid_root(vcs_root)) self.vcs_root = vcs_root self.pkg_root = pkg_root self.package = get_developer_package(pkg_root) self.type_settings = self.package.config.plugins.release_vcs self.settings = self.type_settings.get(self.name())
def get_relative_to_remote(self): """Return the number of commits we are relative to the remote. Negative is behind, positive in front, zero means we are matched to remote. """ s = self.git("status", "--short", "-b")[0] r = re.compile("\[([^\]]+)\]") toks = r.findall(s) if toks: try: s2 = toks[-1] adj, n = s2.split() assert (adj in ("ahead", "behind")) n = int(n) return -n if adj == "behind" else n except Exception as e: raise ReleaseVCSError( ("Problem parsing first line of result of 'git status " "--short -b' (%s):\n%s") % (s, str(e))) else: return 0
def _check(modified, path): if modified: modified = [line.split()[-1] for line in modified] raise ReleaseVCSError( "%s is not in a state to release - please commit outstanding " "changes: %s" % (path, ', '.join(modified)))