def treeherder(ui, repo, tree=None, rev=None, **opts): """Open Treeherder showing build status for the specified revision. The command receives a tree name and a revision to query. The tree is required because a revision/changeset may existing in multiple repositories. """ if not tree: raise util.Abort('A tree must be specified.') if not rev: raise util.Abort('A revision must be specified.') tree, repo_url = resolve_trees_to_uris([tree])[0] if not repo_url: raise util.Abort("Don't know about tree: %s" % tree) r = MercurialRepository(repo_url) node = repo[rev].hex() push = r.push_info_for_changeset(node) if not push: raise util.Abort("Could not find push info for changeset %s" % node) push_node = push.last_node url = treeherder_url(tree, push_node) import webbrowser webbrowser.get('firefox').open(url)
def treeherder(ui, repo, tree=None, rev=None, **opts): """Open Treeherder showing build status for the specified revision. The command receives a tree name and a revision to query. The tree is required because a revision/changeset may existing in multiple repositories. """ if not tree: raise util.Abort('A tree must be specified.') if not rev: raise util.Abort('A revision must be specified.') tree, repo_url = resolve_trees_to_uris([tree])[0] if not repo_url: raise util.Abort("Don't know about tree: %s" % tree) r = MercurialRepository(repo_url) node = repo[rev].hex() push = r.push_info_for_changeset(node) if not push: raise util.Abort("Could not find push info for changeset %s" % node) push_node = push.last_node url = treeherder_url(tree, push_node[0:12]) import webbrowser webbrowser.get('firefox').open(url)
def template_firstpushtreeherder(repo, ctx, **args): """:firstpushtreeherder: String. Treeherder URL for the first push of this changeset. """ pushes = list(repo.changetracker.pushes_for_changeset(ctx.node())) if not pushes: return None push = pushes[0] tree, node = push[0], push[4] return treeherder_url(tree, hex(node))
def template_firstpushtreeherder(repo, ctx, **args): """:firstpushtreeherder: String. Treeherder URL for the first push of this changeset. """ pushes = list(repo.changetracker.pushes_for_changeset(ctx.node())) if not pushes: return None push = pushes[0] tree, node = push[0], push[4] return treeherder_url(tree, hex(node)[0:12])
def print_changeset_pushes(ui, repo, rev, all=False): if not repo.changetracker: ui.warn('Local database appears to be disabled.') return 1 ctx = repo[rev] node = ctx.node() pushes = repo.changetracker.pushes_for_changeset(node) pushes = [p for p in pushes if all or p[0] in RELEASE_TREES] if not pushes: ui.warn('No pushes recorded for changeset: ', str(ctx), '\n') return 1 longest_tree = max(len(p[0]) for p in pushes) + 2 longest_user = max(len(p[3]) for p in pushes) + 2 ui.write(str(ctx.rev()), ':', str(ctx), ' ', ctx.description(), '\n') ui.write('Release ', 'Tree'.ljust(longest_tree), 'Date'.ljust(20), 'Username'.ljust(longest_user), 'Build Info\n') for tree, push_id, when, user, head_node in pushes: releases = set() release = '' versions = {} if tree == 'beta': versions = repo._beta_releases() elif tree == 'release': versions = repo._release_releases() for version, e in versions.items(): vctx = repo[e[0]] if ctx.descendant(vctx): releases.add(version) if len(releases): release = sorted(releases)[0] url = treeherder_url(tree, hex(head_node)) date = datetime.datetime.fromtimestamp(when) ui.write(release.ljust(8), tree.ljust(longest_tree), date.isoformat(), ' ', user.ljust(longest_user), url or '', '\n')
def print_changeset_pushes(ui, repo, rev, all=False): if not repo.changetracker: ui.warn('Local database appears to be disabled.') return 1 ctx = repo[rev] node = ctx.node() pushes = repo.changetracker.pushes_for_changeset(node) pushes = [p for p in pushes if all or p[0] in RELEASE_TREES] if not pushes: ui.warn('No pushes recorded for changeset: ', str(ctx), '\n') return 1 longest_tree = max(len(p[0]) for p in pushes) + 2 longest_user = max(len(p[3]) for p in pushes) + 2 ui.write(ctx.rev(), ':', str(ctx), ' ', ctx.description(), '\n') ui.write('Release ', 'Tree'.ljust(longest_tree), 'Date'.ljust(20), 'Username'.ljust(longest_user), 'Build Info\n') for tree, push_id, when, user, head_node in pushes: releases = set() release = '' versions = {} if tree == 'beta': versions = repo._beta_releases() elif tree == 'release': versions = repo._release_releases() for version, e in versions.items(): vctx = repo[e[0]] if ctx.descendant(vctx): releases.add(version) if len(releases): release = sorted(releases)[0] url = treeherder_url(tree, hex(head_node)[0:12]) date = datetime.datetime.fromtimestamp(when) ui.write(release.ljust(8), tree.ljust(longest_tree), date.isoformat(), ' ', user.ljust(longest_user), url or '', '\n')
def wrappedpushbookmark(orig, pushop): result = orig(pushop) # pushop.ret was renamed to pushop.cgresult in Mercurial 3.2. We can drop # this branch once we drop <3.2 support. if hasattr(pushop, 'cgresult'): origresult = pushop.cgresult else: origresult = pushop.ret # Don't do anything if error from push. if not origresult: return result remoteurl = pushop.remote.url() tree = repository.resolve_uri_to_tree(remoteurl) # We don't support release trees (yet) because they have special flags # that need to get updated. if tree and tree in repository.RELEASE_TREES: return result ui = pushop.ui if tree and tree in ui.configlist('bzpost', 'excludetrees', default=[]): return result if tree: baseuri = repository.resolve_trees_to_uris([tree])[0][1].encode('utf-8') assert baseuri else: # This isn't a known Firefox tree. Fall back to resolving URLs by # hostname. # Only attend Mozilla's server. if not updateunknown(remoteurl, repository.BASE_WRITE_URI, ui): return result baseuri = remoteurl.replace(repository.BASE_WRITE_URI, repository.BASE_READ_URI).rstrip('/') bugsmap = {} lastbug = None lastnode = None for node in pushop.outgoing.missing: ctx = pushop.repo[node] # Don't do merge commits. if len(ctx.parents()) > 1: continue # Our bug parser is buggy for Gaia bump commit messages. if '<*****@*****.**>' in ctx.user(): continue # Pushing to Try (and possibly other repos) could push unrelated # changesets that have been pushed to an official tree but aren't yet # on this specific remote. We use the phase information as a proxy # for "already pushed" and prune public changesets from consideration. if tree == 'try' and ctx.phase() == phases.public: continue bugs = parse_bugs(ctx.description()) if not bugs: continue bugsmap.setdefault(bugs[0], []).append(ctx.hex()[0:12]) lastbug = bugs[0] lastnode = ctx.hex()[0:12] if not bugsmap: return result bzauth = getbugzillaauth(ui) if not bzauth: return result bzurl = ui.config('bugzilla', 'url', 'https://bugzilla.mozilla.org/rest') bugsy = Bugsy(username=bzauth.username, password=bzauth.password, userid=bzauth.userid, cookie=bzauth.cookie, api_key=bzauth.apikey, bugzilla_url=bzurl) def public_url_for_bug(bug): '''Turn 123 into "https://bugzilla.mozilla.org/show_bug.cgi?id=123".''' public_baseurl = bzurl.replace('rest', '').rstrip('/') return '%s/show_bug.cgi?id=%s' % (public_baseurl, bug) # If this is a try push, we paste the Treeherder link for the tip commit, because # the per-commit URLs don't have much value. # TODO roll this into normal pushing so we get a Treeherder link in bugs as well. if tree == 'try' and lastbug: treeherderurl = repository.treeherder_url(tree, lastnode) bug = bugsy.get(lastbug) comments = bug.get_comments() for comment in comments: if treeherderurl in comment.text: return result ui.write(_('recording Treeherder push at %s\n') % public_url_for_bug(lastbug)) bug.add_comment(treeherderurl) return result for bugnumber, nodes in bugsmap.items(): bug = bugsy.get(bugnumber) comments = bug.get_comments() missing_nodes = [] # When testing whether this changeset URL is referenced in a # comment, we only need to test for the node fragment. The # important side-effect is that each unique node for a changeset # is recorded in the bug. for node in nodes: if not any(node in comment.text for comment in comments): missing_nodes.append(node) if not missing_nodes: ui.write(_('bug %s already knows about pushed changesets\n') % bugnumber) continue lines = [] for node in missing_nodes: ctx = pushop.repo[node] lines.append('%s/rev/%s' % (baseuri, ctx.hex())) # description is using local encodings. Depending on the # configured encoding, replacement characters could be involved. We # use encoding.fromlocal() to get the raw bytes, which should be # valid UTF-8. lines.append(encoding.fromlocal(ctx.description()).splitlines()[0]) lines.append('') comment = '\n'.join(lines) ui.write(_('recording push at %s\n') % public_url_for_bug(bugnumber)) bug.add_comment(comment) return result
def wrappedpushbookmark(orig, pushop): result = orig(pushop) # pushop.ret was renamed to pushop.cgresult in Mercurial 3.2. We can drop # this branch once we drop <3.2 support. if hasattr(pushop, 'cgresult'): origresult = pushop.cgresult else: origresult = pushop.ret # Don't do anything if error from push. if not origresult: return result remoteurl = pushop.remote.url() tree = repository.resolve_uri_to_tree(remoteurl) # We don't support release trees (yet) because they have special flags # that need to get updated. if tree and tree in repository.RELEASE_TREES: return result ui = pushop.ui if tree and tree in ui.configlist('bzpost', 'excludetrees', default=[]): return result if tree: baseuri = repository.resolve_trees_to_uris([tree ])[0][1].encode('utf-8') assert baseuri else: # This isn't a known Firefox tree. Fall back to resolving URLs by # hostname. # Only attend Mozilla's server. if not updateunknown(remoteurl, repository.BASE_WRITE_URI, ui): return result baseuri = remoteurl.replace(repository.BASE_WRITE_URI, repository.BASE_READ_URI).rstrip('/') bugsmap = {} lastbug = None lastnode = None for node in pushop.outgoing.missing: ctx = pushop.repo[node] # Don't do merge commits. if len(ctx.parents()) > 1: continue # Our bug parser is buggy for Gaia bump commit messages. if '<*****@*****.**>' in ctx.user(): continue # Pushing to Try (and possibly other repos) could push unrelated # changesets that have been pushed to an official tree but aren't yet # on this specific remote. We use the phase information as a proxy # for "already pushed" and prune public changesets from consideration. if tree == 'try' and ctx.phase() == phases.public: continue bugs = parse_bugs(ctx.description()) if not bugs: continue bugsmap.setdefault(bugs[0], []).append(ctx.hex()) lastbug = bugs[0] lastnode = ctx.hex() if not bugsmap: return result bzauth = getbugzillaauth(ui) if not bzauth: return result bzurl = ui.config('bugzilla', 'url', 'https://bugzilla.mozilla.org/rest') bugsy = Bugsy(username=bzauth.username, password=bzauth.password, userid=bzauth.userid, cookie=bzauth.cookie, api_key=bzauth.apikey, bugzilla_url=bzurl) def public_url_for_bug(bug): '''Turn 123 into "https://bugzilla.mozilla.org/show_bug.cgi?id=123".''' public_baseurl = bzurl.replace('rest', '').rstrip('/') return '%s/show_bug.cgi?id=%s' % (public_baseurl, bug) # If this is a try push, we paste the Treeherder link for the tip commit, because # the per-commit URLs don't have much value. # TODO roll this into normal pushing so we get a Treeherder link in bugs as well. if tree == 'try' and lastbug: treeherderurl = repository.treeherder_url(tree, lastnode) bug = bugsy.get(lastbug) comments = bug.get_comments() for comment in comments: if treeherderurl in comment.text: return result ui.write( _('recording Treeherder push at %s\n') % public_url_for_bug(lastbug)) bug.add_comment(treeherderurl) return result for bugnumber, nodes in bugsmap.items(): bug = bugsy.get(bugnumber) comments = bug.get_comments() missing_nodes = [] # When testing whether this changeset URL is referenced in a # comment, we only need to test for the node fragment. The # important side-effect is that each unique node for a changeset # is recorded in the bug. for node in nodes: if not any(node in comment.text for comment in comments): missing_nodes.append(node) if not missing_nodes: ui.write( _('bug %s already knows about pushed changesets\n') % bugnumber) continue lines = [] for node in missing_nodes: ctx = pushop.repo[node] lines.append('%s/rev/%s' % (baseuri, ctx.hex())) # description is using local encodings. Depending on the # configured encoding, replacement characters could be involved. We # use encoding.fromlocal() to get the raw bytes, which should be # valid UTF-8. lines.append(encoding.fromlocal(ctx.description()).splitlines()[0]) lines.append('') comment = '\n'.join(lines) ui.write(_('recording push at %s\n') % public_url_for_bug(bugnumber)) bug.add_comment(comment) return result