mirror of
https://github.com/mozilla/gecko-dev.git
synced 2024-12-02 10:00:54 +00:00
Backed out 8 changesets (bug 1569046, bug 1210157) for flake8 failure on base.py CLOSED TREE
Backed out changeset eec9458e553d (bug 1210157) Backed out changeset 06d452ac1d16 (bug 1210157) Backed out changeset 0f5311bbd75b (bug 1210157) Backed out changeset 975727f55b82 (bug 1569046) Backed out changeset a65032b442bc (bug 1210157) Backed out changeset 25aef37f3556 (bug 1210157) Backed out changeset 5d26b3d7d4a0 (bug 1210157) Backed out changeset f044d6116869 (bug 1210157)
This commit is contained in:
parent
3284eefc86
commit
4ab1c12e2b
@ -10,39 +10,35 @@
|
||||
# bootstrap support. It does this through various means, including fetching
|
||||
# content from the upstream source repository.
|
||||
|
||||
from __future__ import absolute_import, print_function, unicode_literals
|
||||
# If we add unicode_literals, optparse breaks on Python 2.6.1 (which is needed
|
||||
# to support OS X 10.6).
|
||||
|
||||
from __future__ import absolute_import, print_function
|
||||
|
||||
WRONG_PYTHON_VERSION_MESSAGE = '''
|
||||
Bootstrap currently only runs on Python 2.7 or Python 3.5+.
|
||||
Please try re-running with python2.7 or python3.5+.
|
||||
Bootstrap currently only runs on Python 2.7 or Python 2.6.
|
||||
Please try re-running with python2.7 or python2.6.
|
||||
|
||||
If these aren't available on your system, you may need to install them.
|
||||
Look for a "python2" or "python3.5" package in your package manager.
|
||||
Look for a "python2" or "python27" package in your package manager.
|
||||
'''
|
||||
|
||||
import sys
|
||||
|
||||
major, minor = sys.version_info[:2]
|
||||
if (major == 2 and minor < 7) or (major == 3 and minor < 5):
|
||||
if sys.version_info[:2] not in [(2, 6), (2, 7)]:
|
||||
print(WRONG_PYTHON_VERSION_MESSAGE)
|
||||
sys.exit(1)
|
||||
|
||||
import os
|
||||
import shutil
|
||||
from StringIO import StringIO
|
||||
import tempfile
|
||||
import zipfile
|
||||
|
||||
from io import BytesIO
|
||||
from optparse import OptionParser
|
||||
|
||||
# NOTE: This script is intended to be run with a vanilla Python install. We
|
||||
# have to rely on the standard library instead of Python 2+3 helpers like
|
||||
# the six module.
|
||||
try:
|
||||
from urllib2 import urlopen
|
||||
except ImportError:
|
||||
from urllib.request import urlopen
|
||||
import zipfile
|
||||
|
||||
from optparse import OptionParser
|
||||
|
||||
# The next two variables define where in the repository the Python files
|
||||
# reside. This is used to remotely download file content when it isn't
|
||||
@ -71,7 +67,7 @@ def fetch_files(repo_url, repo_rev, repo_type):
|
||||
if repo_type == 'hgweb':
|
||||
url = repo_url + '/archive/%s.zip/python/mozboot' % repo_rev
|
||||
req = urlopen(url=url, timeout=30)
|
||||
data = BytesIO(req.read())
|
||||
data = StringIO(req.read())
|
||||
data.seek(0)
|
||||
zip = zipfile.ZipFile(data, 'r')
|
||||
for f in zip.infolist():
|
||||
@ -158,9 +154,6 @@ def main(args):
|
||||
'instead of using the default interactive prompt.')
|
||||
parser.add_option('--no-interactive', dest='no_interactive', action='store_true',
|
||||
help='Answer yes to any (Y/n) interactive prompts.')
|
||||
parser.add_option('--debug', dest='debug', action='store_true',
|
||||
help='Print extra runtime information useful for debugging and '
|
||||
'bug reports.')
|
||||
|
||||
options, leftover = parser.parse_args(args)
|
||||
|
||||
@ -172,14 +165,8 @@ def main(args):
|
||||
print('Could not load the bootstrap Python environment.\n')
|
||||
print('This should never happen. Consider filing a bug.\n')
|
||||
print('\n')
|
||||
|
||||
if options.debug:
|
||||
# Raise full tracebacks during debugging and for bug reporting.
|
||||
raise
|
||||
|
||||
print(e)
|
||||
return 1
|
||||
|
||||
dasboot = cls(choice=options.application_choice, no_interactive=options.no_interactive,
|
||||
vcs=options.vcs)
|
||||
dasboot.bootstrap()
|
||||
|
@ -2,7 +2,8 @@
|
||||
# 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/.
|
||||
|
||||
from __future__ import absolute_import, print_function, unicode_literals
|
||||
# If we add unicode_literals, Python 2.6.1 (required for OS X 10.6) breaks.
|
||||
from __future__ import absolute_import, print_function
|
||||
|
||||
import errno
|
||||
import os
|
||||
@ -280,7 +281,7 @@ def ensure_android_packages(sdkmanager_tool, packages=None, no_interactive=False
|
||||
|
||||
# Emulate yes. For a discussion of passing input to check_output,
|
||||
# see https://stackoverflow.com/q/10103551.
|
||||
yes = '\n'.join(['y']*100).encode("UTF-8")
|
||||
yes = '\n'.join(['y']*100)
|
||||
proc = subprocess.Popen(args,
|
||||
stdout=subprocess.PIPE,
|
||||
stderr=subprocess.STDOUT,
|
||||
|
@ -2,7 +2,7 @@
|
||||
# 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/.
|
||||
|
||||
from __future__ import absolute_import, print_function, unicode_literals
|
||||
from __future__ import absolute_import, print_function
|
||||
|
||||
import os
|
||||
import sys
|
||||
@ -18,12 +18,6 @@ from mozboot.linux_common import (
|
||||
StyloInstall,
|
||||
)
|
||||
|
||||
# NOTE: This script is intended to be run with a vanilla Python install. We
|
||||
# have to rely on the standard library instead of Python 2+3 helpers like
|
||||
# the six module.
|
||||
if sys.version_info < (3,):
|
||||
input = raw_input
|
||||
|
||||
|
||||
class ArchlinuxBootstrapper(NodeInstall, StyloInstall, SccacheInstall,
|
||||
ClangStaticAnalysisInstall, BaseBootstrapper):
|
||||
@ -196,7 +190,7 @@ class ArchlinuxBootstrapper(NodeInstall, StyloInstall, SccacheInstall,
|
||||
'This is potentially unsecure so I recommend that you carefully '
|
||||
'read each package description and check the sources.'
|
||||
'These packages will be built in ' + path + '.')
|
||||
choice = input('Do you want to continue? (yes/no) [no]')
|
||||
choice = raw_input('Do you want to continue? (yes/no) [no]')
|
||||
if choice != 'yes':
|
||||
sys.exit(1)
|
||||
|
||||
|
@ -9,21 +9,11 @@ import os
|
||||
import re
|
||||
import subprocess
|
||||
import sys
|
||||
import urllib2
|
||||
|
||||
from distutils.version import LooseVersion
|
||||
from mozboot import rust
|
||||
|
||||
# NOTE: This script is intended to be run with a vanilla Python install. We
|
||||
# have to rely on the standard library instead of Python 2+3 helpers like
|
||||
# the six module.
|
||||
if sys.version_info < (3,):
|
||||
from urllib2 import urlopen
|
||||
input = raw_input
|
||||
else:
|
||||
from urllib.request import urlopen
|
||||
|
||||
|
||||
|
||||
NO_MERCURIAL = '''
|
||||
Could not find Mercurial (hg) in the current shell's path. Try starting a new
|
||||
shell and running the bootstrapper again.
|
||||
@ -413,18 +403,30 @@ class BaseBootstrapper(object):
|
||||
|
||||
def check_output(self, *args, **kwargs):
|
||||
"""Run subprocess.check_output even if Python doesn't provide it."""
|
||||
# TODO Legacy Python 2.6 code, can be removed.
|
||||
# We had a custom check_output() function for Python 2.6 backward
|
||||
# compatibility. Since py2.6 support was dropped we can remove this
|
||||
# method.
|
||||
return subprocess.check_output(*args, **kwargs)
|
||||
fn = getattr(subprocess, 'check_output', BaseBootstrapper._check_output)
|
||||
|
||||
return fn(*args, **kwargs)
|
||||
|
||||
@staticmethod
|
||||
def _check_output(*args, **kwargs):
|
||||
"""Python 2.6 compatible implementation of subprocess.check_output."""
|
||||
proc = subprocess.Popen(stdout=subprocess.PIPE, *args, **kwargs)
|
||||
output, unused_err = proc.communicate()
|
||||
retcode = proc.poll()
|
||||
if retcode:
|
||||
cmd = kwargs.get('args', args[0])
|
||||
e = subprocess.CalledProcessError(retcode, cmd)
|
||||
e.output = output
|
||||
raise e
|
||||
|
||||
return output
|
||||
|
||||
def prompt_int(self, prompt, low, high, limit=5):
|
||||
''' Prompts the user with prompt and requires an integer between low and high. '''
|
||||
valid = False
|
||||
while not valid and limit > 0:
|
||||
try:
|
||||
choice = int(input(prompt))
|
||||
choice = int(raw_input(prompt))
|
||||
if not low <= choice <= high:
|
||||
print("ERROR! Please enter a valid option!")
|
||||
limit -= 1
|
||||
@ -443,7 +445,7 @@ class BaseBootstrapper(object):
|
||||
''' Prompts the user with prompt and requires a yes/no answer.'''
|
||||
valid = False
|
||||
while not valid:
|
||||
choice = input(prompt + ' (Yn): ').strip().lower()[:1]
|
||||
choice = raw_input(prompt + ' (Yn): ').strip().lower()[:1]
|
||||
if choice == '':
|
||||
choice = 'y'
|
||||
if choice not in ('y', 'n'):
|
||||
@ -488,8 +490,7 @@ class BaseBootstrapper(object):
|
||||
|
||||
info = self.check_output([path, version_param],
|
||||
env=env,
|
||||
stderr=subprocess.STDOUT,
|
||||
universal_newlines=True)
|
||||
stderr=subprocess.STDOUT)
|
||||
match = re.search(name + ' ([a-z0-9\.]+)', info)
|
||||
if not match:
|
||||
print('ERROR! Unable to identify %s version.' % name)
|
||||
@ -702,9 +703,7 @@ class BaseBootstrapper(object):
|
||||
|
||||
def ensure_rust_targets(self, rustup, rust_version):
|
||||
"""Make sure appropriate cross target libraries are installed."""
|
||||
target_list = subprocess.check_output(
|
||||
[rustup, 'target', 'list'], universal_newlines=True
|
||||
)
|
||||
target_list = subprocess.check_output([rustup, 'target', 'list'])
|
||||
targets = [line.split()[0] for line in target_list.splitlines()
|
||||
if 'installed' in line or 'default' in line]
|
||||
print('Rust supports %s targets.' % ', '.join(targets))
|
||||
@ -773,7 +772,7 @@ class BaseBootstrapper(object):
|
||||
that will be used to validate the downloaded file using the given
|
||||
digest algorithm. The value of digest can be any value accepted by
|
||||
hashlib.new. The default digest used is 'sha256'."""
|
||||
f = urlopen(url)
|
||||
f = urllib2.urlopen(url)
|
||||
h = hashlib.new(digest)
|
||||
with open(dest, 'wb') as out:
|
||||
while True:
|
||||
@ -813,8 +812,7 @@ class BaseBootstrapper(object):
|
||||
output = subprocess.check_output([java,
|
||||
'-XshowSettings:properties',
|
||||
'-version'],
|
||||
stderr=subprocess.STDOUT,
|
||||
universal_newlines=True).rstrip()
|
||||
stderr=subprocess.STDOUT).rstrip()
|
||||
|
||||
# -version strings are pretty free-form, like: 'java version
|
||||
# "1.8.0_192"' or 'openjdk version "11.0.1" 2018-10-16', but the
|
||||
|
@ -2,23 +2,19 @@
|
||||
# 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/.
|
||||
|
||||
from __future__ import absolute_import, print_function, unicode_literals
|
||||
# If we add unicode_literals, Python 2.6.1 (required for OS X 10.6) breaks.
|
||||
from __future__ import absolute_import, print_function
|
||||
|
||||
import platform
|
||||
import sys
|
||||
import os
|
||||
import subprocess
|
||||
|
||||
# NOTE: This script is intended to be run with a vanilla Python install. We
|
||||
# have to rely on the standard library instead of Python 2+3 helpers like
|
||||
# the six module.
|
||||
if sys.version_info < (3,):
|
||||
try:
|
||||
from ConfigParser import (
|
||||
Error as ConfigParserError,
|
||||
RawConfigParser,
|
||||
)
|
||||
input = raw_input
|
||||
else:
|
||||
except ImportError:
|
||||
from configparser import (
|
||||
Error as ConfigParserError,
|
||||
RawConfigParser,
|
||||
@ -62,7 +58,6 @@ APPLICATIONS_LIST = [
|
||||
('GeckoView/Firefox for Android', 'mobile_android'),
|
||||
]
|
||||
|
||||
# TODO Legacy Python 2.6 code, can be removed.
|
||||
# This is a workaround for the fact that we must support python2.6 (which has
|
||||
# no OrderedDict)
|
||||
APPLICATIONS = dict(
|
||||
@ -223,7 +218,7 @@ def update_or_create_build_telemetry_config(path):
|
||||
if not config.has_section('build'):
|
||||
config.add_section('build')
|
||||
config.set('build', 'telemetry', 'true')
|
||||
with open(path, 'w') as f:
|
||||
with open(path, 'wb') as f:
|
||||
config.write(f)
|
||||
return True
|
||||
|
||||
@ -303,7 +298,7 @@ class Bootstrapper(object):
|
||||
print(CLONE_VCS.format(repo_name, vcs))
|
||||
|
||||
while True:
|
||||
dest = input(CLONE_VCS_PROMPT.format(vcs))
|
||||
dest = raw_input(CLONE_VCS_PROMPT.format(vcs))
|
||||
dest = dest.strip()
|
||||
if not dest:
|
||||
return ''
|
||||
@ -648,8 +643,7 @@ def current_firefox_checkout(check_output, env, hg=None):
|
||||
try:
|
||||
node = check_output([hg, 'log', '-r', '0', '--template', '{node}'],
|
||||
cwd=path,
|
||||
env=env,
|
||||
universal_newlines=True)
|
||||
env=env)
|
||||
if node in HG_ROOT_REVISIONS:
|
||||
return ('hg', path)
|
||||
# Else the root revision is different. There could be nested
|
||||
|
@ -2,7 +2,7 @@
|
||||
# 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/.
|
||||
|
||||
from __future__ import absolute_import, print_function, unicode_literals
|
||||
from __future__ import absolute_import
|
||||
|
||||
import platform
|
||||
|
||||
|
@ -2,7 +2,7 @@
|
||||
# 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/.
|
||||
|
||||
from __future__ import absolute_import, print_function, unicode_literals
|
||||
from __future__ import absolute_import, print_function
|
||||
|
||||
from mozboot.base import BaseBootstrapper
|
||||
from mozboot.linux_common import (
|
||||
@ -109,8 +109,8 @@ class DebianBootstrapper(NasmInstall, NodeInstall, StyloInstall, ClangStaticAnal
|
||||
self.which('python3.5')])
|
||||
|
||||
if not have_python3:
|
||||
python3_packages = self.check_output(
|
||||
['apt-cache', 'pkgnames', 'python3'], universal_newlines=True)
|
||||
python3_packages = self.check_output([
|
||||
'apt-cache', 'pkgnames', 'python3'])
|
||||
python3_packages = python3_packages.splitlines()
|
||||
|
||||
if 'python3' in python3_packages:
|
||||
|
@ -2,7 +2,7 @@
|
||||
# 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/.
|
||||
|
||||
from __future__ import absolute_import, print_function, unicode_literals
|
||||
from __future__ import absolute_import
|
||||
import sys
|
||||
|
||||
from mozboot.base import BaseBootstrapper
|
||||
|
@ -2,7 +2,7 @@
|
||||
# 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/.
|
||||
|
||||
from __future__ import absolute_import, print_function, unicode_literals
|
||||
from __future__ import absolute_import
|
||||
|
||||
from mozboot.base import BaseBootstrapper
|
||||
from mozboot.linux_common import (
|
||||
@ -54,8 +54,7 @@ class GentooBootstrapper(NasmInstall, NodeInstall, StyloInstall, ClangStaticAnal
|
||||
@staticmethod
|
||||
def _get_distdir():
|
||||
# Obtain the path held in the DISTDIR portage variable
|
||||
output = subprocess.check_output(
|
||||
['emerge', '--info'], universal_newlines=True)
|
||||
output = subprocess.check_output(['emerge', '--info'])
|
||||
match = re.search('^DISTDIR="(?P<distdir>.*)"$', output, re.MULTILINE)
|
||||
return match.group('distdir')
|
||||
|
||||
@ -99,8 +98,7 @@ class GentooBootstrapper(NasmInstall, NodeInstall, StyloInstall, ClangStaticAnal
|
||||
output = self.check_output(['emerge', '--pretend', '--fetchonly',
|
||||
'oracle-jdk-bin'],
|
||||
env=None,
|
||||
stderr=subprocess.STDOUT,
|
||||
universal_newlines=True)
|
||||
stderr=subprocess.STDOUT)
|
||||
except subprocess.CalledProcessError as e:
|
||||
output = e.output
|
||||
|
||||
|
@ -6,7 +6,7 @@
|
||||
# needed to install Stylo and Node dependencies. This class must come before
|
||||
# BaseBootstrapper in the inheritance list.
|
||||
|
||||
from __future__ import absolute_import, print_function, unicode_literals
|
||||
from __future__ import absolute_import, print_function
|
||||
|
||||
import os
|
||||
|
||||
|
@ -2,7 +2,7 @@
|
||||
# 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/.
|
||||
|
||||
from __future__ import absolute_import, print_function, unicode_literals
|
||||
from __future__ import absolute_import, print_function
|
||||
|
||||
import ctypes
|
||||
import os
|
||||
|
@ -2,7 +2,7 @@
|
||||
# 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/.
|
||||
|
||||
from __future__ import absolute_import, print_function, unicode_literals
|
||||
from __future__ import absolute_import
|
||||
|
||||
from mozboot.base import BaseBootstrapper
|
||||
|
||||
|
@ -267,8 +267,7 @@ class OSXBootstrapper(BaseBootstrapper):
|
||||
print(INSTALL_XCODE_COMMAND_LINE_TOOLS_STEPS)
|
||||
sys.exit(1)
|
||||
|
||||
output = self.check_output(['/usr/bin/clang', '--version'],
|
||||
universal_newlines=True)
|
||||
output = self.check_output(['/usr/bin/clang', '--version'])
|
||||
match = RE_CLANG_VERSION.search(output)
|
||||
if match is None:
|
||||
raise Exception('Could not determine Clang version.')
|
||||
@ -296,8 +295,7 @@ class OSXBootstrapper(BaseBootstrapper):
|
||||
self._ensure_homebrew_found()
|
||||
cmd = [self.brew] + extra_brew_args
|
||||
|
||||
installed = self.check_output(cmd + ['list'],
|
||||
universal_newlines=True).split()
|
||||
installed = self.check_output(cmd + ['list']).split()
|
||||
|
||||
printed = False
|
||||
|
||||
@ -388,10 +386,7 @@ class OSXBootstrapper(BaseBootstrapper):
|
||||
self.port = self.which('port')
|
||||
assert self.port is not None
|
||||
|
||||
installed = set(
|
||||
self.check_output(
|
||||
[self.port, 'installed'],
|
||||
universal_newlines=True).split())
|
||||
installed = set(self.check_output([self.port, 'installed']).split())
|
||||
|
||||
missing = [package for package in packages if package not in installed]
|
||||
if missing:
|
||||
@ -412,10 +407,7 @@ class OSXBootstrapper(BaseBootstrapper):
|
||||
|
||||
self._ensure_macports_packages(packages)
|
||||
|
||||
pythons = set(
|
||||
self.check_output(
|
||||
[self.port, 'select', '--list', 'python'],
|
||||
universal_newlines=True).split('\n'))
|
||||
pythons = set(self.check_output([self.port, 'select', '--list', 'python']).split('\n'))
|
||||
active = ''
|
||||
for python in pythons:
|
||||
if 'active' in python:
|
||||
@ -467,7 +459,7 @@ class OSXBootstrapper(BaseBootstrapper):
|
||||
one.
|
||||
'''
|
||||
installed = []
|
||||
for name, cmd in PACKAGE_MANAGER.items():
|
||||
for name, cmd in PACKAGE_MANAGER.iteritems():
|
||||
if self.which(cmd) is not None:
|
||||
installed.append(name)
|
||||
|
||||
|
@ -2,7 +2,8 @@
|
||||
# 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/.
|
||||
|
||||
from __future__ import absolute_import, print_function, unicode_literals
|
||||
# If we add unicode_literals, Python 2.6.1 (required for OS X 10.6) breaks.
|
||||
from __future__ import absolute_import, print_function
|
||||
|
||||
import os
|
||||
import sys
|
||||
|
@ -1,3 +1,4 @@
|
||||
[DEFAULT]
|
||||
skip-if = python == 3
|
||||
|
||||
[test_write_config.py]
|
||||
|
@ -2,7 +2,7 @@
|
||||
# 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/.
|
||||
|
||||
from __future__ import absolute_import, print_function, unicode_literals
|
||||
from __future__ import absolute_import
|
||||
|
||||
import mozunit
|
||||
import pytest
|
||||
@ -41,13 +41,13 @@ def read(path):
|
||||
|
||||
@pytest.fixture
|
||||
def config_path(tmpdir):
|
||||
return str(tmpdir.join('machrc'))
|
||||
return unicode(tmpdir.join('machrc'))
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def write_config(config_path):
|
||||
def _config(contents):
|
||||
with open(config_path, 'w') as f:
|
||||
with open(config_path, 'wb') as f:
|
||||
f.write(contents)
|
||||
return _config
|
||||
|
||||
|
@ -2,7 +2,7 @@
|
||||
# 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/.
|
||||
|
||||
from __future__ import absolute_import, print_function, unicode_literals
|
||||
from __future__ import absolute_import, print_function
|
||||
|
||||
import hashlib
|
||||
import os
|
||||
|
@ -2,7 +2,7 @@
|
||||
# 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/.
|
||||
|
||||
from __future__ import absolute_import, print_function, unicode_literals
|
||||
from __future__ import absolute_import, print_function
|
||||
|
||||
import ctypes
|
||||
import os
|
||||
|
@ -2,7 +2,7 @@
|
||||
# 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/.
|
||||
|
||||
from __future__ import absolute_import, print_function, unicode_literals
|
||||
from __future__ import absolute_import
|
||||
|
||||
from distutils.core import setup
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user