add release

This commit is contained in:
Maximilian Hils 2016-02-15 14:58:53 +01:00
commit dd2c500294
6 changed files with 625 additions and 0 deletions

6
release/.env Normal file
View File

@ -0,0 +1,6 @@
DIR="$( dirname "${BASH_SOURCE[0]}" )"
ACTIVATE_DIR="$(if [ -f "$DIR/../venv.mitmproxy/bin/activate" ]; then echo 'bin'; else echo 'Scripts'; fi;)"
if [ -z "$VIRTUAL_ENV" ] && [ -f "$DIR/../venv.mitmproxy/$ACTIVATE_DIR/activate" ]; then
echo "Activating mitmproxy virtualenv..."
source "$DIR/../venv.mitmproxy/$ACTIVATE_DIR/activate"
fi

9
release/.gitignore vendored Normal file
View File

@ -0,0 +1,9 @@
.DS_Store
MANIFEST
*.py[cdo]
*.swp
*.swo
/build
/dist
/mitmproxy_rtool.egg-info

83
release/README.mkd Normal file
View File

@ -0,0 +1,83 @@
General build and release utilities for the mitmproxy, netlib and pathod
projects. These tools assume a directory structure with all repositories at the
same level, for example:
/src
/mitmproxy
/netlib
/pathod
/release
# Release policies
- By default, every release is a new minor (`0.x`) release and it will be
pushed for all three projects.
- Only if an emergency bugfix is needed, we push a new `0.x.y` bugfix release
for a single project. This matches with what we do in `setup.py`:
"netlib>=%s, <%s" % (version.MINORVERSION, version.NEXT_MINORVERSION)
# Release Checklist
## Check out release versions
- Check out the versions of pathod, netlib and mitmproxy due to be released
- Verify that repositories are in a clean state:
`./build git status`
- Ensure that the website style assets have been compiled for production, and
synced to the docs.
- Render the docs, update CONTRIBUTORS file:
./build docs contributors
## Test
- Test the source distributions:
./build test
This does the following:
- creates a venv in release/venv
- creates source distributions in release/release
- installs the source distributions in the venv
- and runs all installed tools
## Release
- Make a release commit for all projects, tag and push it:
./build git commit -am "Release v0.13"
./build git tag v0.13
./build git push --tags
- Build the OSX binaries
- Follow instructions in osx-binaries
- Move to download dir:
mv ./tmp/osx-mitmproxy-VERSION.tar.gz ~/mitmproxy/www.mitmproxy.org/src/download
- Move all source distributions from `./dist` to the server:
mv ./dist/* ~/mitmproxy/www.mitmproxy.org/src/download
- Upload distributions in `./dist` to PyPI:
./build upload
You can test with [testpypi.python.org](https://testpypi.python.org/pypi) by passing `--repository test`.
([more info](https://tom-christie.github.io/articles/pypi/))
- Now bump the version number to be ready for the next cycle
`./build set-version 0.13`

68
release/rtool.pem Normal file
View File

@ -0,0 +1,68 @@
-----BEGIN RSA PRIVATE KEY-----
Proc-Type: 4,ENCRYPTED
DEK-Info: AES-128-CBC,7A4E07094E1AC0B74BB3D172A73A62DB
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
-----END RSA PRIVATE KEY-----
-----BEGIN PUBLIC KEY-----
MIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEAs0xeGx+bc+NCLGRJDr6y
qv825685l1Tb6lhLvYVYUFJfcxL1V79ML13CO09TPxGTbmKw7iI33ef9N2mUHHVe
24EjcSnYZ2Zx5zYIY4ejnDKCzM0md9XA3RpfTI9F3zGsJPt72veyHy6alCbF4NgX
FgAvp2UJmWwTLnJj3oIEwfGJeEcQRKjCFjSJlHGxbB6L0MSCgRtDxwlBfinzkriZ
0C8YyVv6u+DidHtobbZvwk/4uV3+KhoDfkpT2VE57a2KXr4c9Fekzr6szduFMkcI
KPqKouOOnaiNjOoKofbZ5I6cB6qoMkh+ebJvfPuEYBwhHmDDhpdjdk5v3LkeD5kt
0GtOaF5A3LbdZhgsC+1v1dHrL6Vndrb9KhO8ATHvlzJOonV73AoJFygp6upCvIt+
tPUuy3VE0kjc2mJDDJr5V28pSBJ5r9uz1rXZWP+9aduR3M+RUGzHS7cUdsl+lHyQ
TFQAQIDhWXyokMGqVZBm9xJ2KcDiQAEr5+kP0BrhPmBtfHSnLA1tIXlFcOqakvyQ
q1cACMvwbkZpupTqMhCr2S4V9dKxnlO5EIWYJmme9moU/zu1KA0mcwqfnNeDOR2J
6c3/x3E+AqBoFzDQb6LQ2Iw+9bkKWKgDml4paLPu/ot8vA2ZB9/mujFUvp/1MLuD
5I1jQDXFbjAgAnaSyqyW368CAwEAAQ==
-----END PUBLIC KEY-----

439
release/rtool.py Normal file
View File

@ -0,0 +1,439 @@
#!/usr/bin/env python
from __future__ import absolute_import, print_function, division
from os.path import join
import contextlib
import os
import shutil
import subprocess
import re
import shlex
import runpy
import zipfile
import tarfile
import platform
import click
import pysftp
import fnmatch
from six.moves import shlex_quote
# 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"
if platform.system() == "Windows":
def Archive(name):
a = zipfile.ZipFile(name, "w")
a.add = a.write
return a
else:
def Archive(name):
return tarfile.open(name, "w:gz")
RELEASE_DIR = join(os.path.dirname(os.path.realpath(__file__)))
DIST_DIR = join(RELEASE_DIR, "dist")
ROOT_DIR = join(RELEASE_DIR, "..")
BUILD_DIR = join(RELEASE_DIR, "build")
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")
ALL_PROJECTS = {
"netlib": {
"tools": [],
"vfile": join(ROOT_DIR, "netlib/netlib/version.py"),
"dir": join(ROOT_DIR, "netlib"),
"python_version": "py2.py3" # this is the format in wheel filenames
},
"pathod": {
"tools": ["pathod", "pathoc"],
"vfile": join(ROOT_DIR, "pathod/libpathod/version.py"),
"dir": join(ROOT_DIR, "pathod"),
"python_version": "py2"
},
"mitmproxy": {
"tools": ["mitmproxy", "mitmdump", "mitmweb"],
"vfile": join(ROOT_DIR, "mitmproxy/libmproxy/version.py"),
"dir": join(ROOT_DIR, "mitmproxy"),
"python_version": "py2"
}
}
if platform.system() == "Windows":
ALL_PROJECTS["mitmproxy"]["tools"].remove("mitmproxy")
projects = {}
def get_version(project):
return runpy.run_path(projects[project]["vfile"])["VERSION"]
def get_snapshot_version(project):
last_tag, tag_dist, commit = subprocess.check_output(
["git", "describe", "--tags", "--long"],
cwd=projects[project]["dir"]
).strip().rsplit("-", 2)
tag_dist = int(tag_dist)
if tag_dist == 0:
return get_version(project)
else:
return "{version}dev{tag_dist:04}-{commit}".format(
version=get_version(project), # this should already be the next version
tag_dist=tag_dist,
commit=commit
)
def archive_name(project):
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=project,
version=get_version(project),
platform=platform_tag,
ext=ext
)
def sdist_name(project):
return "{project}-{version}.tar.gz".format(
project=project,
version=get_version(project)
)
def wheel_name(project):
return "{project}-{version}-{py_version}-none-any.whl".format(
project=project,
version=get_version(project),
py_version=projects[project]["python_version"]
)
@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.
"""
pythonpath = os.environ.get("PYTHONPATH", "")
os.environ["PYTHONPATH"] = ""
yield
os.environ["PYTHONPATH"] = pythonpath
@contextlib.contextmanager
def chdir(path):
old_dir = os.getcwd()
os.chdir(path)
yield
os.chdir(old_dir)
@click.group(chain=True)
@click.option(
'--project', '-p',
multiple=True, type=click.Choice(ALL_PROJECTS.keys()), default=ALL_PROJECTS.keys()
)
def cli(project):
"""
mitmproxy build tool
"""
for name in project:
projects[name] = ALL_PROJECTS[name]
@cli.command("contributors")
def contributors():
"""
Update CONTRIBUTORS.md
"""
for project, conf in projects.items():
with chdir(conf["dir"]):
print("Updating %s/CONTRIBUTORS..." % project)
contributors_data = subprocess.check_output(
shlex.split("git shortlog -n -s")
)
with open("CONTRIBUTORS", "w") as f:
f.write(contributors_data)
@cli.command("set-version")
@click.argument('version')
def set_version(version):
"""
Update version information
"""
print("Update versions...")
version = ", ".join(version.split("."))
for p, conf in projects.items():
print("Update %s..." % os.path.normpath(conf["vfile"]))
with open(conf["vfile"], "rb") as f:
content = f.read()
new_content = re.sub(
r"IVERSION\s*=\s*\([\d,\s]+\)", "IVERSION = (%s)" % version,
content
)
with open(conf["vfile"], "wb") as f:
f.write(new_content)
def _git(project, args):
print("%s> %s..." % (project, " ".join(shlex_quote(a) for a in args)))
subprocess.check_call(
["git"] + list(args),
cwd=projects[project]["dir"]
)
@cli.command("git")
@click.argument('args', nargs=-1, required=True)
def git(args):
"""
Run a git command on every project
"""
for project, conf in projects.items():
_git(project, args)
print("")
@cli.command("sdist")
def sdist():
"""
Build a source distribution
"""
with empty_pythonpath():
print("Building release...")
if os.path.exists(DIST_DIR):
shutil.rmtree(DIST_DIR)
for project, conf in projects.items():
print("Creating %s source distribution..." % project)
subprocess.check_call(
[
"python", "./setup.py", "-q",
"sdist", "--dist-dir", DIST_DIR, "--formats=gztar",
"bdist_wheel", "--dist-dir", DIST_DIR,
],
cwd=conf["dir"]
)
print("Creating virtualenv for test install...")
if os.path.exists(VENV_DIR):
shutil.rmtree(VENV_DIR)
subprocess.check_call(["virtualenv", "-q", VENV_DIR])
with chdir(DIST_DIR):
for project, conf in projects.items():
print("Installing %s..." % project)
subprocess.check_call([VENV_PIP, "install", "-q", sdist_name(project)])
print("Running binaries...")
for project, conf in projects.items():
for tool in conf["tools"]:
tool = join(VENV_DIR, VENV_BIN, tool)
print("> %s --version" % tool)
print(subprocess.check_output([tool, "--version"]))
print("Virtualenv available for further testing:")
print("source %s" % os.path.normpath(join(VENV_DIR, VENV_BIN, "activate")))
@cli.command("bdist")
@click.option("--use-existing-sdist/--no-use-existing-sdist", default=False)
@click.argument("pyinstaller_version", envvar="PYINSTALLER_VERSION", default="PyInstaller~=3.1.1")
@click.pass_context
def bdist(ctx, use_existing_sdist, pyinstaller_version):
"""
Build a binary distribution
"""
if os.path.exists(PYINSTALLER_TEMP):
shutil.rmtree(PYINSTALLER_TEMP)
if os.path.exists(PYINSTALLER_DIST):
shutil.rmtree(PYINSTALLER_DIST)
if not use_existing_sdist:
ctx.invoke(sdist)
print("Installing PyInstaller...")
subprocess.check_call([VENV_PIP, "install", "-q", pyinstaller_version])
for p, conf in projects.items():
if conf["tools"]:
with Archive(join(DIST_DIR, archive_name(p))) as archive:
for tool in conf["tools"]:
spec = join(conf["dir"], "release", "%s.spec" % tool)
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",
spec
]
)
# Test if it works at all O:-)
executable = join(PYINSTALLER_DIST, tool)
if platform.system() == "Windows":
executable += ".exe"
print("> %s --version" % executable)
subprocess.check_call([executable, "--version"])
archive.add(executable, os.path.basename(executable))
print("Packed {}.".format(archive_name(p)))
@cli.command("upload-release")
@click.option('--username', prompt=True)
@click.password_option(confirmation_prompt=False)
@click.option('--repository', default="pypi")
@click.option("--sdist/--no-sdist", default=True)
@click.option("--wheel/--no-wheel", default=True)
def upload_release(username, password, repository, sdist, wheel):
"""
Upload source distributions to PyPI
"""
for project in projects.keys():
files = []
if sdist:
files.append(sdist_name(project))
if wheel:
files.append(wheel_name(project))
for f in files:
print("Uploading {} to {}...".format(f, repository))
subprocess.check_call([
"twine",
"upload",
"-u", username,
"-p", password,
"-r", repository,
join(DIST_DIR, f)
])
@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)
@click.option("--sdist/--no-sdist", default=False)
@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, sdist, wheel, bdist):
"""
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:
for project, conf in projects.items():
dir_name = "snapshots/v{}".format(get_version(project))
sftp.makedirs(dir_name)
with sftp.cd(dir_name):
files = []
if sdist:
files.append(sdist_name(project))
if wheel:
files.append(wheel_name(project))
if bdist and conf["tools"]:
files.append(archive_name(project))
for f in files:
local_path = join(DIST_DIR, f)
remote_filename = f.replace(get_version(project), get_snapshot_version(project))
symlink_path = "../{}".format(f.replace(get_version(project), "latest"))
old_version = f.replace(get_version(project), "*")
for f in sftp.listdir():
if fnmatch.fnmatch(f, old_version):
print("Removing {}...".format(f))
sftp.remove(f)
print("Uploading {} as {}...".format(f, remote_filename))
with click.progressbar(length=os.stat(local_path).st_size) as bar:
sftp.put(
local_path,
"." + remote_filename,
callback=lambda done, total: bar.update(done - bar.pos)
)
# We hide the file during upload.
sftp.rename("." + remote_filename, remote_filename)
# add symlink
if sftp.lexists(symlink_path):
print("Removing {}...".format(symlink_path))
sftp.remove(symlink_path)
sftp.symlink("v{}/{}".format(get_version(project), remote_filename), symlink_path)
@cli.command("wizard")
@click.option('--next-version', prompt=True)
@click.option('--username', prompt="PyPI Username")
@click.password_option(confirmation_prompt=False, prompt="PyPI Password")
@click.option('--repository', default="pypi")
@click.pass_context
def wizard(ctx, next_version, username, password, repository):
"""
Interactive Release Wizard
"""
for project, conf in projects.items():
is_dirty = subprocess.check_output(["git", "status", "--porcelain"], cwd=conf["dir"])
if is_dirty:
raise RuntimeError("%s repository is not clean." % project)
# update contributors file
ctx.invoke(contributors)
# Build test release
ctx.invoke(bdist)
try:
click.confirm("Please test the release now. Is it ok?", abort=True)
except click.Abort:
# undo changes
ctx.invoke(git, args=["checkout", "CONTRIBUTORS"])
raise
# Everything ok - let's ship it!
for p in projects.keys():
_git(p, ["tag", "v" + get_version(p)])
ctx.invoke(git, args=["push", "--tags"])
ctx.invoke(
upload_release,
username=username, password=password, repository=repository
)
click.confirm("Now please wait until CI has built binaries. Finished?")
# version bump commit
ctx.invoke(set_version, version=next_version)
ctx.invoke(
git, args=["commit", "-a", "-m", "bump version"]
)
ctx.invoke(git, args=["push"])
click.echo("All done!")
if __name__ == "__main__":
cli()

20
release/setup.py Normal file
View File

@ -0,0 +1,20 @@
from setuptools import setup
setup(
name='mitmproxy-rtool',
version="1.0",
py_modules=["rtool"],
install_requires=[
"click>=6.2, <7.0",
"twine>=1.6.5, <1.7",
"virtualenv>=14.0.5, <14.1",
"wheel>=0.29.0, <0.30",
"six>=1.10.0, <1.11",
"pysftp>=0.2.8, <0.3",
],
entry_points={
"console_scripts": [
"rtool=rtool:cli",
],
},
)