diff --git a/.travis.yml b/.travis.yml index a317decec..9e33cadc2 100644 --- a/.travis.yml +++ b/.travis.yml @@ -54,6 +54,13 @@ jobs: - python scripts/set_build.py - docker run -v "$(pwd):/src/lbry" lbry/pyinstaller34_32bits:py371 lbry/scripts/wine_build.sh - sudo zip -j dist/lbrynet-windows.zip dist/lbrynet.exe + deploy: + provider: releases + api_key: $GITHUB_OAUTH_TOKEN + file: dist/lbrynet-windows.zip + skip_cleanup: true + overwrite: true + draft: true addons: artifacts: working_dir: dist @@ -64,6 +71,7 @@ jobs: - &build name: "Linux" + env: OS=linux install: - pip3 install pyinstaller - pip3 install git+https://github.com/lbryio/torba.git @@ -74,7 +82,13 @@ jobs: - chmod +x dist/lbrynet - zip -j dist/lbrynet-${OS}.zip dist/lbrynet - ./dist/lbrynet --version - env: OS=linux + deploy: + provider: releases + api_key: $GITHUB_OAUTH_TOKEN + file: dist/lbrynet-${OS}.zip + skip_cleanup: true + overwrite: true + draft: true addons: artifacts: working_dir: dist diff --git a/scripts/release.py b/scripts/release.py new file mode 100644 index 000000000..e9d20201b --- /dev/null +++ b/scripts/release.py @@ -0,0 +1,165 @@ +import os +import re +import io +import sys +import json +import argparse +from datetime import date +from getpass import getpass + +try: + import github3 +except ImportError: + print('To run release tool you need to install github3.py:') + print('') + print(' $ pip install github3.py') + print('') + sys.exit(1) + + +AREA_RENAME = { + 'api': 'API' +} + + +def get_github(): + config_path = os.path.expanduser('~/.lbry-release-tool.json') + if os.path.exists(config_path): + with open(config_path, 'r') as config_file: + config = json.load(config_file) + return github3.login(token=config['token']) + print('GitHub Credentials') + username = input('username: ') + password = getpass('password: ') + gh = github3.authorize( + username, password, ['repo'], 'lbry release tool', + two_factor_callback=lambda: input('Enter 2FA: ') + ) + with open(config_path, 'w') as config_file: + json.dump({'token': gh.token}, config_file) + return github3.login(token=gh.token) + + +def get_labels(pr, prefix): + for label in pr.labels: + label_name = label['name'] + if label_name.startswith(f'{prefix}: '): + yield label_name[len(f'{prefix}: '):] + + +def get_label(pr, prefix): + for label in get_labels(pr, prefix): + return label + + +class Version: + + def __init__(self, major=0, minor=0, micro=0, rc=None): + self.major = int(major) + self.minor = int(minor) + self.micro = int(micro) + self.rc = rc + + @classmethod + def from_string(cls, version_string): + (major, minor, micro), rc = version_string.split('.'), None + if 'rc' in micro: + micro, rc = micro.split('rc') + return cls(major, minor, micro, rc) + + @classmethod + def from_content(cls, content): + src = content.decoded.decode('utf-8') + version = re.search('__version__ = "(.*?)"', src).group(1) + return cls.from_string(version) + + def increment(self, part): + cls = self.__class__ + if part == 'major': + return cls(self.major+1) + elif part == 'minor': + return cls(self.major, self.minor+1) + elif part == 'micro': + return cls(self.major, self.minor, self.micro+1) + elif part == 'rc': + if self.rc is None: + return cls(self.major, self.minor, self.micro+1, 1) + else: + return cls(self.major, self.minor, self.micro, self.rc+1) + else: + raise ValueError(f'unknown version part: {part}') + + @property + def tag(self): + return f'v{self}' + + def __str__(self): + version = '.'.join(str(p) for p in [self.major, self.minor, self.micro]) + if self.rc is not None: + version += f'rc{self.rc}' + return version + + +def release(args): + gh = get_github() + repo = gh.repository('lbryio', 'lbry') + version_file = repo.file_contents('lbrynet/__init__.py') + + current_version = Version.from_content(version_file) + print(f'Current Version: {current_version}') + new_version = current_version.increment(args.increment) + print(f' New Version: {new_version}') + + current_release = repo.release_from_tag(current_version.tag) + + areas = {} + for pr in gh.search_issues(f"merged:>={current_release._json_data['created_at']} repo:lbryio/lbry"): + for area_name in get_labels(pr, 'area'): + area = areas.setdefault(area_name, []) + area.append(f' * [{get_label(pr, "type")}] {pr.title} ({pr.html_url})') + + area_names = list(areas.keys()) + area_names.sort() + + body = io.StringIO() + w = lambda s: body.write(s+'\n') + + w(f'## [{new_version}] - {date.today().isoformat()}') + for area in area_names: + prs = areas[area] + area = AREA_RENAME.get(area, area.capitalize()) + w('') + w(f'### {area}') + for pr in prs: + w(pr) + + commit = version_file.update( + new_version.tag, + version_file.decoded.decode('utf-8').replace(str(current_version), str(new_version)).encode() + )['commit'] + + repo.create_tag( + tag=new_version.tag, + message=new_version.tag, + sha=commit.sha, + obj_type='commit', + tagger=commit.committer + ) + + repo.create_release( + new_version.tag, + name=new_version.tag, + body=body.getvalue(), + draft=True, + prerelease=True + ) + + +def main(): + parser = argparse.ArgumentParser() + parser.add_argument("increment", choices=['major', 'minor', 'micro', 'rc']) + release(parser.parse_args()) + + +if __name__ == "__main__": + main()