You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

120 lines
4.0 KiB

  1. #!/usr/bin/env python
  2. from __future__ import unicode_literals
  3. import base64
  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. sys.path.insert(0, os.path.dirname(os.path.dirname(os.path.abspath(__file__))))
  13. from youtube_dl.compat import (
  14. compat_basestring,
  15. compat_input,
  16. compat_getpass,
  17. compat_print,
  18. compat_urllib_request,
  19. )
  20. from youtube_dl.utils import (
  21. make_HTTPS_handler,
  22. sanitized_Request,
  23. )
  24. class GitHubReleaser(object):
  25. _API_URL = 'https://api.github.com/repos/rg3/youtube-dl/releases'
  26. _UPLOADS_URL = 'https://uploads.github.com/repos/rg3/youtube-dl/releases/%s/assets?name=%s'
  27. _NETRC_MACHINE = 'github.com'
  28. def __init__(self, debuglevel=0):
  29. self._init_github_account()
  30. https_handler = make_HTTPS_handler({}, debuglevel=debuglevel)
  31. self._opener = compat_urllib_request.build_opener(https_handler)
  32. def _init_github_account(self):
  33. try:
  34. info = netrc.netrc().authenticators(self._NETRC_MACHINE)
  35. if info is not None:
  36. self._username = info[0]
  37. self._password = info[2]
  38. compat_print('Using GitHub credentials found in .netrc...')
  39. return
  40. else:
  41. compat_print('No GitHub credentials found in .netrc')
  42. except (IOError, netrc.NetrcParseError):
  43. compat_print('Unable to parse .netrc')
  44. self._username = compat_input(
  45. 'Type your GitHub username or email address and press [Return]: ')
  46. self._password = compat_getpass(
  47. 'Type your GitHub password and press [Return]: ')
  48. def _call(self, req):
  49. if isinstance(req, compat_basestring):
  50. req = sanitized_Request(req)
  51. # Authorizing manually since GitHub does not response with 401 with
  52. # WWW-Authenticate header set (see
  53. # https://developer.github.com/v3/#basic-authentication)
  54. b64 = base64.b64encode(
  55. ('%s:%s' % (self._username, self._password)).encode('utf-8')).decode('ascii')
  56. req.add_header('Authorization', 'Basic %s' % b64)
  57. response = self._opener.open(req).read().decode('utf-8')
  58. return json.loads(response)
  59. def list_releases(self):
  60. return self._call(self._API_URL)
  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. def create_asset(self, release_id, asset):
  73. asset_name = os.path.basename(asset)
  74. url = self._UPLOADS_URL % (release_id, asset_name)
  75. # Our files are small enough to be loaded directly into memory.
  76. data = open(asset, 'rb').read()
  77. req = sanitized_Request(url, data)
  78. mime_type, _ = mimetypes.guess_type(asset_name)
  79. req.add_header('Content-Type', mime_type or 'application/octet-stream')
  80. return self._call(req)
  81. def main():
  82. parser = optparse.OptionParser(usage='%prog CHANGELOG VERSION BUILDPATH')
  83. options, args = parser.parse_args()
  84. if len(args) != 3:
  85. parser.error('Expected a version and a build directory')
  86. changelog_file, version, build_path = args
  87. with io.open(changelog_file, encoding='utf-8') as inf:
  88. changelog = inf.read()
  89. mobj = re.search(r'(?s)version %s\n{2}(.+?)\n{3}' % version, changelog)
  90. body = mobj.group(1) if mobj else ''
  91. releaser = GitHubReleaser()
  92. new_release = releaser.create_release(
  93. version, name='youtube-dl %s' % version, body=body)
  94. release_id = new_release['id']
  95. for asset in os.listdir(build_path):
  96. compat_print('Uploading %s...' % asset)
  97. releaser.create_asset(release_id, os.path.join(build_path, asset))
  98. if __name__ == '__main__':
  99. main()