Last active
March 31, 2018 00:51
-
-
Save pior/710c18cc516bdf646b1f7965634f7272 to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#!/usr/bin/env python3 | |
import argparse | |
import fileinput | |
import re | |
import sys | |
from subprocess import run, PIPE, STDOUT | |
def title(msg): | |
print(f'\n🍄 {msg}\n') | |
def fatal(msg): | |
print(f'\n💥 Fatal: {msg}\n') | |
sys.exit(1) | |
def capture(args): | |
result = run(args, check=True, stdout=PIPE) | |
return result.stdout.strip().decode() | |
def get_git_branch(): | |
return capture(['git', 'rev-parse', '--abbrev-ref', 'HEAD']) | |
def get_git_tags(remote=False): | |
if remote: | |
output = capture(['git', 'ls-remote', '--tags', 'origin']) | |
else: | |
output = capture(['git', 'show-ref', '--tags']) | |
lines = re.findall(r'refs/tags/([^^\n]+)', output) | |
return list(set(lines)) | |
def is_git_clean(): | |
result = run(['git', 'diff-index', '--quiet', 'HEAD', '--'], check=False, stdout=PIPE) | |
return result.returncode == 0 | |
def update_version_setup_py(version): | |
changed = False | |
for line in fileinput.input('setup.py', inplace=True): | |
if re.match(r'VERSION\s*=', line): | |
print(f"VERSION = '{version}' # maintained by release tool") | |
changed = True | |
else: | |
print(line, end='') | |
return changed | |
def is_twine_installed(): | |
result = run(['which', 'twine']) | |
return result.returncode == 0 | |
def release(options): | |
version_string = options.version | |
if version_string.startswith('v'): | |
fatal('A version can\'t begin with a v') | |
version = f'v{version_string}' | |
# Checks | |
if options.upload: | |
if not is_twine_installed(): | |
fatal('twine is not installed') | |
if options.only_on_branch: | |
current_branch = get_git_branch() | |
if current_branch != options.only_on_branch: | |
fatal(f'not on the {options.only_on_branch} branch. ({current_branch})') | |
if not is_git_clean(): | |
fatal('uncommited files') | |
if version in get_git_tags(remote=False): | |
fatal(f'tag already exists locally ({version})') | |
if version in get_git_tags(remote=True): | |
fatal(f'tag already exists remotely ({version})') | |
# Prepare | |
title('Updating setup.py...') | |
changed = update_version_setup_py(version_string) | |
if not changed: | |
fatal('failed to update setup.py') | |
# Build | |
title('Building distribution...') | |
run(['rm', '-rf', 'dist'], check=True) | |
result = run(['python', 'setup.py', 'sdist', 'bdist_wheel'], stdout=PIPE) | |
if result.returncode != 0: | |
fatal('failed to build distribution') | |
# Create | |
title('Create the release commit') | |
run(['git', 'add', 'setup.py'], check=True) | |
run(['git', 'commit', '-m', f'Release {version}'], check=True) | |
title('Creating tag') | |
run(['git', 'tag', '-a', '-m', f'Release tag for {version}', version], check=True) | |
# Post actions | |
if options.push: | |
title('Pushing to git upstream') | |
run(['git', 'push', '--follow-tags'], check=True) | |
else: | |
title('Don\'t forget to push:\n git push --follow-tags\n') | |
if options.upload: | |
title('Uploading to PyPI') | |
run(['twine', 'upload', 'dist/*'], check=True) | |
def main(): | |
parser = argparse.ArgumentParser() | |
parser.add_argument('version', help='Version string without the "v"') | |
parser.add_argument('--only-on-branch', type=str, metavar='BRANCH-NAME') | |
parser.add_argument('--push', default=False, action='store_true') | |
parser.add_argument('--upload', default=False, action='store_true') | |
args = parser.parse_args() | |
release(args) | |
if __name__ == '__main__': | |
main() |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment