create-github-release.py (3608B)
1 #!/usr/bin/env python 2 from __future__ import unicode_literals 3 4 import io 5 import json 6 import mimetypes 7 import netrc 8 import optparse 9 import os 10 import re 11 import sys 12 13 sys.path.insert(0, os.path.dirname(os.path.dirname(os.path.abspath(__file__)))) 14 15 from youtube_dl.compat import ( 16 compat_basestring, 17 compat_getpass, 18 compat_print, 19 compat_urllib_request, 20 ) 21 from youtube_dl.utils import ( 22 make_HTTPS_handler, 23 sanitized_Request, 24 ) 25 26 27 class GitHubReleaser(object): 28 _API_URL = 'https://api.github.com/repos/ytdl-org/youtube-dl/releases' 29 _UPLOADS_URL = 'https://uploads.github.com/repos/ytdl-org/youtube-dl/releases/%s/assets?name=%s' 30 _NETRC_MACHINE = 'github.com' 31 32 def __init__(self, debuglevel=0): 33 self._init_github_account() 34 https_handler = make_HTTPS_handler({}, debuglevel=debuglevel) 35 self._opener = compat_urllib_request.build_opener(https_handler) 36 37 def _init_github_account(self): 38 try: 39 info = netrc.netrc().authenticators(self._NETRC_MACHINE) 40 if info is not None: 41 self._token = info[2] 42 compat_print('Using GitHub credentials found in .netrc...') 43 return 44 else: 45 compat_print('No GitHub credentials found in .netrc') 46 except (IOError, netrc.NetrcParseError): 47 compat_print('Unable to parse .netrc') 48 self._token = compat_getpass( 49 'Type your GitHub PAT (personal access token) and press [Return]: ') 50 51 def _call(self, req): 52 if isinstance(req, compat_basestring): 53 req = sanitized_Request(req) 54 req.add_header('Authorization', 'token %s' % self._token) 55 response = self._opener.open(req).read().decode('utf-8') 56 return json.loads(response) 57 58 def list_releases(self): 59 return self._call(self._API_URL) 60 61 def create_release(self, tag_name, name=None, body='', draft=False, prerelease=False): 62 data = { 63 'tag_name': tag_name, 64 'target_commitish': 'master', 65 'name': name, 66 'body': body, 67 'draft': draft, 68 'prerelease': prerelease, 69 } 70 req = sanitized_Request(self._API_URL, json.dumps(data).encode('utf-8')) 71 return self._call(req) 72 73 def create_asset(self, release_id, asset): 74 asset_name = os.path.basename(asset) 75 url = self._UPLOADS_URL % (release_id, asset_name) 76 # Our files are small enough to be loaded directly into memory. 77 data = open(asset, 'rb').read() 78 req = sanitized_Request(url, data) 79 mime_type, _ = mimetypes.guess_type(asset_name) 80 req.add_header('Content-Type', mime_type or 'application/octet-stream') 81 return self._call(req) 82 83 84 def main(): 85 parser = optparse.OptionParser(usage='%prog CHANGELOG VERSION BUILDPATH') 86 options, args = parser.parse_args() 87 if len(args) != 3: 88 parser.error('Expected a version and a build directory') 89 90 changelog_file, version, build_path = args 91 92 with io.open(changelog_file, encoding='utf-8') as inf: 93 changelog = inf.read() 94 95 mobj = re.search(r'(?s)version %s\n{2}(.+?)\n{3}' % version, changelog) 96 body = mobj.group(1) if mobj else '' 97 98 releaser = GitHubReleaser() 99 100 new_release = releaser.create_release( 101 version, name='youtube-dl %s' % version, body=body) 102 release_id = new_release['id'] 103 104 for asset in os.listdir(build_path): 105 compat_print('Uploading %s...' % asset) 106 releaser.create_asset(release_id, os.path.join(build_path, asset)) 107 108 109 if __name__ == '__main__': 110 main()