2013-03-06 21:58:40 +04:00
|
|
|
# This Source Code Form is subject to the terms of the Mozilla Public
|
|
|
|
# License, v. 2.0. If a copy of the MPL was not distributed with this
|
|
|
|
# file, # You can obtain one at http://mozilla.org/MPL/2.0/.
|
|
|
|
|
2015-06-22 03:39:09 +03:00
|
|
|
from __future__ import absolute_import, unicode_literals
|
2013-03-06 21:58:40 +04:00
|
|
|
|
2013-12-06 07:35:52 +04:00
|
|
|
import sys
|
2014-01-08 05:51:44 +04:00
|
|
|
import os
|
|
|
|
import stat
|
|
|
|
import platform
|
2014-03-21 08:43:28 +04:00
|
|
|
import errno
|
2016-08-05 23:35:12 +03:00
|
|
|
import subprocess
|
2013-12-06 07:35:52 +04:00
|
|
|
|
2013-03-06 21:58:40 +04:00
|
|
|
from mach.decorators import (
|
|
|
|
CommandArgument,
|
|
|
|
CommandProvider,
|
|
|
|
Command,
|
|
|
|
)
|
|
|
|
|
2015-09-05 19:27:23 +03:00
|
|
|
from mozbuild.base import MachCommandBase, MozbuildObject
|
2013-12-06 07:35:52 +04:00
|
|
|
|
2013-03-06 21:58:40 +04:00
|
|
|
|
|
|
|
@CommandProvider
|
|
|
|
class SearchProvider(object):
|
2013-05-09 04:56:30 +04:00
|
|
|
@Command('dxr', category='misc',
|
2017-01-06 17:52:07 +03:00
|
|
|
description='Search for something in DXR.')
|
2013-03-07 00:23:21 +04:00
|
|
|
@CommandArgument('term', nargs='+', help='Term(s) to search for.')
|
|
|
|
def dxr(self, term):
|
|
|
|
import webbrowser
|
|
|
|
term = ' '.join(term)
|
2014-05-14 19:26:17 +04:00
|
|
|
uri = 'http://dxr.mozilla.org/mozilla-central/search?q=%s&redirect=true' % term
|
2013-03-07 00:23:21 +04:00
|
|
|
webbrowser.open_new_tab(uri)
|
|
|
|
|
2013-05-09 04:56:30 +04:00
|
|
|
@Command('mdn', category='misc',
|
2017-01-06 17:52:07 +03:00
|
|
|
description='Search for something on MDN.')
|
2013-03-06 21:58:40 +04:00
|
|
|
@CommandArgument('term', nargs='+', help='Term(s) to search for.')
|
|
|
|
def mdn(self, term):
|
|
|
|
import webbrowser
|
|
|
|
term = ' '.join(term)
|
|
|
|
uri = 'https://developer.mozilla.org/search?q=%s' % term
|
|
|
|
webbrowser.open_new_tab(uri)
|
|
|
|
|
2013-05-09 04:56:30 +04:00
|
|
|
@Command('google', category='misc',
|
2017-01-06 17:52:07 +03:00
|
|
|
description='Search for something on Google.')
|
2013-03-06 21:58:40 +04:00
|
|
|
@CommandArgument('term', nargs='+', help='Term(s) to search for.')
|
|
|
|
def google(self, term):
|
|
|
|
import webbrowser
|
|
|
|
term = ' '.join(term)
|
|
|
|
uri = 'https://www.google.com/search?q=%s' % term
|
|
|
|
webbrowser.open_new_tab(uri)
|
|
|
|
|
2013-05-09 04:56:30 +04:00
|
|
|
@Command('search', category='misc',
|
2017-01-06 17:52:07 +03:00
|
|
|
description='Search for something on the Internets. '
|
|
|
|
'This will open 3 new browser tabs and search for the term on Google, '
|
|
|
|
'MDN, and DXR.')
|
2013-03-06 21:58:40 +04:00
|
|
|
@CommandArgument('term', nargs='+', help='Term(s) to search for.')
|
|
|
|
def search(self, term):
|
|
|
|
self.google(term)
|
|
|
|
self.mdn(term)
|
2016-07-08 11:31:30 +03:00
|
|
|
self.dxr(term)
|
2013-03-06 21:58:40 +04:00
|
|
|
|
2013-07-15 21:00:09 +04:00
|
|
|
|
|
|
|
@CommandProvider
|
|
|
|
class UUIDProvider(object):
|
|
|
|
@Command('uuid', category='misc',
|
2017-01-06 17:52:07 +03:00
|
|
|
description='Generate a uuid.')
|
2013-10-02 12:06:39 +04:00
|
|
|
@CommandArgument('--format', '-f', choices=['idl', 'cpp', 'c++'],
|
2013-07-15 21:00:09 +04:00
|
|
|
help='Output format for the generated uuid.')
|
2013-10-02 12:06:39 +04:00
|
|
|
def uuid(self, format=None):
|
2013-07-15 21:00:09 +04:00
|
|
|
import uuid
|
|
|
|
u = uuid.uuid4()
|
2013-10-02 12:06:39 +04:00
|
|
|
if format in [None, 'idl']:
|
2013-07-15 21:00:09 +04:00
|
|
|
print(u)
|
2013-10-02 12:06:39 +04:00
|
|
|
if format is None:
|
|
|
|
print('')
|
|
|
|
if format in [None, 'cpp', 'c++']:
|
2013-07-15 21:00:09 +04:00
|
|
|
u = u.hex
|
|
|
|
print('{ 0x%s, 0x%s, 0x%s, \\' % (u[0:8], u[8:12], u[12:16]))
|
|
|
|
pairs = tuple(map(lambda n: u[n:n+2], range(16, 32, 2)))
|
|
|
|
print((' { ' + '0x%s, ' * 7 + '0x%s } }') % pairs)
|
2013-07-30 03:57:28 +04:00
|
|
|
|
2013-10-16 21:13:00 +04:00
|
|
|
|
2016-08-05 23:35:12 +03:00
|
|
|
@CommandProvider
|
|
|
|
class RageProvider(MachCommandBase):
|
|
|
|
@Command('rage', category='misc',
|
|
|
|
description='Express your frustration')
|
|
|
|
def rage(self):
|
|
|
|
"""Have a bad experience developing Firefox? Run this command to
|
|
|
|
express your frustration.
|
|
|
|
|
|
|
|
This command will open your default configured web browser to a short
|
|
|
|
form where you can submit feedback. Just close the tab when done.
|
|
|
|
"""
|
|
|
|
import getpass
|
|
|
|
import urllib
|
|
|
|
import webbrowser
|
|
|
|
|
|
|
|
# Try to resolve the current user.
|
|
|
|
user = None
|
|
|
|
with open(os.devnull, 'wb') as null:
|
|
|
|
if os.path.exists(os.path.join(self.topsrcdir, '.hg')):
|
|
|
|
try:
|
|
|
|
user = subprocess.check_output(['hg', 'config',
|
|
|
|
'ui.username'],
|
|
|
|
cwd=self.topsrcdir,
|
|
|
|
stderr=null)
|
|
|
|
|
|
|
|
i = user.find('<')
|
|
|
|
if i >= 0:
|
|
|
|
user = user[i + 1:-2]
|
|
|
|
except subprocess.CalledProcessError:
|
|
|
|
pass
|
|
|
|
elif os.path.exists(os.path.join(self.topsrcdir, '.git')):
|
|
|
|
try:
|
|
|
|
user = subprocess.check_output(['git', 'config', '--get',
|
|
|
|
'user.email'],
|
|
|
|
cwd=self.topsrcdir,
|
|
|
|
stderr=null)
|
|
|
|
except subprocess.CalledProcessError:
|
|
|
|
pass
|
|
|
|
|
|
|
|
if not user:
|
|
|
|
try:
|
|
|
|
user = getpass.getuser()
|
|
|
|
except Exception:
|
|
|
|
pass
|
|
|
|
|
2017-01-06 17:52:07 +03:00
|
|
|
url = 'https://docs.google.com/a/mozilla.com/forms/d/e/1FAIpQLSeDVC3IXJu5d33Hp_ZTCOw06xEUiYH1pBjAqJ1g_y63sO2vvA/viewform' # noqa: E501
|
2016-08-05 23:35:12 +03:00
|
|
|
if user:
|
|
|
|
url += '?entry.1281044204=%s' % urllib.quote(user)
|
|
|
|
|
|
|
|
print('Please leave your feedback in the opened web form')
|
|
|
|
webbrowser.open_new_tab(url)
|
|
|
|
|
|
|
|
|
2013-10-16 21:13:00 +04:00
|
|
|
@CommandProvider
|
|
|
|
class PastebinProvider(object):
|
|
|
|
@Command('pastebin', category='misc',
|
2017-01-06 17:52:07 +03:00
|
|
|
description='Command line interface to pastebin.mozilla.org.')
|
2013-10-16 21:13:00 +04:00
|
|
|
@CommandArgument('--language', default=None,
|
|
|
|
help='Language to use for syntax highlighting')
|
2015-04-24 04:08:30 +03:00
|
|
|
@CommandArgument('--poster', default='',
|
2013-10-16 21:13:00 +04:00
|
|
|
help='Specify your name for use with pastebin.mozilla.org')
|
|
|
|
@CommandArgument('--duration', default='day',
|
|
|
|
choices=['d', 'day', 'm', 'month', 'f', 'forever'],
|
|
|
|
help='Keep for specified duration (default: %(default)s)')
|
|
|
|
@CommandArgument('file', nargs='?', default=None,
|
|
|
|
help='Specify the file to upload to pastebin.mozilla.org')
|
|
|
|
def pastebin(self, language, poster, duration, file):
|
|
|
|
import urllib
|
2015-06-25 22:12:15 +03:00
|
|
|
import urllib2
|
2013-10-16 21:13:00 +04:00
|
|
|
|
2015-04-24 04:02:56 +03:00
|
|
|
URL = 'https://pastebin.mozilla.org/'
|
2013-10-16 21:13:00 +04:00
|
|
|
|
|
|
|
FILE_TYPES = [{'value': 'text', 'name': 'None', 'extension': 'txt'},
|
2017-01-06 17:52:07 +03:00
|
|
|
{'value': 'bash', 'name': 'Bash', 'extension': 'sh'},
|
|
|
|
{'value': 'c', 'name': 'C', 'extension': 'c'},
|
|
|
|
{'value': 'cpp', 'name': 'C++', 'extension': 'cpp'},
|
|
|
|
{'value': 'html4strict', 'name': 'HTML', 'extension': 'html'},
|
|
|
|
{'value': 'javascript', 'name': 'Javascript', 'extension': 'js'},
|
|
|
|
{'value': 'javascript', 'name': 'Javascript', 'extension': 'jsm'},
|
|
|
|
{'value': 'lua', 'name': 'Lua', 'extension': 'lua'},
|
|
|
|
{'value': 'perl', 'name': 'Perl', 'extension': 'pl'},
|
|
|
|
{'value': 'php', 'name': 'PHP', 'extension': 'php'},
|
|
|
|
{'value': 'python', 'name': 'Python', 'extension': 'py'},
|
|
|
|
{'value': 'ruby', 'name': 'Ruby', 'extension': 'rb'},
|
|
|
|
{'value': 'css', 'name': 'CSS', 'extension': 'css'},
|
|
|
|
{'value': 'diff', 'name': 'Diff', 'extension': 'diff'},
|
|
|
|
{'value': 'ini', 'name': 'INI file', 'extension': 'ini'},
|
|
|
|
{'value': 'java', 'name': 'Java', 'extension': 'java'},
|
|
|
|
{'value': 'xml', 'name': 'XML', 'extension': 'xml'},
|
|
|
|
{'value': 'xml', 'name': 'XML', 'extension': 'xul'}]
|
2013-10-16 21:13:00 +04:00
|
|
|
|
|
|
|
lang = ''
|
|
|
|
|
|
|
|
if file:
|
|
|
|
try:
|
|
|
|
with open(file, 'r') as f:
|
|
|
|
content = f.read()
|
|
|
|
# TODO: Use mime-types instead of extensions; suprocess('file <f_name>')
|
|
|
|
# Guess File-type based on file extension
|
|
|
|
extension = file.split('.')[-1]
|
|
|
|
for l in FILE_TYPES:
|
|
|
|
if extension == l['extension']:
|
|
|
|
print('Identified file as %s' % l['name'])
|
|
|
|
lang = l['value']
|
|
|
|
except IOError:
|
|
|
|
print('ERROR. No such file')
|
|
|
|
return 1
|
|
|
|
else:
|
|
|
|
content = sys.stdin.read()
|
|
|
|
duration = duration[0]
|
|
|
|
|
|
|
|
if language:
|
|
|
|
lang = language
|
|
|
|
|
|
|
|
params = [
|
|
|
|
('parent_pid', ''),
|
|
|
|
('format', lang),
|
|
|
|
('code2', content),
|
|
|
|
('poster', poster),
|
|
|
|
('expiry', duration),
|
|
|
|
('paste', 'Send')]
|
|
|
|
|
|
|
|
data = urllib.urlencode(params)
|
|
|
|
print('Uploading ...')
|
|
|
|
try:
|
|
|
|
req = urllib2.Request(URL, data)
|
|
|
|
response = urllib2.urlopen(req)
|
|
|
|
http_response_code = response.getcode()
|
|
|
|
if http_response_code == 200:
|
|
|
|
print(response.geturl())
|
|
|
|
else:
|
|
|
|
print('Could not upload the file, '
|
2017-01-06 17:52:07 +03:00
|
|
|
'HTTP Response Code %s' % (http_response_code))
|
2013-10-16 21:13:00 +04:00
|
|
|
except urllib2.URLError:
|
|
|
|
print('ERROR. Could not connect to pastebin.mozilla.org.')
|
|
|
|
return 1
|
|
|
|
return 0
|
2013-12-06 07:35:52 +04:00
|
|
|
|
|
|
|
|
2014-01-08 05:51:44 +04:00
|
|
|
@CommandProvider
|
|
|
|
class FormatProvider(MachCommandBase):
|
2014-01-30 05:27:44 +04:00
|
|
|
@Command('clang-format', category='misc',
|
2017-01-06 17:52:07 +03:00
|
|
|
description='Run clang-format on current changes')
|
|
|
|
@CommandArgument('--show', '-s', action='store_true',
|
|
|
|
help='Show diff output on instead of applying changes')
|
2014-01-30 05:27:44 +04:00
|
|
|
def clang_format(self, show=False):
|
2015-06-25 22:12:15 +03:00
|
|
|
import urllib2
|
|
|
|
|
2014-01-28 05:11:46 +04:00
|
|
|
plat = platform.system()
|
2017-03-15 13:20:54 +03:00
|
|
|
fmt = plat.lower() + "/clang-format-5.0~svn297730"
|
|
|
|
fmt_diff = "clang-format-diff-5.0~svn297730"
|
2014-01-08 05:51:44 +04:00
|
|
|
|
2016-12-19 17:28:16 +03:00
|
|
|
# We are currently using an unmodified snapshot of upstream clang-format.
|
2017-03-15 13:20:54 +03:00
|
|
|
# This is a temporary work around until clang 5.0 has been released with our changes.
|
2014-01-08 05:51:44 +04:00
|
|
|
if plat == "Windows":
|
|
|
|
fmt += ".exe"
|
|
|
|
else:
|
|
|
|
arch = os.uname()[4]
|
2014-01-28 05:11:46 +04:00
|
|
|
if (plat != "Linux" and plat != "Darwin") or arch != 'x86_64':
|
2014-01-08 05:51:44 +04:00
|
|
|
print("Unsupported platform " + plat + "/" + arch +
|
2014-01-28 05:11:46 +04:00
|
|
|
". Supported platforms are Windows/*, Linux/x86_64 and Darwin/x86_64")
|
2014-01-08 05:51:44 +04:00
|
|
|
return 1
|
|
|
|
|
|
|
|
os.chdir(self.topsrcdir)
|
2014-01-28 05:11:46 +04:00
|
|
|
self.prompt = True
|
2014-01-08 05:51:44 +04:00
|
|
|
|
|
|
|
try:
|
|
|
|
if not self.locate_or_fetch(fmt):
|
|
|
|
return 1
|
2017-01-06 17:51:40 +03:00
|
|
|
clang_format_diff = self.locate_or_fetch(fmt_diff, python_script=True)
|
2014-01-08 05:51:44 +04:00
|
|
|
if not clang_format_diff:
|
|
|
|
return 1
|
|
|
|
|
|
|
|
except urllib2.HTTPError as e:
|
|
|
|
print("HTTP error {0}: {1}".format(e.code, e.reason))
|
|
|
|
return 1
|
|
|
|
|
|
|
|
from subprocess import Popen, PIPE
|
2014-01-30 05:27:44 +04:00
|
|
|
|
|
|
|
if os.path.exists(".hg"):
|
2017-03-21 00:31:15 +03:00
|
|
|
diff_process = Popen(["hg", "diff", "-U0", "-r", ".^",
|
2017-01-06 17:52:07 +03:00
|
|
|
"--include", "glob:**.c", "--include", "glob:**.cpp",
|
|
|
|
"--include", "glob:**.h",
|
2014-01-30 05:27:44 +04:00
|
|
|
"--exclude", "listfile:.clang-format-ignore"], stdout=PIPE)
|
|
|
|
else:
|
2016-12-19 17:28:16 +03:00
|
|
|
git_process = Popen(["git", "diff", "--no-color", "-U0", "HEAD^"], stdout=PIPE)
|
2014-03-21 08:43:28 +04:00
|
|
|
try:
|
|
|
|
diff_process = Popen(["filterdiff", "--include=*.h", "--include=*.cpp",
|
|
|
|
"--exclude-from-file=.clang-format-ignore"],
|
|
|
|
stdin=git_process.stdout, stdout=PIPE)
|
|
|
|
except OSError as e:
|
|
|
|
if e.errno == errno.ENOENT:
|
|
|
|
print("Can't find filterdiff. Please install patchutils.")
|
|
|
|
else:
|
|
|
|
print("OSError {0}: {1}".format(e.code, e.reason))
|
|
|
|
return 1
|
|
|
|
|
2014-01-30 05:27:44 +04:00
|
|
|
args = [sys.executable, clang_format_diff, "-p1"]
|
2014-01-30 05:27:44 +04:00
|
|
|
if not show:
|
2017-01-06 17:52:07 +03:00
|
|
|
args.append("-i")
|
2014-01-30 05:27:44 +04:00
|
|
|
cf_process = Popen(args, stdin=diff_process.stdout)
|
|
|
|
return cf_process.communicate()[0]
|
2014-01-08 05:51:44 +04:00
|
|
|
|
2017-01-06 17:51:40 +03:00
|
|
|
def locate_or_fetch(self, root, python_script=False):
|
2016-12-19 17:28:16 +03:00
|
|
|
import urllib2
|
2017-01-06 17:51:40 +03:00
|
|
|
import hashlib
|
|
|
|
bin_sha = {
|
2017-03-15 13:20:54 +03:00
|
|
|
"Windows": "0cbfc306df48f01bfe804e5e89cef73b3abe8f884fb7a5208f8895897f19ec45c13760787298192bd37de057d0ded091640c7d504438e06ec880f071a38db89c", # noqa: E501
|
|
|
|
"Linux": "e6da4f6df074bfb15caefcf7767eb5670c02bb4768ba86ae4ab6b35235b53db012900a4f9e9a950ee140158a19532a71f21b986f511826bebc16f2ef83984e57", # noqa: E501
|
|
|
|
"Darwin": "18000940a11e5ab0c1fe950d4360292216c8e963dd708679c4c5fb8cc845f5919cef3f58a7e092555b8ea6b8d8a809d66153ea6d1e7c226a2c4f2b0b7ad1b2f3", # noqa: E501
|
|
|
|
"python_script": "34b6934a48a263ea3f88d48c2981d61ae6698823cfa689b9b0c8a607c224437ca0b9fdd434d260bd790d52a98455e2c2e2c745490d327ba84b4e22b7bb55b757", # noqa: E501
|
2017-01-06 17:51:40 +03:00
|
|
|
}
|
|
|
|
|
2014-01-28 05:11:46 +04:00
|
|
|
target = os.path.join(self._mach_context.state_dir, os.path.basename(root))
|
2017-01-06 17:52:07 +03:00
|
|
|
|
2014-01-08 05:51:44 +04:00
|
|
|
if not os.path.exists(target):
|
2017-01-06 17:51:40 +03:00
|
|
|
tooltool_url = "https://api.pub.build.mozilla.org/tooltool/sha512/"
|
|
|
|
if self.prompt and raw_input("Download clang-format executables from {0} (yN)? ".format(tooltool_url)).lower() != 'y': # noqa: E501,F821
|
2014-01-08 05:51:44 +04:00
|
|
|
print("Download aborted.")
|
2016-12-23 01:15:00 +03:00
|
|
|
return None
|
2014-01-28 05:11:46 +04:00
|
|
|
self.prompt = False
|
2017-01-06 17:51:40 +03:00
|
|
|
plat = platform.system()
|
|
|
|
if python_script:
|
|
|
|
# We want to download the python script (clang-format-diff)
|
|
|
|
dl = bin_sha["python_script"]
|
|
|
|
else:
|
|
|
|
dl = bin_sha[plat]
|
|
|
|
u = tooltool_url + dl
|
2014-01-08 05:51:44 +04:00
|
|
|
print("Downloading {0} to {1}".format(u, target))
|
|
|
|
data = urllib2.urlopen(url=u).read()
|
|
|
|
temp = target + ".tmp"
|
2017-01-06 17:51:40 +03:00
|
|
|
# Check that the checksum of the downloaded data matches the hash
|
|
|
|
# of the file
|
|
|
|
sha512Hash = hashlib.sha512(data).hexdigest()
|
|
|
|
if sha512Hash != dl:
|
|
|
|
print("Checksum verification for {0} failed: {1} found instead of {2} ".format(target, sha512Hash, dl)) # noqa: E501
|
|
|
|
return 1
|
2014-01-08 05:51:44 +04:00
|
|
|
with open(temp, "wb") as fh:
|
|
|
|
fh.write(data)
|
|
|
|
fh.close()
|
|
|
|
os.chmod(temp, os.stat(temp).st_mode | stat.S_IXUSR | stat.S_IXGRP | stat.S_IXOTH)
|
|
|
|
os.rename(temp, target)
|
|
|
|
return target
|
|
|
|
|
2017-01-06 17:52:07 +03:00
|
|
|
|
2015-09-05 19:27:23 +03:00
|
|
|
def mozregression_import():
|
|
|
|
# Lazy loading of mozregression.
|
|
|
|
# Note that only the mach_interface module should be used from this file.
|
|
|
|
try:
|
|
|
|
import mozregression.mach_interface
|
|
|
|
except ImportError:
|
|
|
|
return None
|
|
|
|
return mozregression.mach_interface
|
|
|
|
|
|
|
|
|
|
|
|
def mozregression_create_parser():
|
|
|
|
# Create the mozregression command line parser.
|
|
|
|
# if mozregression is not installed, or not up to date, it will
|
|
|
|
# first be installed.
|
|
|
|
cmd = MozbuildObject.from_environment()
|
|
|
|
cmd._activate_virtualenv()
|
|
|
|
mozregression = mozregression_import()
|
|
|
|
if not mozregression:
|
|
|
|
# mozregression is not here at all, install it
|
|
|
|
cmd.virtualenv_manager.install_pip_package('mozregression')
|
|
|
|
print("mozregression was installed. please re-run your"
|
2015-10-05 23:08:35 +03:00
|
|
|
" command. If you keep getting this message please "
|
|
|
|
" manually run: 'pip install -U mozregression'.")
|
2015-09-05 19:27:23 +03:00
|
|
|
else:
|
|
|
|
# check if there is a new release available
|
|
|
|
release = mozregression.new_release_on_pypi()
|
|
|
|
if release:
|
|
|
|
print(release)
|
|
|
|
# there is one, so install it. Note that install_pip_package
|
|
|
|
# does not work here, so just run pip directly.
|
|
|
|
cmd.virtualenv_manager._run_pip([
|
|
|
|
'install',
|
|
|
|
'mozregression==%s' % release
|
|
|
|
])
|
|
|
|
print("mozregression was updated to version %s. please"
|
|
|
|
" re-run your command." % release)
|
|
|
|
else:
|
|
|
|
# mozregression is up to date, return the parser.
|
|
|
|
return mozregression.parser()
|
|
|
|
# exit if we updated or installed mozregression because
|
|
|
|
# we may have already imported mozregression and running it
|
|
|
|
# as this may cause issues.
|
|
|
|
sys.exit(0)
|
|
|
|
|
|
|
|
|
|
|
|
@CommandProvider
|
|
|
|
class MozregressionCommand(MachCommandBase):
|
|
|
|
@Command('mozregression',
|
|
|
|
category='misc',
|
|
|
|
description=("Regression range finder for nightly"
|
|
|
|
" and inbound builds."),
|
|
|
|
parser=mozregression_create_parser)
|
|
|
|
def run(self, **options):
|
|
|
|
self._activate_virtualenv()
|
|
|
|
mozregression = mozregression_import()
|
|
|
|
mozregression.run(options)
|