sapling/hgext/rage.py
Jun Wu 584656dff3 codemod: join the auto-formatter party
Summary:
Turned on the auto formatter. Ran `arc lint --apply-patches --take BLACK **/*.py`.
Then run `arc lint` again so some other autofixers like spellchecker etc. looked
at the code base. Manually accept the changes whenever they make sense, or use
a workaround (ex. changing "dict()" to "dict constructor") where autofix is false
positive. Disabled linters on files that are hard (i18n/polib.py) to fix, or less
interesting to fix (hgsubversion tests), or cannot be fixed without breaking
OSS build (FBPYTHON4).

Conflicted linters (test-check-module-imports.t, part of test-check-code.t,
test-check-pyflakes.t) are removed or disabled.

Duplicated linters (test-check-pyflakes.t, test-check-pylint.t) are removed.

An issue of the auto-formatter is lines are no longer guarnateed to be <= 80
chars. But that seems less important comparing with the benefit auto-formatter
provides.

As we're here, also remove test-check-py3-compat.t, as it is currently broken
if `PYTHON3=/bin/python3` is set.

Reviewed By: wez, phillco, simpkins, pkaush, singhsrb

Differential Revision: D8173629

fbshipit-source-id: 90e248ae0c5e6eaadbe25520a6ee42d32005621b
2018-05-25 22:17:29 -07:00

403 lines
12 KiB
Python

# Copyright 2014 Facebook Inc.
#
"""upload useful diagnostics and give instructions for asking for help
[rage]
# Name of the rpm binary
rpmbin = rpm
"""
import os
import re
import socket
import struct
import subprocess
import tempfile
import time
import traceback
from functools import partial
from mercurial import (
bookmarks,
cmdutil,
commands,
encoding,
error,
progress,
pycompat,
registrar,
scmutil,
util,
)
from mercurial.i18n import _
from . import shareutil
from .remotefilelog import constants, shallowutil
cmdtable = {}
command = registrar.command(cmdtable)
_failsafeerrors = []
def _failsafe(func):
try:
return func()
except Exception as ex:
index = len(_failsafeerrors) + 1
message = "[%d]: %s\n%s\n" % (index, str(ex), traceback.format_exc())
_failsafeerrors.append(message)
return "(Failed. See footnote [%d])" % index
def shcmd(cmd, input=None, check=True, keeperr=True):
_, _, _, p = util.popen4(cmd)
out, err = p.communicate(input)
if check and p.returncode:
raise error.Abort(cmd + " error: " + err)
elif keeperr:
out += err
return out
def which(name):
""" """
for p in encoding.environ.get("PATH", "/bin").split(pycompat.ospathsep):
path = os.path.join(p, name)
if os.path.exists(path):
return path
return None
rageopts = [
("p", "preview", None, _("print diagnostic information without doing arc paste"))
]
def localconfig(ui):
result = []
for section, name, value in ui.walkconfig():
source = ui.configsource(section, name)
if source.find("/etc/") == -1 and source.find("/default.d/") == -1:
result.append("%s.%s=%s # %s" % (section, name, value, source))
return result
def obsoleteinfo(repo, hgcmd):
"""Return obsolescence markers that are relevant to smartlog revset"""
unfi = repo.unfiltered()
revs = scmutil.revrange(unfi, ["smartlog()"])
hashes = "|".join(unfi[rev].hex() for rev in revs)
markers = hgcmd("debugobsolete", rev=[])
pat = re.compile("(^.*(?:" + hashes + ").*$)", re.MULTILINE)
relevant = pat.findall(markers)
return "\n".join(relevant)
def usechginfo():
"""FBONLY: Information about whether chg is enabled"""
files = {"system": "/etc/mercurial/usechg", "user": os.path.expanduser("~/.usechg")}
result = []
for name, path in files.items():
if os.path.exists(path):
with open(path) as f:
value = f.read().strip()
else:
value = "(not set)"
result.append("%s: %s" % (name, value))
return "\n".join(result)
def rpminfo(ui):
"""FBONLY: Information about RPM packages"""
result = set()
rpmbin = ui.config("rage", "rpmbin", "rpm")
for name in ["hg", "hg.real"]:
path = which(name)
if not path:
continue
result.add(shcmd("%s -qf %s" % (rpmbin, path), check=False))
return "".join(result)
def infinitepushbackuplogs(ui, repo):
"""Contents of recent infinitepush log files."""
logdir = ui.config("infinitepushbackup", "logdir")
if not logdir:
return "infinitepushbackup.logdir not set"
try:
username = util.shortuser(ui.username())
except Exception:
username = "unknown"
userlogdir = os.path.join(logdir, username)
if not os.path.exists(userlogdir):
return "log directory does not exist: %s" % userlogdir
# Log filenames are the reponame with the date (YYYYMMDD) appended.
reponame = os.path.basename(repo.origroot)
logfiles = sorted([f for f in os.listdir(userlogdir) if f[:-8] == reponame])
if not logfiles:
return "no log files found for %s in %s" % (reponame, userlogdir)
# Display the last 100 lines from the most recent log files.
logs = []
linelimit = 100
for logfile in reversed(logfiles):
loglines = open(os.path.join(userlogdir, logfile)).readlines()
linecount = len(loglines)
if linecount > linelimit:
logcontent = " ".join(loglines[-linelimit:])
logs.append(
"%s (first %s lines omitted):\n %s\n"
% (logfile, linecount - linelimit, logcontent)
)
break
else:
logcontent = " ".join(loglines)
logs.append("%s:\n %s\n" % (logfile, logcontent))
linelimit -= linecount
return "".join(reversed(logs))
def readfsmonitorstate(repo):
"""
Read the fsmonitor.state file and pretty print some information from it.
Based on file format version 4. See hgext/fsmonitor/state.py for real
implementation.
"""
f = repo.vfs("fsmonitor.state", "rb")
versionbytes = f.read(4)
version = struct.unpack(">I", versionbytes)[0]
data = f.read()
state = data.split("\0")
hostname, clock, ignorehash = state[0:3]
files = state[3:-1] # discard empty entry after final file
numfiles = len(files)
prettyfiles = "\n".join(files[:20])
return """\
version: %d
hostname: %s
clock: %s
ignorehash: %s
files (first 20 of %d):
%s
""" % (
version,
hostname,
clock,
ignorehash,
numfiles,
prettyfiles,
)
def _makerage(ui, repo, **opts):
srcrepo = shareutil.getsrcrepo(repo)
def format(pair, basic=True):
if basic:
fmt = "%s: %s\n"
else:
fmt = "%s:\n---------------------------\n%s\n"
return fmt % pair
def hgcmd(cmdname, *args, **additional_opts):
cmd, opts = cmdutil.getcmdanddefaultopts(cmdname, commands.table)
opts.update(additional_opts)
_repo = repo
if "_repo" in opts:
_repo = opts["_repo"]
del opts["_repo"]
ui.pushbuffer(error=True)
try:
cmd(ui, _repo, *args, **opts)
finally:
return ui.popbuffer()
def hgsrcrepofile(filename):
if srcrepo.vfs.exists(filename):
return srcrepo.vfs(filename).read()
else:
return "File not found: %s" % srcrepo.vfs.join(filename)
basic = [
("date", time.ctime()),
("unixname", encoding.environ.get("LOGNAME")),
("hostname", socket.gethostname()),
("repo location", _failsafe(lambda: repo.root)),
(
"active bookmark",
_failsafe(lambda: bookmarks._readactive(repo, repo._bookmarks)),
),
(
"hg version",
_failsafe(lambda: __import__("mercurial.__version__").__version__.version),
),
(
"obsstore size",
_failsafe(lambda: str(srcrepo.vfs.stat("store/obsstore").st_size)),
),
]
oldcolormode = ui._colormode
ui._colormode = None
detailed = [
("df -h", _failsafe(lambda: shcmd("df -h", check=False))),
(
"hg sl (filtered)",
_failsafe(lambda: hgcmd("smartlog", template="{sl_debug}")),
),
# unfiltered smartlog for recent hidden changesets, including full
# node identity
(
"hg sl (unfiltered)",
_failsafe(
lambda: hgcmd(
"smartlog", _repo=repo.unfiltered(), template="{node}\n{sl_debug}"
)
),
),
# smartlog as the user sees it
(
'first 20 lines of "hg status"',
_failsafe(lambda: "\n".join(hgcmd("status").splitlines()[:20])),
),
("hg blackbox -l60", _failsafe(lambda: hgcmd("blackbox", limit=60))),
("hg summary", _failsafe(lambda: hgcmd("summary"))),
("hg config (local)", _failsafe(lambda: "\n".join(localconfig(ui)))),
("hg sparse", _failsafe(lambda: hgcmd("sparse"))),
("usechg", _failsafe(usechginfo)),
("rpm info", _failsafe(partial(rpminfo, ui))),
("klist", _failsafe(lambda: shcmd("klist", check=False))),
("ifconfig", _failsafe(lambda: shcmd("ifconfig"))),
(
"airport",
_failsafe(
lambda: shcmd(
"/System/Library/PrivateFrameworks/Apple80211."
+ "framework/Versions/Current/Resources/airport "
+ "--getinfo",
check=False,
)
),
),
("hg debugobsolete <smartlog>", _failsafe(lambda: obsoleteinfo(repo, hgcmd))),
(
"infinitepush backup state",
_failsafe(lambda: hgsrcrepofile("infinitepushbackupstate")),
),
(
"infinitepush backup logs",
_failsafe(lambda: infinitepushbackuplogs(ui, repo)),
),
("hg config (all)", _failsafe(lambda: hgcmd("config"))),
("fsmonitor state", _failsafe(lambda: readfsmonitorstate(repo))),
]
msg = ""
if util.safehasattr(repo, "name"):
# Add the contents of both local and shared pack directories.
packlocs = {
"local": lambda category: shallowutil.getlocalpackpath(
repo.svfs.vfs.base, category
),
"shared": lambda category: shallowutil.getcachepackpath(repo, category),
}
for loc, getpath in packlocs.iteritems():
for category in constants.ALL_CATEGORIES:
path = getpath(category)
detailed.append(
(
"%s packs (%s)" % (loc, constants.getunits(category)),
"%s:\n%s"
% (path, _failsafe(lambda: shcmd("ls -lhS %s" % path))),
)
)
# This is quite slow, so we don't want to do it by default
if ui.configbool("rage", "fastmanifestcached", False):
detailed.append(
(
'hg sl -r "fastmanifestcached()"',
_failsafe(lambda: hgcmd("smartlog", rev=["fastmanifestcached()"])),
)
)
msg = (
"\n".join(map(format, basic))
+ "\n"
+ "\n".join(map(lambda x: format(x, False), detailed))
)
if _failsafeerrors:
msg += "\n" + "\n".join(_failsafeerrors)
ui._colormode = oldcolormode
return msg
@command("^rage", rageopts, _("hg rage"))
def rage(ui, repo, *pats, **opts):
"""collect useful diagnostics for asking help from the source control team
The rage command collects useful diagnostic information.
By default, the information will be uploaded to Phabricator and
instructions about how to ask for help will be printed.
After submitting to Phabricator, it prints configerable advice::
[rage]
advice = Please see our FAQ guide: https://...
"""
with progress.spinner(ui, "Generating paste"):
msg = _makerage(ui, repo, **opts)
if opts.get("preview"):
ui.write("%s\n" % msg)
return
with progress.spinner(ui, "Saving paste"):
try:
p = subprocess.Popen(
["arc", "paste", "--lang", "hgrage", "--title", "hgrage"],
stdout=subprocess.PIPE,
stdin=subprocess.PIPE,
stderr=subprocess.PIPE,
shell=pycompat.iswindows,
)
out, err = p.communicate(input=msg + "\n")
ret = p.returncode
except OSError:
ui.write(_("Failed calling arc. (is it in your PATH?)\n"))
ret = 1
if ret:
fd, tmpname = tempfile.mkstemp(prefix="hg-rage-")
with os.fdopen(fd, r"w") as tmpfp:
tmpfp.write(msg)
ui.write(
_(
"Failed to post the diagnostic paste to Phabricator, "
"but its contents have been written to:\n\n"
)
)
ui.write(_(" %s\n") % tmpname, label="rage.link")
ui.write(
_("\nPlease include this file in the %s.\n")
% ui.config("ui", "supportcontact")
)
else:
ui.write(
_("Please post in %s with the following link:\n\n")
% (ui.config("ui", "supportcontact"))
)
ui.write(" " + out + "\n", label="rage.link")
ui.write(ui.config("rage", "advice", "") + "\n")
colortable = {"rage.link": "blue bold"}