diff --git a/appveyor.yml b/appveyor.yml index 3c63834d3..ec8877dac 100644 --- a/appveyor.yml +++ b/appveyor.yml @@ -4,6 +4,10 @@ environment: skip_branch_with_pr: true +environment: + GH_TOKEN: + secure: LiI5jyuHUw6XbH4kC3gP1HX4P/v4rwD/gCNtaFhQu2AvJz1/1wALkp5ECnIxRySN + clone_folder: C:\projects\lbry-electron # Install scripts. (runs after repo cloning) @@ -37,21 +41,11 @@ build_script: - node_modules\.bin\build -p never # for debugging, see what was built - dir dist + - python release_on_tag.py test: off + artifacts: - path: dist\*.exe name: LBRY - -deploy: - - provider: GitHub - release: $(APPVEYOR_REPO_TAG_NAME) - description: 'Release' - auth_token: - secure: LiI5jyuHUw6XbH4kC3gP1HX4P/v4rwD/gCNtaFhQu2AvJz1/1wALkp5ECnIxRySN - artifact: LBRY - draft: false - prerelease: true - on: - appveyor_repo_tag: true # deploy on tag push only diff --git a/build.sh b/build.sh index e6435b27a..2113aa673 100755 --- a/build.sh +++ b/build.sh @@ -83,6 +83,6 @@ if [ "$FULL_BUILD" == "true" ]; then # it to reliably work and it also seemed difficult to configure. Not proud of # this, but it seemed better to write my own. pip install PyGithub uritemplate - python release-on-tag.py + python release_on_tag.py deactivate fi diff --git a/release-on-tag.py b/release-on-tag.py deleted file mode 100644 index 6d913a653..000000000 --- a/release-on-tag.py +++ /dev/null @@ -1,161 +0,0 @@ -import argparse -import glob -import json -import logging -import os -import platform -import re -import subprocess -import sys -import zipfile - -import github -import requests -import uritemplate - -from lbrynet.core import log_support - - -def main(args=None): - parser = argparse.ArgumentParser() - parser.add_argument('--file', help='artifact to publish') - parser.add_argument('--label', help='text to append to `file`') - parser.add_argument('--zip', action='store_true') - parser.add_argument( - '--force', action='store_true', - help='ignores whether the repo is currently tagged, publishes a draft release') - args = parser.parse_args(args) - - gh_token = os.environ['GH_TOKEN'] - auth = github.Github(gh_token) - current_repo = auth.get_repo(current_repo_name()) - - if args.file: - artifact = args.file - else: - artifact = get_artifact() - - current_tag = None - if not args.force: - try: - current_tag = subprocess.check_output( - ['git', 'describe', '--exact-match', 'HEAD']).strip() - except subprocess.CalledProcessError: - log.info('Stopping as we are not currently on a tag') - return - if not check_repo_has_tag(current_repo, current_tag): - log.info('Tag %s is not in repo %s', current_tag, current_repo) - # TODO: maybe this should be an error - return - - release = get_release(current_repo, current_tag, args.force) - asset_to_upload = get_asset(artifact, args.label, args.zip) - upload_asset(release, asset_to_upload, gh_token) - - -def current_repo_name(): - pattern = 'github.com[:/](.*)\.git' - remote = subprocess.check_output('git remote -v'.split()) - m = re.search(pattern, remote) - if not m: - raise Exception('Unable to parse repo name from remote: {}'.format(remote)) - return m.group(1) - - -def check_repo_has_tag(repo, target_tag): - tags = repo.get_tags().get_page(0) - for tag in tags: - if tag.name == target_tag: - return True - return False - - -def get_release(current_repo, current_tag=None, draft=False): - assert current_tag or draft, 'either current_tag or draft must be set' - need_new_release = False - if not draft and current_tag: - try: - release = current_repo.get_release(current_tag) - log.info('Using an existing release for %s', current_tag) - except github.UnknownObjectException: - need_new_release = True - if draft or need_new_release: - log.info('Creating a new release for %s:%s', current_repo, current_tag) - tag = current_tag or 'draft' - release_name = current_tag or 'draft' - msg = 'Release' # TODO: parse changelogs to get a better message - try: - # we have a race condition where its possible that between checking - # for the release and now, another build agent has come along and already - # created a release - release = current_repo.create_git_release(tag, release_name, msg, draft) - except github.GithubException: - log.info('Failed to create a release, maybe somebody already has', exc_info=True) - release = current_repo.get_release(current_tag) - return release - - -def get_artifact(): - system = platform.system() - if system == 'Darwin': - return glob.glob('dist/mac/LBRY*.dmg')[0] - elif system == 'Linux': - return glob.glob('dist/LBRY*.deb')[0] - else: - raise Exception("I don't know about any artifact on {}".format(system)) - - -def get_asset(filename, label=None, use_zip=False): - if label: - label = '-{}'.format(label) - else: - label = '' - base, ext = os.path.splitext(filename) - if use_zip: - # TODO: probably want to clean this up - zipfilename = '{}{}.zip'.format(base, label) - with zipfile.ZipFile(zipfilename, 'w') as myzip: - myzip.write(filename) - asset_to_uplaod = zipfilename - else: - asset_to_upload = '{}{}{}'.format(base, label, ext) - return asset_to_upload - - -def upload_asset(release, asset_to_upload, token): - basename = os.path.basename(asset_to_upload) - - if is_asset_already_uploaded(release, basename): - return - - upload_uri = uritemplate.expand( - release.upload_url, {'name': basename}) - # using requests.post fails miserably with SSL EPIPE errors. I spent - # half a day trying to debug before deciding to switch to curl. - cmd = [ - 'curl', '-sS', '-X', 'POST', '-u', ':{}'.format(os.environ['GH_TOKEN']), - '--header', 'Content-Type:application/zip', - '--data-binary', '@{}'.format(asset_to_upload), upload_uri - ] - raw_output = subprocess.check_output(cmd, stderr=subprocess.STDOUT) - output = json.loads(raw_output) - if 'errors' in output: - raise Exception(output) - else: - log.info('Successfully uploaded to %s', output['browser_download_url']) - - -def is_asset_already_uploaded(release, basename): - for asset in release.raw_data['assets']: - if asset['name'] == basename: - log.info('File %s has already been uploaded to %s', basename, release.tag_name) - return True - return False - - -if __name__ == '__main__': - log = logging.getLogger('release-on-tag') - log_support.configure_console(level='DEBUG') - sys.exit(main()) -else: - log = logging.getLogger(__name__) diff --git a/release_on_tag.py b/release_on_tag.py new file mode 100644 index 000000000..8842b5ed5 --- /dev/null +++ b/release_on_tag.py @@ -0,0 +1,127 @@ +import argparse +import glob +import json +import logging +import os +import platform +import re +import subprocess +import sys +import zipfile + +import github +import requests +import uritemplate + +from lbrynet.core import log_support + + +def main(args=None): + gh_token = os.environ['GH_TOKEN'] + auth = github.Github(gh_token) + app_repo = auth.get_repo('lbryio/lbry-app') + # TODO: switch lbryio/lbrynet-daemon to lbryio/lbry + daemon_repo = auth.get_repo('lbryio/lbrynet-daemon') + + artifact = get_artifact() + + current_tag = None + try: + current_tag = subprocess.check_output( + ['git', 'describe', '--exact-match', 'HEAD']).strip() + except subprocess.CalledProcessError: + log.info('Stopping as we are not currently on a tag') + return + if not check_repo_has_tag(app_repo, current_tag): + log.info('Tag %s is not in repo %s', current_tag, app_repo) + # TODO: maybe this should be an error + return + + release = get_release(app_repo, current_tag) + upload_asset(release, artifact, gh_token) + + release = get_release(daemon_repo, current_tag) + artifact = os.path.join('app', 'dist', 'lbrynet-daemon') + if platform.system() == 'Windows': + artifact += '.exe' + asset_to_upload = get_asset(artifact, get_system_label()) + upload_asset(release, asset_to_upload, gh_token) + + +def check_repo_has_tag(repo, target_tag): + tags = repo.get_tags().get_page(0) + for tag in tags: + if tag.name == target_tag: + return True + return False + + +def get_release(current_repo, current_tag): + return current_repo.get_release(current_tag) + + +def get_artifact(): + system = platform.system() + if system == 'Darwin': + return glob.glob('dist/mac/LBRY*.dmg')[0] + elif system == 'Linux': + return glob.glob('dist/LBRY*.deb')[0] + elif system == 'Windows': + return glob.glob('dist/LBRY*.exe')[0] + else: + raise Exception("I don't know about any artifact on {}".format(system)) + + +def get_system_label(): + system = platform.system() + if system == 'Darwin': + return 'macOS' + else: + return system + + +def get_asset(filename, label): + label = '-{}'.format(label) + base, ext = os.path.splitext(filename) + # TODO: probably want to clean this up + zipfilename = '{}{}.zip'.format(base, label) + with zipfile.ZipFile(zipfilename, 'w') as myzip: + myzip.write(filename) + return zipfilename + + +def upload_asset(release, asset_to_upload, token): + basename = os.path.basename(asset_to_upload) + if is_asset_already_uploaded(release, basename): + return + upload_uri = uritemplate.expand( + release.upload_url, {'name': basename}) + # using requests.post fails miserably with SSL EPIPE errors. I spent + # half a day trying to debug before deciding to switch to curl. + cmd = [ + 'curl', '-sS', '-X', 'POST', '-u', ':{}'.format(os.environ['GH_TOKEN']), + '--header', 'Content-Type:application/zip', + '--data-binary', '@{}'.format(asset_to_upload), upload_uri + ] + raw_output = subprocess.check_output(cmd, stderr=subprocess.STDOUT) + output = json.loads(raw_output) + if 'errors' in output: + raise Exception(output) + else: + log.info('Successfully uploaded to %s', output['browser_download_url']) + + +def is_asset_already_uploaded(release, basename): + for asset in release.raw_data['assets']: + if asset['name'] == basename: + log.info('File %s has already been uploaded to %s', basename, release.tag_name) + return True + return False + + +if __name__ == '__main__': + log = logging.getLogger('release-on-tag') + log_support.configure_console(level='DEBUG') + sys.exit(main()) +else: + log = logging.getLogger(__name__)