mitmproxy/release/rtool.py

335 lines
11 KiB
Python
Raw Normal View History

#!/usr/bin/env python3
2015-11-29 01:46:08 +00:00
import contextlib
import fnmatch
2015-11-29 01:46:08 +00:00
import os
import platform
import runpy
import shlex
2015-11-29 01:46:08 +00:00
import shutil
import subprocess
import sys
2015-11-29 02:38:23 +00:00
import tarfile
import zipfile
2016-07-30 02:07:48 +00:00
from os.path import join, abspath, normpath, dirname, exists, basename
2015-11-29 01:46:08 +00:00
import click
2016-02-11 17:51:47 +00:00
import pysftp
2015-11-29 01:46:08 +00:00
# https://virtualenv.pypa.io/en/latest/userguide.html#windows-notes
# scripts and executables on Windows go in ENV\Scripts\ instead of ENV/bin/
if platform.system() == "Windows":
VENV_BIN = "Scripts"
else:
VENV_BIN = "bin"
# ZipFile and tarfile have slightly different APIs. Fix that.
2015-11-29 01:46:08 +00:00
if platform.system() == "Windows":
def Archive(name):
2016-02-11 17:51:47 +00:00
a = zipfile.ZipFile(name, "w")
2015-11-29 01:46:08 +00:00
a.add = a.write
return a
else:
def Archive(name):
2016-02-11 17:51:47 +00:00
return tarfile.open(name, "w:gz")
2015-11-29 01:46:08 +00:00
2016-07-30 02:07:48 +00:00
ROOT_DIR = abspath(join(dirname(__file__), ".."))
RELEASE_DIR = join(ROOT_DIR, "release")
2015-11-29 01:46:08 +00:00
BUILD_DIR = join(RELEASE_DIR, "build")
DIST_DIR = join(RELEASE_DIR, "dist")
PYINSTALLER_SPEC = join(RELEASE_DIR, "specs")
2015-11-29 01:46:08 +00:00
PYINSTALLER_TEMP = join(BUILD_DIR, "pyinstaller")
PYINSTALLER_DIST = join(BUILD_DIR, "binaries")
VENV_DIR = join(BUILD_DIR, "venv")
VENV_PIP = join(VENV_DIR, VENV_BIN, "pip")
VENV_PYINSTALLER = join(VENV_DIR, VENV_BIN, "pyinstaller")
# Project Configuration
VERSION_FILE = join(ROOT_DIR, "mitmproxy", "version.py")
PROJECT_NAME = "mitmproxy"
PYTHON_VERSION = "py2.py3"
BDISTS = {
"mitmproxy": ["mitmproxy", "mitmdump", "mitmweb"],
"pathod": ["pathoc", "pathod"]
2015-11-29 01:46:08 +00:00
}
if platform.system() == "Windows":
BDISTS["mitmproxy"].remove("mitmproxy")
TOOLS = [
tool
for tools in BDISTS.values()
for tool in tools
]
2015-11-29 01:46:08 +00:00
2015-12-03 16:55:42 +00:00
def get_version() -> str:
2016-02-15 23:22:38 +00:00
return runpy.run_path(VERSION_FILE)["VERSION"]
2015-11-29 01:46:08 +00:00
2015-12-03 16:55:42 +00:00
def git(args: str) -> str:
with chdir(ROOT_DIR):
return subprocess.check_output(["git"] + shlex.split(args)).decode()
def get_snapshot_version() -> str:
last_tag, tag_dist, commit = git("describe --tags --long").strip().rsplit("-", 2)
2016-02-11 17:51:47 +00:00
tag_dist = int(tag_dist)
if tag_dist == 0:
2016-02-15 23:22:38 +00:00
return get_version()
2016-02-11 17:51:47 +00:00
else:
2016-03-31 16:07:47 +00:00
# The wheel build tag (we use the commit) must start with a digit, so we include "0x"
return "{version}dev{tag_dist:04}-0x{commit}".format(
2016-02-15 23:22:38 +00:00
version=get_version(), # this should already be the next version
2016-02-11 17:51:47 +00:00
tag_dist=tag_dist,
commit=commit
2016-02-11 17:51:47 +00:00
)
def archive_name(bdist: str) -> str:
2016-02-11 17:51:47 +00:00
platform_tag = {
"Darwin": "osx",
"Windows": "win32",
"Linux": "linux"
}.get(platform.system(), platform.system())
if platform.system() == "Windows":
ext = "zip"
else:
ext = "tar.gz"
return "{project}-{version}-{platform}.{ext}".format(
project=bdist,
2016-02-15 23:22:38 +00:00
version=get_version(),
2016-02-11 17:51:47 +00:00
platform=platform_tag,
ext=ext
)
def wheel_name() -> str:
2016-02-11 17:51:47 +00:00
return "{project}-{version}-{py_version}-none-any.whl".format(
project=PROJECT_NAME,
2016-02-15 23:22:38 +00:00
version=get_version(),
py_version=PYTHON_VERSION
2015-11-29 18:05:58 +00:00
)
2015-11-29 01:46:08 +00:00
2015-12-03 16:55:42 +00:00
2015-11-29 01:46:08 +00:00
@contextlib.contextmanager
def empty_pythonpath():
"""
Make sure that the regular python installation is not on the python path,
which would give us access to modules installed outside of our virtualenv.
"""
2015-12-03 16:55:42 +00:00
pythonpath = os.environ.get("PYTHONPATH", "")
2015-11-29 01:46:08 +00:00
os.environ["PYTHONPATH"] = ""
yield
os.environ["PYTHONPATH"] = pythonpath
@contextlib.contextmanager
def chdir(path: str):
2015-11-29 01:46:08 +00:00
old_dir = os.getcwd()
os.chdir(path)
yield
os.chdir(old_dir)
@click.group(chain=True)
def cli():
2015-11-29 01:46:08 +00:00
"""
mitmproxy build tool
"""
pass
2015-11-29 01:46:08 +00:00
2015-12-03 16:55:42 +00:00
2015-11-29 01:46:08 +00:00
@cli.command("contributors")
def contributors():
"""
Update CONTRIBUTORS.md
"""
2016-02-15 23:22:38 +00:00
with chdir(ROOT_DIR):
print("Updating CONTRIBUTORS...")
contributors_data = git("shortlog -n -s")
with open("CONTRIBUTORS", "wb") as f:
f.write(contributors_data.encode())
2015-11-29 01:46:08 +00:00
2015-12-03 16:55:42 +00:00
@cli.command("wheel")
def make_wheel():
2015-11-29 01:46:08 +00:00
"""
Build wheel
2015-11-29 01:46:08 +00:00
"""
with empty_pythonpath():
if exists(DIST_DIR):
2015-11-29 01:46:08 +00:00
shutil.rmtree(DIST_DIR)
print("Creating wheel...")
subprocess.check_call(
[
"python", "./setup.py", "-q",
2016-06-27 05:13:42 +00:00
"bdist_wheel", "--dist-dir", DIST_DIR, "--universal"
],
cwd=ROOT_DIR
)
2015-11-29 01:46:08 +00:00
print("Creating virtualenv for test install...")
if exists(VENV_DIR):
2015-11-29 01:46:08 +00:00
shutil.rmtree(VENV_DIR)
subprocess.check_call(["virtualenv", "-q", VENV_DIR])
with chdir(DIST_DIR):
print("Install wheel into virtualenv...")
2016-06-27 05:13:42 +00:00
# lxml...
2016-06-27 05:25:13 +00:00
if platform.system() == "Windows" and sys.version_info[0] == 3:
subprocess.check_call(
[VENV_PIP, "install", "-q", "https://snapshots.mitmproxy.org/misc/lxml-3.6.0-cp35-cp35m-win32.whl"]
)
subprocess.check_call([VENV_PIP, "install", "-q", wheel_name()])
2015-11-29 01:46:08 +00:00
print("Running tools...")
for tool in TOOLS:
tool = join(VENV_DIR, VENV_BIN, tool)
print("> %s --version" % tool)
print(subprocess.check_output([tool, "--version"]).decode())
2015-11-29 01:46:08 +00:00
print("Virtualenv available for further testing:")
print("source %s" % normpath(join(VENV_DIR, VENV_BIN, "activate")))
2015-11-29 01:46:08 +00:00
@cli.command("bdist")
@click.option("--use-existing-wheel/--no-use-existing-wheel", default=False)
2016-02-06 00:22:27 +00:00
@click.argument("pyinstaller_version", envvar="PYINSTALLER_VERSION", default="PyInstaller~=3.1.1")
@click.argument("setuptools_version", envvar="SETUPTOOLS_VERSION", default="setuptools>=25.1.0,!=25.1.1")
2015-11-29 01:46:08 +00:00
@click.pass_context
def make_bdist(ctx, use_existing_wheel, pyinstaller_version, setuptools_version):
2015-11-29 01:46:08 +00:00
"""
Build a binary distribution
"""
if exists(PYINSTALLER_TEMP):
2015-11-29 01:46:08 +00:00
shutil.rmtree(PYINSTALLER_TEMP)
if exists(PYINSTALLER_DIST):
2015-11-29 01:46:08 +00:00
shutil.rmtree(PYINSTALLER_DIST)
if not use_existing_wheel:
ctx.invoke(make_wheel)
2015-11-29 01:46:08 +00:00
print("Installing PyInstaller and setuptools...")
subprocess.check_call([VENV_PIP, "install", "-q", pyinstaller_version, setuptools_version])
print(subprocess.check_output([VENV_PIP, "freeze"]).decode())
2015-11-29 01:46:08 +00:00
for bdist, tools in BDISTS.items():
with Archive(join(DIST_DIR, archive_name(bdist))) as archive:
2016-02-27 13:26:54 +00:00
for tool in tools:
2016-03-12 17:55:25 +00:00
# This is PyInstaller, so it messes up paths.
# We need to make sure that we are in the spec folder.
with chdir(PYINSTALLER_SPEC):
2016-03-12 17:55:25 +00:00
print("Building %s binary..." % tool)
subprocess.check_call(
[
VENV_PYINSTALLER,
"--clean",
"--workpath", PYINSTALLER_TEMP,
"--distpath", PYINSTALLER_DIST,
# This is PyInstaller, so setting a
# different log level obviously breaks it :-)
# "--log-level", "WARN",
"%s.spec" % tool
]
)
# Test if it works at all O:-)
executable = join(PYINSTALLER_DIST, tool)
if platform.system() == "Windows":
executable += ".exe"
print("> %s --version" % executable)
print(subprocess.check_output([executable, "--version"]).decode())
archive.add(executable, basename(executable))
print("Packed {}.".format(archive_name(bdist)))
2015-11-29 01:46:08 +00:00
2016-02-11 17:51:47 +00:00
@cli.command("upload-release")
2015-11-29 01:46:08 +00:00
@click.option('--username', prompt=True)
@click.password_option(confirmation_prompt=False)
@click.option('--repository', default="pypi")
def upload_release(username, password, repository):
2015-11-29 01:46:08 +00:00
"""
Upload wheels to PyPI
2015-11-29 01:46:08 +00:00
"""
2016-02-27 13:26:54 +00:00
filename = wheel_name()
print("Uploading {} to {}...".format(filename, repository))
subprocess.check_call([
"twine",
"upload",
"-u", username,
"-p", password,
"-r", repository,
join(DIST_DIR, filename)
])
2015-11-29 01:46:08 +00:00
2016-02-11 17:51:47 +00:00
@cli.command("upload-snapshot")
@click.option("--host", envvar="SNAPSHOT_HOST", prompt=True)
@click.option("--port", envvar="SNAPSHOT_PORT", type=int, default=22)
@click.option("--user", envvar="SNAPSHOT_USER", prompt=True)
@click.option("--private-key", default=join(RELEASE_DIR, "rtool.pem"))
@click.option("--private-key-password", envvar="SNAPSHOT_PASS", prompt=True, hide_input=True)
2016-02-11 17:51:47 +00:00
@click.option("--wheel/--no-wheel", default=False)
@click.option("--bdist/--no-bdist", default=False)
def upload_snapshot(host, port, user, private_key, private_key_password, wheel, bdist):
2016-02-11 17:51:47 +00:00
"""
Upload snapshot to snapshot server
"""
with pysftp.Connection(host=host,
port=port,
username=user,
private_key=private_key,
private_key_pass=private_key_password) as sftp:
dir_name = "snapshots/v{}".format(get_version())
sftp.makedirs(dir_name)
with sftp.cd(dir_name):
files = []
if wheel:
files.append(wheel_name())
if bdist:
for bdist in BDISTS.keys():
2016-02-27 13:26:54 +00:00
files.append(archive_name(bdist))
2016-02-11 17:51:47 +00:00
for f in files:
local_path = join(DIST_DIR, f)
remote_filename = f.replace(get_version(), get_snapshot_version())
symlink_path = "../{}".format(f.replace(get_version(), "latest"))
# Upload new version
print("Uploading {} as {}...".format(f, remote_filename))
with click.progressbar(length=os.stat(local_path).st_size) as bar:
# We hide the file during upload
sftp.put(
local_path,
"." + remote_filename,
callback=lambda done, total: bar.update(done - bar.pos)
)
2016-02-11 17:51:47 +00:00
# Delete old versions
old_version = f.replace(get_version(), "*")
for f_old in sftp.listdir():
if fnmatch.fnmatch(f_old, old_version):
print("Removing {}...".format(f_old))
sftp.remove(f_old)
# Show new version
sftp.rename("." + remote_filename, remote_filename)
# update symlink for the latest release
if sftp.lexists(symlink_path):
print("Removing {}...".format(symlink_path))
sftp.remove(symlink_path)
if f != wheel_name():
# "latest" isn't a proper wheel version, so this could not be installed.
# https://github.com/mitmproxy/mitmproxy/issues/1065
sftp.symlink("v{}/{}".format(get_version(), remote_filename), symlink_path)
2015-11-29 01:46:08 +00:00
if __name__ == "__main__":
cli()